--- /dev/null
+' Simple object to handle callbacks
+' Is there a way in BlitzMax to pass pointers to functions??
+
+Type Callback
+ Method callback() Abstract
+End Type
--- /dev/null
+Strict
+'
+' Simple demo program of an astar path finding algorithm
+' The GUI is pretty crappy
+' There are quite a few comments in the areas of importance. Sometimes this makes
+' the code a little harder to read, but this was done for instructional purposes foremost
+
+Import "astar_graph_walker.bmx"
+Import "Callback.bmx"
+
+Global nums:TImage
+
+Local demo:AStarDemo = New AStarDemo
+demo.run()
+End
+
+
+Private
+' Small class that encapsulates a position on the map
+Type MapPos
+ Method isAtPos(otherX, otherY)
+ Return otherX = x And otherY = y
+ End Method
+ Field x,y
+End Type
+
+
+' Class that defines the terrain types that the demo uses
+Type Terrain
+ Method set(weight:Int, filename:String)
+ _weight = weight
+ _image = LoadImage(filename)
+ End Method
+ Field _colour_r,_color_g,_color_b
+ Field _weight:Int
+ Field _image:TImage
+End Type
+
+' Main application class
+Type AStarDemo
+
+' Map stuff
+ Const blockAreaWidth:Int = 600
+ Const blockAreaHeight:Int = 600
+ Const mapHeight:Int = 20
+ Const mapWidth:Int = 20
+ Const blockWidth:Int = 600 / 20
+ Const blockHeight:Int = 600 / 20
+' Block map
+ Field map:Int[mapWidth, mapHeight]
+
+' Node version of map
+ Field nodeMap:AStarNode[mapWidth, mapHeight]
+ Field startPos:MapPos
+ Field endPos:MapPos
+ Field currentMap = 0
+
+' Terrain information
+ Const numTerrainTypes = 4
+ Field terrainFilenames:String[] = [ "road.png", "mountain.png", "water.png", "tree.png" ]
+ Field terrainWeights:Int[] = [ 1, -1, 4, 2 ]
+ Field terrains:Terrain[numTerrainTypes]
+ Field currentTerrainIndex:Int = 1
+ Field terrainLegendBlockX:Int
+ Field terrainLegendBlockY:Int
+
+' Path finding stuff
+' Distance
+ Const numDistanceFunctions = 4
+' Is there a DATA like statement in Blitz?
+ Field distanceNames:String[] = [ "(D) Euclidean Sqr(dx*dx+dy*dy)", ..
+ "(D) Pseudo Euclidean dx*dx+dy*dy", ..
+ "(D) Manhatten dx+dy", ..
+ "(D) Diagonal Shortcut dx>dy ? 1.4*dy + (dx-dy) : 1.4*dx + (dy-dx)" ]
+
+ Field distanceFunction = AStarGraphWalker.distanceEuclidean
+
+' Whether we're allowed to chage the costs with the "Q" and "W" keys
+ Field costChangeAllowed:Int = 0
+' Is the pathfinder allowed to cruise diagonals?
+ Field allowDiagonalPaths:Int = 0
+ Field diagonalsAreLonger:Int = 1
+' Time in millisecs that the last path fund took
+ Field lastRunTime:Int = 0
+ Field path:TList = Null ' The last path that was found
+
+ Field heuristicMultiplier:Float = 1.0
+ Field showProgress:Int = False ' Do we want to show the progress
+
+' UI stuff
+ Field mapButtonBlockX:Int = 0
+ Field mapButtonBlockY:Int = mapHeight + 1
+
+ Field showCosts:Int = 1
+ Field flagRedraw:Int = 1
+ Field textScale:Float = 800.0/1024.0
+ Field message:String = "" ' Any stat messages that are to be displayed
+
+' Input stuff
+' Used to detect mouse movements
+ Field oldBlockX:Int = -1
+ Field oldBlockY:Int = -1
+' Help
+ Field help:String[] = [ ..
+ "A - Allow/Disallow diagonals", ..
+ "Q - Divide terrain costs by 5", ..
+ "W - Multiply terrain costs by 5", ..
+ "D - Cycle distance functions", ..
+ "V - Toggle diagonal multiplier", ..
+ "S - Point mouse and press to set start node", ..
+ "E - Point mouse and press to set end node", ..
+ "P - Run AStar, showing progress", ..
+ "H - Click and drag on 'H' to change heuristic mult.", ..
+ "Click white square to load map of that number", ..
+ "Click red 'S' to save map as map # 'Current Map'", ..
+ "ESC - Quit program", ..
+ "", ..
+ "In progress mode:", ..
+ "Top Number = Cost to get to that node", ..
+ "Mid Number = Heurisitc from node to end", ..
+ "Bottom Number = Cost from start to end", ..
+ " Through that node", ..
+ "Press 'P' to pause", ..
+ "ESC to quit progress mode", ..
+ "", ..
+ "Handy Hints", ..
+ "If DistanceFunc * heuristic Multiplier = ", ..
+ "True distance to goal, fastest path generated", ..
+ "", ..
+ "Heuristic Mult = 0 then optimal path for", ..
+ "chosen Distance Func "..
+ ]
+
+' This is the main entry point for the class
+ Method run()
+
+' Setup the graphics stuff
+ Graphics 1024,768,32
+
+ nums = LoadAnimImage("nums.png",5,8,0,12)
+ Assert nums<> Null,"Can't load number graphic nums.png"
+
+' Initialise dimensions of maps and other useful data
+ initialise()
+
+' Load the first map by default
+ loadMap(currentMap)
+
+' Redraw
+ redraw()
+
+ While Not KeyDown(27)
+
+ Local m:Int = MouseDown(1)
+
+ Local blockX = MouseX() / blockWidth
+ Local blockY = MouseY() / blockHeight
+
+ If m
+ handleMouseDown(blockX, blockY)
+ EndIf
+
+ processStartPlaced(blockX, blockY)
+ processEndPlaced(blockX, blockY)
+ processAllowDiagonalPaths()
+ processShowProgress()
+ processCycleDistanceFunction()
+ processDiagonalsAreLonger()
+ processDecreaseTerrainCost()
+ processIncreaseTerrainCost()
+
+ If flagRedraw
+ oldBlockX = -1 ' Mark for refresh of mouse movement
+ lastRunTime = runAStar()
+ redraw()
+ flagRedraw = False
+ EndIf
+
+ Wend
+ EndGraphics
+ End Method
+
+'-------------------------------------
+' Input handling functions
+'-------------------------------------
+
+ Method processIncreaseTerrainCost()
+ If KeyHit(KEY_W)
+ Local t
+ For t = 0 To numTerrainTypes - 1
+ terrains[t]._weight = terrains[t]._weight * 5
+ Next
+ setRedraw(True)
+ costChangeAllowed = costChangeAllowed + 1
+ EndIf
+ End Method
+
+ Method processDecreaseTerrainCost()
+ If KeyHit(KEY_Q) And costChangeAllowed > 0
+ Local t
+ For t = 0 To numTerrainTypes - 1
+ terrains[t]._weight = terrains[t]._weight / 5
+ Next
+ setRedraw(True)
+ costChangeAllowed = costChangeAllowed - 1
+ EndIf
+ End Method
+
+ Method processDiagonalsAreLonger()
+ If KeyHit(KEY_V)
+ diagonalsAreLonger = 1 - diagonalsAreLonger
+ setRedraw(True)
+ EndIf
+ End Method
+
+' Cycling the distance function to use?
+ Method processCycleDistanceFunction()
+ If KeyHit(KEY_D)
+ distanceFunction = (distanceFunction + 1) Mod numDistanceFunctions
+ setRedraw(True);
+ EndIf
+ End Method
+
+' Clear the last calculated path?
+ Method processClearPath()
+ If KeyHit(KEY_P)
+ path = Null
+ setRedraw(True)
+ EndIf
+ End Method
+
+ Method processShowProgress()
+ If KeyHit(KEY_P)
+ showProgress = True
+ runAStar()
+ showProgress = False
+ EndIf
+ End Method
+
+' Allow diagonal toggled?
+ Method processAllowDiagonalPaths()
+ If KeyHit(KEY_A)
+ allowDiagonalPaths = 1 - allowDiagonalPaths
+ setRedraw(True)
+ EndIf
+ End Method
+
+' Check if they want to move the start node
+ Method processStartPlaced(blockX:Int, blockY:Int)
+ If KeyHit(KEY_S)
+ If blockInMapArea(blockX, blockY)
+' setMapBlock(blockX, blockY,0)
+ startPos.x = blockX
+ startPos.y = blockY
+ setRedraw(True)
+ EndIf
+ EndIf
+ End Method
+
+' Check if they want to move the end node
+ Method processEndPlaced(blockX:Int, blockY:Int)
+ If KeyHit(KEY_E)
+ If blockInMapArea(blockX, blockY)
+' setMapBlock(blockX, blockY,0)
+ endPos.x = blockX
+ endPos.y = blockY
+ setRedraw(True)
+ EndIf
+ EndIf
+ End Method
+
+
+
+' Call this to tell the main engine to redraw
+ Method setRedraw(doRedraw:Int)
+ flagRedraw = doRedraw
+ End Method
+
+' Use this to change a map block while editing, so that redraws are sped up
+ Method setMapBlock(blockX, blockY, block)
+ map[blockX, blockY] = block
+ End Method
+
+' Redraws the map and last found path. You can tell it to draw more than once for double buffer purposes
+ Method redraw(times:Int = 1, flipIt:Int = 1)
+ While times > 0
+ drawMap()
+ drawPath()
+ drawTerrains()
+ drawInfo()
+ drawMapButtons()
+ If flipIt Then Flip
+ times :- 1
+ Wend
+ End Method
+
+ Method saveMap:String(mapNumber:Int)
+ Local stream:TStream = WriteStream("littleendian::map"+mapNumber)
+ If stream = Null
+ Return "Error saving map"
+ EndIf
+ Local x,y;
+ For y = 0 To mapHeight - 1
+ For x = 0 To mapWidth - 1
+ WriteInt(stream,map[x,y])
+ Next
+ Next
+ WriteInt stream, startPos.x
+ WriteInt stream, startPos.y
+ WriteInt stream, endPos.x
+ WriteInt stream, endPos.y
+ CloseStream stream
+ Return "Done"
+ End Method
+
+ Method loadMap(mapNumber:Int)
+ Local stream:TStream = ReadStream("littleendian::map"+mapNumber)
+ If stream = Null
+ Return
+ EndIf
+ Local x,y;
+ For y = 0 To mapHeight - 1
+ For x = 0 To mapWidth - 1
+ map[x,y] = Readint(stream)
+ Next
+ Next
+ startPos.x = Readint(stream)
+ startPos.y = Readint(stream)
+ endPos.x = Readint(stream)
+ endPos.y = Readint(stream)
+ CloseStream stream
+ End Method
+
+
+' Handle mouse click
+ Method handleMouseDown(blockX:Int, blockY:Int)
+
+ If blockX = oldBlockX And blockY = oldBLockY
+ setRedraw(False)
+ Return
+ EndIf
+ oldBlockX = blockX
+ oldBlockY = blockY
+
+' Make sure they can't edit over the start and end positions
+ If startPos.isAtPos(blockX, blockY)
+ setRedraw(False)
+ Return
+ EndIf
+ If endPos.isAtPos(blockX, blockY)
+ setRedraw(False)
+ Return
+ EndIf
+
+' See if they are selecting a new terrain
+ If blockX = terrainLegendBlockX And blockY >= terrainLegendBlockY And blockY < terrainLegendBlockY + numTerrainTypes
+ currentTerrainIndex = blockY - terrainLegendBlockY
+ Return
+ EndIf
+
+ If blockInMapArea(blockX, blockY)
+ map[blockX, blockY] = currentTerrainIndex
+ setRedraw(True)
+ Return
+ EndIf
+
+' See if we have selected a new map
+ If blockX >= mapButtonBlockX And blockY = mapButtonBlockY
+ Local block:Int = blockX - mapButtonBlockX
+ Select block
+ Case 8
+ message = saveMap(currentMap)
+ Case 9
+ handleHeuristic()
+ Default
+ If block < 8
+ currentMap = block
+ loadMap(currentMap)
+ EndIf
+ End Select
+ EndIf
+ setRedraw(True)
+ Return ' Signal that we've moved the mouse and done something
+ End Method
+
+
+ Method handleHeuristic()
+ Local oldX:Int = MouseX()
+ Local oldY:Int = MouseY()
+
+ While MouseDown(1)
+ Local x:Int = MouseX()
+ Local y:Int = MouseY()
+ If x <> oldX
+ heuristicMultiplier = heuristicMultiplier + (x - oldX) / 10.0
+ If heuristicMultiplier < 0 heuristicMultiplier = 0
+ If heuristicMultiplier > 10 heuristicMultiplier = 10
+ runAStar()
+ redraw(2)
+ oldX:Int = x
+ oldY:Int = y
+ EndIf
+ Wend
+ End Method
+
+' Are the block coordinates with in the map area?
+ Method blockInMapArea(blockX:Int, blockY:Int)
+ Return blockX >= 0 And blockY >= 0 And blockX < mapWidth And blockY < mapHeight
+ End Method
+
+
+ Method drawPath()
+ If path <> Null
+ Local a:Object
+ For a:Object = EachIn path
+ Local node:AStarNode = AStarNode(a)
+ SetColor 255,255,0
+ DrawRect node._x * blockWidth, node._y * blockHeight, blockWidth - 1, blockHeight - 1
+ Next
+ EndIf
+ End Method
+
+' Draw the information text in the editor
+ Method drawInfo()
+' Clear
+ Local y = mapButtonBlockY * blockHeight + blockHeight
+ SetColor 0,0,0
+ DrawRect 0, y, blockAreaWidth, blockHeight*5
+
+' Draw the distance function used
+ SetColor 255,255,255
+ DrawText distanceNames[distanceFunction], 0, y
+
+' Whether diagonals are allowed or not
+ Local path:String
+ If allowDiagonalPaths
+ path = "(A) Diagonals allowed"
+ Else
+ path = "(A) Diagonals not allowed"
+ EndIf
+ DrawText path, 0, y + TextHeight(path)
+
+' How long last path walk took
+' Local percentOfFrame:Float = lastRunTime * (60.0/1000.0) * 100.0
+' DrawText "Milli:" + lastRunTime + " % of 60th:" + percentOfFrame, 0, y + FontHeight() * 5
+
+'
+ Local diagonals:String;
+ If diagonalsAreLonger
+ diagonals = "(V) Diagonals are longer than straights"
+ Else
+ diagonals = "(V) Diagonals are same as straights"
+ EndIf
+ DrawText diagonals, 0, y + TextHeight(diagonals) * 2
+
+ DrawText "Heuristic:" + heuristicMultiplier, 0, y + TextHeight(heuristicMultiplier) * 4
+ SetColor 255,0,0
+ DrawText message, 0, y + TextHeight(message) * 5
+ SetColor 255,255,255
+
+' Draw help
+ Local a:String
+ Local count:Int = 0
+ For a:String = EachIn help
+ DrawText a, blockAreaWidth, blockAreaHeight / 4 + count * TextHeight(a)
+ count = count + 1
+ Next
+ End Method
+
+ Method drawMapButtons()
+ Local y = mapButtonBlockY * blockHeight
+ Local x = mapButtonBlockX * blockWidth
+ Local startX = x
+ Local t
+ Local output:String
+ For t = 0 To 9
+ If t < 8
+ SetColor 255,255,255
+ output = t
+ Else If t = 8
+ SetColor 255,0,0
+ output = "S"
+ Else If t = 9
+ SetColor 0,255,0
+ output = "H"
+ EndIf
+ DrawRect x, y, blockWidth - 1, blockHeight
+ SetColor 0,0,0
+ DrawText output, x, y
+ x = x + blockWidth
+ Next
+' Current map
+ y = mapButtonBlockY * blockHeight - blockHeight
+ x = mapButtonBlockX * blockWidth
+ SetColor 0,0,0
+ DrawRect x,y,blockAreaWidth,TextHeight(" ")
+ SetColor 255,255,255
+ DrawText "Current map:" + currentMap, x, y
+
+ End Method
+
+ Method drawTerrains()
+ Local t
+ For t = 0 To numTerrainTypes - 1
+ Local terrain:Terrain = terrains[t]
+ Local x = terrainLegendBlockX * blockWidth
+ Local y = (terrainLegendBlockY + t)* blockHeight
+
+ SetColor 0,0,0
+ DrawRect x, y, blockWidth * 5, blockHeight
+
+ SetColor 255,255,255
+ SetScale blockWidth / 32.0, blockHeight / 32.0
+ DrawImage terrain._image, x, y
+ SetScale 1,1
+
+ If showCosts
+ SetColor 255,255,255
+ DrawText terrain._weight, x + blockWidth , y
+ EndIf
+ Next
+ End Method
+
+ Method drawMap()
+ SetColor 0,0,0
+ DrawRect 0, 0, blockAreaWidth, blockAreaHeight
+ Local x,y;
+ For y = 0 To mapHeight - 1
+ For x = 0 To mapWidth - 1
+ drawMapBlock(x,y)
+ Next
+ Next
+ End Method
+
+ Method drawMapBlock(x:Int, y:Int)
+ SetColor 255,255,255
+ If startPos.isAtPos(x,y)
+ SetColor 255,255,0
+ Else If endPos.isAtPos(x,y)
+ SetColor 255,0,0
+ Else
+' SetColor(terrains[map[x,y]]._colour)
+ EndIf
+ SetScale blockWidth / 32.0, blockHeight / 32.0
+ DrawImage terrains[map[x,y]]._image, x * blockWidth, y * blockHeight
+ SetScale 1,1
+ End Method
+
+ Method drawOnMap(x:Int, y:Int, r,g,b, margin:Int)
+ SetColor r,g,b
+ DrawRect x * blockWidth + margin, y * blockHeight + margin, blockWidth - margin * 2, blockHeight - margin * 2
+ End Method
+
+ Method printOnMap(x:Int, y:Int, text:String, offset:Int)
+ SetColor 255,255,255
+ SetScale textScale,textScale
+ DrawText text, x * blockWidth, y * blockHeight + offset
+ SetScale 1,1
+ End Method
+
+ Method printNums(s:String, x:Int, y:Int, offset:Int)
+ x = x * blockWidth + blockWidth / 2 - (Len(s) * 5)/2
+ y = y * blockHeight + offset
+ SetColor 255,255,255
+ SetMaskColor 255,0,255
+ SetScale 1,1
+ Local t
+ For t = 0 To Len(s) - 1
+ DrawImage nums, x, y, Byte(s[t]) - 46
+ x:+5
+ Next
+ End Method
+
+
+' Initialise the map and edit stuff
+ Method initialise()
+
+' blockWidth = blockAreaWidth / mapWidth
+' blockHeight = blockAreaHeight / mapHeight
+
+' Setup other interactive pieces
+ terrainLegendBlockX = mapWidth + 1
+ terrainLegendBlockY = 0
+
+' map = Array:Int[mapWidth, mapHeight]
+' nodeMap = Array:AStarNode[mapWidth, mapHeight]
+
+' Initialise terrain types
+ Local t
+ For t = 0 To numTerrainTypes - 1
+ Local newTerrain:Terrain = New Terrain
+ newTerrain.set(terrainWeights[t], terrainFilenames[t])
+ terrains[t] = newTerrain
+ Next
+
+ startPos = New MapPos
+ startPos.x = 1
+ startPos.y = 1
+
+ endPos = New MapPos
+ endPos.x = mapWidth - 2
+ endPos.y = mapHeight - 2
+
+' Initialise the map
+ SeedRnd MilliSecs()
+ Local y
+ Local x
+ For y = 0 To mapHeight - 1
+ For x = 0 To mapWidth - 1
+ Local value:Int = 0'Rand(0,numTerrainTypes - 1)
+
+ If y = 0 Or y = mapHeight - 1 Or x = 0 Or x = mapWidth - 1
+ value = 1
+ EndIf
+
+ map[x,y] = value 'Readint(stream)
+ Next
+ Next
+ End Method
+
+' This runs AStar with the current setup
+
+ Method runAStar()
+
+' The first thing you need to do before using the AStarGraphWalker is to create your nodes, and link them up
+' with edges. What I do is first make an array of the nodes, this makes it easy to map nodes to map blocks if
+' I need to and other things.
+' Then I go over the node array and create links to the neighbouring nodes.
+'
+' NOTE: In my implementation you will notice that between two blocks, two edges are created.
+' Block 1 has an edge to block 2 (which is it's neighbour) and viceversa. If you wanted to
+' simplify this, you could, but you'd have to change the datastructure a little. I prefer my way (Except of course
+' that more memory is consumed) because I can have different costs to get from A to B than from B to A, for example
+' if A was at the top of a hill and B at the bottom. In that case, going from A to B is generally easier than B to A
+ Local x:Int
+ Local y:Int
+ For y = 0 To mapHeight - 1
+ For x = 0 To mapWidth - 1
+ nodeMap[x,y] = New AStarNode
+ Local node:AStarNode = nodeMap[x,y]
+ node._x = x ' AStar needs these positions for distance estimation
+ node._y = y
+ Next
+ Next
+' This is just an array of offsets to give us our neighbours
+ Const offsetCount:Int = 8
+ Local xOffset:Int[] = [ 0,1,1,1,0,-1,-1,-1 ]
+ Local yOffset:Int[] = [ -1,-1,0,1,1,1,0,-1 ]
+
+' How we move through the offsets, so we can support "no diagnoal" paths
+ Local offsetStep:Int = 1
+ If Not allowDiagonalPaths
+ offsetStep = 2
+ EndIf
+
+' This loop builds up all the neighbour lists for a node
+ For y = 0 To mapHeight - 1
+ For x = 0 To mapWidth - 1
+ If map[x,y] = 1
+ Continue ' We don't worry about this node if we aren't passable
+ EndIf
+ Local node:AStarNode = nodeMap[x,y]
+' Now look around the map for neighbours and make nodes
+' Joining the current one with a neighbour
+ Local off = 0
+ While off < offsetCount
+ Local neighbourX = x + xOffset[off]
+ Local neighbourY = y + yOffset[off]
+' Check that the neighbour position is within the map bounds and is actually
+' not block 1 which I've designated as a block we can't go through at all so no point
+' making a neighbour of it
+ If map[neighbourX, neighbourY] <> 1 And neighbourX >= 0 And neighbourX < mapWidth And neighbourY >=0 And neighbourY < mapHeight
+ Local value:Int = terrains[map[x,y]]._weight
+ Local neighbourValue:Int = terrains[map[neighbourX,neighbourY]]._weight
+
+' Here is where you calculate the costs of the edge between two nodes. Because our map is square and the neighbour
+' I'm looking at is adjacent to the current node, then I can just take an average of the costs of each block.
+' E.G If the current block is water, and the neighbour is forest, I'd be walking half in water and half in forest
+' This isn't really necessary but I do it anyway
+ Local edgeCost:Float = (value + neighbourValue) / 2.0
+' Now if it's a diagonal we might want to make the cost of this edge higher to signify that
+' travelling diagonally in a suare environment like ours is a longer distance
+ If diagonalsAreLonger And xOffset[off] <> 0 And yOffset[off] <> 0
+ edgeCost = edgeCost * 1.4;
+ EndIf
+ node.addNeighbour(nodeMap[neighbourX, neighbourY], edgeCost)
+ EndIf
+ off = off + offsetStep
+ Wend
+ Next
+ Next
+
+' We have our node, so we can set up our AStarGraphWalker now.
+ Local walker:AStarGraphWalker = New AStarGraphWalker
+
+ walker.setParameters(nodeMap[startPos.x,startPos.y], nodeMap[endPos.x,endPos.y], mapWidth * mapHeight)
+ walker.setDistanceFunction(distanceFunction) ' If not called, default is distanceEuclidean
+ walker.setHeuristicMultiplier(heuristicMultiplier) ' If not called, default to 1
+
+' Set up our callback information. The callback is called after one node has been popped and processed
+ Local callback:WalkerCallback = New WalkerCallback
+ callback._walker = walker
+ callback._application = Self
+ If showProgress
+ walker.setCallback(callback)
+ EndIf
+
+ Local start:Int = MilliSecs()
+ Local res:Int = walker.walk()
+ Local time:Int = MilliSecs() - start
+
+ If res
+ path = walker.getFinalPath()
+ If showProgress
+ drawPath()
+ Flip
+ EndIf
+ Else
+ path = Null
+ EndIf
+'EndRem
+
+' Tidy up after ourselves
+ walker.setCallback(Null)
+
+' Need to make sure memory gets freed because we have references to objects all over
+' the place
+ For y = 0 To mapHeight - 1
+ For x = 0 To mapWidth - 1
+ Local node:AStarNode = nodeMap[x,y]
+ Local neighbour:AStarNeighbourInfo
+ For neighbour:AStarNeighbourInfo = EachIn node.getNeighbours()
+ neighbour.free()
+ Next
+ walker.resetNode(node)
+ nodeMap[x,y] = Null
+ Next
+ Next
+ Return time
+ End Method
+EndType
+
+' This is our personal callback function so that we can draw the map. In a real implementation you might not have this
+' or maybe the callback allows you to check to see if the algorithm is taking too long and so pause for a frame and continue
+' next frame. You'd need some more work that though
+Type WalkerCallback Extends AStarCallback
+
+ Method New()
+ _waitForKey = True ' If ever set to false, then we are ignoring the callback so we just return
+ End Method
+
+ Method callback()
+ If _waitForKey = False ' If not waiting for key, then we've finished
+ While KeyDown(27) Wend
+ _application.setRedraw(True)
+ Return
+ EndIf
+ Local currentNode:AStarNode = node
+
+ _application.redraw(1,0)
+ Local x:Int
+ Local y:Int
+
+ For y = 0 To _application.mapHeight - 1
+ For x = 0 To _application.mapWidth - 1
+ Local node:AStarNode = _application.nodeMap[x,y]
+ Local col_r=128,col_g=128,col_b=128
+ Local do:Int = 0;
+ If node.inClosed() Or Not node.inOpen() Then
+ Continue
+ EndIf
+ If node.inOpen() Then
+ col_r=0 ; col_g=0 ;col_b=255
+ do = 1
+ EndIf
+ If do Then
+ _application.drawOnMap(node._x, node._y, col_r, col_g, col_b , 2 )
+ _application.printNums(oneDec(node.costToGetHere()), node._x , node._y, 4 )
+ _application.printNums(oneDec(node._goalDistance), node._x , node._y, 11 )
+ _application.printNums(oneDec(node._key), node._x , node._y, 18 )
+ EndIf
+ Next
+ Next
+ Flip
+
+ If KeyHit(KEY_P)
+ WaitKey
+ EndIf
+ If KeyDown(27)
+ _waitForKey = False
+ EndIf
+
+ End Method
+
+' Makes a single decimal place string from a number - ugly....
+ Method oneDec:String(number:Float)
+ Return String(Int(number)) + "." + Int((number-Int(number))*10)
+ End Method
+
+ Field _walker:AStarGraphWalker;
+ Field _application:AStarDemo;
+ Field _waitForKey:Int
+EndType
+
+
--- /dev/null
+' Class for walking an astar graph
+' It's up to you to make the graph yourself.
+
+
+Import "astar_node.bmx"
+Import "priority_queue.bmx"
+Import "Callback.bmx"
+
+' Customised callback class
+Type AStarCallback Extends Callback
+ Field node:AStarNode;
+ Field queue:PriorityQueue
+End Type
+
+Type AStarGraphWalker
+
+' public
+' Constructor
+ Method New()
+ _queue = New PriorityQueue
+ _finalPath = New TList
+ End Method
+
+' public
+' Sets the parameters for walking
+' startNode - The first node in the graph where searching will start from
+' endNode - The node you're trying to find the path to
+' maxNodes - The maximum number of nodes in the graph you want to walk
+
+ Method setParameters(startNode:AStarNode, endNode:AStarNode, maxNodes:Int )
+ Assert startNode <> Null,"startNode Null"
+ Assert endNode <> Null,"endNode Null"
+ Assert maxNodes > 0,"maxNodes <= 0"
+ _start = startNode; ' The start of the graph
+ _end = endNode; ' The start of the graph
+ _queue.setMaxSize(maxNodes)
+ _parametersSet = True;
+ End Method
+
+' public
+' Sets the callback function
+' callback - A object with a callback() mtehod. Derive from Callback Type
+ Method setCallback(callback:AStarCallback)
+ _callback = callback
+ End Method
+
+ Method setDistanceFunction(func:Int)
+ _distanceFunction = func
+ End Method
+
+ Method setHeuristicMultiplier(heuristic:Float)
+ _heuristicMultiplier = heuristic
+ End Method
+
+' public
+' Returns the final path after a path find
+ Method getFinalPath:TList()
+ Assert _lastWalkSucceeded, "Can't return path as last path walk failed"
+ Return _finalPath
+ End Method
+
+' private
+' Makes the list of successors that will be searched next
+' ARGUMENTS:
+' node - The node who's successors we're looking at
+' endNode - The destination node we're trying to get to
+ Method _makeSuccessors(node:AStarNode, endNode:AStarNode)
+ Local done:Int = False
+ For neighbour:AStarNeighbourInfo = EachIn node.getNeighbours()
+ Local neighbourNode:AStarNode = neighbour.getNode()
+ Assert neighbourNode,"Node is NULL"
+
+' Only look at neighbours that aren't the start node and also aren't in
+' the closed list (We'd be backtracking)
+ If neighbourNode <> _start And Not neighbourNode.inClosed()
+' Calculate total cost to get to this neighbour based on edge cost to neighbour +
+' current cost to get to us
+ Local cost:Float = neighbour.edgeCost() + node.costToGetHere()
+' Estimate a distance to the goal from the neighbour
+ Local goalDistance:Float = _distanceTo(neighbourNode, endNode)
+' If heuristic was 0 then we'd have an optimal search...
+ goalDistance = goalDistance * _heuristicMultiplier
+' What we guess the total cost would be to get from start to finish through
+' this neighbour
+ Local totalCostOfNeighbourPath:Float = goalDistance + cost
+
+' If we haven't visited this neighbour node yet at all, save it for later visiting
+' This line used to be If Not neighbourNode.inClosed() And Not neighbourNode.inOpen()
+' Don't need it as now we have the optimisation above that doesn't enter here if they
+' neighbour is in the closed list
+' If Not neighbourNode.inClosed()And Not neighbourNode.inOpen()
+ If Not neighbourNode.inOpen()
+' Assume we'll go from us to neighbour by setting us as the parent
+ neighbourNode.setParent(node)
+' Set the PQueue key as the total cost of this path to the goal
+' Queue is sorted smallest first so we always look at shortest distances
+ neighbourNode.setKey(totalCostOfNeighbourPath) ' Goes in queue based on total distance
+' Save what we calculated that the cost was to get to this neighbour
+ neighbourNode.setCostToGetHere(cost) ' What we consider it's cost is
+ neighbourNode.setGoalDistance(goalDistance) ' What we consider it's cost is
+ neighbourNode.setInOpen(True)
+ _queue.insert(neighbourNode)
+ Else
+' OK, neighbour is in a list (Actually must be in Open list at this point)
+' so see if we have a better path to it by seeing if the cost to get
+' to this neighbour the other way is more than the cost from our node
+' to this neighbour.
+' If it is, then our path is better
+ If neighbourNode.costToGetHere() > cost
+' If it was in the closed list, then we're going to put it in the open list
+' cause we want to now be able to look at it again as a possible path
+' If neighbourNode.inClosed()
+' neighbourNode.setInClosed(False)
+' EndIf
+' Above is removed because of optimisation
+ neighbourNode.setParent(node)
+ neighbourNode.setKey(totalCostOfNeighbourPath) ' Goes in queue based on total distance
+ neighbourNode.setGoalDistance(goalDistance) ' Estimate to get to goal
+ neighbourNode.setCostToGetHere(cost) ' What we consider it's cost is
+'TODO: Optimise this. Rather than remove and add, we could shift in the queue if
+' we knew it's index.
+' Removed if below because optimisation allows us to know that we must be in the open list to get here
+' If neighbourNode.inOpen()
+ pos:Int = _queue.find(neighbourNode)
+ Assert pos > 0, "Was going to remove item that wasn't in queue!"
+ _queue.remove(pos)
+ neighbourNode.setInOpen(False)
+' EndIf
+ _queue.insert(neighbourNode)
+ neighbourNode.setInOpen(True)
+ EndIf
+ EndIf
+ EndIf
+' If _callback <> Null
+' _callback.node = node
+' _callback.queue = _queue
+' _callback.callback()
+' Flip;WaitKey
+' EndIf
+ Next
+ End Method
+
+
+' public
+' Method to walk the graph, finding the shortest path
+'
+' RETURNS:
+' False - Failed to find path to the end node
+' True - Found a path to the end
+' PRE: Must have called setParameters first
+ Method walk()
+ Assert _parametersSet,"Must call setParameters() first"
+
+ _lastWalkSucceeded = False
+
+ Local startNode:AStarNode = _start
+ Local endNode:AStarNode =_end
+
+ ' Initialise starting node's information
+ Local distanceToGoal:Float = _distanceTo(_start, _end)
+ startNode.setCostToGetHere(0)
+ startNode.setKey(distanceToGoal * _heuristicMultiplier + startNode.costToGetHere())
+ startNode.setParent(Null)
+ startNode.setInOpen(True)
+ _queue.insert(startNode)
+
+ While _queue.size() > 0
+ Local node:AStarNode = AStarNode(_queue.remove())
+' node.setInOpen(False)
+ node.setInClosed(True)
+
+' Have we found our destination???
+ If node = endNode Then
+ Local currentNode:AStarNode = node
+ While currentNode <> Null
+ _finalPath.AddFirst(currentNode)
+ currentNode = currentNode.getParent()
+ Wend
+ _lastWalkSucceeded = True
+ _queue = Null
+ Return True
+ EndIf
+
+ _makeSuccessors(node, endNode)
+ If _callback <> Null
+ _callback.node = node
+ _callback.queue = _queue
+ _callback.callback()
+ EndIf
+' temp
+
+ Wend
+ Return False
+ End Method
+
+' Resets a node so that it's ready for a new path find. Call this from whatever manages the nodes
+' as AStarGraphWalker, doesn't actually know how many, or what nodes you have, but it does know how
+' to reset one
+ Method resetNode(node:AStarNode)
+ node._parentNode = Null
+ node.setInClosed(False)
+ node.setInOpen(False)
+ End Method
+
+'private
+
+' Returns an estimated distance between two nodes
+ Method _distanceTo:Float(startNode:AStarNode, endNode:AStarNode)
+ Local startX = startNode._x
+ Local startY = startNode._y
+ Local endX = endNode._x
+ Local endY = endNode._y
+ Local dx = Abs(endX - startX)
+ Local dy = Abs(endY - startY)
+'TODO: I had distanceFunction without the _ below and Blitz Didn't complain
+ Select _distanceFunction
+ Case distanceEuclidean
+ Return Sqr( dx * dx + dy * dy )
+ Case distancePseudoEuclidean
+ Return dx * dx + dy * dy
+ Case distanceManhatten
+ Return dx + dy
+ Case distanceDiagonalShortcut
+ If dx > dy
+ Return 1.4*dy + (dx-dy)
+ Else
+ Return 1.4*dx + (dy-dx)
+ End If
+ Default
+ Assert 0,"Bad distance function"
+ End Select
+ End Method
+
+' Fields
+
+' Possible ways to calculate distance. It's good to specify the edge costs between nodes
+' relative to your distance calculations because as they are related. For instance, if you calculate edge costs
+' using simple Euclidean distance, so that two adjacent blocks would be 1 away or 1.4 (if diagonal)
+' multiplied by some small "difficulty factor", say 1 for normal roads, or 2 for water
+' Then distanceEuclidean is a good estimator of distance and distancePseudoEuclidean
+' tends to override the edgecosts and the pathfinder sort of busts through them.
+' This can be a good thing as it could provide a simple way to make a unit "dumber"
+ Const distanceEuclidean = 0
+ Const distancePseudoEuclidean = 1
+ Const distanceManhatten = 2
+ Const distanceDiagonalShortcut = 3
+
+ Field _heuristicMultiplier = 1 ' 0 should generate "optimal" path
+ Field _start:AStarNode
+ Field _end:AStarNode
+ Field _distanceMode:Int = distanceEuclidean
+ Field _queue:PriorityQueue
+ Field _parametersSet = False
+ Field _finalPath:TList
+ Field _callback:AStarCallback = Null
+ Field _distanceFunction = distanceEuclidean
+ Field _lastWalkSucceeded = False
+
+EndType
--- /dev/null
+' This is an implementation of an AStar Algorithm
+
+Strict
+Import "priority_queue.bmx"
+
+'Private
+' Each AStar node has a list of neighbours that it connects to
+' This is the AStarNeighbourInfo type. AStarNeighbourInfo
+' holds both a reference to the neighbouring nodes, but also information
+' about the cost to get to that node
+Type AStarNeighbourInfo
+ Method getNode:AStarNode()
+ Return _neighbour
+ End Method
+
+ Method edgeCost:Float()
+ Return _edgeCost
+ End Method
+
+ Method free()
+ _neighbour = Null
+ End Method
+' private
+ Field _neighbour:AStarNode
+ Field _edgeCost:Float ' Cost to get to this neighbour
+EndType
+
+Public
+' These are the nodes in the AStar graph
+Type AStarNode Extends PriorityQueueNode
+' public
+
+' Call this to add a fully constructed neighbour node to this node
+ Method addNeighbour(neighbourNode:AStarNode, edgeCost:Float)
+ Local neighbourInfo:AStarNeighbourInfo = New AStarNeighbourInfo
+
+ neighbourInfo._neighbour = neighbourNode
+ neighbourInfo._edgeCost = edgeCost
+
+ _neighbours.AddLast(neighbourInfo)
+ End Method
+
+ Method getNeighbours:TList()
+ Return _neighbours
+ End Method
+
+ Method setGoalDistance(goalDistance:Float)
+ _goalDistance = goalDistance
+ End Method
+
+ Method goalDistance:Float()
+ Return _goalDistance
+ End Method
+
+ Method setCostToGetHere(cost:Float)
+ _costFromStart = cost
+ End Method
+
+ Method costToGetHere:Float()
+ Return _costFromStart
+ End Method
+
+' Yes using PTR here is what we want
+ Method setParent(parent:AStarNode)
+ _parentNode = parent
+ End Method
+
+ Method getParent:AStarNode()
+ Return _parentNode
+ End Method
+
+' Sets whether the node is in the open list or not
+ Method setInOpen(inOpen:Int)
+ _inOpen = inOpen
+ End Method
+
+ Method inOpen()
+ Return _inOpen
+ End Method
+
+' Sets whether the node is in the closed list or not
+ Method setInClosed(inClosed:Int)
+ _inClosed = inClosed
+ End Method
+
+ Method inClosed()
+ Return _inClosed
+ End Method
+
+' Initialise new nodes to default values
+ Method New()
+ _costFromStart = 0
+ _parentNode = Null
+ _neighbours = New TList
+ _inOpen = False
+ _inClosed = False
+ _goalDistance = 0
+ End Method
+
+' private
+ Field _costFromStart:Float ' We keep track of cost to get to this node
+ Field _goalDistance:Float ' Goal to distance
+ Field _parentNode:AStarNode ' Used to trace back from the end to the start of the finished path
+ Field _neighbours:TList ' The neighbours of this node
+
+ Field _inOpen:Int ' Keep track of what list the node is in
+ Field _inClosed:Int
+
+' TODO:
+' These are required in order to calculate distance
+' What would be better is to provide something like an AStarPos object
+' with a method to get distance between them that way you could provide a more
+' generic mechanism for distanc calculation
+ Field _x:Int
+ Field _y:Int
+
+End Type
--- /dev/null
+Strict
+'Module PUB.PriorityQueue
+
+'ModuleInfo "Author: Aaron Koolen"
+'ModuleInfo "Version: 1.0"
+
+Rem
+This is an implementation of a priority queue implemented as
+a heap stored in an array.
+Note, it might not be the most optimised code, as I don't know some
+of the BlitzMax tricks that I might be able to do here.
+Let me know if you see anything simple too slow or crappy.
+End Rem
+
+
+' You should derive your nodes from this class
+'
+Type PriorityQueueNode
+ Method setKey(key:Float)
+ _key = key;
+ End Method
+ Field _key:Float ' I don't think blitz max has the concept of templated types?
+End Type
+
+
+' Main PriorityQueue class
+Type PriorityQueue
+' private
+ Field _contents:PriorityQueueNode[]
+ Field _size:Int = 0
+ Field _sizeSet:Int = 0
+
+' public
+
+' Must call this first before you do anything
+ Method setMaxSize(newSize:Int)
+ Assert newSize > 0, "Must set size of queue > 0"
+ _contents = New PriorityQueueNode[newSize + 1] ' We don't add any, you have to add them
+ _size = 0
+ _sizeSet = newSize
+ End Method
+
+'---------------------------------------
+' Queue manipulation
+'---------------------------------------
+
+' Insert a node into the queue
+ Method insert(newNode:PriorityQueueNode)
+ Assert _sizeSet > 0,"Must call setMaxSize first with size > 0"
+ Assert _size < _sizeSet, "Queue is full"
+ _size = _size + 1
+ _contents[_size] = newNode
+ _reheapUp(_size)
+ End Method
+
+' Remove and return a node from the queue and re-sort queue
+ Method remove:PriorityQueueNode(index:Int = 1)
+ Local node:PriorityQueueNode = _contents[index]
+ _contents[index] = _contents[_size]
+ _size = _size - 1
+ _reheapDown(index, _size)
+ Return node
+ End Method
+
+'---------------------------------------
+' Queue searching
+'---------------------------------------
+ Method find:Int(node:PriorityQueueNode)
+ Local t
+ For t = 1 To _size
+ If node = _contents[t]
+ Return t
+ EndIf
+ Next
+ Return 0
+ End Method
+' Return the current size of the queue
+ Method size:Int()
+ Return _size
+ End Method
+
+' Helper function for returning the nodes as an ordered list
+ Method returnList:TList()
+ Local list:Tlist = New TList
+ Local a:PriorityQueueNode
+ For a = EachIn _contents
+ list.AddLast(remove(1)) ' The '1' here removes the top item
+ Next
+ Return list
+ End Method
+
+'------------------------------------------------
+' PRIVATE
+'------------------------------------------------
+' Reheap an inserted item
+' ARGUMENTS:
+' index - The Index of the newly inserted item
+ Method _reheapUp(index:Int)
+ Local parentIndex = _parent(index)
+ Local ok = False
+ While( index > 1 And ok = False )
+ If _contents[parentIndex]._key < _contents[index]._key Then
+ ok = True
+ Else
+ Local temp:PriorityQueueNode = _contents[parentIndex]
+ _contents[parentIndex] = _contents[index]
+ _contents[index] = temp
+ index = parentIndex
+ parentIndex = _parent(index)
+ EndIf
+ Wend
+ Return index
+ End Method
+
+' Reheaps downward - Called after a delete of a node
+' ARGUMENTS:
+' root - Index of the root (Top of tree) item
+' bottom - The index of the last item in the tree
+ Method _reheapDown(root:Int, bottom:Int)
+ Local ok = False
+ Local maxChild = 0
+
+ While _left(root) <= bottom And ok = False
+ If _left(root) = bottom
+ maxChild = _left(root)
+ Else
+ If _contents[_left(root)]._key < _contents[_right(root)]._key
+ maxChild = _left(root)
+ Else
+ maxChild = _right(root)
+ EndIf
+ EndIf
+ If Not (_contents[root]._key < _contents[maxChild]._key) Then
+ Local t:PriorityQueueNode = _contents[root]
+ _contents[root] = _contents[maxChild]
+ _contents[maxChild] = t
+ root = maxChild
+ Else
+ ok = True
+ EndIf
+ Wend
+ Return root
+ End Method
+
+' Returns the index of the parent node to a child node
+ Method _parent:Int(childIndex:Int)
+ Return childIndex / 2
+ End Method
+
+' Returns the index of the left child of a node
+ Method _left:Int(siblingIndex:Int)
+ Return siblingIndex * 2
+ End Method
+
+' Returns the index of the right child of a node
+ Method _right:Int(siblingIndex:Int)
+ Return siblingIndex * 2 +1
+ End Method
+
+End Type
+
--- /dev/null
+'Tempest
+'Coded by David Bird
+Strict
+
+Const CWidth#=640
+Const CHeight#=480
+Const K# = 50
+
+Global CCenterX#=CWidth/2.0
+Global CCenterY#=(CHeight/3.0)'*2
+
+Global SHOT_LIST:TList = New TList
+Global theLevel:Level
+
+Function TFormSZ#(x#, z#)
+ z:+5
+ Return (x/(z/K))
+EndFunction
+
+Function TForm(x#, y#, z#, x2d# Var, y2d# Var )
+ z:+5
+ y:+100
+ x2d = CCenterX+(x/(z/K))
+ y2d = CCenterY+(y/(z/K))
+EndFunction
+
+'Setup Graphics mode
+Graphics CWidth,CHeight,32
+
+HideMouse
+
+
+Global MainPlayer:Player
+
+Type Player
+ Field e_Index 'the edge the player is on
+ Field zPos
+
+ Field scl#=0.5 'where on the edge 0-1
+
+ Method SetEdge(index)
+ e_index = index
+ EndMethod
+
+ Method AddShot()
+ Shot.Create(theLevel.edges[e_Index])
+ EndMethod
+
+ Method ShiftLeft()
+ If e_Index=0
+ e_Index = theLevel.e_Cnt-1
+ Else
+ e_Index:-1
+ EndIf
+ EndMethod
+
+ Method ShiftRight()
+ If e_Index=theLevel.e_cnt-1
+ e_Index = 0
+ Else
+ e_Index:+1
+ EndIf
+ EndMethod
+
+ Method Update()
+ 'Control it
+ If KeyHit(KEY_SPACE) self.AddShot()
+
+ If KeyDown(KEY_LEFT)
+ scl:-0.1
+ If scl<0 Then
+ self.ShiftLeft()
+ scl:+1
+ EndIf
+ EndIf
+ If KeyDown(KEY_RIGHT)
+ scl:+0.1
+ If scl>1 Then
+ self.ShiftRight()
+ scl:-1
+ EndIf
+ EndIf
+ SetRotation 0
+ 'Draw it
+ SetColor 255,255,0
+ Local zz#
+ Local x#[3],y#[3]
+ Select theLevel.state
+ Case Level_Begin
+ zz# = theLevel.position
+ Case Level_Ready
+ zpos=theLevel.position
+ zz = zpos
+ Case Level_Complete
+ zz = zpos
+ EndSelect
+
+
+ Local zh# = 4
+ Local e:Edge = theLevel.edges[e_Index]
+ TForm e.p1.x, e.p1.y, zz,x[0],y[0]
+
+ TForm e.p2.x+ ( (e.p1.x - e.p2.x) * scl ), e.p2.y+ ( (e.p1.y - e.p2.y) * scl ),zz-zh,x[1],y[1]
+
+ TForm e.p2.x, e.p2.y, zz,x[2],y[2]
+ DrawLine x[0],y[0],x[1],y[1]
+ DrawLine x[1],y[1],x[2],y[2]
+ EndMethod
+
+
+ Function Create:Player()
+ Local p:Player = New Player
+
+ Return p
+ EndFunction
+EndType
+
+Type Point
+ Field x#,y#
+ Field e0:edge
+ Field e1:edge
+
+ Function Create:Point( x#, y# )
+ Local p:Point = New Point
+ p.x=x
+ p.y=y
+ Return p
+ EndFunction
+
+EndType
+
+Type Edge
+ Field p1:point
+ Field p2:point
+
+ Field xx#,yy#
+
+ Method Draw( zd1#, zd2# )
+ If zd1<1 zd1=1
+ If zd2<1 zd2=1
+
+ 'draw the edge at zero position,
+ 'the depth line and the far point
+ Local x#[4],y#[4]
+ TForm p1.x,p1.y,zd1, x[0],y[0]
+ TForm p1.x,p1.y,zd2, x[1],y[1]
+
+ TForm p2.x,p2.y,zd1, x[2],y[2]
+ TForm p2.x,p2.y,zd2, x[3],y[3]
+
+ DrawLine x[0],y[0],x[1],y[1]
+ DrawLine x[0],y[0],x[2],y[2]
+ DrawLine x[1],y[1],x[3],y[3]
+ DrawLine x[3],y[3],x[2],y[2]
+ EndMethod
+
+ Function Create:Edge(p1:Point, p2:Point)
+ Local e:Edge = New edge
+
+ 'assign the points
+ e.p1=p1
+ e.p2=p2
+
+ 'linkem up
+ p1.e1=e
+ p2.e0=e
+
+ 'store the midpoint for speeding up
+ e.xx =( ( p2.x - p1.x ) / 2.0 ) + p1.x
+ e.yy =( ( p2.y - p1.y ) / 2.0 ) + p1.y
+
+ Return e
+ EndFunction
+EndType
+
+Const Level_Begin = 0
+Const Level_Complete = 1
+Const Level_Ready = 2
+
+Type Level
+ Field depth# = 400
+ Field position# = 1500
+ Field move# = 0
+ Field state = Level_Begin
+
+ Field points:TList
+ Field edges:Edge[10],e_cnt,e_cap=10
+
+ Method AddPoint:Point(x#,y#)
+ Local p:Point = Point.Create( x, y )
+ points.AddLast( p )
+ Return p
+ EndMethod
+
+ Method AddEdge:Edge( p1:Point, p2:point )
+ Local e:Edge = Edge.Create( p1, p2 )
+ If e_cnt>=e_cap
+ e_cap:+10
+ edges=edges[..e_cap]
+ EndIf
+ edges[e_cnt] = e
+ e_cnt:+1
+ Return e
+ EndMethod
+
+ Method Update()
+ Select state
+ Case Level_Begin
+ If position>50
+ position:-10
+ Else
+ state=Level_Ready
+ EndIf
+ Case Level_Ready
+
+ Case Level_Complete
+ position:-10
+ EndSelect
+
+ EndMethod
+
+ Method Draw()
+ Local a=0
+ Select state
+ Case Level_Begin
+
+ Case Level_Ready
+
+ Case Level_Complete
+ EndSelect
+ SetRotation 0
+ SetColor 0,0,100
+ For a=0 Until e_cnt
+ edges[a].Draw(position,position+depth)
+ Next
+ EndMethod
+
+ Function Create:Level()
+ Local l:Level = New Level
+ l.points = New TList
+
+ Return l
+ EndFunction
+EndType
+
+Type Shot
+ Field e:edge ' the edge its on
+ Field z# ' its position
+ Field r# ' rotation
+ Field xx#,yy#
+
+ Method Draw()
+ SetColor Rand(255),Rand(255),Rand(255)
+ Local zz = z+theLevel.position
+ Local sz = TFormSZ(10,zz)
+ Local pxx#,pyy#
+
+ TForm(xx,yy,zz,pxx,pyy)
+
+ For Local a=0 Until 360 Step 45
+ SetRotation r+a
+ DrawLine pxx+sz,pyy,pxx-sz,pyy
+ Next
+ r:+15
+ SetRotation 0
+ EndMethod
+
+ Method Update()
+ z:+5
+ Local bad:Baddies
+ If z>theLevel.depth
+ SHOT_LIST.Remove(Self)
+ Return
+ Else
+ 'check for collisions
+ For bad = EachIn BaddieList
+ If bad.CheckColl(e,z)
+ SHOT_LIST.Remove(Self)
+ Return
+ EndIf
+ Next
+ EndIf
+
+ EndMethod
+
+ Function Create:Shot(e:Edge)
+ Local ns:Shot = New Shot
+ ns.e = e
+ ns.xx =e.xx
+ ns.yy =e.yy
+ ns.z = -5
+ SHOT_LIST.AddLast( ns )
+ Return ns
+ EndFunction
+EndType
+
+Function UpdateShots()
+ Local s:shot
+ For s=EachIn SHOT_LIST
+ s.Update
+ Next
+ For s=EachIn SHOT_LIST
+ s.Draw()
+ Next
+EndFunction
+
+Global BaddieList:Tlist = New TList
+
+Function UpdateBaddies()
+ Local b:Baddies
+ For b = EachIn Baddielist
+ b.Update()
+ Next
+ For b = EachIn Baddielist
+ b.Draw()
+ Next
+EndFunction
+
+Type Baddies
+ Field OnEdge:Edge
+
+ Method Update() Abstract
+ Method Draw() Abstract
+ Method CheckColl(e:edge,z#) Abstract
+EndType
+
+Type Crawler Extends Baddies
+ Field EdgeIndex 'used to traverse theLevel edgelist
+ Field typ '0 just slide up the tube
+ '1 rolls round the tube
+
+ Field Pause 'the pause before changing edge
+ Field dir 'direction left or right
+ Field angle 'the angle when changing lanes
+
+ Method Update()
+ EndMethod
+ Method Draw()
+ EndMethod
+ Method CheckColl(e:edge,z#)
+ EndMethod
+
+ Function Create:Crawler(index,typ = 0)
+ Local c:Crawler = New Crawler
+ c.OnEdge = theLevel.edges[index]
+ c.typ = typ
+ BaddieList.AddLast c
+ Return c
+ EndFunction
+EndType
+
+Type Spikes Extends Baddies
+ Field height#=0
+ Field grow_speed#
+
+ Method CheckColl(e:edge,z#)
+ 'check to see if any will hit
+ Local sp# = theLevel.Depth-height
+ If e = OnEdge
+ If z>sp
+ height:-40
+ If height<0
+ BaddieList.Remove(Self)
+ Return True
+ EndIf
+ Return True
+ EndIf
+ EndIf
+ Return False
+ EndMethod
+
+ Method Draw()
+ Local xx#,yy#,zz1#,zz2#
+ zz1 = theLevel.position+theLevel.depth
+ zz2 = zz1 - height
+
+ Local x#[2],y#[2]
+ xx =( ( OnEdge.p2.x - OnEdge.p1.x ) / 2.0 ) + OnEdge.p1.x
+ yy =( ( OnEdge.p2.y - OnEdge.p1.y ) / 2.0 ) + OnEdge.p1.y
+ TForm(xx,yy,zz1,x[0],y[0])
+ TForm(xx,yy,zz2,x[1],y[1])
+ SetColor 0,255,0
+ DrawLine x[0],y[0],x[1],y[1]
+ SetColor 255,0,0
+ Plot x[1],y[1]
+ EndMethod
+
+ Method Update()
+ If height<theLevel.Depth
+ height:+grow_speed
+ Else
+ height = theLevel.Depth
+ EndIf
+ EndMethod
+
+ Function Create:Spikes(index,speed#)
+ Local s:Spikes = New Spikes
+ s.OnEdge=theLevel.Edges[index]
+ s.height=10
+ s.grow_speed# = speed
+ BaddieList.AddLast s
+ Return s
+ EndFunction
+EndType
+
+
+Local a
+Local p1:Point
+Local p2:Point
+Local fp:Point
+theLevel = Level.Create()
+MainPlayer = Player.Create()
+
+For a=0 Until 360 Step 30
+ p1 = theLevel.AddPoint( Cos(a)*280, Sin(a)*180 )
+ If p2
+ theLevel.AddEdge(p1,p2)
+ Else
+ fp = p1
+ EndIf
+ p2=p1
+Next
+Local lastedge:edge = theLevel.AddEdge(fp,p2)
+MainPlayer.SetEdge( 0 )
+
+For a=0 Until 10
+ spikes.Create( a ,Rnd(0.5,1.0))
+Next
+
+'main loop
+While Not KeyDown(KEY_ESCAPE)
+ Cls
+ theLevel.Update()
+ theLevel.Draw()
+ UpdateShots()
+ UpdateBaddies()
+
+ MainPlayer.Update()
+
+ Flip
+Wend
+End
+
--- /dev/null
+Strict
+Incbin "media/back.png"
+Incbin "media/blocks.png"
+Incbin "media/part.png"
+Incbin "media/pointer.PNG"
+Incbin "media/shine.png"
+
+Graphics 640,480
+Global backIm:TImage = LoadImage("incbin::media/back.png")
+AutoMidHandle 1
+Global blocks:TImage = LoadAnimImage("incbin::media/blocks.png",32,32,0,16)
+Global partImg:TImage = LoadImage("incbin::media/part.png")
+Global mousePoint:TImage= LoadImage("incbin::media/pointer.PNG")
+Global shine_img:TImage = LoadImage("incbin::media/shine.png")
+AutoMidHandle 0
+Global Map[8,14]
+Global t1x,t1y,t2x,t2y
+Global mouse_left_state
+Global selection_done
+Global rotation# = 0
+Global Center_X#
+Global Center_Y#
+Global mt1,mt2
+Global Tile_Rad#
+Global THE_Axis
+Global FLIPSPEED=8
+Global Scn_Flash#=0
+Global shine_pos#=0
+
+HideMouse
+While Not KeyDown(KEY_ESCAPE)
+ Cls
+ DrawLayout()
+ If selection_done =0
+ FillGrid()
+ EndIf
+
+ DrawGrid()
+
+ If selection_done
+ do_swap_tiles()
+ Else
+ UpdateSelection()
+ EndIf
+
+ SetBlend MASKBLEND
+ DrawText mouse_left_state,0,0
+ DrawText t1x+","+t1y,0,12
+ DrawText t2x+","+t2y,0,23
+ DrawImage mousePoint,MouseX(),MouseY()
+ Flip
+Wend
+
+End
+Function UpdateSelection()
+ Local x=MouseX()-192
+ Local y=MouseY()-24
+ Local dx,dy
+
+ If MouseDown(1)
+ Select mouse_left_state
+ Case 0
+ If x>=0 And x<256
+ If y>=0 And y<448
+ 'select tile1
+ t1x=Floor(x/32.0)
+ t1y=Floor(y/32.0)
+ EndIf
+ EndIf
+ 'get tile 1
+ mouse_left_state=1
+ Case 2
+ If x>=0 And x<256
+ If y>=0 And y<448
+ 'select tile1
+ t2x=Floor(x/32.0)
+ t2y=Floor(y/32.0)
+ EndIf
+ EndIf
+ mouse_left_state=3
+ EndSelect
+ Else
+ Select mouse_left_state
+ Case 1
+ mouse_left_state=2
+ Case 3
+ 'check that only 1 tile away and not diag
+ dx=Abs(t2x-t1x)
+ dy=Abs(t2y-t1y)
+ If dx=1 And dy=0
+' Switch(t1x,t1y,t2x,t2y)
+ selection_done=1
+ ElseIf dx=0 And dy=1
+' Switch(t1x,t1y,t2x,t2y)
+ selection_done=1
+ EndIf
+ mouse_left_state=0
+ Case 99
+ mouse_left_state=0
+ EndSelect
+ EndIf
+
+EndFunction
+
+'add tiles to array at the top
+Function FillGrid()
+ Local x,y,tl,tlc
+ For x=0 Until 8
+ If map[x,0]=0
+ map[x,0]=1+Rnd(1)*7'15
+ EndIf
+ Next
+ 'Fall
+ For y=12 To 0 Step -1
+ For x=0 Until 8
+ If map[x,y]>0
+ If map[x,y+1]=0
+ map[x,y+1]=map[x,y]
+ map[x,y]=0
+ EndIf
+ EndIf
+ Next
+ Next
+
+ For y=0 Until 14
+ For x=0 Until 8
+ tl=map[x,y]
+ If tl>0
+ If Counttile(x,y,tl,0,tlc)
+ KillTiles(x,y,tlc,0)
+ ElseIf Counttile(x,y,tl,1,tlc)
+ KillTiles(x,y,tlc,1)
+ ElseIf Counttile(x,y,tl,2,tlc)
+ KillTiles(x,y,tlc,2)
+ ElseIf Counttile(x,y,tl,3,tlc)
+ KillTiles(x,y,tlc,3)
+ EndIf
+
+ EndIf
+ Next
+ Next
+EndFunction
+
+Function KillTiles(x,y,c,dir)
+ Local d
+ For d=0 Until c
+ Select dir
+ Case 0
+ map[x,y]=0
+ y:-1
+ Case 1
+ map[x,y]=0
+ x:+1
+ Case 2
+ map[x,y]=0
+ y:+1
+ Case 3
+ map[x,y]=0
+ x:-1
+ EndSelect
+ Next
+EndFunction
+
+Function CountTile(x,y,ty,dir, cn Var)
+ cn = 0
+ Select dir
+ Case 0 ' Up
+ While y>0
+ If map[x,y]=ty
+ cn:+1
+ Else
+ Return cn>2
+ EndIf
+ y=y-1
+ Wend
+ Return cn>2
+ Case 1 ' Right
+ While x<8
+ If map[x,y]=ty
+ cn:+1
+ Else
+ Return cn>2
+ EndIf
+ x:+1
+ Wend
+ Return cn>2
+ Case 2 ' Down
+ While y<14
+ If map[x,y]=ty
+ cn:+1
+ Else
+ Return cn>2
+ EndIf
+ y:+1
+ Wend
+ Return cn>2
+ Case 3 ' Left
+ While x>0
+ If map[x,y]=ty
+ cn:+1
+ Else
+ Return cn>2
+ EndIf
+ x:-1
+ Wend
+ Return cn>2
+ EndSelect
+ cn = 0
+ Return 0
+EndFunction
+
+Function DrawGrid()
+ SetColor 255,255,255
+ Local x,y
+ For y=0 Until 14
+ For x=0 Until 8
+ If selection_done
+ If map[x,y]>0
+ If (x=t1x And y=t1y) Or (x=t2x And y=t2y)
+ Else
+ SetBlend MASKBLEND
+ DrawImage blocks,208+x*32,32+y*32,map[x,y]-1
+ EndIf
+ EndIf
+ Else
+ If map[x,y]>0
+ DrawImage blocks,208+x*32,32+y*32,map[x,y]-1
+ EndIf
+ EndIf
+ Next
+ Next
+ SetViewport 0,0,640,480
+EndFunction
+
+Function DrawLayout()
+ Cls
+ If Scn_Flash<0
+ Scn_Flash:+0.2
+ Else
+ Scn_Flash=0
+ EndIf
+ SetBlend SOLIDBLEND
+ SetColor 255,255,255
+ TileImage backIm,0,0
+ SetColor 128,128,128
+ SetViewport 192,24,256,448
+ Cls
+ TileImage backIm,0,0
+ If shine_pos#=0 SetViewport 0,0,640,480
+EndFunction
+
+Function CausesPop()
+ Local x,y,tl,tlc
+ For y=0 Until 14
+ For x=0 Until 8
+ tl=map[x,y]
+ If tl>0
+ If Counttile(x,y,tl,0,tlc)
+ Return 1
+ ElseIf Counttile(x,y,tl,1,tlc)
+ Return 1
+ ElseIf Counttile(x,y,tl,2,tlc)
+ Return 1
+ ElseIf Counttile(x,y,tl,3,tlc)
+ Return 1
+ EndIf
+ EndIf
+ Next
+ Next
+ Return 0
+EndFunction
+
+Function Switch(x0,y0,x1,y1)
+ Local a
+ a=map[x0,y0]
+ map[x0,y0]=map[x1,y1]
+ map[x1,y1]=a
+ If CausesPop() Return 1
+ a=map[x0,y0]
+ map[x0,y0]=map[x1,y1]
+ map[x1,y1]=a
+ Return 0
+EndFunction
+
+Function Do_Swap_Tiles()
+ Local x1,x2,y1,y2
+ Local size1#,size2#
+ Select selection_done
+ Case 1
+ 'setup tile swap
+ rotation=180
+ selection_done = 2
+ 'calculate center
+ x1 = 208+t1x*32
+ x2 = 208+t2x*32
+ y1 = 32+t1y*32
+ y2 = 32+t2y*32
+ Center_X = ((x2-x1)/2)+x1
+ Center_Y = ((y2-y1)/2)+y1
+ Tile_Rad = 16
+ 'check side
+ If t1y=t2y
+ If t1x>t2x
+ mt1 = map[t2x,t2y]
+ mt2 = map[t1x,t1y]
+ Else
+ mt1 = map[t1x,t1y]
+ mt2 = map[t2x,t2y]
+ EndIf
+ Else
+ If t1y>t2y
+ mt1 = map[t2x,t2y]
+ mt2 = map[t1x,t1y]
+ Else
+ mt1 = map[t1x,t1y]
+ mt2 = map[t2x,t2y]
+ EndIf
+ EndIf
+
+ THE_Axis = 1
+ If t1y=t2y THE_Axis = 0
+ Case 2
+ If rotation<0
+ If Switch(t1x,t1y,t2x,t2y)
+ selection_done = 0
+ Else
+ selection_done = 3
+ EndIf
+ Else
+ rotation:-FLIPSPEED
+ size2# = 0.80+0.20*Sin(rotation)
+ size1# = 0.80+0.20*Sin(-rotation)
+ Select THE_Axis
+ Case 0 'x
+ SetScale size1,size1
+ DrawImage blocks, Center_X+Tile_Rad*Cos(rotation), Center_Y, mt1-1
+ SetScale size2,size2
+ DrawImage blocks, Center_X-Tile_Rad*Cos(rotation), Center_Y, mt2-1
+ SetScale 1,1
+ Case 1 ' y
+ SetScale size1,size1
+ DrawImage blocks, Center_X, Center_Y+Tile_Rad*Cos(rotation), mt1-1
+ SetScale size2,size2
+ DrawImage blocks, Center_X, Center_Y-Tile_Rad*Cos(rotation), mt2-1
+ SetScale 1,1
+ EndSelect
+ ' DrawImage blocks,192+t2x*32,24+t2y*32,mt2
+ EndIf
+ Case 3 '
+ If rotation>=180
+ selection_done = 0
+ Else
+ rotation:+FLIPSPEED
+ size2# = 0.80+0.20*Sin(rotation)
+ size1# = 0.80+0.20*Sin(-rotation)
+ Select THE_Axis
+ Case 0 'x
+ SetScale size1,size1
+ DrawImage blocks, Center_X+Tile_Rad*Cos(rotation), Center_Y, mt1-1
+ SetScale size2,size2
+ DrawImage blocks, Center_X-Tile_Rad*Cos(rotation), Center_Y, mt2-1
+ SetScale 1,1
+ Case 1 ' y
+ SetScale size1,size1
+ DrawImage blocks, Center_X, Center_Y+Tile_Rad*Cos(rotation), mt1-1
+ SetScale size2,size2
+ DrawImage blocks, Center_X, Center_Y-Tile_Rad*Cos(rotation), mt2-1
+ SetScale 1,1
+ EndSelect
+ EndIf
+ EndSelect
+
+EndFunction
--- /dev/null
+Strict
+'
+' Game Demo Coded By David Bird (Birdie)
+'
+Incbin "media/sand.png"
+Incbin "media/ship1.png"
+Incbin "media/cloud.png"
+Incbin "media/shot.png"
+Incbin "media/shot2.PNG"
+Incbin "media/mud.png"
+Incbin "media/smoke.png"
+Incbin "media/scan.png"
+Incbin "media/zombie_0.png"
+Incbin "media/zombie_1.png"
+Incbin "media/zombie_2.PNG"
+Incbin "media/zombie_3.PNG"
+Incbin "media/zombie_4.PNG"
+Incbin "media/zombie_5.PNG"
+Incbin "media/zombie_6.png"
+Incbin "media/zombie_7.PNG"
+Incbin "media/Title.png"
+Incbin "media/HitSpace.png"
+
+Global C_ScreenWidth# = 640, C_ScreenHeight# = 480
+Global C_ScreenMidX# =C_ScreenWidth/2
+Global C_ScreenMidY# =C_ScreenHeight/2
+Global MapPosition_X#=400, MapPosition_Y#=400
+Global WorldSize = 8192
+Global Scanner_Rot#
+Global Scanner_X#= C_ScreenWidth - 80
+Global Scanner_Y#= 80
+Global Scanner_Scale# = 0.5
+Global ObjectList:TList = New TList
+Global DetailList:TList = New TList
+Global CloudList:TList = New TList
+Global ScanList:TList = New TList
+
+Global TitleWidth#,TitleHeight#
+Graphics C_ScreenWidth, C_ScreenHeight ,32
+HideMouse
+AutoImageFlags MASKEDIMAGE|FILTEREDIMAGE|MIPMAPPEDIMAGE
+
+'media globals
+Global media_sand:TImage
+Global media_ship1:TImage
+Global media_cloud:TImage
+Global media_shots:TImage
+Global media_shot2:TImage
+Global media_mud:TImage
+Global media_smoke:TImage
+Global Media_scan:TImage
+Global media_Title:TImage
+Global media_HitSP:TImage
+
+Global Media_zombie:TImage[8]
+LoadMedia()
+
+Local P1:Player = New Player
+ObjectList.AddLast p1
+
+'title screen
+
+While Not KeyDown(KEY_SPACE)
+ Cls
+ SetBlend SOLIDBLEND
+ SetColor 255,255,255
+ TileImage Media_sand,MapPosition_X,MapPosition_Y
+ MapPosition_X:+1
+ SetRotation 0
+ SetBlend ALPHABLEND
+ SetColor 255,255,255
+ SetAlpha 1
+ SetScale TitleWidth,TitleHeight
+ DrawImage Media_Title,C_ScreenMidX,C_ScreenMidY-24
+ SetScale 1,1
+ SetAlpha 0.5
+ DrawImage media_HitSP, C_ScreenMidX, C_ScreenHeight - media_HitSP.height
+ Flip
+Wend
+
+Local a
+For a=0 Until 100
+ cloud.Create( Rnd(-WorldSize,WorldSize), Rnd(-WorldSize,WorldSize) )
+Next
+For a=0 Until 250
+ Baddie.Create(Rnd(-1000,1000), Rnd(-1000,1000), 0)
+Next
+
+While Not KeyDown(KEY_ESCAPE)
+ Cls
+ UpdateObjects()
+
+ DrawLevel(p1)
+ DrawObjects()
+ DrawScanner( P1 )
+
+ Flip
+Wend
+End
+
+Function LoadMedia()
+ SetHandle 0.5,0.5
+ AutoMidHandle True
+ media_sand = LoadImage("incbin::media/sand.png")
+ media_ship1 = LoadImage("incbin::media/ship1.png")
+ media_cloud = LoadImage("incbin::media/cloud.png")
+ media_shots = LoadImage("incbin::media/shot.png")
+ media_shot2 = LoadImage("incbin::media/shot2.PNG")
+ media_scan = LoadImage("incbin::media/scan.png")
+ media_mud = LoadImage("incbin::media/mud.png")
+ media_smoke = LoadImage("incbin::media/smoke.png")
+ media_Title = LoadImage("incbin::media/Title.png")
+ media_HitSP = LoadImage("incbin::media/HitSpace.png")
+
+ 'scale to fullscreen
+
+ TitleWidth = media_title.width / C_ScreenWidth
+ TitleHeight= media_title.height / (C_ScreenHeight+48)
+ Media_zombie[0] = LoadAnimImage("incbin::media/zombie_0.png",32,64,0,17)
+ Media_zombie[1] = LoadAnimImage("incbin::media/zombie_1.png",32,64,0,17)
+ Media_zombie[2] = LoadAnimImage("incbin::media/zombie_2.PNG",32,64,0,17)
+ Media_zombie[3] = LoadAnimImage("incbin::media/zombie_3.PNG",32,64,0,17)
+ Media_zombie[4] = LoadAnimImage("incbin::media/zombie_4.PNG",32,64,0,17)
+ Media_zombie[5] = LoadAnimImage("incbin::media/zombie_5.PNG",32,64,0,17)
+ Media_zombie[6] = LoadAnimImage("incbin::media/zombie_6.png",32,64,0,17)
+ Media_zombie[7] = LoadAnimImage("incbin::media/zombie_7.PNG",32,64,0,17)
+
+EndFunction
+
+Function DrawLevel(o:Entity)
+ 'using the object{o} position and direction to draw the map
+' MapPosition_X = MapPosition_X + ((o.x - MapPosition_X)*0.25)
+' MapPosition_Y = MapPosition_Y + ((o.y - MapPosition_Y)*0.25)
+ MapPosition_X = o.x
+ MapPosition_Y = o.y
+ SetBlend SOLIDBLEND
+ SetColor 255,255,255
+ SetScale 1,1
+ TileImage Media_sand,MapPosition_X,MapPosition_Y
+
+EndFunction
+
+Function UpdateObjects()
+ Local e:Entity
+ For e=EachIn ObjectList
+ e.Update()
+ Next
+EndFunction
+
+Function DrawObjects()
+ Local e:Entity
+ Local c:Cloud
+ For c=EachIn CloudList
+ c.Update()
+ Next
+
+ 'Draw Shadows
+ For e=EachIn ObjectList
+ e.DrawShadow()
+ Next
+ 'Draw Details
+ Local d:Detail
+ For d=EachIn DetailList
+ d.Update(MapPosition_X, MapPosition_Y)
+ Next
+
+
+ 'Draw objects without shadows
+ For e=EachIn ObjectList
+ e.DrawBody()
+ Next
+
+ 'DrawClouds
+
+ For c=EachIn CloudList
+ c.DrawShadow()
+ Next
+ For c=EachIn CloudList
+ c.DrawBody()
+ Next
+
+EndFunction
+
+Type Entity
+ Field x#,y#
+ Field height#
+ Field rotation#
+ Field spd#=1
+
+ Method Update() Abstract
+
+ Method DrawBody() Abstract
+ Method DrawShadow() Abstract
+EndType
+
+Type Player Extends Entity
+ Field RotationSpd#
+ Field liftSpd#
+ Field thrust#
+ Field osc#
+
+ Method Update()
+ If KeyDown(KEY_UP)
+ thrust:+0.01
+ If thrust>0.5
+ thrust = 0.5
+ EndIf
+ EndIf
+ If KeyDown(KEY_DOWN)
+ thrust:-0.05
+ EndIf
+ spd:+thrust
+
+ If thrust<0
+ thrust = 0
+ EndIf
+
+ If spd>8
+ spd=8
+ EndIf
+ If spd<0.5 spd = 0.5
+
+ If KeyHit(KEY_SPACE)
+ Local a,thei#
+ thei = height +(3.5*Cos(osc) )
+
+ Shots.Create x, y, spd+5, thei, rotation
+ For a=0 Until 3
+ Shots.Create x, y, spd+5, thei, rotation+Rnd(-1.75,1.75)
+ Next
+ EndIf
+ Local sz#=14*(1+(height/140.0))
+ Local sd#=16
+ Local cs#= Cos(rotation-90)
+ Local sn#= Sin(rotation-90)
+ Local ddx# = sz
+ Local ddy# = 9
+
+ Local tx1# = ddx*cs + ddy*sn
+ Local ty1# = ddx*sn - ddy*cs
+ ddx=-ddx
+ Local tx2# = ddx*cs + ddy*sn
+ Local ty2# = ddx*sn - ddy*cs
+
+ Local thei#
+ thei = height +(3.5*Cos(osc) )
+ Local deltasm#= ( 0.5 * (spd/8.0))
+ Trail.Create( x+tx1, y+ty1, thei, deltasm, rotation )
+ Trail.Create( x+tx2, y+ty2, thei, deltasm, rotation )
+
+ Local tx#= 20 * Cos(rotation)
+ Local ty#= 20 * Sin(rotation)
+ Local rd= 255*thrust*2
+ Local gn= rd*0.7
+ Local bl= gn*0.5
+ ColTrail.Create( x-tx, y-ty, thei, 0.3, rotation, [rd, gn, bl] ,2.0)
+
+ ColTrail.Create( x-tx, y-ty, thei, 0.3, rotation, [rd*2, 0, 0] ,1.0)
+
+ If KeyDown(KEY_RIGHT)
+ rotationSpd:+0.25
+ If rotationSpd>2
+ rotationSpd=2
+ EndIf
+ EndIf
+ If KeyDown(KEY_LEFT)
+ rotationSpd:-0.25
+ If rotationSpd<-2
+ rotationSpd=-2
+ EndIf
+ EndIf
+ rotation:+rotationSpd
+ rotationSpd:*0.95
+
+ If KeyDown(KEY_A)
+ liftSpd:-0.02
+ If liftSpd<-1
+ liftSpd=-1
+ EndIf
+ EndIf
+ If KeyDown(KEY_Z)
+ liftSpd:+0.02
+ If liftSpd>1
+ liftSpd=1
+ EndIf
+ EndIf
+ height:+liftSpd
+
+ liftspd:*0.985
+
+ If height<4
+ height = 4
+ liftspd = 0
+ EndIf
+ If height>50
+ height = 50
+ liftspd=0
+ EndIf
+ x=x+(spd*Cos(rotation))
+ y=y+(spd*Sin(rotation))
+ If x>WorldSize x=-WorldSize
+ If x<-WorldSize x=WorldSize
+ If y>WorldSize y=-WorldSize
+ If y<-WorldSize y=WorldSize
+ osc:+1
+ EndMethod
+
+ Method DrawShadow()
+ Local dx#,dy#,sz#,thei#
+ thei = height +(3.5*Cos(osc) )
+ sz =(0.002*thei)
+ SetRotation rotation+90
+ SetBlend ALPHABLEND
+ SetScale 0.25+sz,0.25+sz
+ SetColor 0,0,0
+ SetAlpha 0.5
+ DrawImage media_ship1,C_ScreenMidX + (x-MapPosition_X), C_ScreenMidY + ( y - MapPosition_Y )
+ EndMethod
+
+ Method DrawBody()
+ Local dx#,dy#,sz#,thei#
+ thei = height +(3.5*Cos(osc) )
+ sz =(0.002*thei)
+ SetAlpha 1
+ SetRotation rotation+90
+ SetBlend MASKBLEND
+ SetColor 255,255,255
+ dx=thei/3.0
+ dy=thei
+ SetScale 0.25+sz, 0.25+sz
+ DrawImage media_ship1,(x-MapPosition_X+dx) + C_ScreenMidX,(y-MapPosition_Y+dy) + C_ScreenMidY
+ EndMethod
+ Function Create:Player(x#,y#)
+ Local p:Player = New Player
+ p.x=x
+ p.y=y
+ p.height = 4
+ p.spd=1
+ ObjectList.AddFirst p
+ Return p
+ EndFunction
+EndType
+
+Type Cloud
+ Field x#,y#,height#, rotation
+
+ Method Update()
+ x:+1
+ y:+0.1
+ If x>WorldSize x=-WorldSize
+ If x<-WorldSize x=WorldSize
+ If y>WorldSize y=-WorldSize
+ If y<-WorldSize y=WorldSize
+ EndMethod
+
+ Method DrawBody()
+ Local dx#,dy#
+ dx=height/2.0
+ dy=height
+ SetBlend LIGHTBLEND
+ SetAlpha 1
+ SetScale 2.4,2.4
+ SetRotation rotation
+ SetColor 255,255,255
+ DrawImage media_cloud,(MapPosition_X+dx-x) + C_ScreenMidX,(MapPosition_Y+dy-y) + C_ScreenMidY
+ EndMethod
+
+ Method DrawShadow()
+ SetBlend ALPHABLEND
+ SetColor 0,0,0
+ SetAlpha 0.2
+ SetScale 4, 4
+ SetRotation rotation
+ DrawImage media_cloud,(MapPosition_X-x) + C_ScreenMidX,(MapPosition_Y-y) + C_ScreenMidY
+ EndMethod
+
+ Function Create:Cloud( x#, y# )
+ Local c:Cloud = New Cloud
+ c.x= x
+ c.y= y
+ c.rotation = Rnd(360)
+ c.height = 75
+ CloudList.AddLast c
+ Return c
+ EndFunction
+EndType
+
+Type Shots Extends Entity
+ Field life
+ Method DrawBody()
+ Local dx#,dy#
+ dx=height/3.0
+ dy=height
+ SetBlend MASKBLEND
+ SetColor 255,255,255
+ SetAlpha 1
+ SetScale 1, 1
+ SetRotation rotation + 90
+ DrawImage media_shots,(MapPosition_X+dx-x) + C_ScreenMidX,(MapPosition_Y+dy-y) + C_ScreenMidY
+ EndMethod
+
+ Method DrawShadow()
+ SetBlend ALPHABLEND
+ SetColor 0,0,0
+ SetAlpha 0.2
+ SetScale 1, 1
+ SetRotation rotation + 90
+ DrawImage media_shots,(MapPosition_X-x) + C_ScreenMidX,(MapPosition_Y-y) + C_ScreenMidY
+ EndMethod
+ Method Update()
+ x=x+(spd*Cos(rotation))
+ y=y+(spd*Sin(rotation))
+ If x>WorldSize x=-WorldSize
+ If x<-WorldSize x=WorldSize
+ If y>WorldSize y=-WorldSize
+ If y<-WorldSize y=WorldSize
+ If life<80
+ height:-0.75
+ EndIf
+ Trail.Create( x, y, height, 0.3, rotation )
+ If height<0
+ ObjectList.Remove Self
+ Mud.Create(x, y, rotation)
+ Local ee#
+ ee=1
+ SmokeEmitter.create( x#,y#, 0,ee, ee, ee )
+ Else
+ life:-1
+ If life<0
+ ObjectList.Remove Self
+ EndIf
+ EndIf
+ EndMethod
+
+ Function Create:Shots(x#,y#,spd#,hei#,rot#)
+ Local s:Shots = New Shots
+ s.x=x
+ s.y=y
+ s.rotation = rot
+ s.spd = spd
+ s.height = hei
+ s.life = 100
+ ObjectList.AddFirst s
+ Return s
+ EndFunction
+EndType
+
+Type Detail
+ Field life
+ Field x#,y#,rot#,alp#
+ Field col[3]
+
+ Method Update( wx#, wy# ) Abstract
+EndType
+
+Type Trail Extends Detail
+ Field height#
+ Field size#
+
+ Method Update( wx#, wy# )
+ Local dx#, dy#
+ alp:-0.005
+ If alp<0
+ life = 0
+ EndIf
+
+ life:-1
+ If life<0
+ DetailList.Remove Self
+ Else
+ dx=height/3.0
+ dy=height
+ SetAlpha alp
+ SetBlend alphablend
+ SetRotation rot
+ SetColor 255,255,255
+ size:+0.05
+ SetScale 1,size
+ DrawRect (wx-x+dx)+C_ScreenMidX,(wy-y+dy)+C_ScreenMidY,10,1
+ EndIf
+ EndMethod
+
+ Function Create:Trail(x#,y#,hei#,alp#,rot#)
+ Local t:Trail = New Trail
+ t.x=x
+ t.y=y
+ t.height= hei
+ t.alp = alp
+ t.rot = rot
+ t.life= 100
+ t.size= 1
+ DetailList.AddLast t
+ EndFunction
+EndType
+
+Type ColTrail Extends Detail
+ Field height#,size#
+
+ Method Update( wx#, wy# )
+ Local dx#, dy#
+ alp:-0.01
+ size:*0.9
+ If alp<0
+ life = 0
+ EndIf
+
+ life:-1
+ If life<0
+ DetailList.Remove Self
+ Else
+ dx=height/3.0
+ dy=height
+ SetAlpha alp
+ SetBlend LIGHTBLEND
+ SetRotation rot
+ SetColor Col[0],Col[1],Col[2]
+ SetScale size,size
+ DrawRect (wx-x+dx)+C_ScreenMidX,(wy-y+dy)+C_ScreenMidY,10,4
+ EndIf
+ EndMethod
+
+ Function Create:ColTrail(x#,y#,hei#,alp#,rot#,col[],size#)
+ Local t:ColTrail = New ColTrail
+ t.x=x
+ t.y=y
+ t.height= hei
+ t.alp = alp
+ t.rot = rot
+ t.life= 100
+ t.size=size
+ t.col = col
+ DetailList.AddLast t
+ EndFunction
+EndType
+
+Type Mud Extends Detail
+
+ Method Update( wx#, wy# )
+ SetRotation rot
+ SetBlend ALPHABLEND
+ If life<50
+ alp = Float(life)/100.0
+ EndIf
+ SetAlpha alp
+ SetColor 0,0,0
+ SetScale 0.5,0.5
+
+ DrawImage media_mud,(wx-x)+C_ScreenMidX,(wy-y)+C_ScreenMidY
+ life:-1
+ If life<0
+ DetailList.Remove Self
+ EndIf
+ EndMethod
+
+ Function Create:Mud(x#,y#, rot#)
+ Local m:Mud = New Mud
+ m.x = x
+ m.y = y
+ m.rot = rot
+ m.alp = 0.5
+ m.life = 200
+ DetailList.AddLast M
+ EndFunction
+EndType
+
+Type smoke_prt
+ Field x#,y#,height# 'local to emitter
+ Field scl#
+ Field alp#
+ Field dx#,dy#
+ Field life
+ Field rot#, rotdir#
+
+ Method Draw( tx#, ty# )
+ y:-dy
+ x:+dx
+ life:-1
+ alp:-0.005
+ scl:+0.017
+ If alp<=0
+ life =0
+ Return
+ EndIf
+ SetBlend LightBlend
+ SetAlpha alp
+ SetScale scl,scl
+ SetRotation rot
+ rot:+rotdir
+ DrawImage media_smoke, tx+x, ty+y
+ EndMethod
+
+ Function Create:smoke_prt(scl#,alp#,x#,y#,dx#,dy#,life)
+ Local s:Smoke_prt = New Smoke_prt
+ s.x=x
+ s.y=y
+ s.dx=dx
+ s.dy=dy
+ s.life=life
+ s.alp=alp
+ s.scl=scl
+ s.rotdir = Rnd(-4,4)
+ s.rot = Rnd(360)
+ Return s
+ EndFunction
+EndType
+
+Type SmokeEmitter Extends Detail
+ Field smlist:TList = New TList
+
+ Field rd#,gn#,bl#, max_cnt=50, cur_cnt
+
+ Method Update( wx#, wy# )
+ Local sp:smoke_prt
+ SetColor 255*rd,255*gn,255*bl
+ For sp=EachIn smlist
+ sp.Draw((wx-x)+C_ScreenMidX, (wy-y)+C_ScreenMidY)
+ If sp.life<0
+ 'remove it and add another
+ smlist.remove sp
+ cur_cnt:-1
+ EndIf
+ Next
+ If cur_cnt<max_cnt
+ smlist.addfirst( smoke_prt.Create( 0.2, 0.3, 0, 0, Rnd( -0.1, 0.1 ), Rnd(0.1,1), 40 ) )
+ cur_cnt:+1
+ EndIf
+ life:-1
+ If life<0
+ DetailList.Remove Self
+ smlist.clear()
+ EndIf
+ EndMethod
+
+ Function Create:SmokeEmitter(x#,y#, rot#,cr#,cg#,cb#)
+ Local sm:SmokeEmitter = New SmokeEmitter
+ sm.x = x
+ sm.y = y
+ sm.rot = rot
+ sm.alp = 0.20
+ sm.life = 50
+ sm.rd=cr
+ sm.gn=cg
+ sm.bl=cb
+
+ DetailList.AddLast sm
+ EndFunction
+EndType
+
+Type Baddie Extends Entity
+ Field frame = 0
+ Field frm_p = 2
+ Field frm_t = 0
+ Field direct= 0
+ Field life
+
+ Method Update()
+ frm_t:+1
+ If frm_t>frm_p
+ frm_t= 0
+ frame:+1
+ If frame = 17 frame = 0
+ EndIf
+ x=x+(spd*Cos(rotation-90))
+ y=y+(spd*Sin(rotation-90))
+ If x>WorldSize x=-WorldSize
+ If x<-WorldSize x=WorldSize
+ If y>WorldSize y=-WorldSize
+ If y<-WorldSize y=WorldSize
+
+ spd=0.3
+ rotation:+1
+ If rotation<0 rotation:+360
+ If rotation>=360 rotation:-360
+ direct = rotation / 45
+ EndMethod
+ Method DrawShadow()
+ Local dx = -12
+ Local dy = -4
+ SetBlend alphaBlend
+ SetColor 0,0,0
+ SetAlpha 0.3
+ SetRotation -30
+ SetScale 0.43,0.47
+ DrawImage Media_zombie[direct],(MapPosition_X-x) + C_ScreenMidX+dx,(MapPosition_Y-y) + C_ScreenMidY+dy, frame
+
+ EndMethod
+ Method DrawBody()
+ SetBlend alphaBlend
+ SetColor 255,255,255
+ SetAlpha 1
+ SetRotation 0
+ SetScale 0.4,0.4
+ DrawImage Media_zombie[direct],(MapPosition_X-x) + C_ScreenMidX,(MapPosition_Y-y) + C_ScreenMidY, frame
+
+ EndMethod
+
+ Function Create:Baddie(x#,y#,spd#)
+ Local s:Baddie = New Baddie
+ s.x=x
+ s.y=y
+ s.rotation = Rand(350)
+ s.spd = Rnd(1,2)
+ s.height = 0
+ s.life = 100
+ ObjectList.AddFirst s
+ Return s
+ EndFunction
+
+EndType
+
+Type Scan_Object
+ Field alp#,x#,y#,typ
+ Method Update(cx#, cy#)
+ alp:-0.01
+ If alp<0
+ ScanList.Remove Self
+ Else
+ SetAlpha alp
+ DrawRect cx+x,cy+y,5,5
+ EndIf
+ EndMethod
+ Function Create:Scan_Object(x#, y#, typ )
+ Local so:Scan_Object = New Scan_Object
+ so.x=x
+ so.y=y
+ so.typ=typ
+ so.alp=0.4
+ ScanList.AddLast so
+ EndFunction
+EndType
+
+Function DrawScanner(p:Player) 'according to a certain player
+ SetBlend LightBlend
+ SetColor 255,255,255
+ SetAlpha 0.5
+ SetRotation Scanner_Rot+90
+ Scanner_rot:-5
+ If scanner_rot<0 scanner_rot:+360
+
+ SetScale Scanner_Scale,Scanner_Scale
+ DrawImage media_scan,Scanner_X,Scanner_Y
+ Local b:Baddie
+ Local so:Scan_Object
+ Local dx#,dy#,ang#,ln#
+
+ For b=EachIn ObjectList
+ 'get angle to object
+ dx=p.x-b.x
+ dy=p.y-b.y
+ ln=Sqr(dx*dx+dy*dy)
+ If ln<1200
+ ang = ATan2(dx,dy)
+ If ang<0 ang=360+ang
+ If Abs(ang-(Scanner_rot))<2
+ 'add a new dot on the scanner
+ Scan_Object.Create(dx/20.0, dy/20.0, 0 )
+ EndIf
+ EndIf
+ Next
+ SetBlend LightBlend
+ SetAlpha 0.25
+ SetColor 48,64,48
+ SetRotation 0
+ DrawRect Scanner_X-64,Scanner_Y-64,256,256
+ SetColor 0,255,0
+ For so=EachIn ScanList
+ so.Update(scanner_x,Scanner_Y)
+ Next
+EndFunction
+
+
--- /dev/null
+
+
+Graphics 640,480,32
+
+AutoMidHandle True
+Global BMX01IMG:TImage = LoadImage("media/B-Max.png",FILTEREDIMAGE|DYNAMICIMAGE)
+ConvertToBW BMX01IMG,0
+Global FLM01IMG:TImage = LoadAnimImage("media/flmstp.png",126,66,0,10)
+
+While Not KeyDown(KEY_ESCAPE)
+ Cls
+
+ SetColor 255,255,255
+ SetBlend ALPHABLEND
+ SetScale 1,1
+ SetAlpha Rnd(0.75,0.95)
+ DrawImage bmx01img,320+Rnd(-1,1),240+Rnd(-1,1),0
+ If Rand(40)=5
+ SetColor 128,128,128
+ SetBlend addativeBlend
+ x=Rnd(640)
+ DrawLine x,0,x+Rnd(-5,5),Rnd(400,480)
+ EndIf
+ SetBlend MASKBLEND
+ SetColor 255,255,255
+ SetScale 6.5,7.5
+ DrawImage FLM01IMG,320,240,a
+ a:+1
+ a=a Mod 10
+ Flip
+Wend
+
+
+Function ConvertToBW(i:TImage,frame)
+ Local col,a,r,g,b,cc,x=0,y=0
+ Local pix:TPixmap
+
+ pix=LockImage(i,frame)
+ While y<i.height
+ x=0
+ While x<i.width
+ col = ReadPixel( pix, x, y )
+ a = ( col & $ff000000)
+ r = ( col & $ff0000 ) Shr 16
+ g = ( col & $ff00 ) Shr 8
+ b = ( col & $ff )
+ cc= (r+g+b)/3
+ col = a | (cc Shl 16) | (cc Shl 8) | cc
+ WritePixel( pix, x, y, col )
+ x=x+1
+ Wend
+ y=y+1
+ Wend
+ UnlockImage i,frame
+EndFunction
+
--- /dev/null
+Rem
+ NEHE OpenGL Lesson 36: Radial Blur & Rendering To A Texture
+ converted to blitzmax by David Bird
+EndRem
+Strict
+
+'User Defined Variables
+Global C_WIDTH = 640
+Global C_HEIGHT= 480
+Global angle# 'Used To Rotate The Helix
+Global vertexes#[4,3] 'Holds Float Info For 4 Sets Of Vertices
+Global normal#[3] 'An Array To Store The Normal Data
+Global BlurTexture 'An Unsigned Int To Store The Texture Number
+Global tSize=256
+Const RAD_TO_DEG! = 57.2957795130823208767981548141052
+
+Function Cos_R!(rads!)
+ Return Cos(rads * RAD_TO_DEG)
+EndFunction
+
+Function Sin_R!(rads!)
+ Return Sin(rads * RAD_TO_DEG)
+EndFunction
+
+Function EmptyTexture() ' Create An Empty Texture
+ Local txtnumber ' Texture ID
+ Local data[tSize*tSize*4]
+ glGenTextures(1, Varptr txtnumber ) ' Create 1 Texture
+ glBindTexture(GL_TEXTURE_2D, txtnumber) ' Bind The Texture
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, 128, 128, 0,..
+ GL_RGBA, GL_UNSIGNED_BYTE, data) ' Build Texture Using Information In data
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR)
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR)
+ Return txtnumber ' Return The Texture ID
+EndFunction
+
+Function ReduceToUnit(vector:Float Ptr) ' Reduces A Normal Vector (3 Coordinates)
+ Local length#
+ ' Calculates The Length Of The Vector
+ length = Sqr((vector[0]*vector[0]) + (vector[1]*vector[1]) + (vector[2]*vector[2]))
+ If length = 0.0 length=1.0 ' Prevents Divide By 0 Error By Providing
+
+ vector[0]:/ length ' Dividing Each Element By
+ vector[1]:/ length ' The Length Results In A
+ vector[2]:/ length ' Unit Normal Vector.
+End Function
+
+Function ProcessHelix() ' Draws A Helix
+ Local a
+ Local x# ' Helix x Coordinate
+ Local y# ' Helix y Coordinate
+ Local z# ' Helix z Coordinate
+ Local phi# ' Angle
+ Local theta# ' Angle
+ Local v#,u# ' Angles
+ Local r# ' Radius Of Twist
+ Local twists = 5 ' 5 Twists
+ Local glfMaterialColor#[]=[0.4#,0.2#,0.8#,1.0#] ' Set The Material Color
+ Local specular#[]=[1.0#,1.0#,1.0#,1.0#] ' Sets Up Specular Lighting
+ Local tv1#[3],tv2#[3]
+
+ glLoadIdentity() ' Reset The Modelview Matrix
+ gluLookAt(0, 5, 50, 0, 0, 0, 0, 1, 0) ' Eye Position (0,5,50) Center Of Scene (0,0,0), Up On Y Axis
+
+ glPushMatrix() ' Push The Modelview Matrix
+
+ glTranslatef(0,0,-50) ' Translate 50 Units Into The Screen
+ glRotatef(angle/2.0,1,0,0) ' Rotate By angle/2 On The X-Axis
+ glRotatef(angle/3.0,0,1,0) ' Rotate By angle/3 On The Y-Axis
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,glfMaterialColor)
+ glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specular)
+
+ r=1.5 ' Radius
+
+ glBegin(GL_QUADS) ' Begin Drawing Quads
+ For phi=0 Until 360 Step 20.0 ' 360 Degrees In Steps Of 20
+ For theta=0 Until (360*twists) Step 20.0 ' 360 Degrees * Number Of Twists In Steps Of 20
+ v=(phi/180.0*3.142) ' Calculate Angle Of First Point ( 0 )
+ u=(theta/180.0*3.142) ' Calculate Angle Of First Point ( 0 )
+
+ x=(cos_r(u)*(2.0+cos_r(v) ))*r ' Calculate x Position (1st Point)
+ y=(sin_r(u)*(2.0+cos_r(v) ))*r ' Calculate y Position (1st Point)
+ z=((( u-(2.0*3.142)) + sin_r(v) )* r) ' Calculate z Position (1st Point)
+
+ vertexes[0,0]=x ' Set x Value Of First Vertex
+ vertexes[0,1]=y ' Set y Value Of First Vertex
+ vertexes[0,2]=z ' Set z Value Of First Vertex
+
+ v=(phi/180.0*3.142) ' Calculate Angle Of Second Point ( 0 )
+ u=((theta+20)/180.0*3.142) ' Calculate Angle Of Second Point ( 20 )
+
+ x=(cos_r(u)*(2.0+cos_r(v) ))*r ' Calculate x Position (2nd Point)
+ y=(sin_r(u)*(2.0+cos_r(v) ))*r ' Calculate y Position (2nd Point)
+ z=((( u-(2.0*3.142)) + sin_r(v) ) * r) ' Calculate z Position (2nd Point)
+
+ vertexes[1,0]=x ' Set x Value Of Second Vertex
+ vertexes[1,1]=y ' Set y Value Of Second Vertex
+ vertexes[1,2]=z ' Set z Value Of Second Vertex
+
+ v=((phi+20)/180.0*3.142) ' Calculate Angle Of Third Point ( 20 )
+ u=((theta+20)/180.0*3.142) ' Calculate Angle Of Third Point ( 20 )
+
+ x=(cos_r(u)*(2.0+cos_r(v) ))*r ' Calculate x Position (3rd Point)
+ y=(sin_r(u)*(2.0+cos_r(v) ))*r ' Calculate y Position (3rd Point)
+ z=((( u-(2.0*3.142)) + sin_r(v) ) * r) ' Calculate z Position (3rd Point)
+
+ vertexes[2,0]=x ' Set x Value Of Third Vertex
+ vertexes[2,1]=y ' Set y Value Of Third Vertex
+ vertexes[2,2]=z ' Set z Value Of Third Vertex
+
+ v=((phi+20)/180.0*3.142) ' Calculate Angle Of Fourth Point ( 20 )
+ u=((theta)/180.0*3.142) ' Calculate Angle Of Fourth Point ( 0 )
+
+ x=Float(cos_r(u)*(2.0+cos_r(v) ))*r ' Calculate x Position (4th Point)
+ y=Float(sin_r(u)*(2.0+cos_r(v) ))*r ' Calculate y Position (4th Point)
+ z=Float((( u-(2.0*3.142)) + sin_r(v) ) * r) ' Calculate z Position (4th Point)
+
+ vertexes[3,0]=x ' Set x Value Of Fourth Vertex
+ vertexes[3,1]=y ' Set y Value Of Fourth Vertex
+ vertexes[3,2]=z ' Set z Value Of Fourth Vertex
+
+ For a=0 Until 3
+ ' Calculate The Vector From Point 1 To Point 0
+ tv1[a] = vertexes[0,a] - vertexes[1,a] ' Vector 1.x=Vertex[0].x-Vertex[1].x
+ tv2[a] = vertexes[1,a] - vertexes[2,a] ' Vector 2.x=Vertex[0].x-Vertex[1].x
+ ' Compute The Cross Product To Give Us A Surface Normal
+ Next
+ normal[0] = tv1[1]*tv2[2] - tv1[2]*tv2[1]
+ normal[1] = tv1[2]*tv2[0] - tv1[0]*tv2[2]
+ normal[2] = tv1[0]*tv2[1] - tv1[1]*tv2[0]
+ ReduceToUnit(normal) ' Normalize The Vectors
+
+ glNormal3f(normal[0],normal[1],normal[2]) ' Set The Normal
+
+ ' Render The Quad
+ glVertex3f(vertexes[0,0],vertexes[0,1],vertexes[0,2])
+ glVertex3f(vertexes[1,0],vertexes[1,1],vertexes[1,2])
+ glVertex3f(vertexes[2,0],vertexes[2,1],vertexes[2,2])
+ glVertex3f(vertexes[3,0],vertexes[3,1],vertexes[3,2])
+ Next
+ Next
+ glEnd() ' Done Rendering Quads
+
+ glPopMatrix() ' Pop The Matrix
+EndFunction
+
+Function ViewOrtho() ' Set Up An Ortho View
+ glMatrixMode(GL_PROJECTION) ' Select Projection
+ glPushMatrix() ' Push The Matrix
+ glLoadIdentity() ' Reset The Matrix
+ glOrtho( 0, C_WIDTH ,C_HEIGHT , 0, -10, 10 ) ' Select Ortho Mode (640x480)
+ glMatrixMode(GL_MODELVIEW) ' Select Modelview Matrix
+ glPushMatrix() ' Push The Matrix
+ glLoadIdentity() ' Reset The Matrix
+EndFunction
+
+Function ViewPerspective() ' Set Up A Perspective View
+ glMatrixMode( GL_PROJECTION ) ' Select Projection
+ glPopMatrix() ' Pop The Matrix
+ glMatrixMode( GL_MODELVIEW ) ' Select Modelview
+ glPopMatrix() ' Pop The Matrix
+EndFunction
+
+Function RenderToTexture() ' Renders To A Texture
+ glViewport(0,0,tSize,tSize) ' Set Our Viewport (Match Texture Size)
+ ProcessHelix() ' Render The Helix
+
+ glBindTexture(GL_TEXTURE_2D,BlurTexture) ' Bind To The Blur Texture
+ ' Copy Our ViewPort To The Blur Texture (From 0,0 To 128,128... No Border)
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 0, 0, tSize, tSize, 0)
+ glClearColor(0.0, 0.0, 0.25, 0.5) ' Set The Clear Color To Medium Blue
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) ' Clear The Screen And Depth Buffer
+ glViewport(0 , 0,C_WIDTH ,C_HEIGHT) ' Set Viewport (0,0 to 640x480)
+EndFunction
+
+Function DrawBlur(times, inc#) ' Draw The Blurred Image
+ Local num
+ Local spost# = 0.0 ' Starting Texture Coordinate Offset
+ Local alphainc# = 0.9 / Float(times) ' Fade Speed For Alpha Blending
+ Local alpha# = 0.1 ' Starting Alpha Value
+
+ ' Disable AutoTexture Coordinates
+ glDisable(GL_TEXTURE_GEN_S)
+ glDisable(GL_TEXTURE_GEN_T)
+
+ glEnable(GL_TEXTURE_2D) ' Enable 2D Texture Mapping
+ glDisable(GL_DEPTH_TEST) ' Disable Depth Testing
+ glBlendFunc(GL_SRC_ALPHA,GL_ONE) ' Set Blending Mode
+ glEnable(GL_BLEND) ' Enable Blending
+ glBindTexture(GL_TEXTURE_2D,BlurTexture) ' Bind To The Blur Texture
+ ViewOrtho() ' Switch To An Ortho View
+ alphainc = alpha / times ' alphainc=0.2f / Times To Render Blur
+
+ glBegin(GL_QUADS) ' Begin Drawing Quads
+ For num = 0 Until times ' Number Of Times To Render Blur
+ glColor4f(1.0, 1.0, 1.0, alpha) ' Set The Alpha Value (Starts At 0.2)
+ glTexCoord2f(0+spost,1-spost) ' Texture Coordinate ( 0, 1 )
+ glVertex2f(0,0) ' First Vertex ( 0, 0 )
+
+ glTexCoord2f(0+spost,0+spost) ' Texture Coordinate ( 0, 0 )
+ glVertex2f(0,C_HEIGHT) ' Second Vertex ( 0, 480 )
+
+ glTexCoord2f(1-spost,0+spost) ' Texture Coordinate ( 1, 0 )
+ glVertex2f(C_WIDTH,C_HEIGHT) ' Third Vertex ( C_WIDTH, 480 )
+
+ glTexCoord2f(1-spost,1-spost) ' Texture Coordinate ( 1, 1 )
+ glVertex2f(C_WIDTH,0) ' Fourth Vertex ( C_WIDTH, 0 )
+
+ spost:+ inc ' Gradually Increase spost (Zooming Closer To Texture Center)
+ alpha = alpha - alphainc ' Gradually Decrease alpha (Gradually Fading Image Out)
+ Next
+ glEnd() ' Done Drawing Quads
+
+ ViewPerspective() ' Switch To A Perspective View
+
+ glEnable(GL_DEPTH_TEST) ' Enable Depth Testing
+ glDisable(GL_TEXTURE_2D) ' Disable 2D Texture Mapping
+ glDisable(GL_BLEND) ' Disable Blending
+
+
+ glBindTexture(GL_TEXTURE_2D,0) ' Unbind The Blur Texture
+EndFunction
+
+Function Initialize ()
+ ' Start Of User Initialization
+ angle = 0.0 ' Set Starting Angle To Zero
+ BlurTexture = EmptyTexture() ' Create Our Empty Texture
+ glViewport(0 , 0,C_WIDTH ,C_HEIGHT) ' Set Up A Viewport
+ glMatrixMode(GL_PROJECTION) ' Select The Projection Matrix
+ glLoadIdentity() ' Reset The Projection Matrix
+ gluPerspective(50, Float(C_WIDTH)/Float(C_HEIGHT), 5, 2000) ' Set Our Perspective
+ glMatrixMode(GL_MODELVIEW) ' Select The Modelview Matrix
+ glLoadIdentity() ' Reset The Modelview Matrix
+ glEnable(GL_DEPTH_TEST) ' Enable Depth Testing
+
+ Local global_ambient#[]=[0.2#, 0.2#, 0.2#, 1.0#] ' Set Ambient Lighting To Fairly Dark Light (No Color)
+ Local light0pos#[]= [0.0#, 5.0#, 10.0#, 1.0#] ' Set The Light Position
+ Local light0ambient#[]= [0.2#, 0.2#, 0.2#, 1.0#] ' More Ambient Light
+ Local light0diffuse#[]= [0.3#, 0.3#, 0.3#, 1.0#] ' Set The Diffuse Light A Bit Brighter
+ Local light0specular#[]=[0.8#, 0.8#, 0.8#, 1.0#] ' Fairly Bright Specular Lighting
+
+ Local lmodel_ambient#[]=[ 0.2#,0.2#,0.2#,1.0#] ' And More Ambient Light
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT,lmodel_ambient) ' Set The Ambient Light Model
+
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient) ' Set The Global Ambient Light Model
+ glLightfv(GL_LIGHT0, GL_POSITION, light0pos) ' Set The Lights Position
+ glLightfv(GL_LIGHT0, GL_AMBIENT, light0ambient) ' Set The Ambient Light
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, light0diffuse) ' Set The Diffuse Light
+ glLightfv(GL_LIGHT0, GL_SPECULAR, light0specular) ' Set Up Specular Lighting
+ glEnable(GL_LIGHTING) ' Enable Lighting
+ glEnable(GL_LIGHT0) ' Enable Light0
+ glShadeModel(GL_SMOOTH) ' Select Smooth Shading
+ glMateriali(GL_FRONT, GL_SHININESS, 128)
+ glClearColor(0.0, 0.0, 0.0, 0.5) ' Set The Clear Color To Black
+ Return 1 ' Return TRUE (Initialization Successful)
+EndFunction
+
+Function Update(milliseconds#) ' Perform Motion Updates Here
+ If KeyDown(KEY_ESCAPE) End
+ angle:+(milliseconds/5.0) ' Update angle Based On The Clock
+EndFunction
+
+Function Draw() ' Draw The Scene
+ glClearColor(0.0, 0.0, 0.0, 0.25) ' Set The Clear Color To Black
+ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) ' Clear Screen And Depth Buffer
+ glLoadIdentity() ' Reset The View
+
+ RenderToTexture() ' Render To A Texture
+ ProcessHelix() ' Draw Our Helix
+ DrawBlur(25,.02) ' Draw The Blur Effect
+ glFlush () ' Flush The GL Rendering Pipeline
+EndFunction
+
+GLGraphics C_WIDTH,C_HEIGHT,32
+
+Initialize()
+
+Repeat
+ Update(25)
+ Draw()
+ Flip
+Forever
--- /dev/null
+'
+' Using Lightblend/image as a light
+'
+Strict
+
+'Include media in the final file
+Incbin "media/fl.png"
+Incbin "media/light.png"
+Incbin "media/B-Max.png"
+
+'set the graphics mode
+Graphics 640, 480, 32
+
+'Hide the pointer
+HideMouse
+
+'All images will be handled from the center
+AutoMidHandle True
+
+'Load images in now. incbin points the file to the included images
+Global backImage:TImage = LoadImage("incbin::media/fl.png")
+Global logoImage:TImage = LoadImage("incbin::media/B-Max.png")
+Global lightImage:TImage = LoadImage("incbin::media/light.png")
+
+'setup some vars now
+Local x#, y#, tim#
+Local mx, my
+Local lightcolor[] = [255,255,255]
+Local BackColor[] = [128,128,128]
+
+'Main Loop
+While Not KeyDown(KEY_ESCAPE)
+ x:+5*Sin(tim)
+ y:+1
+ tim:+4
+ mx = MouseX()
+ my = MouseY()
+
+ 'draw the backgound
+ SetScale 1,1
+ SetColor BackColor[0],BackColor[1],BackColor[2]
+ TileImage backImage,x,y
+
+ 'draw the logo
+ SetBlend MaskBlend
+ SetColor 0,0,0
+ DrawImage logoimage,340,260
+ SetColor 100,100,100
+ DrawImage logoimage,320,240
+
+ 'Draw the light
+ SetBlend LightBlend
+ SetColor LightColor[0],LightColor[1],LightColor[2]
+ SetScale 1.5+1*Cos(tim), 1.5+1*Cos(tim)
+ DrawImage lightimage, mx, my
+
+ 'draw the mousepointer
+ SetBlend SolidBlend
+ SetColor 255,255,255
+ SetScale 1,1
+ DrawLine mx-5,my,mx+5,my
+ DrawLine mx,my-5,mx,my+5
+ Flip
+Wend
+
+End
+
--- /dev/null
+
+Strict
+
+Const WIDTH = 640,HEIGHT = 480, DEPTH = 32
+Const ShadowOn = 1
+Const ShadowSize = 10
+
+Global gtime
+Global Pipes_img:TImage
+Global Tiles_img:TImage
+Global logo_img:TImage
+Global paddle:TImage
+Global ballvis:TImage
+'Setup the level
+Global Tilelist:TList
+Global Balllist:TList
+Global playerX#,PlayerY#
+Global Score
+
+Private
+ Global ballcount=0
+
+ Function Minf#(a#,b#)
+ If a<b Return a
+ Return b
+ EndFunction
+ Function Maxf#(a#,b#)
+ If a>b Return a
+ Return b
+ EndFunction
+Public
+
+Type ball
+ Field x#,y#
+ Field dx#,dy#,spd#,rot#=0
+
+ Field visual
+
+ Method Update()
+ x:+ (dx * spd)
+ y:+ (dy * spd)
+ If x<34 Or x>606
+ dx=-dx
+ EndIf
+ If y<50
+ dy=-dy
+ EndIf
+ If y>Height-8
+ ballcount:-1
+ BallList.Remove(Self)
+ Else
+ If dy>0
+ If y>playery-8
+ If x>playerx-32 And x<playerx+32
+ dy=dy*-1
+ EndIf
+ EndIf
+ EndIf
+ rot:+10
+ EndIf
+ EndMethod
+
+ Method Draw(offx,offy)
+ SetRotation rot
+ DrawImage ballvis,x+offx,y+offy
+ SetRotation 0
+ EndMethod
+
+ Function Create:Ball(x=Width/2 , y=Height/2)
+ Local b:Ball = New Ball
+ ballcount:+1
+ b.x = x
+ b.y = y
+ b.dx = Rnd(-2, 2)
+ b.dy = Rnd(-2, 2)
+ b.spd = 4'0.1
+ Return b
+ EndFunction
+EndType
+
+'all tiles are a standard size so
+Type Tile
+ Field x#,y#
+ Field typ = 0
+ Field state = 0
+ Field rot#=0,size#=1
+
+ Method Draw(offx,offy)
+ Select state
+ Case 0
+ SetRotation rot
+ If size>1
+ SetScale size,size
+ size=size*0.9
+ Else
+ size = 1
+ SetScale 0.95+(0.05*Cos(gTime)),0.95+(0.05*Sin(gTime))
+ EndIf
+ Case 1
+ SetRotation rot
+ SetScale size,size
+ EndSelect
+ Select typ
+ Case 0
+ DrawImage tiles_img,x+offx,y+offy+(2*Sin(gtime)),0
+ Case 1
+ DrawImage tiles_img,x+offx,y+offy+(2*Sin(gtime)),1
+ Case 2
+ DrawImage tiles_img,x+offx,y+offy+(2*Sin(gtime)),2
+ Case 3
+ DrawImage tiles_img,x+offx,y+offy+(2*Sin(gtime)),3
+ Case 4
+ DrawImage tiles_img,x+offx,y+offy+(2*Sin(gtime)),4
+ EndSelect
+
+ SetScale 1,1
+ SetRotation 0
+ EndMethod
+
+ Method Update()
+ Local c
+ Local b:Ball
+ If state = 0
+ 'Check this tile for collision with all of the balls
+ For b=EachIn BallList
+ If b.x>x-4 And b.x<x+24
+ If b.y>y-4 And b.y<y+24
+ b.dy=-b.dy
+ Select typ
+ Case 1
+ If ballcount=1
+ For c=0 Until 2
+ BallList.AddLast(ball.Create(b.x,b.y))
+ Next
+ EndIf
+ state = 1
+ size = 1
+ Case 2
+ typ = 3
+ size=1.5
+ Case 3
+ typ = 4
+ size=1.5
+ Default
+ Score:+((1+typ)*100)
+ state = 1
+ EndSelect
+ Return
+ EndIf
+ EndIf
+ Next
+ Else
+ y:+4
+ rot:+5
+ size:-.005
+ If y>HEIGHT
+ BallList.Remove(b)
+ EndIf
+ EndIf
+ EndMethod
+
+
+ Function Create:Tile(x=0,y=0,typ=0)
+ Local t:Tile = New Tile
+ t.x=x
+ t.y=y
+ t.typ = typ
+ Return t
+ EndFunction
+EndType
+
+Graphics WIDTH,HEIGHT,DEPTH
+
+AutoMidHandle True
+
+'Media
+Global back:TImage[2]
+back[0] = LoadImage("media\back1.png")
+back[1] = LoadImage("media\back2.png")
+Pipes_img=LoadAnimImage("media\pipes.png",32,32,0,4)
+Tiles_img=LoadAnimImage("media\tiles.png",32,20,0,5)
+paddle = LoadImage("media\paddle.png")
+ballvis = LoadImage("media\ball.png")
+logo_img=LoadImage("media\B-Max.png")
+
+
+Tilelist:TList = New TList
+Balllist:TList = New TList
+playerX# = Width/2
+PlayerY# = Height-40
+Score=0
+
+ResetGame()
+
+
+
+HideMouse
+While Not KeyDown(KEY_ESCAPE)
+
+ 'Update Players Position
+ playerx = minf(574,maxf(64,MouseX()))
+ 'Update Balls
+ UpdateBalls()
+ 'Update Tiles
+ UpdateTiles()
+ 'Draw Level
+ DrawLevel()
+
+ gTime:+10
+
+ SetAlpha .75
+ SetColor 0,0,255
+ DrawRect 0,0,Width,20
+
+ SetBlend ALPHABLEND
+
+ SetAlpha 0.5
+ SetColor 0,0,0
+ DrawText "Score:"+Score,4,4
+
+ SetAlpha 1
+ SetColor 255,255,255
+ DrawText "Score:"+Score+" "+ballcount,2,2
+
+ Flip
+Wend
+
+End
+
+
+Function DrawLevel()
+ Local w,aa#
+ TileImage back[1],0,gTime/20
+ SetBlend ALPHABLEND
+ DrawImage logo_img,width/2,height/2
+ aa#=0.5+(0.5*Cos(gtime/50))
+ SetBlend AlphaBLEND
+ SetAlpha aa
+ TileImage back[0],0,gTime/10
+
+ If ShadowOn
+ SetColor 0,0,0
+ SetBlend AlphaBLEND
+ SetAlpha 0.5
+ DrawPipes ShadowSize+16,ShadowSize+16
+
+ DrawTiles ShadowSize+16,ShadowSize+10
+ DrawPlayer ShadowSize,ShadowSize
+ DrawBalls ShadowSize,ShadowSize
+ EndIf
+
+ SetColor 255,255,255
+ SetBlend MASKBLEND
+ SetAlpha 1
+ DrawPipes()
+ DrawTiles()
+ DrawPlayer()
+ DrawBalls()
+EndFunction
+
+Function ResetGame()
+ TileList = New TList
+ BallList = New TList
+ Local x,y
+ For y=0 Until 5
+ For x=0 Until 18
+ Tilelist.AddLast(Tile.Create(38+x*32,(y*24)+66,4-Y))
+ Next
+ Next
+
+ BallList.AddLast(Ball.Create())
+EndFunction
+
+Function DrawPipes(x=16,y=16)
+ Local tmp
+
+ 'top
+ For tmp=0 Until 18
+ DrawImage Pipes_img,x+32+(tmp*32),y+16,3
+ Next
+
+ 'sides
+ For tmp=0 Until 14
+ DrawImage Pipes_img,x,y+48+(tmp*32),2
+ DrawImage Pipes_img,x+Width-32,y+48+(tmp*32),2
+ Next
+
+ 'Corners
+ DrawImage Pipes_img,x,y+16 ,0
+ DrawImage Pipes_img,x+Width-32,y+16,1
+
+EndFunction
+
+Function DrawTiles(x_off=10, y_off=10)
+ Local tl:Tile
+ Local any=0
+ For tl=EachIn TileList
+ tl.Draw(x_off, y_off)
+ any=1
+ Next
+ If Not any
+ ResetGame()
+ score:+10000
+ EndIf
+EndFunction
+
+Function DrawBalls(x_off=0, y_off=0)
+ Local bl:Ball
+ For bl=EachIn balllist
+ bl.Draw(x_off, y_off)
+ Next
+EndFunction
+
+Function UpdateBalls()
+ If ballcount=0
+ BallList.AddLast(Ball.Create(Width/2,Height/2))
+ Else
+ Local bl:Ball
+ For bl = EachIn BallList
+ bl.Update()
+ Next
+ EndIf
+EndFunction
+
+Function UpdateTiles()
+ Local tl:Tile
+ For tl=EachIn tilelist
+ tl.Update()
+ Next
+EndFunction
+
+Function DrawPlayer(x_off=0,y_off=0)
+ DrawImage paddle, playerx+x_off, playery+y_off
+End Function
--- /dev/null
+<?xml version='1.0'?>
+<!--This project file was generated by Protean
+Editing it may render it inoperable. You have been warned!-->
+<xml>
+ <Project GUID="2EF9C725-F0F3-40F8-AFF9-155E7F196071" Expanded="True">
+ <Version>
+ <Formatting Short="%major%.%minor%" Long="%major%.%minor%.%build%.%revision%" />
+ <Values Major="0" Minor="1" Build="11" Revision="1113" />
+ <Rules RuleData="0,0,1,0,0,0,0,0,0,0,0,1,5000,0,0,0,0,0,0,0,0" />
+ </Version>
+ <Configurations>
+ <Configuration Name="Release">
+ <Property Name="Blitz_BeforeEXE_Options">True,True,True</Property>
+ <Property Name="Blitz_OverrideDebug">False</Property>
+ <Property Name="Blitz_IncludeLongVersion">True</Property>
+ <Property Name="Blitz_Compiler">Blitz Basic</Property>
+ <Property Name="Blitz_AfterCompile_Options">True,True,True</Property>
+ <Property Name="Blitz_IncludeShortVersion">True</Property>
+ <Property Name="Blitz_DebugEnabled">False</Property>
+ <Property Name="Blitz_ShortVersionVariable">VersionShort</Property>
+ <Property Name="Blitz_BeforeCheck_Options">True,True,True</Property>
+ <Property Name="Blitz_AfterEXE_Options">True,True,True</Property>
+ <Property Name="Blitz_LongVersionVariable">VersionLong</Property>
+ <Property Name="Blitz_BeforeCompile_Options">True,True,True</Property>
+ <Property Name="Blitz_CompileType">0</Property>
+ <Property Name="Blitz_AfterCheck_Options">True,True,True</Property>
+ <Property Name="Blitz_CommandLineEnabled">True</Property>
+ </Configuration>
+ </Configurations>
+ <Options>
+ <WorkingDirectory>D:\BlitzMax\robhutchinson\digesteroids</WorkingDirectory>
+ <SelectedConfiguration>Release</SelectedConfiguration>
+ </Options>
+ <File GUID="473AF815-1E27-48BD-9630-70D541441EB9" Open="True" Line="0" Column="0" Name="MathUtil.bmx">
+ <Configurations>
+ <Configuration Name="Release" />
+ </Configurations>
+ </File>
+ <File GUID="B02F06E0-4386-49CC-B5F8-35AB8BF082C8" Open="True" Line="0" Column="0" Name="simplephysics.bmx">
+ <Configurations>
+ <Configuration Name="Release" />
+ </Configurations>
+ </File>
+ <File GUID="5ED55F16-174B-42BD-A47A-1F99E39E1DF1" Open="True" Line="0" Column="0" Name="dynamicgame.bmx">
+ <Configurations>
+ <Configuration Name="Release" />
+ </Configurations>
+ </File>
+ <File GUID="9AF2C6A5-D96E-4391-9CA8-911FE7253975" Open="True" Line="901" Column="57" Name="digesteroids.bmx">
+ <Configurations>
+ <Configuration Name="Release" />
+ </Configurations>
+ </File>
+ <File GUID="31498CEA-525C-457E-A80B-754134C4A5F5" Open="False" Line="0" Column="0" Name="minitimer.bmx">
+ <Configurations>
+ <Configuration Name="Release" />
+ </Configurations>
+ </File>
+ <File GUID="9B56E13A-1332-43FE-BB98-B1218E5F4BEA" Open="False" Line="0" Column="0" Name="graphics\bullet1.png">
+ <Configurations>
+ <Configuration Name="Release" />
+ </Configurations>
+ </File>
+ <File GUID="8569E9CB-E52A-4E82-81BA-C3A58B63B620" Open="False" Line="0" Column="0" Name="graphics\bullet2.png">
+ <Configurations>
+ <Configuration Name="Release" />
+ </Configurations>
+ </File>
+ <File GUID="EA76DE94-7251-4DE9-B305-DDA538BBE239" Open="False" Line="0" Column="0" Name="graphics\digestive.png">
+ <Configurations>
+ <Configuration Name="Release" />
+ </Configurations>
+ </File>
+ <File GUID="B3632217-1AAA-45AE-90F4-D7D6113B69D7" Open="False" Line="0" Column="0" Name="graphics\ship.png">
+ <Configurations>
+ <Configuration Name="Release" />
+ </Configurations>
+ </File>
+ <File GUID="45A32819-5298-4540-A707-E135C65C7672" Open="False" Line="0" Column="0" Name="graphics\stars.png">
+ <Configurations>
+ <Configuration Name="Release" />
+ </Configurations>
+ </File>
+ <Directory Expanded="False" Name="graphics">
+ <Configurations>
+ <Configuration Name="Release" />
+ </Configurations>
+ </Directory>
+ </Project>
+</xml>
\ No newline at end of file
--- /dev/null
+' *******************************************************************
+' Source: Math Util
+' Version: 1.00
+' Author: Rob Hutchinson 2004
+' Email: rob@proteanide.co.uk
+' WWW: http://www.proteanide.co.uk/
+' -------------------------------------------------------------------
+' Some generic functions. Certainly nothing to do with Math. HURRAH!
+' -------------------------------------------------------------------
+' Required:
+' - Nothing.
+' *******************************************************************
+
+Type MathUtil
+
+'#Region Method: QWrapF
+ ' Wraps only once, if the value is out of the range by twice as much, this function will return incorrect values,
+ ' However, it is faster than Wrap. Use Wrap for accurate results.
+ Function QWrapF:Float(Value:Float,Minimum:Float,Maximum:Float)
+ If Value > Maximum
+ Return Minimum + (Value Mod Maximum)
+ ElseIf Value < Minimum
+ Return Maximum - Abs(Minimum - Value)
+ EndIf
+ Return Value
+ End Function
+'#End Region
+'#Region Method: QWrapDegrees
+ Function QWrapDegrees:Double(Value:Double)
+ If Value > 359.0
+ Return (0.0 + (Value Mod 359.0))
+ ElseIf Value < 0.0
+ Return (359.0 - Abs(0.0 - Value))
+ EndIf
+ Return Value
+ End Function
+'#End Region
+'#Region Method: Wrap
+ Function Wrap:Float(Value:Float,Minimum:Float,Maximum:Float)
+ Local Difference:Float = Maximum - Minimum
+ While ((Value < Minimum) Or (Value => Maximum))
+ If Value => Maximum
+ Value:- Difference
+ Else
+ If Value < Minimum
+ Value:+ Difference
+ EndIf
+ EndIf
+ Wend
+ Return value
+ End Function
+'#End Region
+'#Region Method: CurveValue
+ Function CurveValue:Float(Current:Float,Destination:Float,Curve:Int)
+ Current = Current + ( (Destination - Current) /Curve)
+ Return Current
+ End Function
+'#End Region
+
+End Type
--- /dev/null
+' *******************************************************************
+' Digesteroids - V1.0
+' --------------------------------------------------------
+' An evil biscuit corporation are using their army of
+' sweet snack food to take over the world. Use your ship
+' to take down the evil invading corporation before
+' they infest the world with dunkables sweetmeal biscuits.
+' REQUIRES 1024x768x32! @75Hz
+' --------------------------------------------------------
+' Author: Rob Hutchinson 2004
+' Email: rob@proteanide.co.uk
+' Written entirely in Protean IDE:
+' WWW: http://www.proteanide.co.uk/
+' --------------------------------------------------------
+' Yep, there's stuff in here, I didnt get time to implement
+' Pickups, Weapons and Vortexes.
+' *******************************************************************
+
+Strict
+
+' Import various utilities.
+Import "simplephysics.bmx"
+Import "dynamicgame.bmx"
+Import "MathUtil.bmx"
+
+' Screen settings.
+Const WIDTH = 1024 ' Width of the screen.
+Const HEIGHT = 768 ' Height of the screen.
+Const DEPTH = 32 ' Depth of the screen.
+Const REFRESHRATE = 75 ' How often to update the screen (Per second).
+
+' Game Settings
+Const DYNAMICTIMING = True ' If true then the game will try to keep up with
+Const DESIREDFPS = 75 ' The Desired FPS of the game, can be independant of Refresh Rate.
+
+Function ScreenPan(OffsetX:Float, OffsetY:Float)
+ Local Width,Height,Depth,Hz
+ Width=GraphicsWidth()
+ Height=GraphicsHeight()
+ Depth=GraphicsDepth()
+ Hz=GraphicsHertz()
+ glMatrixMode GL_PROJECTION
+ glLoadIdentity
+ glOrtho 0,Width,Height,0,-1,1
+ glTranslatef OffsetX, OffsetY,0
+ glMatrixMode GL_MODELVIEW
+EndFunction
+
+Function QFlushKeys()
+ ' Quick implementation of flushkeys as BMX doesnt have one at time of writing, sure it
+ ' will be added though.
+ For Local Key:Int = 0 To 255
+ KeyHit(Key)
+ KeyDown(Key)
+ Next
+End Function
+
+'#Region Scene: MainMenu
+ ' The main menu scene.
+ Type MainMenuScene Extends T2DDynamicGameScene
+
+'#Region DrawTextCentered
+ Function DrawTextCentered:Int(Text:String,Y:Int)
+ Local Out:Int = (WIDTH / 2) - (TextWidth(Text) / 2)
+ DrawText(Text, Out, Y)
+ Return Out
+ End Function
+'#End Region
+
+'#Region Declarations
+ Const STARS_PER_SECOND = 6
+ Field BackColor:Float
+ Field Direction:Float = 0.01
+ Field ExitGame:Int = False
+ Field Music:TSound
+ Field MusicChannel:TChannel
+ Field Title:TImage
+ Field LowerTitle:TImage
+ Field Options:TImage
+ Field Craft:TImage
+ Field EnteredText:String = ""
+ Field LastHighScore:Int
+ Field ShowCredits:Int = False
+
+ Field Viewing:Int = VIEW_MENU
+ Field HoverOver:Int = VIEW_START
+
+ Const VIEW_MENU = 0
+ Const VIEW_START = 0
+ Const VIEW_INSTRUCTIONS = 1
+ Const VIEW_HIGHSCORES = 2
+ Const VIEW_QUIT = 3
+ Const VIEW_ENTERHIGH = 4
+
+ Field Stars:TPhysicsProviderCollection = New TPhysicsProviderCollection
+ Field World:TWorldPhysicsProvider = TWorldPhysicsProvider.Create(0.0, 0.0, 0.0, True)
+'#End Region
+
+'#Region Method: FinishScoreEntry
+ Method FinishScoreEntry()
+ Self.ViewMenu(VIEW_HIGHSCORES)
+ Scores.Add(Self.LastHighScore, Self.EnteredText)
+
+ ' Add this to the high scores..
+ Self.LastHighScore = 0
+ Self.EnteredText = ""
+ End Method
+'#End Region
+'#Region Method: ViewMenu
+ Method ViewMenu(Menu:Int)
+ Self.Viewing = Menu
+ Self.HoverOver = Menu
+ QFlushKeys()
+ End Method
+'#End Region
+
+'#Region Method: Update
+ Method Update()
+ Self.Stars.ApplyPhysics()
+
+ ' Add new stars..
+ For Local CountStars:Int = 0 To STARS_PER_SECOND
+ Local Star:TMenuStar = TMenuStar.Create(World, Self, 10.0)
+ Self.Stars.AddLast(Star)
+ Next
+
+ ' Do enter key checking..
+ Select Self.Viewing
+ Case VIEW_MENU
+ If KeyHit(KEY_ENTER) Then
+ Select Self.HoverOver
+ Case VIEW_START
+ Self.TerminateMainLoop = True
+
+ Case VIEW_INSTRUCTIONS
+ Self.ViewMenu(VIEW_INSTRUCTIONS)
+
+ Case VIEW_HIGHSCORES
+ Self.ViewMenu(VIEW_HIGHSCORES)
+
+ Case VIEW_QUIT
+ Self.TerminateMainLoop = True
+ Self.ExitGame = True
+
+ End Select
+ EndIf
+
+ If KeyHit(KEY_UP)
+ Self.HoverOver :- 1
+ If Self.HoverOver < VIEW_START Then Self.HoverOver = VIEW_START
+ EndIf
+ If KeyHit(KEY_DOWN)
+ Self.HoverOver :+ 1
+ If Self.HoverOver > VIEW_QUIT Then Self.HoverOver = VIEW_QUIT
+ EndIf
+
+ If KeyHit(KEY_ESCAPE) Then
+ Self.TerminateMainLoop = True
+ Self.ExitGame = True
+ Else
+ If KeyHit(KEY_C)
+ Self.TerminateMainLoop = True
+ Self.ShowCredits = True
+ EndIf
+ EndIf
+
+ Case VIEW_INSTRUCTIONS, VIEW_HIGHSCORES
+ If KeyHit(KEY_ENTER) Or KeyHit(KEY_ESCAPE)
+ Self.Viewing = VIEW_MENU
+ EndIf
+
+ Case VIEW_ENTERHIGH
+ ' Check for alpha keys.
+ For Local Count:Int = KEY_A To KEY_Z
+ If KeyHit(Count) Then Self.EnteredText = Self.EnteredText + Chr(Count)
+ Next
+ ' Check for number key hits
+ For Local Count:Int = KEY_0 To KEY_9
+ If KeyHit(Count) Then Self.EnteredText = Self.EnteredText + Chr(Count)
+ Next
+ ' Special case, check for space bar
+ If KeyHit(KEY_SPACE)
+ Self.EnteredText = Self.EnteredText + " "
+ EndIf
+ ' Remove a character when user hits backspace.
+ If KeyHit(KEY_BACKSPACE)
+ If Len(Self.EnteredText) > 0
+ Self.EnteredText = Mid(Self.EnteredText,0,Len(Self.EnteredText))
+ EndIf
+ EndIf
+ ' If 5 characters are entered, end high score entry.
+ If Len(Self.EnteredText) = 5 Then Self.FinishScoreEntry()
+
+ If KeyHit(KEY_ENTER)
+ Self.FinishScoreEntry()
+ EndIf
+
+ End Select
+
+ End Method
+'#End Region
+'#Region Method: Render
+ Method Render()
+ Cls
+
+ ScreenPan(0,0)
+
+ SetTransform
+ SetAlpha 1.0
+ SetBlend(SOLIDBLEND)
+ Self.Stars.Draw()
+
+ SetColor 255,255,255
+ SetBlend(ALPHABLEND)
+ DrawImage(Self.Title,0,40)
+ DrawImage(Self.LowerTitle,0,660)
+
+ Local StartPosX:Int = 150
+ Local StartPosY:Int = 245
+
+ Select Self.Viewing
+ Case VIEW_MENU
+ ' Showing main menu
+ DrawImage(Self.Options,450,330)
+ SetRotation -90
+ Select Self.HoverOver
+ Case VIEW_START
+ DrawImage(Self.Craft,416,352)
+
+ Case VIEW_INSTRUCTIONS
+ DrawImage(Self.Craft,416,352 + 37)
+
+ Case VIEW_HIGHSCORES
+ DrawImage(Self.Craft,416,352 + 74)
+
+ Case VIEW_QUIT
+ DrawImage(Self.Craft,416,352 + 145)
+
+ End Select
+
+ Case VIEW_HIGHSCORES
+ Scores.Render(385,310)
+
+ Case VIEW_ENTERHIGH
+ DrawTextCentered("CONGRATULATIONS!",StartPosY)
+ DrawTextCentered("You have a new high score!",StartPosY+50)
+ DrawTextCentered("Please enter your name (5 characters)",StartPosY+65)
+ Local TextX:Int = DrawTextCentered(Self.EnteredText,StartPosY+250)
+ DrawRect(TextX + TextWidth(Self.EnteredText), StartPosY+250, 12, 15)
+
+ Case VIEW_INSTRUCTIONS
+ DrawText("Welcome to Digesteroids!",StartPosX,StartPosY)
+ DrawText("------------------------",StartPosX,StartPosY+20)
+ DrawText("An evil biscuit corporation are using their army of sweet snack food to take over the world.",StartPosX,StartPosY+50)
+ DrawText("Fortunately, the ingredients the evil corporation used are vulnerable to big sweaty laser",StartPosX,StartPosY+70)
+ DrawText("blasts. Use your ship's cannon to take out the corporation before they infest the world with",StartPosX,StartPosY+90)
+ DrawText("sweetmeal dunkables biscuits. But be careful, space is small (just big enough to fit inside",StartPosX,StartPosY+110)
+ DrawText("your screen), it is also cyclical, so beware of incoming oval shaped objects.",StartPosX,StartPosY+130)
+
+ DrawText("Keys:",StartPosX,StartPosY+180)
+ DrawText(" - UP CURSOR ARROW = Thrust",StartPosX,StartPosY+200)
+ DrawText(" - DOWN CURSOR ARROW = Teleport",StartPosX,StartPosY+220)
+ DrawText(" - LEFT CURSOR ARROW = Rotate Ship Left",StartPosX,StartPosY+240)
+ DrawText(" - RIGHT CURSOR ARROW = Rotate Ship Right",StartPosX,StartPosY+260)
+ DrawText(" - ESCAPE = QUIT",StartPosX,StartPosY+280)
+
+ DrawText("Yes, that's right, it's a glorified asteroids game!",StartPosX,StartPosY+320)
+ SetColor 255,0,0
+ DrawText("Note: Your score is based on the speed at which your ship is travelling when it fired.",StartPosX,StartPosY+340)
+
+ End Select
+ End Method
+'#End Region
+'#Region Method: Start
+ Method Start()
+ Self.MusicChannel = AllocChannel()
+ If Self.Music = Null Then Self.Music = LoadSound("sounds\menu.ogg",True)
+ PlaySound(Self.Music,Self.MusicChannel)
+ SetChannelVolume MusicChannel,1.0
+
+ ' Load GFX..
+ If Self.Title = Null Then Self.Title = LoadImage("graphics\title.png")
+ If Self.LowerTitle = Null Then Self.LowerTitle = LoadImage("graphics\lower.png")
+ If Self.Options = Null Then Self.Options = LoadImage("graphics\options.png")
+ If Self.Craft = Null Then Self.Craft = LoadImage("graphics\ship.png")
+
+ ' Add a screen full of stars.
+ For Local CountStars:Int = 0 To 1000
+ Local Star:TMenuStar = TMenuStar.Create(World, Self, 10.0)
+ Star.X = Rnd(WIDTH)
+ Self.Stars.AddLast(Star)
+ Next
+ End Method
+'#End Region
+'#Region Method: Finish
+ Method Finish()
+ Self.Stars.Clear()
+ Self.MusicChannel.Stop()
+ Self.MusicChannel = Null
+ End Method
+'#End Region
+
+ End Type
+'#End Region
+'#Region Scene: MainGame
+ ' Main Game scene
+ Type MainGameScene Extends T2DDynamicGameScene
+
+'#Region Declarations
+ Const STARS_PER_SECOND = 3
+ Const FINISH_AT_LEVEL = 6
+ Field Level:Int = 1
+ Field Completed:Int = False
+
+ Field DigestiveImage:TImage
+ Field ShipImage:TImage
+ Field StarsImage:TImage
+
+ Field LargeImpactSound:TSound
+
+ Field World:TWorldPhysicsProvider = TWorldPhysicsProvider.Create(0.0, 0.0, 0.0, True)
+
+ Field Digestives:TPhysicsProviderCollection = New TPhysicsProviderCollection
+ Field Bullets:TPhysicsProviderCollection = New TPhysicsProviderCollection
+ Field Stars:TPhysicsProviderCollection = New TPhysicsProviderCollection
+
+ Field Chunks:TFountain
+ Const CHUNK_COUNT = 2
+ Field ChunkImages:TImage[CHUNK_COUNT + 1]
+
+ Field Player:TPlayer
+ Field Shake:Float
+
+ Field Pickups:TList = New TList
+'#End Region
+
+'#Region Method: Update
+ Method Update()
+ If KeyHit(KEY_ESCAPE) Then Self.TerminateMainLoop = True
+
+ CheckEndOfLevel() ' Check if we need to move to the next level (IE, all digestives gone)
+
+ ' Add new stars..
+ For Local CountStars:Int = 0 To STARS_PER_SECOND
+ Local Speed:Float = Self.Player.Speed()
+ If Speed < 1.0 Then Speed = 1.0
+ Local Star:TStar = TStar.Create(World, Self, Speed)
+ Self.Stars.AddLast(Star)
+ Next
+
+ ' Update screenshake...
+ Self.Shake :/ 1.02
+ If Self.Shake < 0.0 Then Self.Shake = 0.0
+ If Self.Shake > 6.0 Then Self.Shake = 6.0
+
+ ' Update all the stars.
+ Self.Stars.ApplyPhysics()
+
+ ' Update the chunks
+ Self.Chunks.UpdateWithFriction(TPhysicsProvider.AxisX | TPhysicsProvider.AxisY)
+
+ ' Update all the digestives.
+ Self.Digestives.ApplyPhysics()
+
+ ' Update all the bullets.
+ Self.Bullets.ApplyPhysics()
+
+ ' Update the player.
+ Self.Player.Update()
+ Self.Player.ApplyFriction(TPhysicsProvider.AxisX | TPhysicsProvider.AxisY)
+ Self.Player.ApplyPhysics()
+ End Method
+'#End Region
+'#Region Method: Render
+ Method Render()
+ SetClsColor 0,0,0
+ Cls
+
+ If Self.Shake > 0.0 Then
+ ScreenPan(Rnd(Shake),Rnd(Shake))
+ EndIf
+
+ ' Draw the spazzy psychodelic effect if it is enabled.
+ Self.DrawPsychodelic()
+
+ SetAlpha 1
+ SetTransform
+ ' Draw all the stars.
+ Self.Stars.Draw()
+
+ Self.Chunks.Draw()
+
+ ' Draw the digestives.
+ SetColor 255,255,255
+ SetBlend(ALPHABLEND)
+ Digestives.Draw()
+
+ ' Draw the bullets
+ SetBlend(LIGHTBLEND)
+ Bullets.Draw()
+
+ ' Draw the player.
+ SetBlend(ALPHABLEND)
+ Player.Draw()
+
+ ' Draw the hud
+ DrawHUD()
+ End Method
+'#End Region
+'#Region Method: Start
+ Method Start()
+ AutoMidHandle True
+
+ Self.Shake = 0
+ Self.Chunks = TFountain.Create(Self.World,TRectangle.Create(0,0,WIDTH,HEIGHT))
+
+ ' Load in the required Images...
+ If Self.DigestiveImage = Null Then Self.DigestiveImage = LoadImage("graphics\digestive.png")
+ If Self.ShipImage = Null Then Self.ShipImage = LoadImage("graphics\ship.png")
+ If Self.StarsImage = Null Then Self.StarsImage = LoadImage("graphics\stars.png")
+
+ For Local LoadCount:Int = 0 To CHUNK_COUNT
+ Self.ChunkImages[LoadCount] = LoadImage("graphics\piece" + String(LoadCount + 1) + ".png")
+ Next
+
+ ' Set up the player
+ Self.Player = TPlayer.Create(Self.World, Self.ShipImage,Self)
+ Self.Player.LoadAssets()
+
+ ' Load up the sounds.
+ If Self.LargeImpactSound = Null Then Self.LargeImpactSound = LoadSound("Sounds\ImpactLarge.wav")
+
+ ' Reset the game..
+ Self.ResetGame()
+
+ ' Create all the weapons..
+ Self.Pickups.Clear()
+ Local Bullet1Image:TImage = LoadImage("graphics\bullet1.png")
+ Local Fire1Sound:TSound = LoadSound("sounds\fire.wav")
+
+' Local Blaster:TWeapon = TWeapon.Create(Speed,Graphic,Size,Radius,Veer,FireRate,MainGame,Sound)
+ Local Blaster:TWeapon = TWeapon.Create(10, Bullet1Image, 0.65, 4.0, 0,30, Self, Fire1Sound, Bullet1Image, "Blaster")
+ Local FastBlaster:TWeapon = TWeapon.Create(9, Bullet1Image, 0.3, 4.0, 0.0, 15, Self, Fire1Sound, Bullet1Image, "Blaster Mk2")
+ Local SprayBlaster:TWeapon = TWeapon.Create(8, Bullet1Image, 0.35, 4.0, 5.0, 1, Self, Fire1Sound, Bullet1Image, "Blaster Mk3")
+
+ Local BlasterPickup:TPickup = TPickup.Create(TPickup.MODIFIER_WEAPON,0,0,True,Blaster,Bullet1Image,0.01,Bullet1Image)
+ Local FastBlasterPickup:TPickup = TPickup.Create(TPickup.MODIFIER_WEAPON,0,0,True,FastBlaster,Bullet1Image,0.01,Bullet1Image)
+ Local SprayBlasterPickup:TPickup = TPickup.Create(TPickup.MODIFIER_WEAPON,0,0,True,SprayBlaster,Bullet1Image,0.008,Bullet1Image)
+
+ Local Score1000Pickup:TPickup = TPickup.Create(TPickup.MODIFIER_SCORE,1000,0,True,Null,Null,0.02,Bullet1Image)
+ Local Score500Pickup:TPickup = TPickup.Create(TPickup.MODIFIER_SCORE,500,0,True,Null,Null,0.03,Bullet1Image)
+
+ Local RotationPickup:TPickup = TPickup.Create(TPickup.MODIFIER_ROTATION,1.1,0,True,Null,Null,0.01,Bullet1Image)
+ Local PsychodelicPickup:TPickup = TPickup.Create(TPickup.MODIFIER_PSYCHODELIC,0,1000,False,Null,Null,0.04,Bullet1Image)
+
+ Local LivesPickup:TPickup = TPickup.Create(TPickup.MODIFIER_LIVES,1,0,True,Null,Null,0.001,Bullet1Image)
+
+ Self.Player.Weapon = Blaster
+
+ End Method
+'#End Region
+'#Region Method: Finish
+ Method Finish()
+ Self.Player.ThrusterChannel.Stop
+ Self.Player.ThrusterChannel = Null
+ End Method
+'#End Region
+
+'#Region Method: CheckEndOfLevel
+ Method CheckEndOfLevel()
+ If Digestives.IsEmpty() = True
+ ' No more digestives..
+ Level :+ 1
+ If Level = FINISH_AT_LEVEL Then
+ ' This was the last level.
+ Self.Completed = True
+ Self.TerminateMainLoop = True
+ Else
+ ' Move to the next level
+ GotoLevel(Level)
+ EndIf
+ EndIf
+ End Method
+'#End Region
+'#Region Method: DrawHUD
+ Method DrawHUD()
+ SetColor 255,255,255
+ SetAlpha 1.0
+ SetRotation 0
+ DrawText "Level: " + Level,0,0
+ Local Lives:String = "Lives: " + Player.Lives
+ Local Score:String = "Score: " + Player.Score
+ DrawText Score,(WIDTH / 2) - (TextWidth(Score) / 2) ,0
+ DrawText Lives,(WIDTH - TextWidth(Lives)) ,0
+
+ Local fr$="000"+Int(Self.Player.Speed()*1000) Mod 1000
+ Local sp$=Int(Self.Player.Speed()*1000)/1000+"."+fr[fr.length-3..]
+
+ DrawText "Speed: " + sp,0,15
+ DrawText "Teleports: " + Self.Player.Teleports,0,30
+ End Method
+'#End Region
+'#Region Method: ResetGame
+ Method ResetGame()
+ Digestives.Clear()
+ Bullets.Clear()
+ Stars.Clear()
+ Chunks.Particles.Clear()
+ Player.Lives = 5
+ Player.Reset()
+ GotoLevel(1)
+ End Method
+'#End Region
+'#Region Method: GotoLevel
+ Method GotoLevel(ToLevel:Int)
+ Level = ToLevel
+ For Local Count:Int = 1 To ToLevel
+ Local Digestive:TDigestive = TDigestive.Create(World, DigestiveImage,Self)
+ Digestive.X = Rnd(0,WIDTH)
+ Digestive.Y = Rnd(0,HEIGHT)
+ Digestive.Rotation = Rnd(360)
+ Digestive.Weight = 5
+ Digestive.SetVelocityFromAngle(Rnd(360.0),Rnd(0.5,1.5))
+ Digestive.HitSound = Self.LargeImpactSound
+ Digestives.AddLast(Digestive)
+ Next
+ End Method
+'#End Region
+'#Region Method: DrawPsychodelic
+ Field PsychoAngle:Float = 0
+ Field Psycho:Int = False
+
+ Method DrawPsychodelic()
+ If Psycho = True
+ SetColor Rnd(0.0,255),Rnd(0.0,255),Rnd(0.0,255)
+ PsychoAngle :+ 8
+ SetBlend(SOLIDBLEND)
+ TileImage(StarsImage,Sin(PsychoAngle) * 200,Cos(PsychoAngle) * 200)
+ Else
+ SetColor 255,255,255
+ EndIf
+ End Method
+'#End Region
+'#Region Method: FindSafeLocation
+ Method FindSafeLocation:TPointD(Radius:Float)
+ While True
+ Local X:Int = Rnd(0,WIDTH)
+ Local Y:Int = Rnd(0,HEIGHT)
+ If Self.IsLocationSafe(X,Y,Radius)
+ Local Out:TPointD = New TPointD
+ Out.X = X
+ Out.Y = Y
+ Return Out
+ EndIf
+ Wend
+ End Method
+'#End Region
+'#Region Method: IsLocationSafe
+ Method IsLocationSafe:Int(X:Int,Y:Int,Radius:Float)
+ Local TryCircle:TCircle = New TCircle
+ TryCircle.X = X
+ TryCircle.Y = Y
+ TryCircle.Radius = Radius
+ For Local Item:TDigestive = EachIn Self.Digestives
+ If Item.Circle.CollidesWith(TryCircle)
+ Return False
+ EndIf
+ Next
+ Return True
+ End Method
+'#End Region
+
+ End Type
+'#End Region
+'#Region Scene: Ending
+ ' The ending scene.
+ Type EndingScene Extends T2DDynamicGameScene
+
+'#Region Declarations
+ Const STARS_PER_SECOND = 0
+ Const MAGNET_COUNT = 5
+ Field ScrollPoint:Float
+ Field Music:TSound
+ Field MusicChannel:TChannel
+ Field Title:TImage
+ Field Time:Int = 1000000
+
+ Field TEXTS_COUNT = 23
+ Field Texts:String[TEXTS_COUNT]
+
+ Field Stars:TPhysicsProviderCollection = New TPhysicsProviderCollection
+ Field World:TWorldPhysicsProvider = TWorldPhysicsProvider.Create(0.2, 0.0, 0.0, True)
+'#End Region
+
+'#Region Method: Update
+ Method Update()
+ Self.Stars.ApplyPhysics()
+
+ If KeyHit(KEY_ESCAPE) Then
+ Self.TerminateMainLoop = True
+ EndIf
+
+ Self.Time :+ 1
+ If Self.Time > 100 Then
+ Self.Time = 0
+ ' Add a new magnet
+ Local ThisMagnet:TEndMagnet = New TEndMagnet
+ ThisMagnet.X = Rnd(WIDTH)
+ ThisMagnet.Y = Rnd(HEIGHT)
+ ThisMagnet.Radius = Rnd(200,500)
+ ThisMagnet.Polarity = TMagnet.PositivePolarity
+ ThisMagnet.Strength = Rnd(0.1,0.5)
+ Self.World.Magnets.AddLast(ThisMagnet)
+ Self.World.ApplyMagnets = True
+ EndIf
+
+
+ Local X:Int = WIDTH/2
+ Local Y:Int = HEIGHT/2
+ For Local Magnet:TEndMagnet = EachIn Self.World.Magnets
+ For Local Count:Int = 0 To STARS_PER_SECOND
+ Local ThisStar:TEndingStar = TEndingStar.Create(Self.World, Self, Rnd(1.0,5.0), Magnet.X + (Sin(Rnd(360.0)) * 5.0), Magnet.Y + (Cos(Rnd(360.0)) * 5.0))
+ ThisStar.SetVelocityFromAngle(Rnd(360),Rnd(1.0,5.0))
+ Self.Stars.AddLast(ThisStar)
+ Magnet.Stars :+ 1
+ Next
+ If Magnet.Stars > 500 Then
+ Self.World.Magnets.Remove(Magnet)
+ EndIf
+ Next
+
+ ' Update the scoller
+ Self.ScrollPoint :- 0.5
+ If Self.Scrollpoint < -((TEXTS_COUNT * 25) + 100)
+ Self.ScrollPoint = HEIGHT + 20
+ EndIf
+
+ End Method
+'#End Region
+'#Region Method: Render
+ Method Render()
+ Cls
+
+ Self.Stars.Draw()
+
+ SetColor 255,255,255
+ For Local Count:Int = 0 To TEXTS_COUNT - 1
+ MainMenuScene.DrawTextCentered(Self.Texts[Count],Self.ScrollPoint + (25 * Count))
+ Next
+ End Method
+'#End Region
+'#Region Method: Start
+ Method Start()
+ Self.MusicChannel = AllocChannel()
+ If Self.Music = Null Then Self.Music = LoadSound("sounds\ending.ogg",True)
+ If Self.MusicChannel <> Null
+ PlaySound(Self.Music,Self.MusicChannel)
+ EndIf
+
+ Self.Texts[0] = "Cast of Characters"
+ Self.Texts[1] = "------------------"
+ Self.Texts[2] = ""
+ Self.Texts[3] = "Space Craft.......................Intrepid"
+ Self.Texts[4] = "Digestive...............Sweet Meal Biscuit"
+ Self.Texts[5] = "Star...............................Himself"
+ Self.Texts[6] = ""
+ Self.Texts[7] = "Written by Rob Hutchinson in roughly 5 days."
+ Self.Texts[8] = "Email: rob@proteanide.co.uk"
+ Self.Texts[9] = "Web: http://www.proteanide.co.uk/"
+ Self.Texts[10] = ""
+ Self.Texts[11] = "Special Thanks To"
+ Self.Texts[12] = "-----------------"
+ Self.Texts[13] = "Richard Makepeace.............Inspiration"
+ Self.Texts[14] = "James Readman.................Inspiration"
+ Self.Texts[15] = ""
+ Self.Texts[16] = "Greets"
+ Self.Texts[17] = "------"
+ Self.Texts[18] = "Pickup, Cabsy, Peters, ZanaX, Compona, Helen, + all other family and friends."
+ Self.Texts[19] = ""
+ Self.Texts[20] = ""
+ Self.Texts[21] = ""
+ Self.Texts[22] = "Thank you for playing."
+
+ Self.ScrollPoint = HEIGHT + 20
+ End Method
+'#End Region
+'#Region Method: Finish
+ Method Finish()
+ Self.Stars.Clear()
+ If Self.MusicChannel <> Null Then
+ Self.MusicChannel.Stop()
+ Self.MusicChannel = Null
+ EndIf
+ End Method
+'#End Region
+
+ End Type
+'#End Region
+
+Type TCircle
+
+ Field X:Int
+ Field Y:Int
+ Field Radius:Float
+
+ Method CollidesWith:Int(Circle:TCircle)
+ Local Distance:Double = TPhysicsUtility.DistanceBetweenPoints(Self.X,Self.Y,Circle.X,Circle.Y)
+ If Distance < (Self.Radius + Circle.Radius) Then Return True
+ Return False
+ End Method
+
+ Method Draw()
+ SetBlend(ALPHABLEND)
+ SetAlpha(0.5)
+ SetRotation 0
+ DrawCircle(X,Y,Int(Radius))
+ End Method
+
+ Function DrawCircle(X,Y,Radius)
+ DrawOval(X - Radius,Y - Radius,Radius * 2,Radius * 2)
+ End Function
+
+End Type
+Type TDigestive Extends TPhysicsProvider
+ Field Image:TImage
+ Field Alpha:Float = 1.0
+ Field Rotation:Float = 0
+ Field Scale:Float = 1.0
+ Field Circle:TCircle = New TCircle
+ Field GameScene:MainGameScene
+ Const InitialRadius:Float = 70.0
+ Field HitSound:TSound
+
+ Method SplitIntoChunks(X:Int,Y:Int,Multiplier:Float)
+ GameScene.Digestives.Remove(Self)
+
+ Local Channel:TChannel = AllocChannel()
+ If Channel <> Null
+ Channel.SetVolume(Self.Scale)
+ PlaySound(Self.HitSound, Channel)
+ EndIf
+
+ Self.GameScene.Chunks.X = X
+ Self.GameScene.Chunks.Y = Y
+
+ Self.GameScene.Player.Score :+ Int(((Self.Scale * 100.0) + 4.0) * Multiplier)
+
+ Self.GameScene.Shake :+ (Self.Scale * 3.5)
+
+
+ If Self.Scale > 0.2
+ For Local Count:Int = 0 To Int(Self.Scale * 5)
+ Local Digestive:TDigestive = TDigestive.Create(Self.World, Self.Image, Self.GameScene)
+ Digestive.X = Self.X
+ Digestive.Y = Self.Y
+ Digestive.Rotation = Rnd(360)
+ Digestive.Weight = 5
+ Digestive.SetVelocityFromAngle(Rnd(360.0),Rnd(0.5,1.5))
+ Digestive.SetDrawScale(Self.Scale / 2)
+ Digestive.HitSound = Self.HitSound
+ GameScene.Digestives.AddLast(Digestive)
+ Next
+
+ For Local ChunkCount:Int = 0 To 4 * (Self.Scale * 10)
+ Local Particle:TStaticParticle = Self.GameScene.Chunks.AddStaticParticle(Self.GameScene.ChunkImages[(Int(Rnd(0,MainGameScene.CHUNK_COUNT)))], Rnd(360.0), Rnd(0.5,10.0), 0, 0.01, [255,255,255])
+ Particle.Friction = 0.97
+ Particle.Size = Rnd(0.5,1.0)
+ Particle.Rotation = Rnd(-5.0,5.0)
+ Next
+ Else
+ For Local ChunkCount2:Int = 0 To Rnd(3,8)
+ Local Particle:TStaticParticle = Self.GameScene.Chunks.AddStaticParticle(Self.GameScene.ChunkImages[(Int(Rnd(0,MainGameScene.CHUNK_COUNT)))], Rnd(360.0), Rnd(0.5,10.0), 0, 0.01, [255,255,255])
+ Particle.Friction = 0.97
+ Particle.Size = Rnd(0.2,0.5)
+ Particle.Rotation = Rnd(-5.0,5.0)
+ Next
+ End If
+ End Method
+
+ Method Draw()
+ SetAlpha Self.ALPHA
+ SetRotation Self.Rotation
+ SetScale Self.Scale,Self.Scale
+ DrawImage(Self.Image, Self.X, Self.Y)
+ SetScale 1,1
+ End Method
+
+ Method SetDrawScale(Scale:Float)
+ Self.Scale = Scale
+ Self.Circle.Radius = Self.InitialRadius * Self.Scale
+ End Method
+
+ Method PhysicsApplied()
+ Rotation:+2
+ Local HalfWidth:Int = ((Image.Width * Self.Scale) / 2)
+ Local HalfHeight:Int = ((Image.Height * Self.Scale) / 2)
+ If X < -HalfWidth Then X = WIDTH + HalfWidth
+ If Y < -HalfHeight Then Y = HEIGHT + HalfHeight
+ If X > WIDTH + HalfWidth Then X = -HalfWidth
+ If Y > HEIGHT + HalfHeight Then Y = -HalfHeight
+ Self.Circle.X = Self.X
+ Self.Circle.Y = Self.Y
+ End Method
+
+ Function Create:TDigestive(World:TWorldPhysicsProvider,Image:TImage,GameScene:MainGameScene)
+ Local Out:TDigestive = New TDigestive
+ Out.GameScene = GameScene
+ Out.Circle.Radius = InitialRadius
+ Out.World = World
+ Out.Image = Image
+ Return Out
+ End Function
+
+End Type
+Type TPlayer Extends TPhysicsProvider
+
+ Field Lives:Int
+ Field Score:Int
+ Field Weapon:TWeapon
+ Field Dead:Int = True
+ Field Teleports:Int = 2
+
+ Field Image:TImage
+ Field Rotation:Float
+ Field Acceleration:Float
+ Field MaxAcceleration:Float = 4.0
+ Field Motion:Float = 0.001
+ Field AccelerationDropMultiplier:Float = 8
+ Field Circle:TCircle = New TCircle
+ Field GameScene:MainGameScene
+
+ Field FireRateCount:Float = 0
+
+ Field Thruster:TFountain
+ Field Death:TFountain
+
+ Field ThrusterImage:TImage
+ Field DeathImage:TImage
+ Field SparkleImage:TImage
+ Field ThrusterSound:TSound
+ Field CrashSound:TSound
+ Field ThrusterChannel:TChannel
+ Field TeleportSound:TSound
+
+ Method Draw()
+ SetTransform
+ ' Render the thruster...
+ SetBlend LIGHTBLEND
+ SetScale 0.6, 0.6
+ SetAlpha 1.0
+ Self.Thruster.Draw()
+ Self.Death.Draw()
+
+ If Not Self.Dead Then
+ ' Render the player.
+ SetBlend ALPHABLEND
+ SetAlpha 1.0
+ SetScale 1.0,1.0
+ SetColor 255,255,255
+ SetRotation 360 - Self.Rotation
+ DrawImage(Self.Image, Self.X, Self.Y)
+ EndIf
+ End Method
+
+ Method PhysicsApplied()
+ Local HalfWidth:Int = (Image.Width / 2)
+ Local HalfHeight:Int = (Image.Height / 2)
+ If X < -HalfWidth Then X = WIDTH + HalfWidth
+ If Y < -HalfHeight Then Y = HEIGHT + HalfHeight
+ If X > WIDTH + HalfWidth Then X = -HalfWidth
+ If Y > HEIGHT + HalfHeight Then Y = -HalfHeight
+ Self.Circle.X = Self.X
+ Self.Circle.Y = Self.Y
+ End Method
+
+ Method Update()
+ ' Update thruster and death particles.
+ Local Particle:TStaticParticle = Self.Thruster.AddStaticParticle(Self.ThrusterImage, Self.Rotation + 180 + Rnd(-5.0,5.0), 1.0, 1, 0.04, [255,255,255])
+
+ Local ActualRotation:Float = Self.Rotation + 180
+
+ Self.Thruster.X = Self.X + (Sin(ActualRotation) * 10)
+ Self.Thruster.Y = Self.Y + (Cos(ActualRotation) * 10)
+
+ Local ThisSpeed:Float = Self.Speed()
+
+ If ThisSpeed < 0 Then ThisSpeed = 0
+ If ThisSpeed > 10.0 Then ThisSpeed = 10.0
+
+ ' Add particles to the thruster..
+ Particle.Size = (ThisSpeed / 10)
+ Self.ThrusterChannel.SetVolume(ThisSpeed / 10)
+ Self.ThrusterChannel.SetPan(Float(Self.X / Float(WIDTH / 2.0) - 1.0))
+ Self.GameScene.Shake:+ 0.10 * (ThisSpeed / 10.0)
+
+ Self.Thruster.Update()
+ Self.Death.UpdateWithFriction(TPhysicsProvider.AxisX | TPhysicsProvider.AxisY)
+
+ If Self.Dead
+ ' Player is currently dead, wait for a nice chance to put them back down.
+ If Self.Lives < 1 Then
+ ' Wait for the death particles to become zeroed.
+ If Self.Death.Particles.IsEmpty() Then
+ Self.GameScene.TerminateMainLoop = True
+ EndIf
+ Else
+ ' Check to see if the center spot is safe to plop the ship on.
+ If Self.GameScene.IsLocationSafe(WIDTH/2, HEIGHT/2, 70)
+ Self.Dead = False
+ For Local Pops:Int = 0 To 200
+ Local Particle:TStaticParticle = Self.Death.AddStaticParticle(Self.SparkleImage, Rnd(360.0), Rnd(0.5,2.0), 0, Rnd(0.01, 0.03), [255,255,255])
+ Particle.X = Self.X
+ Particle.Y = Self.Y
+ Particle.Rotation = Rnd(-1.0,1.0)
+ Particle.Friction = 0.97
+ Particle.Size = Rnd(0.1,0.2)
+ Next
+ EndIf
+ EndIf
+ Else
+ ' Update fire rate counter...
+ Self.FireRateCount :- 1
+
+ If KeyDown(KEY_UP)
+ ' Thrust
+ Self.Acceleration :+ Self.Motion
+ ' Clamp the acceleration
+ If Self.Acceleration > Self.MaxAcceleration Then Self.Acceleration = Self.MaxAcceleration
+ ' Add acceleration.
+ Self.IncreaseVelocityFromAngle(Self.Rotation,Self.Acceleration)
+ Else
+ Self.Acceleration :- (Self.Motion * Self.AccelerationDropMultiplier)
+ If Self.Acceleration < 0.0 Then Self.Acceleration = 0
+ EndIf
+
+ If KeyDown(KEY_LEFT)
+ ' RotateLeft
+ Self.Rotation :+ 3
+ EndIf
+
+ If KeyDown(KEY_RIGHT)
+ ' RotateLeft
+ Self.Rotation :- 3
+ EndIf
+
+ If KeyDown(KEY_SPACE)
+ ' FIRE!
+ If Self.FireRateCount <= 0 Then
+ Self.FireRateCount = Self.Weapon.FireRate
+ Self.Weapon.Fire(Self)
+ End If
+ EndIf
+
+ If KeyHit(KEY_DOWN)
+ ' TELEPORT
+ If Self.Teleports > 0 Then
+ Self.Teleports :- 1
+ Local Location:TPointD = Self.GameScene.FindSafeLocation(20)
+
+ PlaySound(Self.TeleportSound)
+ Self.Death.X = Self.X
+ Self.Death.Y = Self.Y
+
+ For Local Pops:Int = 0 To 50
+ Local Particle:TStaticParticle = Self.Death.AddStaticParticle(Self.SparkleImage, Rnd(360.0), Rnd(0.5,10.0), 0, 0.02, [255,255,255])
+ Particle.X = Self.X
+ Particle.Y = Self.Y
+ Particle.Rotation = Rnd(-1.0,1.0)
+ Particle.Friction = 0.97
+ Particle.Size = Rnd(0.1,0.2)
+ Next
+
+ Self.X = Int(Location.X)
+ Self.Y = Int(Location.Y)
+ Self.Death.X = Self.X
+ Self.Death.Y = Self.Y
+
+ For Local Pops:Int = 0 To 20
+ Local Particle:TStaticParticle = Self.Death.AddStaticParticle(Self.SparkleImage, Rnd(360.0), Rnd(0.5,2.0), 0, 0.02, [255,255,255])
+ Particle.X = Self.X
+ Particle.Y = Self.Y
+ Particle.Rotation = Rnd(-1.0,1.0)
+ Particle.Friction = 0.97
+ Particle.Size = Rnd(0.1,0.2)
+ Next
+
+ EndIf
+ EndIf
+
+ ' COLLISION DETECT AGAINST DIGESTIVES!
+ ' ---------------------------------------------------------
+ For Local Item:TDigestive = EachIn Self.GameScene.Digestives
+ If Item.Circle.CollidesWith(Self.Circle)
+ Self.Lives :- 1
+ Self.Dead = True
+ PlaySound(Self.CrashSound)
+
+ Local PlayerSpeed:Double = Self.Speed()
+
+ Self.GameScene.Shake :+ PlayerSpeed / 1.5
+ If PlayerSpeed > 10.0
+ Item.SplitIntoChunks(Self.X,Self.Y,Self.Speed())
+ EndIf
+
+ Self.Death.X = Self.X
+ Self.Death.Y = Self.Y
+ For Local Pops:Int = 0 To 100
+ If PlayerSpeed > 10 Then PlayerSpeed = 10
+ Local Angle:Double = Self.Angle() + Rnd(-(80 - (8.0 * PlayerSpeed)), 80 - (8.0 * PlayerSpeed))
+ Local Particle:TStaticParticle = Self.Death.AddStaticParticle(Self.DeathImage, Angle, Rnd(0.1, Self.Speed()), 0, 0.03, [255,255,255])
+ Particle.Friction = 0.98
+ Particle.Size = Rnd(0.1,0.7)
+ Particle.Color = [Rand(80,255),24,Rand(24,128)]
+ Next
+
+ For Local Pops:Int = 0 To 300
+ Local Particle:TStaticParticle = Self.Death.AddStaticParticle(Self.DeathImage, Rnd(360.0), Rnd(0.1, 7.0), 0, Rnd(0.01,0.04), [255,255,255])
+ Particle.Friction = 0.99
+ Particle.Size = Rnd(0.1,0.4)
+ Particle.Color = [Rand(80,255),24,Rand(24,200)]
+ Next
+
+ Self.Reset()
+ EndIf
+ Next
+ EndIf
+
+ End Method
+
+ Method Reset()
+ Self.X = WIDTH / 2
+ Self.Y = HEIGHT / 2
+ Self.Acceleration = 0
+ Self.VelocityX = 0
+ Self.VelocityY = 0
+ Self.Teleports = 2
+ Self.Dead = 2
+ End Method
+
+ Method LoadAssets()
+ If Self.ThrusterImage = Null Then Self.ThrusterImage = LoadImage("graphics\bullet1.png")
+ If Self.DeathImage = Null Then Self.DeathImage = LoadImage("graphics\pop.png")
+ If Self.SparkleImage = Null Then Self.SparkleImage = LoadImage("graphics\sparkle.png")
+
+ ' Load in the sounds...
+ If Self.ThrusterSound = Null Then Self.ThrusterSound = LoadSound("sounds\thrust.wav",True)
+ If Self.CrashSound = Null Then Self.CrashSound = LoadSound("sounds\crash.wav",False)
+ If Self.TeleportSound = Null Then Self.TeleportSound = LoadSound("sounds\teleport.wav",False)
+
+ Self.ThrusterChannel = AllocChannel()
+ Self.ThrusterChannel.SetVolume(0)
+ PlaySound(Self.ThrusterSound,Self.ThrusterChannel)
+ End Method
+
+ Function Create:TPlayer(World:TWorldPhysicsProvider, Image:TImage, GameScene:MainGameScene)
+ Local Out:TPlayer = New TPlayer
+ Out.Thruster = TFountain.Create(World, TRectangle.Create(0,0,WIDTH,HEIGHT))
+ Out.Death = TFountain.Create(World, TRectangle.Create(0,0,WIDTH,HEIGHT))
+ Out.GameScene = GameScene
+ Out.Circle.Radius = 10
+ Out.World = World
+ Out.Image = Image
+ Out.Friction = 0.985
+ Out.Reset()
+ Return Out
+ End Function
+
+End Type
+Type TBullet Extends TPhysicsProvider
+
+ Field Image:TImage
+ Field Circle:TCircle = New TCircle
+ Field GameScene:MainGameScene
+ Field Size:Float = 1.0
+ Field ScoreMultiplier:Float = 1
+ Field TTL:Int
+ Field Fading:Float = 1.0
+
+ Const TIME_TO_LIVE = 100
+
+ Method Draw()
+ SetScale(Self.Size,Self.Size)
+ SetAlpha(Self.Fading)
+ DrawImage(Self.Image, Self.X, Self.Y)
+ End Method
+
+ Method PhysicsApplied()
+ Local HalfWidth:Int = (Image.Width / 2)
+ Local HalfHeight:Int = (Image.Height / 2)
+ If X < -HalfWidth Then X = WIDTH + HalfWidth
+ If Y < -HalfHeight Then Y = HEIGHT + HalfHeight
+ If X > WIDTH + HalfWidth Then X = -HalfWidth
+ If Y > HEIGHT + HalfHeight Then Y = -HalfHeight
+
+ ' Shall we kill it off?
+ Self.TTL :+ 1
+ If Self.TTL > TIME_TO_LIVE Then
+ Self.Fading :- 0.015
+ If Self.Fading < 0.05
+ Self.GameScene.Bullets.Remove(Self)
+ EndIf
+ EndIf
+
+' If X < -HalfWidth Or Y < -HalfHeight Or X > WIDTH + HalfWidth Or Y > HEIGHT + HalfHeight Then
+ ' ' Bullet went off't screen
+ ' Self.GameScene.Bullets.Remove(Self)
+ ' EndIf
+
+ Self.Circle.X = Self.X
+ Self.Circle.Y = Self.Y
+
+ ' COLLISION DETECT AGAINST DIGESTIVES!
+ ' ---------------------------------------------------------
+ For Local Item:TDigestive = EachIn Self.GameScene.Digestives
+ If Item.Circle.CollidesWith(Self.Circle)
+ If Self.Fading > 0.5
+ Self.GameScene.Bullets.Remove(Self)
+ Item.SplitIntoChunks(Self.Circle.X,Self.Circle.Y,Self.ScoreMultiplier)
+ Exit
+ EndIf
+ EndIf
+ Next
+ End Method
+
+ Function Create:TBullet(World:TWorldPhysicsProvider, Image:TImage, Player:TPlayer, Speed:Float, GameScene:MainGameScene, Size:Float, Radius:Float, Veer:Float)
+ Local Out:TBullet = New TBullet
+ Out.GameScene = GameScene
+ Out.SetVelocityFromAngle(Player.Rotation + Rnd(-Veer,Veer),Speed)
+ Out.Size = Size
+ Out.Circle.Radius = Radius * Size
+ Out.X = Player.X
+ Out.Y = Player.Y
+ Out.World = World
+ Out.Image = Image
+ Return Out
+ End Function
+
+End Type
+Type TStar Extends TPhysicsProvider
+
+ Field Color[]
+ Field GameScene:MainGameScene
+
+ Method Draw()
+ SetColor Self.Color[0],Self.Color[1],Self.Color[2]
+ DrawRect(X,Y,1,1)
+ End Method
+
+ Method PhysicsApplied()
+ Self.VelocityX :* 1.01
+ Self.VelocityY :* 1.01
+
+ Local HalfWidth:Int = 5
+ Local HalfHeight:Int = 5
+ If X < -HalfWidth Or Y < -HalfHeight Or X > WIDTH + HalfWidth Or Y > HEIGHT + HalfHeight Then
+ ' Bullet went off't screen
+ Self.GameScene.Stars.Remove(Self)
+ EndIf
+ End Method
+
+ Function Create:TStar(World:TWorldPhysicsProvider,GameScene:MainGameScene, Speed:Float)
+ Local Out:TStar = New TStar
+ Local Pigment = Rand(24,255)
+ Out.GameScene = GameScene
+ Out.Color = [Pigment,Pigment,Pigment]
+ Out.SetVelocityFromAngle(Rnd(360),Rnd(0.2,0.9) * Speed)
+ Out.X = WIDTH / 2
+ Out.Y = HEIGHT / 2
+ Out.World = World
+ Return Out
+ End Function
+
+End Type
+Type TMenuStar Extends TPhysicsProvider
+
+ Field Color[]
+ Field MenuScene:MainMenuScene
+
+ Method Draw()
+ SetColor Self.Color[0],Self.Color[1],Self.Color[2]
+ DrawRect(X,Y,1,1)
+ End Method
+
+ Method PhysicsApplied()
+' Self.VelocityX :* 1.01
+' Self.VelocityY :* 1.01
+
+ Local HalfWidth:Int = 5
+ Local HalfHeight:Int = 5
+ If X < -HalfWidth Or Y < -HalfHeight Or X > WIDTH + HalfWidth Or Y > HEIGHT + HalfHeight Then
+ ' Bullet went off't screen
+ Self.MenuScene.Stars.Remove(Self)
+ EndIf
+ End Method
+
+ Function Create:TMenuStar(World:TWorldPhysicsProvider,MenuScene:MainMenuScene, Speed:Float)
+ Local Out:TMenuStar = New TMenuStar
+ Local Pigment = Rand(24,255)
+ Out.MenuScene = MenuScene
+ Out.Color = [Pigment,Pigment,Pigment]
+ Out.SetVelocityFromAngle(90,Rnd(0.2,0.9) * Speed)
+ Out.X = 0
+ Out.Y = Rnd(HEIGHT)
+ Out.World = World
+ Return Out
+ End Function
+
+End Type
+Type TEndingStar Extends TPhysicsProvider
+
+ Field Color[]
+ Field EndScene:EndingScene
+
+ Method Draw()
+ SetColor Self.Color[0],Self.Color[1],Self.Color[2]
+ DrawRect(X,Y,1,1)
+ End Method
+
+ Method PhysicsApplied()
+' Self.VelocityX :* 1.01
+' Self.VelocityY :* 1.01
+
+ Local HalfWidth:Int = 5
+ Local HalfHeight:Int = 5
+ If X < -HalfWidth Or Y < -HalfHeight Or X > WIDTH + HalfWidth Or Y > HEIGHT + HalfHeight Then
+ ' Bullet went off't screen
+ Self.EndScene.Stars.Remove(Self)
+ EndIf
+ End Method
+
+ Function Create:TEndingStar(World:TWorldPhysicsProvider, EndScene:EndingScene, Speed:Float, X:Int, Y:Int)
+ Local Out:TEndingStar = New TEndingStar
+ Local Pigment = Rand(24,255)
+ Out.EndScene = EndScene
+ Out.Color = [Pigment,Pigment,Pigment]
+ Out.X = X
+ Out.Y = Y
+ Out.World = World
+ Return Out
+ End Function
+
+End Type
+Type TPickup Extends TLink
+
+ Const MODIFIER_SCORE = 0
+ Const MODIFIER_LIVES = 1
+ Const MODIFIER_ROTATION = 2
+ Const MODIFIER_PSYCHODELIC = 3
+ Const MODIFIER_WEAPON = 4
+
+ Field Modifies:Int = MODIFIER_SCORE
+ Field ByValue:Float
+ Field LastsFor:Int
+ Field Time:Int
+ Field IsPermanent:Int = False
+ Field Weapon:TWeapon
+ Field Icon:TImage
+ Field Probability:Double
+ Field OwnedIcon:TImage
+
+ Method Clone:TPickup()
+ Local Out:TPickup = New TPickup
+ Out.Modifies = Self.Modifies
+ Out.ByValue = Self.ByValue
+ Out.LastsFor = Self.LastsFor
+ Out.IsPermanent = Self.IsPermanent
+ Out.Weapon = Self.Weapon
+ Out.Icon = Self.Icon
+ Out.Probability = Self.Probability
+ Out.OwnedIcon = Out.OwnedIcon
+ Return Out
+ End Method
+
+ Function Create:TPickup(Modifies:Int,ByValue:Float,LastsFor:Int,IsPermanent:Int,Weapon:TWeapon,Icon:TImage,Probability:Double,OwnedIcon:TImage)
+ Local Out:TPickup = New TPickup
+ Out.Modifies = Modifies
+ Out.ByValue = ByValue
+ Out.LastsFor = LastsFor
+ Out.IsPermanent = IsPermanent
+ Out.Weapon = Weapon
+ Out.Icon = Icon
+ Out.Probability = Probability
+ Out.OwnedIcon = OwnedIcon
+ Return Out
+ End Function
+
+End Type
+Type TWeapon Extends TLink
+
+ Field Speed:Float
+ Field Graphic:TImage
+ Field Size:Float
+ Field Radius:Float
+ Field Veer:Float
+ Field FireRate:Int
+ Field MainGame:MainGameScene
+ Field Sound:TSound
+ Field Name:String
+ Field Icon:TImage
+
+ Method Fire(Player:TPlayer)
+ Local Shot:TBullet = TBullet.Create(MainGame.World, Self.Graphic, Player, Self.Speed + Player.Speed(), Self.MainGame, Self.Size, Self.Radius, Self.Veer)
+ Shot.ScoreMultiplier = (Player.Speed() * 4.0)
+ If Shot.ScoreMultiplier < 1.0 Then Shot.ScoreMultiplier = 1.0
+ Self.MainGame.Bullets.AddLast(Shot)
+ PlaySound(Self.Sound)
+ End Method
+
+ Function Create:TWeapon(Speed:Float,Graphic:TImage,Size:Float,Radius:Float,Veer:Float,FireRate:Int,MainGame:MainGameScene,Sound:TSound,Icon:TImage,Name:String)
+ Local Out:TWeapon = New TWeapon
+ Out.Speed = Speed
+ Out.Graphic = Graphic
+ Out.Size = Size
+ Out.Radius = Radius
+ Out.Veer = Veer
+ Out.FireRate = FireRate
+ Out.MainGame = MainGame
+ Out.Sound = Sound
+ Out.Icon = Icon
+ Out.Name = Name
+ Return Out
+ End Function
+
+End Type
+Type TEndMagnet Extends TMagnet
+
+ Field Stars:Int
+
+End Type
+Type THighScores
+
+ Const SCORE_COUNT = 10
+ Field Scores:Int[SCORE_COUNT]
+ Field Names:String[SCORE_COUNT]
+
+ Method Render(X:Int, Y:Int)
+ For Local Count:Int = 0 To SCORE_COUNT - 1
+ DrawText(Self.Names[Count], X,Y + (Count * 20))
+ Next
+
+ For Local Count2:Int = 0 To SCORE_COUNT - 1
+ DrawText(Self.Scores[Count2], X + 200,Y + (Count2 * 20))
+ Next
+ End Method
+
+ Method IsHighScore:Int(Score:Int)
+ For Local Count:Int = 0 To SCORE_COUNT - 1
+ If Score > Self.Scores[Count] Then
+ Return True
+ EndIf
+ Next
+ Return False
+ End Method
+
+ Method Add(Score:Int,Name:String)
+ ' Find out where we should put the score..
+ Local PlaceAt:Int = SCORE_COUNT - 1
+ For Local Count:Int = SCORE_COUNT - 1 To 0 Step -1
+ If Score > Self.Scores[Count] Then
+ PlaceAt = Count
+ EndIf
+ Next
+
+ ' Shuffle them all down..
+ For Local Shuffle:Int = SCORE_COUNT - 2 To PlaceAt Step -1
+ Self.Scores[Shuffle + 1] = Self.Scores[Shuffle]
+ Self.Names[Shuffle + 1] = Self.Names[Shuffle]
+ Next
+
+ Self.Scores[PlaceAt] = Score
+ Self.Names[PlaceAt] = Name
+ End Method
+
+ Method Save(File:String)
+ Local Out:TStream = OpenStream(File,False,True)
+ If Out <> Null
+ For Local Count:Int = 0 To SCORE_COUNT - 1
+ WriteInt(Out,Self.Scores[Count])
+ WriteLine(Out,Self.Names[Count])
+ Next
+ CloseStream(Out)
+ EndIf
+ End Method
+
+ Function Load:THighScores(File:String)
+ Local In:TStream = OpenStream(File,True,False)
+ Local Out:THighScores = New THighScores
+ If In = Null
+ ' Fill it full of high scores..
+ Out.Names[0] = "Loki"
+ Out.Names[1] = "Booty"
+ Out.Names[2] = "Rix"
+ Out.Names[3] = "Jamez"
+ Out.Names[4] = "Pies"
+ Out.Names[5] = "Bobny"
+ Out.Names[6] = "Berty"
+ Out.Names[7] = "Billy"
+ Out.Names[8] = "Bonny"
+ Out.Names[9] = "Boxer"
+
+ Out.Scores[0] = 100000
+ Out.Scores[1] = 80000
+ Out.Scores[2] = 50000
+ Out.Scores[3] = 40000
+ Out.Scores[4] = 30000
+ Out.Scores[5] = 20000
+ Out.Scores[6] = 10000
+ Out.Scores[7] = 5000
+ Out.Scores[8] = 4000
+ Out.Scores[9] = 1000
+ Else
+ For Local Count:Int = 0 To SCORE_COUNT - 1
+ Out.Scores[Count] = ReadInt(In)
+ Out.Names[Count] = ReadLine(In)
+ Next
+ CloseStream(In)
+ EndIf
+ Return Out
+ End Function
+
+End Type
+
+' Main game setup.
+' ------------------------
+Local Game:T2DDynamicGame = T2DDynamicGame.Create(WIDTH, HEIGHT, DEPTH, REFRESHRATE)
+Global Scores:THighScores = THighScores.Load("hi.dat")
+Game.DynamicTiming = DYNAMICTIMING
+Game.DesiredFPS = DESIREDFPS
+Game.Initialize() ' Go into graphics mode.
+
+HideMouse
+
+' Create the main game scene.
+Local MenuScene:MainMenuScene = New MainMenuScene
+Local GameScene:MainGameScene = New MainGameScene
+Local EndScene:EndingScene = New EndingScene
+
+Local QuitFlag:Int = False
+
+While Not QuitFlag
+ ' Attach The Game Scene To The Dynamic Game.
+ Game.Setscene(Menuscene)
+
+ ' Start The Main Loop.
+ Game.Mainloop()
+
+ If Not MenuScene.Exitgame
+ If MenuScene.ShowCredits
+ MenuScene.ShowCredits = False
+ Game.SetScene(EndScene)
+ Game.Mainloop()
+ Else
+ Game.Setscene(GameScene)
+ Game.Mainloop()
+
+ QFlushKeys()
+
+ If GameScene.Completed
+ GameScene.Completed = False
+ Game.SetScene(EndScene)
+ Game.Mainloop()
+ EndIf
+
+ QFlushKeys()
+
+ ' Check for high score.
+ If Scores.IsHighScore(GameScene.Player.Score) Then
+ ' We got a high score.
+ MenuScene.ViewMenu(MenuScene.VIEW_ENTERHIGH)
+ MenuScene.LastHighScore = GameScene.Player.Score
+ MenuScene.EnteredText = ""
+ EndIf
+
+ EndIf
+ Else
+ QuitFlag = True
+ EndIf
+Wend
+
+Scores.Save("hi.dat")
+
+' Clean up after we shut down.
+Game.ShutDown()
+
+' Fin
+
+Const DEBUG = True
+Function DebugLog(Text:String)
+ If DEBUG Then Print Text
+End Function
+
+
+
+
+
+
--- /dev/null
+' *******************************************************************
+' Source: Dynamic Game
+' Version: 1.00
+' Author: Rob Hutchinson 2004
+' Email: rob@proteanide.co.uk
+' WWW: http://www.proteanide.co.uk/
+' -------------------------------------------------------------------
+' This include provides an OO approach to game creation. You must
+' first instantiate the T2DDynamicGame class with the Create()
+' method. This controls the game itself, including the main loop.
+' You do not need to inherit T2DDynamicGame. Instead, create types
+' and inherit from T2DDynamicGameScene, this class is used to process
+' a single game scene, such as the main menu or the game itself. You
+' might have types that inherit T2DDynamicGame for the Main Menu,
+' Levels of your game, Bonus levels, credits, menu screens, etc.
+' The functionality is built into each type in the Update and Render
+' methods. Always perform logic operations in the Update method and
+' draw your graphics in the standard way inside Render(). This way
+' your game will automatically benefit from dynamic game timing. To
+' Swap the scene from say, the Main Menu to the game itself, simply
+' call the SetScene() Method on T2DDynamicGame with your new scene.
+' -------------------------------------------------------------------
+' Benefits/Features:
+' - The T2DDynamicGame class handles pretty much everything to do
+' with the game loop for you, it has a dynamic timing routine
+' built into it which will catch up with missing frames.
+' - Allows you to easily run at a specific visual framerate
+' (DesiredFPS) regardless of the refresh rate.
+' -------------------------------------------------------------------
+' Required:
+' - minitimer.bmx - Timer framework.
+' *******************************************************************
+
+Import "minitimer.bmx"
+
+Type T2DDynamicGame
+
+ ' PRIVATE
+ Field Scene:T2DDynamicGameScene
+ Field EndMainLoop:Int = False
+ Field DesiredFPS:Int = 60
+ Field TerminateMainLoop:Int = False
+ Field DynamicTiming:Int = True ' If true dynamic timing is uses, else frame limited timing is used.
+
+ ' PUBLIC
+ Field Width:Int = 1024
+ Field Height:Int = 768
+ Field Depth:Int = 32
+ Field RefreshRate:Int = 60
+
+'#Region Constructor: Create
+ Function Create:T2DDynamicGame(Width:Int,Height:Int,Depth:Int,RefreshRate:Int)
+ Local Out:T2DDynamicGame = New T2DDynamicGame
+ Out.Width = Width
+ Out.Height = Height
+ Out.Depth = Depth
+ Out.RefreshRate = RefreshRate
+ Return Out
+ End Function
+'#End Region
+
+'#Region Method: Initialize
+ Method Initialize()
+ ' Set up the graphics.
+ Graphics(Self.Width, Self.Height, Self.Depth, Self.RefreshRate)
+ End Method
+'#End Region
+'#Region Method: ShutDown
+ Method ShutDown()
+ ' Close down the graphics.
+ Self.FlushScene()
+ EndGraphics()
+ End Method
+'#End Region
+'#Region Method: SetScene
+ Method SetScene(Scene:T2DDynamicGameScene)
+ ' Set a new scene into the game.
+ Self.FlushScene()
+ Self.Scene = Scene
+ Self.Scene.Start()
+ End Method
+'#End Region
+'#Region Method: MainLoop
+ Method MainLoop()
+ ' Repeat the main loop until termination is required.
+ ' Taken from my .NET game framework codenamed: Lita, for more info drop me a line! :) </PLUG>
+ If Self.DynamicTiming = True
+ ' Dynamic timing.
+ Local WaitUntil:Int
+ Local Timer:MiniTimer = New MiniTimer
+ Timer.Reset()
+
+ Local Period:Int = 1000 / Self.DesiredFPS
+ Local Gap:Int
+ Local UpdatesUntil:Float
+
+ WaitUntil = MilliSecs() + Period
+ While (Not TerminateMainLoop) And (Not Self.IsTerminated())
+
+ ' Loop until time has passed.
+ Repeat
+ Until MilliSecs() > WaitUntil
+
+ ' Update for as many times frames were missed.
+ ' First we need to calculate some stats.
+ Gap = (MilliSecs() - Timer.TimeStarted)
+ UpdatesUntil = Float(Gap) / Float(Period)
+
+ ' Perform the updates.
+ If UpdatesUntil > 1.0 Then
+ For Local Count:Int = 1 To Int(UpdatesUntil)
+ Self.Update()
+ Next
+ End If
+
+ ' Reset our timer to start the next run.
+ Timer.Reset()
+ WaitUntil = MilliSecs() + Period
+ Self.Render()
+ Flip()
+' GCCollect
+ 'Application.DoEvents()
+ Wend
+ Else
+ ' Frame limited tbiming.
+ While (Not TerminateMainLoop) And (Not Self.IsTerminated())
+ Self.Update()
+ Self.Render()
+ Flip()
+' GCCollect
+ Wend
+ EndIf
+ Self.TerminateMainLoop = False
+
+ End Method
+'#End Region
+'#Region Method: FlushScene
+ Method FlushScene()
+ ' If there is a scene then we need to kill it off..
+ Self.Finish()
+ Self.Scene = Null
+ End Method
+'#End Region
+'#Region Method: Start
+ Method Start()
+ ' Call start on the scene.
+ If Self.Scene <> Null Then Self.Scene.Start()
+ End Method
+'#End Region
+'#Region Method: Finish
+ Method Finish()
+ ' Call start on the scene.
+ If Self.Scene <> Null Then
+ Self.Scene.Finish()
+ EndIf
+ End Method
+'#End Region
+'#Region Method: Update
+ Method Update()
+ ' Call update on the scene.
+ If Self.Scene <> Null Then Self.Scene.Update()
+ End Method
+'#End Region
+'#Region Method: Render
+ Method Render()
+ ' Call render on the scene.
+ If Self.Scene <> Null Then Self.Scene.Render()
+ End Method
+'#End Region
+'#Region Method: IsTerminated
+ Method IsTerminated:Int()
+ ' Check to see if the scene wants to terminate the main loop.
+ If Self.Scene <> Null Then
+ If Self.Scene.TerminateMainLoop Then
+ Self.Scene.TerminateMainLoop = False
+ Return True
+ EndIf
+ Return False
+ EndIf
+ Return True
+ End Method
+'#End Region
+
+End Type
+
+Type T2DDynamicGameScene
+
+ Field TerminateMainLoop:Int = False
+
+ Method Update() Abstract
+ Method Render() Abstract
+ Method Start() Abstract
+ Method Finish() Abstract
+
+End Type
+
+
+
+
+
+
+
+
--- /dev/null
+' *******************************************************************
+' Source: Mini Timer
+' Version: 1.00
+' Author: Rob Hutchinson 2004
+' Email: rob@proteanide.co.uk
+' WWW: http://www.proteanide.co.uk/
+' -------------------------------------------------------------------
+' This include provides a class for a timer object. The class works
+' in milliseconds. First of all the object can be enabled and
+' disabled at will by setting the Enabled field
+' to true or false. The Interval field can be set
+' to mark a milliseconds interval that, when reached, IntervalReached
+' will become true. You can use the Reset() method to reset the timer
+' and MiillisecondsElapsed() will tell you the number of milliseconds
+' that have passed since you called Reset. Enabled field only has
+' any effect on the IntervalReached function of the timer. If false
+' then the method will always return false.
+' Ported directly from my .NET Framework game library: Lita.
+' -------------------------------------------------------------------
+' Required:
+' - Nothing.
+' *******************************************************************
+
+Type MiniTimer
+
+'#Region Declarations
+ Field TimeStarted:Int
+ Field Interval:Int
+ Field Enabled:Int = True
+'#End Region
+
+'#Region Method: Reset
+ '''-----------------------------------------------------------------------------
+ ''' <summary>
+ ''' Resets the timer.
+ ''' </summary>
+ '''-----------------------------------------------------------------------------
+ Method Reset()
+ Self.TimeStarted = MilliSecs()
+ End Method
+'#End Region
+'#Region Method: MiillisecondsElapsed
+ '''-----------------------------------------------------------------------------
+ ''' <summary>
+ ''' Gets the number of milliseconds that have passed since a call to Reset.
+ ''' </summary>
+ '''-----------------------------------------------------------------------------
+ Method MiillisecondsElapsed:Int()
+ If Self.TimeStarted = 0 Then Return 0
+ Local TimeNow:Int = MilliSecs()
+ Return TimeNow - Self.TimeStarted
+ End Method
+'#End Region
+'#Region Method: IntervalReached
+ '''-----------------------------------------------------------------------------
+ ''' <summary>
+ ''' Returns true if the given interval has been reached.
+ ''' </summary>
+ '''-----------------------------------------------------------------------------
+ Method IntervalReached:Int()
+ If Self.Enabled Then Return (Self.MiillisecondsElapsed() > Self.Interval)
+ End Method
+'#End Region
+
+End Type
+
+
+
+
+
+
+
--- /dev/null
+' Simple Physics Engine - V1.0
+' --------------------------------------------------------
+' Written By: Rob Hutchinson 2004
+
+Strict
+
+Type TRectangle
+
+'#Region Declarations
+ Field X:Int,Y:Int,Width:Int,Height:Int
+'#End Region
+
+'#Region Method: Bottom
+ Method Bottom:Int()
+ Return Y + Height
+ End Method
+'#End Region
+'#Region Method: Right
+ Method Right:Int()
+ Return X + Width
+ End Method
+'#End Region
+'#Region Method: Create
+ Function Create:TRectangle(X:Int,Y:Int,Width:Int,Height:Int)
+ Local Out:TRectangle = New TRectangle
+ Out.X = X
+ Out.Y = Y
+ Out.Width = Width
+ Out.Height = Height
+ Return Out
+ End Function
+'#End Region
+
+'#Region Method: IntersectsWith
+ Method IntersectsWith:Int(Rectangle:TRectangle)
+ If (((Rectangle.X < (Self.X + Self.Width)) And (Self.X < (Rectangle.X + Rectangle.Width))) And (Rectangle.Y < (Self.Y + Self.Height))) Then
+ Return (Self.Y < (Rectangle.Y + Rectangle.Height))
+ End If
+ Return False
+ End Method
+'#End Region
+
+End Type
+
+Type TPhysicsUtility
+
+'#Region Method: DistanceBetweenPoints
+ Function DistanceBetweenPoints:Double(X1:Int, Y1:Int, X2:Int, Y2:Int)
+ Local DeltaX:Int = X2 - X1
+ Local DeltaY:Int = Y2 - Y1
+ Local Calculation:Int = ((DeltaX * DeltaX) + (DeltaY * DeltaY))
+ Return Sqr(Double(Calculation))
+ End Function
+'#End Region
+
+ Function DegreesBetweenPoints(X1:Double, Y1:Double, X2:Double, Y2:Double)
+ Local Out:Double = ATan2(X2 - X1,-(Y2 - Y1))
+ Return 180.0 - Out
+ End Function
+
+End Type
+
+Type TPointD
+
+'#Region Declarations
+ Field X:Double = 0
+ Field Y:Double = 0
+'#End Region
+
+End Type
+
+Type TMagnetCollection Extends TList
+
+'#Region Method: Draw
+ Method Draw()
+ Local Item:TMagnet
+ For Item=EachIn Self
+ Item.Draw()
+ Next
+ End Method
+'#End Region
+
+End Type
+
+Type TPhysicsProviderCollection Extends TList
+
+'#Region Method: Draw
+ Method Draw()
+ Local Item:TPhysicsProvider
+ For Item=EachIn Self
+ Item.Draw()
+ Next
+End Method
+'#End Region
+'#Region Method: ApplyPhysics
+ Method ApplyPhysics:Int()
+ Local Item:TPhysicsProvider
+ Local Count:Int
+ For Item=EachIn Self
+ Item.ApplyPhysics()
+ Count:+1
+ Next
+ Return Count
+ End Method
+'#End Region
+'#Region Method: ApplyPhysicsAndFriction
+ Method ApplyPhysicsAndFriction:Int(Axis:Int)
+ Local Item:TPhysicsProvider
+ Local Count:Int
+ For Item=EachIn Self
+ Item.ApplyFriction(Axis)
+ Item.ApplyPhysics()
+ Item.Draw()
+ Count:+1
+ Next
+ Return Count
+ End Method
+'#End Region
+'#Region Method: ApplyPhysicsAndDraw
+ Method ApplyPhysicsAndDraw()
+ Local Item:TPhysicsProvider
+ For Item=EachIn Self
+ Item.ApplyPhysics()
+ Item.Draw()
+ Next
+End Method
+'#End Region
+
+End Type
+
+Type TMagnet
+'#Region Declarations
+ Field X:Int, Y:Int
+ Field Radius:Double
+
+ Const NegativePolarity = -1
+ Const PositivePolarity = 1
+
+ Field Strength:Double
+ Field Polarity:Int = 1
+'#End Region
+
+
+
+'#Region Method: GetStrengthOfPull
+ Method GetStrengthOfPull:Double(Orbit:Double)
+ If Orbit > Self.Radius Then Return 0
+
+ ' First work out the percentage...
+ Local PercentOfPull:Double = (Orbit / Self.Radius) * 100
+ Return Self.Strength - ((PercentOfPull / 100) * Self.Strength)
+ End Method
+'#End Region
+'#Region Method: GetForces
+ Method GetForces:TPointD(X:Int, Y:Int)
+ Local Out:TPointD=New TPointD
+ Out.X = 0
+ Out.Y = 0
+
+ ' Get Distance Between Points...
+ Local Distance:Double = TPhysicsUtility.DistanceBetweenPoints(X, Y, Self.X, Self.Y)
+ Local Strength:Double = Self.GetStrengthOfPull(Distance)
+ If Strength = 0 Then Return Out
+
+ ' Get the degrees between points..
+ Local Angle:Double = TPhysicsUtility.DegreesBetweenPoints(X, Y,Self.X, Self.Y)
+
+ ' Reverse strength if using negative polarity.
+ If Self.Polarity = NegativePolarity Then Strength = -Strength
+
+ Out.X = Sin(Angle) * Strength
+ Out.Y = Cos(Angle) * Strength
+
+ Return Out
+ End Method
+'#End Region
+'#Region Method: Draw
+ Method Draw()
+ Local Degrees
+ SetColor 255,255,255
+ For Degrees = 0 To 360
+ DrawRect Self.X + (Sin(Degrees) * Self.Radius), Self.Y + (Cos(Degrees) * Self.Radius),1,1
+ Next
+ End Method
+'#End Region
+'#Region Method: Create
+ Function Create:TMagnet(X:Int,Y:Int,Radius:Double,Polarity:Int,Strength:Double)
+ Local Out:TMagnet = New TMagnet
+ Out.X = X
+ Out.Y = Y
+ Out.Radius = Radius
+ Out.Polarity = Polarity
+ Out.Strength = Strength
+ Return Out
+ End Function
+'#End Region
+'#Region Method: SetPosition
+ Method SetPosition(X:Int,Y:Int)
+ Self.X = X
+ Self.Y = Y
+ End Method
+'#End Region
+
+End Type
+
+Type TWorldPhysicsProvider
+
+ Field Loc = 0
+
+'#Region Declarations
+ Field Gravity:Double
+ Field Wind:Double
+ Field Drag:Double
+ Field ApplyMagnets:Int = True
+ Field Magnets:TMagnetCollection = New TMagnetCollection
+'#End Region
+
+'#Region Method: Initialize
+ Method Initialize(Gravity:Double, Wind:Double = 0.0, Drag:Double = 0.0, ApplyMagnets:Int = False)
+ Self.Gravity = Gravity
+ Self.Wind = Wind
+ Self.Drag = Drag
+ Self.ApplyMagnets = ApplyMagnets
+ End Method
+'#End Region
+'#Region Method: Create
+ Function Create:TWorldPhysicsProvider(Gravity:Double, Wind:Double = 0.0, Drag:Double = 0.0, ApplyMagnets:Int = False)
+ Local Out:TWorldPhysicsProvider = New TWorldPhysicsProvider
+ Out.Gravity = Gravity
+ Out.Wind = Wind
+ Out.Drag = Drag
+ Out.ApplyMagnets = ApplyMagnets
+ Return Out
+ End Function
+'#End Region
+
+End Type
+
+Type TPhysicsProvider Extends TLink
+'#Region Declarations
+ ' Location.
+ Field X:Double, Y:Double
+ Field World:TWorldPhysicsProvider = New TWorldPhysicsProvider
+
+ ' Terminal Velocity
+ Field TerminalVelocityX:Double = 40.0, TerminalVelocityY:Double = 40.0
+
+ ' Velocity
+ Field VelocityX:Double, VelocityY:Double
+
+ ' Weight
+ Field Weight:Double = 10.0
+ Field SurfaceArea:Double = 10.0
+
+ ' Bounce Values.
+ Field BounceCoefficientX:Double = 1.0
+ Field BounceCoefficientY:Double = 0.75
+
+ ' The friction being applied to this object.
+ Field Friction:Double = 1.0
+
+ ' Whether or not to apply magnets to the individual object.
+ Field ApplyMagnets:Int = True
+
+ Const AxisX:Int = 1
+ Const AxisY:Int = 2
+
+ Const VerticalPlane = 1
+ Const HorizontalPlane = 2
+'#End Region
+
+' Method remove()
+' World.remove Self
+' End Method
+
+'#Region Method: Double
+ Method Speed:Double()
+ Return Sqr((Self.VelocityX * Self.VelocityX) + (Self.VelocityY * Self.VelocityY))
+ End Method
+'#End Region
+'#Region Method: Momentum
+ Method Momentum:Double()
+ Return Self.Speed() * Self.Weight
+ End Method
+'#End Region
+'#Region Method: ReverseVelocityX
+ Method ReverseVelocityX()
+ Self.VelocityX = -Self.VelocityX
+ End Method
+'#End Region
+'#Region Method: ReverseVelocityY
+ Method ReverseVelocityY()
+ Self.VelocityY = -Self.VelocityY
+ End Method
+'#End Region
+'#Region Method: SnapWithinRectangle
+ Method SnapWithinRectangle(Area:TRectangle)
+ If Self.X < Area.X Then Self.X = Area.X
+ If Self.Y < Area.Y Then Self.Y = Area.Y
+ Local Right:Int = Area.X + Area.Width
+ Local Bottom:Int = Area.Y + Area.Height
+ If Self.X > Right Then Self.X = Right
+ If Self.Y > Bottom Then Self.Y = Bottom
+ End Method
+'#End Region
+'#Region Method: SnapWithinRectangleAndReverseVelocity
+ Method SnapWithinRectangleAndReverseVelocity:Int(Area:TRectangle)
+ Local Out:Int = False
+ If Self.X < Area.X Then
+ Self.X = Area.X
+ Self.ReverseVelocityX()
+ Out = True
+ End If
+ If Self.Y < Area.Y Then
+ Self.Y = Area.Y
+ Self.ReverseVelocityY()
+ Out = True
+ End If
+ Local Right:Int = Area.X + Area.Width
+ Local Bottom:Int = Area.Y + Area.Height
+ If Self.X > Right Then
+ Self.X = Right
+ Self.VelocityX = -Self.VelocityX
+ Out = True
+ End If
+ If Self.Y > Bottom Then
+ Self.Y = Bottom
+ Self.VelocityY = -Self.VelocityY
+ Out = True
+ End If
+ Return Out
+ End Method
+'#End Region
+'#Region Method: ApplyFriction
+ Method ApplyFriction(Axis:Int)
+ If ((Axis & AxisX) = AxisX) Then Self.VelocityX:*Self.Friction
+ If ((Axis & AxisY) = AxisY) Then Self.VelocityY:*Self.Friction
+ End Method
+'#End Region
+'#Region Method: ApplyFrictionX
+ Method ApplyFrictionX()
+ Self.VelocityX:*Self.Friction
+ End Method
+'#End Region
+'#Region Method: ApplyFrictionY
+ Method ApplyFrictionY()
+ Self.VelocityY:*Self.Friction
+ End Method
+'#End Region
+'#Region Method: Bounce
+ Method Bounce(Planes:Int)
+ If ((Planes & HorizontalPlane) = HorizontalPlane)
+ Self.VelocityY = -Self.VelocityY
+ Self.VelocityY:*Self.BounceCoefficientY
+ End If
+
+ If ((Planes & VerticalPlane) = VerticalPlane)
+ Self.VelocityX = -Self.VelocityX
+ Self.VelocityX:*Self.BounceCoefficientX
+ End If
+ End Method
+'#End Region
+'#Region Method: Draw
+ Method Draw() Abstract
+'#End Region
+'#Region Method: PhysicsApplied
+ Method PhysicsApplied() Abstract
+'#End Region
+'#Region Method: ApplyPhysics
+ Method ApplyPhysics()
+ ' Gravity
+ Self.VelocityY:+Self.World.Gravity - (Self.World.Drag * Self.SurfaceArea)
+
+ ' Wind
+ Self.VelocityX:+(Self.World.Wind / Self.Weight) * Self.SurfaceArea
+
+ ' Apply the magnets?
+ If (Self.World.ApplyMagnets = True) And (Self.ApplyMagnets = True) Then
+ Local Magnet:TMagnet
+ For Magnet=EachIn Self.World.Magnets
+ Local Pull:TPointD = Magnet.GetForces(Self.X,Self.Y)
+
+ Self.VelocityX = Self.VelocityX + Pull.X
+ Self.VelocityY = Self.VelocityY + Pull.Y
+ Next
+ End If
+
+ ' Terminal Velocity
+ If Self.VelocityX > Self.TerminalVelocityX Then Self.VelocityX = Self.TerminalVelocityX
+ If Self.VelocityY > Self.TerminalVelocityY Then Self.VelocityY = Self.TerminalVelocityY
+ If Self.VelocityX < -Self.TerminalVelocityX Then Self.VelocityX = -Self.TerminalVelocityX
+ If Self.VelocityY < -Self.TerminalVelocityY Then Self.VelocityY = -Self.TerminalVelocityY
+
+ ' Update
+ Self.Y:+Self.VelocityY
+ Self.X:+Self.VelocityX
+
+ ' Raise the event...
+ Self.PhysicsApplied()
+
+ End Method
+'#End Region
+'#Region Method: SetVelocityFromAngle
+ Method SetVelocityFromAngle(Angle!, Speed!)
+ Self.VelocityX = Sin(Angle) * Speed
+ Self.VelocityY = Cos(Angle) * Speed
+ End Method
+'#End Region
+'#Region Method: IncreaseVelocityFromAngle
+ Method IncreaseVelocityFromAngle(Angle!, Speed!)
+ Self.VelocityX :+ Sin(Angle) * Speed
+ Self.VelocityY :+ Cos(Angle) * Speed
+ End Method
+'#End Region
+'#Region Method: Angle
+ Method Angle!()
+ Return TPhysicsUtility.DegreesBetweenPoints(Self.X, Self.Y, Self.X + Self.VelocityX, Self.Y + Self.VelocityY)
+ End Method
+'#End Region
+
+End Type
+
+' Particle Engine.
+Type TParticleEmitter Extends TLink
+
+'#Region Declarations
+ Field World:TWorldPhysicsProvider
+ Field X:Int, Y:Int
+ Field Particles:TPhysicsProviderCollection = New TPhysicsProviderCollection
+'#End Region
+
+'#Region Method: Update
+ Method Update()
+ Self.Particles.ApplyPhysics()
+ End Method
+'#End Region
+'#Region Method: UpdateWithFriction
+ Method UpdateWithFriction(Axis:Int)
+ Self.Particles.ApplyPhysicsAndFriction(Axis)
+ End Method
+'#End Region
+'#Region Method: Draw
+ Method Draw()
+ Self.Particles.Draw()
+ End Method
+'#End Region
+'#Region Method: UpdateAndDraw
+ Method UpdateAndDraw()
+ Self.Particles.ApplyPhysics()
+ Self.Particles.Draw()
+ End Method
+'#End Region
+'#Region Method: AddParticle
+ Method AddParticle(Particle:IParticle)
+ Self.Particles.AddLast(Particle)
+ End Method
+'#End Region
+'#Region Method: RemoveParticle
+ Method RemoveParticle(Particle:IParticle)
+ Self.Particles.Remove(Particle)
+ End Method
+'#End Region
+
+End Type
+Type IParticle Extends TPhysicsProvider
+
+ Field Angle:Float
+ Field Speed:Float
+ Field Region:TRectangle
+
+ Method Initialize() Abstract
+
+End Type
+Type TStaticParticle Extends IParticle
+
+'#Region Declarations
+ Field LastX:Int
+ Field LastY:Int
+ Field Emitter:TParticleEmitter
+
+ Field LifeTime:Int = -1
+ Field LifeAmount:Int
+ Field FadeSpeed:Float = -1.0
+ Field Color[] = [255,255,255]
+ Field FadeAmount:Float
+ Field Size:Float = 1.0
+ Field ActualRotation:Float = 0.0
+ Field Rotation:Float = 0.0
+
+ Field Graphic:TImage
+'#End Region
+'#Region Constructor
+ Function Create:TStaticParticle(World:TWorldPhysicsProvider, Graphic:TImage, Region:TRectangle)
+ Local Out:TStaticParticle = New TStaticParticle
+ Out.Graphic = Graphic
+ Out.Region = Region
+ Return Out
+ End Function
+'#End Region
+
+'#Region Method: Draw
+ Method Draw()
+ SetColor(Self.Color[0],Self.Color[1],Self.Color[2])
+ SetScale(Self.Size, Self.Size)
+ SetAlpha(1.0 - Self.FadeAmount)
+ Self.ActualRotation :+ Self.Rotation
+ SetRotation(Self.ActualRotation)
+ DrawImage(Self.Graphic, Int(Self.X), Int(Self.Y))
+ End Method
+'#End Region
+
+'#Region Method: Initialize
+ Method Initialize()
+ Self.SetVelocityFromAngle(Self.Angle, Self.Speed)
+ End Method
+'#End Region
+
+'#Region Override: PhysicsApplied
+ Method PhysicsApplied()
+ Local ImageRect:TRectangle = New TRectangle
+ ImageRect.Width = Self.Graphic.Width
+ ImageRect.Height = Self.Graphic.Height
+ ImageRect.X = Int(Self.X - Self.Graphic.handle_x)
+ ImageRect.Y = Int(Self.Y - Self.Graphic.handle_y)
+
+ Local RemoveThis:Int = False
+ If Not Self.Region.IntersectsWith(ImageRect) Then
+ RemoveThis = True
+ Else
+ If Self.LifeTime > -1 Then
+ If Self.LifeAmount >= Self.LifeTime Then
+ ' Start fade out?
+ If Self.FadeSpeed > -1 Then
+ Self.FadeAmount :+ FadeSpeed
+ If Self.FadeAmount >= 1.0 Then
+ RemoveThis = True
+ EndIf
+ Else
+ RemoveThis = True
+ EndIf
+ Else
+ Self.LifeAmount :+ 1 ' its lived a bit longer now.
+ EndIf
+ Else
+ RemoveThis = True
+ EndIf
+ EndIf
+
+ If RemoveThis Then
+ Self.Emitter.RemoveParticle(Self)
+ Self.Emitter = Null
+ EndIf
+ End Method
+'#End Region
+
+End Type
+Type TFountain Extends TParticleEmitter
+
+'#Region Declarations
+ Field Region:TRectangle
+'#End Region
+'#Region Constructor
+ Function Create:TFountain(World:TWorldPhysicsProvider, Region:TRectangle)
+ Local Out:TFountain = New TFountain
+ Out.World = World
+ Out.Region = Region
+ Return Out
+ End Function
+'#End Region
+
+'#Region Method: AddStaticParticle
+ Method AddStaticParticle:TStaticParticle(Image:TImage, Angle:Float, Velocity:Float, LifeTime:Int, FadeSpeed:Float, Color[])
+ Local Particle:TStaticParticle = TStaticParticle.Create(Self.World, Image, Self.Region)
+ Particle.X = Self.X
+ Particle.Y = Self.Y
+ Particle.Angle = Angle
+ Particle.Speed = Velocity
+ Particle.Emitter = Self
+ Particle.FadeSpeed = FadeSpeed
+ Particle.LifeTime = LifeTime
+ Particle.Color = Color
+ Particle.Initialize()
+ Self.AddParticle(Particle)
+ Return Particle
+ End Method
+'#End Region
+
+End Type
+
+
+
+
+
--- /dev/null
+
+Strict
+
+Type TColor
+
+ Method RGBColor:TRGBColor() Abstract
+ Method CMYColor:TCMYColor() Abstract
+ Method HSVColor:THSVColor() Abstract
+
+End Type
+
+Type TRGBColor Extends TColor
+
+ Field _red#,_grn#,_blu#
+
+ Method RGBColor:TRGBColor()
+ Return Self
+ End Method
+
+ Method CMYColor:TCMYColor()
+ Return TCMYColor.CreateCMY( 1-_red,1-_grn,1-_blu )
+ End Method
+
+ Method HSVColor:THSVColor()
+ Local hmin#=_red
+ If _grn<hmin hmin=_grn
+ If _blu<hmin hmin=_blu
+ Local hmax#=_red
+ If _grn>hmax hmax=_grn
+ If _blu>hmax hmax=_blu
+ If hmax-hmin=0 Return THSVColor.CreateHSV( 0,0,hmax )
+ Local hue#,delta#=hmax-hmin
+ Select hmax
+ Case _red hue=(_grn-_blu)/delta
+ Case _grn hue=2+(_blu-_red)/delta
+ Case _blu hue=4+(_red-_grn)/delta
+ End Select
+ hue=hue*60
+ If hue<0 hue=hue+360
+ Return THSVColor.CreateHSV( hue,delta/hmax,hmax )
+ End Method
+
+ Method RED#()
+ Return _red
+ End Method
+
+ Method GREEN#()
+ Return _grn
+ End Method
+
+ Method BLUE#()
+ Return _blu
+ End Method
+
+ Method Set(r#,g#,b#)
+ _red=r
+ _grn=g
+ _blu=b
+ End Method
+
+ Function CreateRGB:TRGBColor( RED#,grn#,blu# )
+ Local color:TRGBColor=New TRGBColor
+ color._red=RED
+ color._grn=grn
+ color._blu=blu
+ Return color
+ End Function
+
+End Type
+
+Type TCMYColor Extends TColor
+
+ Field _cyn#,_mag#,_yel#
+
+ Method RGBColor:TRGBColor()
+ Return TRGBColor.CreateRGB( 1-_cyn,1-_mag,1-_yel )
+ End Method
+
+ Method CMYColor:TCMYColor()
+ Return Self
+ End Method
+
+ Method HSVColor:THSVColor()
+ Return RGBColor().HSVColor()
+ End Method
+
+ Method CYAN#()
+ Return _cyn
+ End Method
+
+ Method MAGENTA#()
+ Return _mag
+ End Method
+
+ Method YELLOW#()
+ Return _yel
+ End Method
+
+ Function CreateCMY:TCMYColor( cyn#,mag#,yel# )
+ Local color:TCMYColor=New TCMYColor
+ color._cyn=cyn
+ color._mag=mag
+ color._yel=yel
+ Return color
+ End Function
+
+End Type
+
+Type THSVColor Extends TColor
+
+ Field _hue#,_sat#,_val#
+
+ Method RGBColor:TRGBColor()
+ If _sat<=0 Return TRGBColor.CreateRGB( _val,_val,_val )
+ Local h#=_hue/60
+ Local i#=Floor( h )
+ Local f#=h-i
+ Local p#=_val*(1-_sat)
+ Local q#=_val*(1-(_sat*f))
+ Local t#=_val*(1-(_sat*(1-f)))
+ Select Int(i)
+ Case 0 Return TRGBColor.CreateRGB( _val,t,p )
+ Case 1 Return TRGBColor.CreateRGB( q,_val,p )
+ Case 2 Return TRGBColor.CreateRGB( p,_val,t )
+ Case 3 Return TRGBColor.CreateRGB( p,q,_val )
+ Case 4 Return TRGBColor.CreateRGB( t,p,_val )
+ Case 5 Return TRGBColor.CreateRGB( _val,p,q )
+ End Select
+ End Method
+
+ Method CMYColor:TCMYColor()
+ Return RGBColor().CMYColor()
+ End Method
+
+ Method HSVColor:THSVColor()
+ Return Self
+ End Method
+
+ Method Hue#()
+ Return _hue
+ End Method
+
+ Method Saturation#()
+ Return _sat
+ End Method
+
+ Method Value#()
+ Return _val
+ End Method
+
+ Function CreateHSV:THSVColor( hue#,sat#,val# )
+ If hue<0 hue=hue+360
+ If hue>=360 hue=hue-360
+ Local color:THSVColor=New THSVColor
+ color._hue=hue
+ color._sat=sat
+ color._val=val
+ Return color
+ End Function
+
+End Type
+
+Global RED:TColor=RGBColor( 1,0,0 )
+Global GREEN:TColor=RGBColor( 0,1,0 )
+Global BLUE:TColor=RGBColor( 0,0,1 )
+
+Global ORANGE:TColor=RGBColor( 1,1,0 )
+
+Global CYAN:TColor=CMYColor( 1,0,0 )
+Global MAGENTA:TColor=CMYColor( 0,1,0 )
+Global YELLOW:TColor=CMYColor( 0,0,1 )
+
+Global BLACK:TColor=HSVColor( 0,0,0 )
+Global WHITE:TColor=HSVColor( 0,0,1 )
+Global GRAY:TColor=HSVColor( 0,0,.5 )
+Global DARKGRAY:TColor=HSVColor( 0,0,.25 )
+Global LIGHTGRAY:TColor=HSVColor( 0,0,.75 )
+
+Rem
+bbdoc: Create a red, green, blue color
+returns: A new color object
+about: @red, @grn and @blu should be in the range 0 to 1.
+End Rem
+Function RGBColor:TRGBColor( RED#,grn#,blu# )
+ Return TRGBColor.CreateRGB( RED,grn,blu )
+End Function
+
+Rem
+bbdoc: Create a cyan, magenta, yellow color
+returns: A new color object
+about: @cyn, @mag and @yel should be in the range 0 to 1.
+End Rem
+Function CMYColor:TCMYColor( cyn#,mag#,yel# )
+ Return TCMYColor.CreateCMY( cyn,mag,yel )
+End Function
+
+Rem
+bbdoc: Create a hue, saturation, value color
+returns: A new color object
+about: @hue should be in the range 0 to 360, @sat and @val should be in the range 0 to 1.
+End Rem
+Function HSVColor:THSVColor( hue#,sat#,val# )
+ Return THSVColor.CreateHSV( hue,sat,val )
+End Function
+
+Rem
+bbdoc: Get red component of a color
+returns: Red component of @color in the range 0 to 1
+End Rem
+Function ColorRed#( color:TColor )
+ Return color.RGBColor().RED()
+End Function
+
+Rem
+bbdoc: Get green component of a color
+returns: Green component of @color in the range 0 to 1
+End Rem
+Function ColorGreen#( color:TColor )
+ Return color.RGBColor().GREEN()
+End Function
+
+Rem
+bbdoc: Get blue component of a color
+returns: Blue component of @color in the range 0 to 1
+End Rem
+Function ColorBlue#( color:TColor )
+ Return color.RGBColor().BLUE()
+End Function
+
+Rem
+bbdoc: Get cyan component of a color
+returns: Cyan component of @color in the range 0 to 1
+End Rem
+Function ColorCyan#( color:TColor )
+ Return color.CMYColor().CYAN()
+End Function
+
+Rem
+bbdoc: Get magenta component of a color
+returns: Magenta component of @color in the range 0 to 1
+End Rem
+Function ColorMagenta#( color:TColor )
+ Return color.CMYColor().MAGENTA()
+End Function
+
+Rem
+bbdoc: Get yellow component of a color
+returns: Yellow component of @color in the range 0 to 1
+End Rem
+Function ColorYellow#( color:TColor )
+ Return color.CMYColor().YELLOW()
+End Function
+
+Rem
+bbdoc: Get hue component of a color
+returns: Hue component of @color in the range 0 to 360
+End Rem
+Function ColorHue#( color:TColor )
+ Return color.HSVColor().Hue()
+End Function
+
+Rem
+bbdoc: Get saturation component of a color
+returns: Saturation component of @color in the range 0 to 1
+End Rem
+Function ColorSaturation#( color:TColor )
+ Return color.HSVColor().Saturation()
+End Function
+
+Rem
+bbdoc: Get value component of a color
+returns: Value component of @color in the range 0 to 1
+End Rem
+Function ColorValue#( color:TColor )
+ Return color.HSVColor().Value()
+End Function
--- /dev/null
+Rem
+
+Firepaint demo:
+
+Hold down mouse button to emit *FIRE*!
+
+EndRem
+
+Strict
+
+'For minimal build...
+Rem
+Framework BRL.D3D7Max2D
+Import BRL.Basic
+Import BRL.System
+Import BRL.PNGLoader
+Import BRL.FreeAudioAudio
+Import BRL.WAVLoader
+End Rem
+
+Import "color.bmx"
+
+Incbin "stars.png"
+Incbin "player.png"
+Incbin "bullet.png"
+Incbin "shoot.wav"
+
+Const WIDTH=640,HEIGHT=480
+Const DEPTH=32,HERTZ=60
+
+Const GRAVITY#=.15,SPARKS_PER_FRAME=55
+
+Global sparks:TList=New TList
+Global bullets:TList=New TList
+
+Type TEntity
+
+ Field link:TLink
+
+ Method remove()
+ link.remove
+ End Method
+
+ Method AddLast( list:TList )
+ link=list.AddLast( Self )
+ End Method
+
+ Method Update() Abstract
+
+End Type
+
+Type TSpark Extends TEntity
+
+ Field x#,y#,xs#,ys#
+ Field color[3],rot#,rots#
+
+ Method Update()
+
+ ys:+GRAVITY
+ x:+xs
+ y:+ys
+
+ If x<0 Or x>=WIDTH Or y>=HEIGHT
+ remove
+ Return
+ EndIf
+
+ rot=rot+rots
+ SetHandle 8,8
+ SetRotation rot#
+ SetAlpha 1-y/HEIGHT
+ SetColor color[0],color[1],color[2]
+ DrawRect x,y,17,17
+ SetHandle 0,0
+
+ End Method
+
+ Function CreateSpark:TSpark( x#,y#,color[] )
+ Local spark:TSpark=New TSpark
+ Local an#=Rnd(360),sp#=Rnd(3,5)
+ spark.x=x
+ spark.y=y
+ spark.xs=Cos(an)*sp
+ spark.ys=Sin(an)*sp
+ spark.rots=Rnd(-15,15)
+ spark.color=color
+ spark.AddLast sparks
+ Return spark
+ End Function
+
+End Type
+
+Type TBullet Extends TEntity
+
+ Field x#,y#,ys#
+ Field rot#,img:TImage
+
+ Method Update()
+ ys:-.01
+ y:+ys
+ If y<0
+ remove
+ Return
+ EndIf
+ rot:+3
+ SetRotation rot
+ DrawImage img,x,y
+ End Method
+
+ Function CreateBullet:TBullet( x#,y#,img:TImage )
+ Local bullet:TBullet=New TBullet
+ bullet.x=x
+ bullet.y=y
+ bullet.ys=-1
+ bullet.img=img
+ bullet.AddLast bullets
+ Return bullet
+ End Function
+
+End Type
+
+Function UpdateEntities( list:TList )
+ For Local entity:TEntity=EachIn list
+ entity.Update
+ Next
+End Function
+
+Graphics WIDTH,HEIGHT,DEPTH,HERTZ
+
+AutoMidHandle True
+
+Local fire:TSound=LoadSound( "incbin::shoot.wav" )
+Local dude:TImage=LoadImage( "incbin::player.png" ),dude_x=WIDTH/2,dude_y=HEIGHT-30
+Local bull:TImage=LoadImage( "incbin::bullet.png" ),bull_x,bull_y
+Local stars:TImage=LoadImage( "incbin::stars.png" ),stars_x,stars_y
+
+Local show_debug,color_rot#
+
+While Not KeyHit( KEY_ESCAPE )
+
+ Cls
+
+ stars_y:+1
+ SetBlend MASKBLEND
+ TileImage stars,stars_x,stars_y
+ TileImage stars,stars_x+7,stars_y*2
+ TileImage stars,stars_x+7,stars_y*3
+
+ If KeyDown( KEY_LEFT )
+ dude_x:-5
+ Else If KeyDown( KEY_RIGHT )
+ dude_x:+5
+ EndIf
+
+ SetBlend MASKBLEND
+ DrawImage dude,dude_x,dude_y
+
+ If KeyHit( KEY_SPACE )
+ PlaySound fire
+ TBullet.CreateBullet dude_x,dude_y-16,bull
+ EndIf
+
+ If MouseDown(1)
+ color_rot:+1.5
+ color_rot:Mod 360
+ Local color:TRGBColor=HSVColor( color_rot,1,1 ).RGBColor()
+ Local rgb[]=[Int(color.Red()*255),Int(color.Green()*255),Int(color.Blue()*255)]
+ For Local k=1 To SPARKS_PER_FRAME
+ TSpark.CreateSpark MouseX(),MouseY(),rgb
+ Next
+ EndIf
+
+ SetBlend MASKBLEND
+ UpdateEntities bullets
+ SetRotation 0
+
+ SetBlend LIGHTBLEND
+ UpdateEntities sparks
+ SetAlpha 1
+ SetRotation 0
+ SetColor 255,255,255
+
+ If KeyHit( Asc("D") ) show_debug=1-show_debug
+
+ If show_debug
+ DrawText "MemAlloced="+GCMemAlloced(),0,0
+ EndIf
+
+ Flip
+
+Wend
--- /dev/null
+Rem
+
+Another Oldskool demo thingy, by FlameDuck and Razorien of Binary Therapy
+
+End Rem
+
+Strict
+
+Type rasterBar
+ Field angle:Double,angleadd:Double,color[],freq:Double
+
+ Function addRasterBar(aSpeed:Double, aFreq:Double, aColor[], aList:TList)
+ Local temp:rasterBar = New rasterBar
+ temp.angle=0
+ temp.angleadd=aSpeed
+ temp.freq=aFreq
+ temp.color = aColor
+ aList.addLast(temp)
+ End Function
+
+ Method drawRasterBar(xstart:Int, ycenter:Int, scale:Double)
+ SetBlend LIGHTBLEND
+ SetColor(color[0],color[1],color[2])
+ angle:+angleadd
+
+ For Local i:Int = 0 To 736
+ Local temp:Int = i+xstart
+ If temp > 0 Or temp < 799
+ DrawImage rastaImage, temp, ycenter + Sin((i+angle)*freq)*scale
+ End If
+ Next
+ End Method
+
+End Type
+
+Type polarVector
+ Field length:Double, angle:Double
+
+ Function Create:polarVector(aLength:Double, anAngle:Double)
+ Local temp:polarVector = New polarVector
+ temp.length=aLength;temp.angle=anAngle
+ Return temp
+ End Function
+End Type
+
+Type baseEntity
+ Field offset:polarVector, angvel:Double
+
+ Method update(anx:Double, any:Double) Abstract
+End Type
+
+Type circleBob
+ Field velocity:polarVector
+ Field x:Double,y:Double
+ Field life:Int
+
+ Function CreateCircleBob:circleBob(anx:Double, any:Double, avelocity:polarVector)
+ Local temp:circleBob = New circleBob
+ temp.x=anx;temp.y=any;temp.velocity=avelocity
+ temp.life=20
+ Return temp
+ End Function
+
+ Method update()
+ x:+Cos(velocity.angle)*velocity.length
+ y:+Sin(velocity.angle)*velocity.length*yaspect
+ life:-1
+ SetAlpha life/40!
+ DrawImage cmBob, x, y
+ End Method
+
+ Method isDead:Int()
+ If life < 1
+ Return True
+ Else If x < 0 Or x > 799
+ Return True
+ Else If y < 0 Or y > 599
+ Return True
+ End If
+ Return False
+ End Method
+
+End Type
+
+Type spawn Extends baseEntity
+ Field cBobs:TList
+
+ Method update(anx:Double,any:Double)
+ offset.angle:+ angvel
+
+ anx:+Cos(offset.angle)*offset.length
+ any:+Sin(offset.angle)*offset.length*yaspect
+ Local temp:circleBob = circleBob.createCircleBob(anx,any, polarVector.Create(Rnd(4,8),ATan2((320-any),(368-anx))))
+ cBobs.addlast(temp)
+
+ For Local mycBob:circleBob = EachIn cBobs
+ mycBob.update()
+ If myCBob.isDead()
+ cBobs.remove(mycBob)
+ End If
+ Next
+
+ End Method
+
+ Function Create:spawn(anAngle:Double, anAngvel:Double, aRadius:Double)
+ Local temp:spawn = New spawn
+ temp.cBobs = New TList
+ temp.offset = polarVector.Create(aRadius, anAngle)
+ temp.angvel=anAngvel
+ Return temp
+ End Function
+
+End Type
+
+Type anchor Extends baseEntity
+ Field spawns:TList
+
+ Method update(anx:Double, any:Double)
+ offset.angle:-angvel
+ For Local mySpawn:spawn = EachIn spawns
+ mySpawn.update(anx+Cos(offset.angle)*offset.length, any+Sin(offset.angle)*offset.length*yaspect)
+ Next
+
+ End Method
+
+ Function Create:anchor(anAngle:Double, anAngvel:Double, aRadius:Double, numSpawns:Int)
+
+ Local ddeg:Double = 360:Double/numSpawns
+ Local myAnchor:anchor = New anchor
+ myAnchor.spawns=New TList
+ myAnchor.offset = PolarVector.Create(aRadius, anAngle)
+ myAnchor.angvel=anAngvel
+ For Local i = 1 To numSpawns
+ Local temp:spawn = spawn.Create(ddeg*i, anAngvel, aRadius/2)
+ myAnchor.spawns.addlast temp
+ Next
+ Return myAnchor
+ End Function
+End Type
+
+Type root
+ Field anchors:TList
+
+ Method update()
+ For Local myAnchor:anchor = EachIn anchors
+ myanchor.update(368,320)
+ Next
+ SetAlpha 1
+
+ End Method
+
+ Function Create:root(anAngvel:Double, aRadius:Double, numAnchors:Int, numSpawns:Int)
+ Local ddeg:Double = 360:Double/numSpawns
+ Local myRoot:root=New root
+ myRoot.anchors = New TList
+ For Local i = 1 To numAnchors
+ Local temp:anchor = anchor.Create(ddeg*i, anAngvel, aRadius, numSpawns)
+ myRoot.anchors.addlast temp
+ Next
+ Return myRoot
+ End Function
+
+
+End Type
+
+Type imageStrip
+ Field effectImages:TImage[16]
+ Field counter:Int
+
+
+ Function loadImages:imageStrip(name:String)
+ Local temp:imageStrip = New imageStrip
+ For Local i:Int = 0 To 15
+ Local fname:String = name+String(i)+".png"
+ temp.effectImages[i] = LoadImage(fname)
+ Next
+ temp.counter = 0
+ Return temp
+ End Function
+
+ Method update(x:Int,y:Int)
+ counter:-1
+ If counter < 0
+ counter=15
+ EndIf
+
+ DrawImage effectImages[counter],x,y
+ End Method
+
+End Type
+
+Graphics 800,600,32
+
+Global yaspect:Double = 3/5!
+Global rastaImage:TImage = LoadImage("rasta.png")
+Global logo:TImage = LoadImage("cmanialogo.png")
+Global cmBob:TImage = LoadImage("circlebob.png")
+MidHandleImage logo
+
+Global maskEffect:imageStrip = imageStrip.LoadImages("anim")
+
+Local myRBList:TList = New TList
+rasterBar.addRasterBar(3!,2!, [255,0,0], myRBList)
+rasterBar.addRasterBar(5!,1.5!, [0,255,0], myRBList)
+rasterBar.addRasterBar(7!,1!, [0,0,255], myRBList)
+
+Local angle:Double = 0
+Local effectRoot:root = root.Create(4, 200, 6, 6)
+
+While Not KeyHit(KEY_ESCAPE)
+
+ Cls
+
+ Local xpos:Int = 124 * Sin(angle)
+
+ For Local i:rasterBar = EachIn myRBList
+ i.drawRasterBar(xpos+32 , 28!, 28!)
+ Next
+
+ SetBlend ALPHABLEND
+
+ DrawImage logo, 400 + xpos,58
+ angle:+4
+ angle:Mod 360
+
+ Local temp = 255*Abs((-180+angle)/180!)
+
+ SetColor temp,temp,temp
+ DrawLine 0,116,799,116
+ SetColor 255,255,255
+ effectRoot.update
+ Flip
+
+End While
+
--- /dev/null
+Rem
+
+Another Oldskool demo thingy, by FlameDuck and Razorien of Binary Therapy
+
+It started as a simple circle scroller example but got somewhat out of hand. :o>
+
+End Rem
+
+Strict
+
+Incbin "circlefont.png"
+Incbin "oldskool.png"
+Incbin "bouncy.ogg"
+Incbin "binarytherapy.png"
+
+Global scrollSpeed:Double = .6
+Global rotangl:Double = 0
+Global osLogo:TImage = LoadImage( "incbin::oldskool.png" )
+Global myFont:TImage = LoadAnimImage( "incbin::circlefont.png",32,32,0,90 )
+Global myBT:TImage = LoadImage( "incbin::binarytherapy.png" )
+
+MidHandleImage myFont
+
+Global scrollytext$ = " In 2004 Binary Therapy Proudly Presents Oldskool 2 Programmed by: FlameDuck Logo by: Razorien ( http://www.razorien.se/ ) Font courtesy of FONText by: Beaker ( http://www.playerfactory.co.uk/ ) Music by: Dr Av ( http://www.mentalillusion.co.uk/ ) This demo was written in the beta phase of BlitzMAX development, the source code is 237 lines total including empty lines and comments ....."
+Global sp = 0; 'The scrollytext pointer.
+Global ld = 0; 'The letter delay counter.
+Global muzak:TSound = LoadSound( "incbin::bouncy.ogg",True )
+
+Type scrollyLetter
+
+ Field rad:Double, angl:Double, letter:Byte, rados:Double
+ Field myList:TList
+
+ Function createScrollyLetter:scrollyLetter(myChar:Byte)
+ Local myLetter:scrollyLetter = New scrollyLetter
+ myLetter.rad = 170
+ myLetter.angl = -90
+ myLetter.letter = myChar
+ Return myLetter
+ End Function
+
+ Method setList(aList:TList)
+ myList = aList
+ End Method
+
+ Method moveScrollyLetter()
+ angl :+ scrollSpeed
+ rados = Cos(angl*3 + rotangl) * 40
+ If angl > 270
+ myList.remove(Self)
+ End If
+
+ End Method
+
+ Method drawLetter()
+ Local x = Cos(angl) * (rad + rados)
+ Local y = Sin(angl) * (rad + rados)
+
+ SetRotation ATan2 ( y , x )
+
+ Local myAlpha:Double = 1
+ If angl < -45
+ myAlpha:Double = (90.0+angl)/45.0
+ Else If angl > 225
+ myAlpha:Double = (270.0-angl)/45.0
+ End If
+
+ SetAlpha myAlpha
+ DrawImage myFont, x + 400 , -y + 240 , letter
+
+ End Method
+
+End Type
+
+Type circleScroller Extends TList
+
+ Method doScroller()
+ rotangl :+ scrollSpeed; rotangl :Mod 360
+
+ ld :+ 1
+ If ld > 20
+ If scrollytext[sp]-33 > 0 And scrollytext[sp]-33 < 90
+ Local myLetter:scrollyLetter = scrollyLetter.createScrollyLetter( scrollytext[sp]-33 )
+ myLetter.setList(Self)
+ addLast myLetter
+ End If
+ sp = (sp+1) Mod Len(scrollytext)
+ ld = 0
+ End If
+
+ SetBlend ALPHABLEND
+
+ Local cLetter:scrollyLetter
+ For cLetter = EachIn Self
+
+ cLetter.moveScrollyLetter
+ cLetter.drawLetter
+
+
+ Next
+ SetBlend MASKBLEND
+
+ SetRotation 0
+ SetAlpha 1
+
+ End Method
+
+End Type
+
+Type star
+ Field x:Double, y:Double, z:Double, angl:Double, anglv:Double, zv:Double
+
+ Function createStar:star()
+ Local myStar:star = New star
+ myStar.x = Rnd(-240,240)
+ myStar.y = Rnd(-240,240)
+ myStar.z = 100
+ myStar.angl = Rnd(0,360)
+ myStar.anglv = Rnd(-5,5)
+ myStar.zv = Rnd(0.5,2)
+ Return myStar
+ End Function
+
+ Method moveStar()
+ z :- zv
+ Local myx = x / z *100
+ Local myy = y / z *100
+
+ If myx < -240 Or myx > 240 Or myy < -240 Or myx > 240 Or z < 1
+ x = Rnd(-240,240)
+ y = Rnd(-240,240)
+ z = 100
+ angl = Rnd(0,360)
+ anglv = Rnd(-5,5)
+ zv = Rnd(0.5,3)
+ End If
+
+ angl = angl + anglv
+ End Method
+
+ Method drawStar()
+ Local myx = x / z *100
+ Local myy = y / z *100
+
+ Local cols = 255*(100-z)/100
+
+ SetColor(cols,cols,cols)
+ Plot myx+400 , myy+240
+
+ End Method
+
+
+End Type
+
+Type starField Extends TList
+
+ Method doStarField()
+
+ Local cStar:star
+ For cStar = EachIn Self
+
+ cStar.moveStar
+ cStar.drawStar
+
+ Next
+ SetColor 255,255,255
+
+ End Method
+
+End Type
+
+Local myCS:circleScroller = New circleScroller
+Local mySF:starField = New starField
+
+Local ba = 0
+Local intro = 0
+Local i = 0
+Local term:Double = 0
+
+Local dd=32
+
+Graphics 640,480,dd
+
+HideMouse
+
+Local myChannel:TChannel = PlaySound (muzak)
+
+While term < 1
+
+ If KeyHit( KEY_ENTER )
+ dd=32-dd
+ Graphics 640,480,dd
+ HideMouse
+ FlushKeys
+ EndIf
+
+ Cls
+
+ mySF.doStarField
+
+ If intro < 80
+ intro :+2
+ Else
+ myCS.doScroller
+ If i < 400
+ mySF.addLast star.createStar()
+ mySF.addLast star.createStar()
+ mySF.addLast star.createStar()
+ mySF.addLast star.createStar()
+ mySF.addLast star.createStar()
+ mySF.addLast star.createStar()
+ mySF.addLast star.createStar()
+ mySF.addLast star.createStar()
+ mySF.addLast star.createStar()
+ mySF.addLast star.createStar()
+ i :+ 1
+ End If
+ End If
+
+ SetBlend SOLIDBLEND
+ DrawImage osLogo,-160+intro*2,0
+
+ SetBlend MASKBLEND
+ DrawImage myBT, 640-myBT.width * intro/80.0, 480-myBT.height - Sin(ba)*20
+
+ SetBlend ALPHABLEND
+ SetAlpha term
+ SetChannelVolume myChannel,1-term
+ SetColor 0,0,0
+ DrawRect 0,0,640,480
+ SetAlpha 1
+ SetColor 255,255,255
+
+ If KeyHit(KEY_ESCAPE) Or term > 0
+ term :+ 0.01
+ End If
+
+ ba :+6; ba :Mod 180
+ Flip
+
+Wend
--- /dev/null
+**** Oldskool2 -- BlitzMAX Edition ****
+
+INTRODUCTION:
+
+This small demo was put together by Binary Therapy for BlitzMAX. It is written
+entirely in BlitzMAX during the course of a few days.
+
+FILES & CREDITS:
+
+This archive should contain the following files:
+
+Size Name Author
+------------------------------------------------
+ 4.832 oldskool2.bmx Mikkel Løkke
+ 1.036 readme.txt Mikkel Løkke
+ 22.740 circlefont.png Mikkel Løkke
+ 1.078 binarytherapy.png Mikkel Løkke
+ 25.841 oldskool.png Andreas Engström
+546.218 bouncy.ogg Rob Farley
+
+COPYRIGHT:
+
+The works contained in this archive have been released into the public domain
+by Binary Therapy in 2004.
+
+AKNOWLEDGEMENTS:
+
+The developement team would like to thank Mark Sibly, Simon Armstrong and
+James L. Boyd of Blitz Research Ltd. ( http://www.blitzbasic.com/ ) for
+BlitzMAX, Robin Shackford of Playerfactory ( http://www.playerfactory.co.uk/ )
+for the excellent FONText tool and Rob Farley of Mental Illusion
+( http://www.mentalillusion.co.uk/ ) for the awesome tune.
\ No newline at end of file
--- /dev/null
+
+' Create an array of (nonsense) strings, one for each letter of the alphabet...
+
+Local drivel:String [26]
+
+' Fill all 26 strings in (remember we include 0, so the last is 25)...
+
+drivel [0] = "Hello"
+drivel [1] = "Golly, Miss Lane"
+drivel [2] = "I like whippets"
+drivel [3] = "Oink"
+drivel [4] = "Apparently not"
+drivel [5] = "Tell me when, Lord, tell me when"
+drivel [6] = "Flat Harry is alive and well"
+drivel [7] = "North, Miss Teschmacher, north!"
+drivel [8] = "Egg-shaped boy"
+drivel [9] = "Say it again"
+drivel [10] = "Krazy Kat is a heppy, heppy kitty"
+drivel [11] = "Death to the Pixies!"
+drivel [12] = "You're wrong"
+drivel [13] = "Maybe tomorrow I'll wanna settle down"
+drivel [14] = "Jumpin' junipers!"
+drivel [15] = "Rock out!"
+drivel [16] = "Brilliant!"
+drivel [17] = "Victoria was my queen"
+drivel [18] = "Leaving so soon?"
+drivel [19] = "Quatermass rules"
+drivel [20] = "C sucks"
+drivel [21] = "Under the stars"
+drivel [22] = "Xylophone solo"
+drivel [23] = "Zebra hell"
+drivel [24] = "Well I never"
+drivel [25] = "Perhaps some other time?"
+
+' Sort the array of strings (type String has a Sort method)...
+
+drivel.Sort
+
+' Print 'em out in alphabetical order...
+
+For a = 0 Until Len (drivel)
+ Print a + " : " + drivel [a]
+Next
+
--- /dev/null
+
+' Ported from another Basic for benchmarking purposes...
+
+Const ITERATIONS = 10000
+
+Local Flags [8191]
+Print "SIEVE OF ERATOSTHENES - " + ITERATIONS + " iterations"
+
+X = MilliSecs ()
+
+For Iter = 1 To ITERATIONS
+
+ Count = 0
+
+ For I = 0 To 8190
+ Flags[I] = 1
+ Next
+
+ For I = 0 To 8190
+ If Flags[I]=1 Then
+ Prime = I + I
+ Prime = Prime + 3
+ K = I + Prime
+ While K <= 8190
+ Flags[K] = 0
+ K = K + Prime
+ Wend
+ Count = Count + 1
+ EndIf
+ Next
+
+Next
+
+X = MilliSecs () - X
+
+Print "1000 iterations took "+(X/1000.0)+" seconds."
+Print "Primes: "+Count
+End
+
--- /dev/null
+
+' Debug Print Queue...
+
+' Copy and paste the DebugQ type and the PrintQ/UpdateQ functions. Use
+' PrintQ to add a debug message to your game, and UpdateQ in your main
+' loop to display/update messages...
+
+Type DebugQ
+
+ Global DebugQList:TList
+
+ Field message$
+ Field alpha# = 1
+
+ Function Print (message$)
+ If DebugQList = Null Then DebugQList= New TList
+ p:DebugQ = New DebugQ
+ p.message = message$
+ DebugQList.AddLast p
+ End Function
+
+ Function Update (alphacut# = 0.01)
+ If DebugQList = Null Then Return
+ y = 0
+ For p:DebugQ = EachIn DebugQList
+ SetBlend ALPHABLEND
+ SetAlpha p.alpha
+ DrawText p.message$, 0, y
+ y = y + TextHeight("")
+ p.alpha = p.alpha - alphacut; If p.alpha < 0 Then DebugQList.Remove p
+ Next
+ SetBlend SOLID ' Need to get old values!
+ SetAlpha 1 ' Need to get old values!
+ End Function
+
+End Type
+
+' Functional interfaces for non-OO'ers...
+
+Function PrintQ (message$)
+ DebugQ.Print message$
+End Function
+
+Function UpdateQ ()
+ DebugQ.Update
+End Function
+
+' D E M O . . .
+
+Graphics 640, 480
+
+Repeat
+
+ Cls
+
+ x = MouseX ()
+ y = MouseY ()
+
+ DrawRect x, y, 32, 32
+
+ ' Add items to debug print queue...
+
+ If MouseHit (1) Then PrintQ "Left mouse button hit!"
+ If MouseHit (2) Then PrintQ "Right mouse button hit!"
+
+ ' Print/remove all debug items...
+
+ UpdateQ
+
+ DrawText "Click mouse...", 0, GraphicsHeight () - 20
+
+ Flip
+
+Until KeyHit (KEY_ESCAPE)
+
+End
+
+
--- /dev/null
+
+' This just shows that when you create an object of type 'Tester',
+' it inherits the New method of 'Test'...
+
+' Base type...
+
+Type Test
+
+ Global Oink
+
+ Field x
+ Field y
+
+ Method New ()
+ x = 100
+ y = 200
+ Oink = Oink + 1
+ End Method
+
+End Type
+
+' Another type extending the base type above...
+
+Type Tester Extends Test
+
+ Field z = 99
+
+End Type
+
+' Create a 'Tester' object...
+
+t:Tester = New Tester
+
+' As well as the 'z' field of 't', x and y have values, demonstrating
+' that they must have been set by the Test.New () method...
+
+Print t.x
+Print t.y
+Print t.z
+
+
--- /dev/null
+
+' Global variables tracking number of Firework objects and
+' number of Particle objects. These are increased and decreased
+' as objects are created/destroyed.
+
+Global Fireworks, Particles
+
+' Force pulling particles down...
+
+Global Gravity# = 0.025
+
+' Global list of Particle objects...
+
+Global ParticleList:TList = New TList
+
+' Particle object definition ('class')...
+
+Type Particle
+
+' Particle properties...
+
+ Field x# ' x position
+ Field y# ' y position
+ Field xs# ' x speed
+ Field ys# ' y speed
+ Field size ' particle size (size x size)
+
+ Field r# ' particle colour (red component)
+ Field g# ' particle colour (green component)
+ Field b# ' particle colour (blue component)
+
+ Field ditch
+
+ ' Particle actions...
+
+ ' The function below is a 'constructor'. Methods work on
+ ' existing objects, so we can't use a method to create an
+ ' object. Instead, we use a function belonging to this object
+ ' type, and call it like so to return a Particle object:
+
+ ' p:Particle = Particle.Create (blah blah)...
+
+ Function Create:Particle (x#, y#, xs#, ys#, size, r#, g#, b#)
+ Local p:Particle = New Particle
+ p.x = x
+ p.y = y
+ p.xs = xs
+ p.ys = ys
+ p.size = size
+ p.r = r
+ p.g = g
+ p.b = b
+ ParticleList.Addlast p
+ Particles = Particles + 1
+ Return p
+ End Function
+
+ ' This function updates all particles by iterating through the
+ ' global Particle list (ParticleList) and calling the Update
+ ' method on each one...
+
+ Function UpdateAll ()
+ For p:Particle = EachIn ParticleList
+ p.Update
+ Next
+ End Function
+
+ ' Updates current particle...
+
+ Method Update ()
+ ApplyForces
+ Draw
+ End Method
+
+ ' Apply x and y speeds, apply gravity and apply position limits...
+
+ Method ApplyForces ()
+ x = x + xs
+ ys = ys + Gravity * size
+ y = y + ys
+ LimitParticle
+ End Method
+
+ ' Draws the particle. This has been kept separate so it can be
+ ' 'over-ridden' in the Firework type below...
+
+ Method Draw ()
+ SetColor r, g, b
+ DrawRect x, y, size, size
+ End Method
+
+ ' Apply limits (if the particle goes off the left or right of
+ ' the screen, we reverse its direction, and if it goes off the
+ ' bottom (as it must, since gravity is pulling it down), we
+ ' remove the particle from the global list. We're also fading
+ ' the particle to black by reducing r, g and b; once they all
+ ' reach zero, we remove it from the list.
+
+ Method LimitParticle ()
+ If x < 0 Or x + size > GraphicsWidth ()
+ xs = -xs
+ x = x + xs
+ EndIf
+ If y + size > GraphicsHeight ()
+ ParticleList.Remove Self
+ Particles = Particles - 1
+ Else
+ r = r - 2; If r < 0 Then r = 0
+ g = g - 2; If g < 0 Then g = 0
+ b = b - 2; If b < 0 Then b = 0
+ If r + b + g = 0 ParticleList.Remove Self; Particles = Particles - 1
+ EndIf
+ End Method
+
+End Type
+
+' This object definition takes the 'Particle' definition and 'extends' it,
+' meaning that it has all of the same fields and methods/functions as the
+' Particle type, but you can add new fields and 'over-ride' methods by
+' simply redefining them...
+
+Type Firework Extends Particle
+
+ ' Here, I've over-ridden the Create function to return a
+ ' Firework type. Note that the parameters must be the same
+ ' as for Particle.Create and that it is added to the global
+ ' list of Particle objects; this is possible because Firework
+ ' objects are still Particle objects, just more souped-up!
+
+ Function Create:Firework (x#, y#, xs#, ys#, size, r#, g#, b#)
+ Local p:Firework = New Firework
+ p.x = x
+ p.y = y
+ p.xs = xs
+ p.ys = ys
+ p.size = size
+ p.r = r
+ p.g = g
+ p.b = b
+ ParticleList.Addlast p
+ Fireworks = Fireworks + 1
+ Return p
+ End Function
+
+ ' Here I've over-ridden the Update method so that when a
+ ' Firework starts to fall (ys > 0.5) it's deleted and spawns
+ ' a random number of normal Particle objects. Note the use of
+ ' the ApplyForces and Draw methods that are 'inherited' from
+ ' the Particle definition (as are the fields such as x, y,
+ ' xs, ys, etc)...
+
+ Method Update ()
+
+ If ys > 0.5
+ ParticleList.Remove Self
+ Fireworks = Fireworks - 1
+ For p = 1 To Rand (100,1000)'50, 150)
+ Particle.Create (x, y, Rnd (-4, 4), Rnd (0, -4), Rnd (1, 2), Rand (120, 255), Rand (120, 255), Rand (120, 255))
+ Next
+ Else
+ ApplyForces
+ Draw
+ EndIf
+
+ End Method
+
+ ' This version of LimitParticle over-rides that defined in the
+ ' plain Particle type. It's interesting to note that although
+ ' the Update method above calls the original ApplyForces method
+ ' defined in the Particle type, that actually calls this over-ridden
+ ' version of LimitParticle.
+
+ Method LimitParticle ()
+ If x < 0 Or x + size > GraphicsWidth ()
+ xs = -xs
+ x = x + xs
+ EndIf
+ End Method
+
+End Type
+
+' D E M O . . .
+
+Graphics 640, 480
+
+SetClsColor 1, 1, 10
+
+astep# = 2 ' Used for the positioning of the spawn point, x, below...
+
+Repeat
+
+ Cls
+
+ ' This is plotting a circle but only using the x position...
+
+ ang# = ang + astep; If ang > (360 - astep) Then ang = 0
+ x# = (GraphicsWidth () / 2) + (GraphicsWidth () / 2) * Sin (ang)
+
+ ' No timers in Blitz Max yet!
+
+ If Rand (0, 1000) > 800
+ Firework.Create (x, GraphicsHeight (), Rnd (-1, 1), Rnd (-4, -12), 4, 255, 255, 255)
+ EndIf
+
+ ' Update all particle (both Particle and Firework objects from
+ ' the global list)...
+
+ Particle.UpdateAll ()
+
+ SetColor 255, 255, 255
+ DrawText "Fireworks: " + Fireworks, 20, 20
+ DrawText "Particles: " + Particles, 20, 40
+
+ Flip
+
+Until KeyHit(KEY_ESCAPE)
+
+End
+
--- /dev/null
+
+This stuff...
+
+The 'graphical examples' folder contains a bunch of demos that show things flying all over the screen, and are mostly quite heavily commented.
+
+The 'language examples' folder shows various aspects of object-oriented code, Blitz's array handling, etc, all commented. They're pretty dry, but provide text output that should help to understand them.
+
+The 'game examples' folder contains 'RockOut', the piece-de-resistance as far as I'm concerned! (Note that once it's compiled it can be copied anywhere with no external files required.)
+
+Enjoy!
+--
+James L Boyd,
+Blitz Support.
--- /dev/null
+
+' -----------------------------------------------------------------------------
+' An example of listing a type and (just) its sub-types...
+' -----------------------------------------------------------------------------
+
+' List to which all objects are added...
+
+Global TestList:TList = New TList
+
+' Base type...
+
+Type Base
+ Field x
+ Field desc$
+End Type
+
+' Example 1...
+
+Type Test1 Extends Base
+ Field y
+End Type
+
+' Example 2...
+
+Type Test2 Extends Base
+ Field z
+End Type
+
+' -----------------------------------------------------------------------------
+' Create objects of all three types...
+' -----------------------------------------------------------------------------
+
+For a = 1 To 10
+
+ t:Base = New Base
+ t.desc = "Base Object " + a
+ TestList.AddLast t
+
+ t1:Test1 = New Test1
+ t1.desc = "Test1 Object " + a
+ TestList.AddLast t1
+
+ t2:Test2 = New Test2
+ t2.desc = "Test2 Object " + a
+ TestList.AddLast t2
+
+Next
+
+' -----------------------------------------------------------------------------
+' List all objects of Base AND Base-extended types...
+' -----------------------------------------------------------------------------
+
+Print ""
+Print "All Base and Base-extended objects..."
+Print ""
+
+For all:Base = EachIn TestList
+ Print all.desc
+Next
+
+' -----------------------------------------------------------------------------
+' List only Test1 objects from Base.TestList...
+' -----------------------------------------------------------------------------
+
+Print ""
+Print "Only Test1 objects..."
+Print ""
+
+For onlyt1:Test1 = EachIn TestList
+ Print onlyt1.desc
+Next
+
+' -----------------------------------------------------------------------------
+' List only Test2 objects from Base.TestList...
+' -----------------------------------------------------------------------------
+
+Print ""
+Print "Only Test2 objects..."
+Print ""
+
+For onlyt2:Test2 = EachIn TestList
+ Print onlyt2.desc
+Next
+
+
--- /dev/null
+
+' Here's an example of using the optional New () method. You could just
+' set the x field to have a default anyway (Field x = 100) but this shows
+' that you can do other things too (in this case, print a message)...
+
+Type Oink
+
+ Field x
+
+ Method New ()
+ Print "Setting the value of x..."
+ x = 100
+ End Method
+
+End Type
+
+' Create an Oink object...
+
+o:Oink = New Oink
+
+' Print out the value of x...
+
+Print o.x
+
--- /dev/null
+
+' This function takes the pointer to an integer variable
+' and adds 1 by treating the variable's memory as an array...
+
+Function AddToVariable (addee:Int Ptr)
+ addee [0] = addee [0] + 1
+End Function
+
+' Test variable, looking forward to a brave new world...
+
+a = 5
+
+' Pass the address of 'a' to function...
+
+AddToVariable (VarPtr (a))
+
+' The result...
+
+Print "Variable a was 5... now it's " + a
+
+
--- /dev/null
+' -----------------------------------------------------------------------------
+' RockOut -- Rocket BlockOut
+' -----------------------------------------------------------------------------
+' Public domain source code by James L Boyd (support @ blitzbasic . com)
+' -----------------------------------------------------------------------------
+' Rocket image © 2004 James L Boyd, with permission granted for freeware/PD use,
+' not that anyone'd really want it anyway...!
+' -----------------------------------------------------------------------------
+
+' -----------------------------------------------------------------------------
+' Constants...
+' -----------------------------------------------------------------------------
+
+' Sizes used for blocks...
+
+Const BLOCKWIDTH = 32
+Const BLOCKHEIGHT = 16
+
+Const GRAPHICS_WIDTH = 640
+Const GRAPHICS_HEIGHT = 480
+
+Const GW2 = GRAPHICS_WIDTH / 2
+Const GH2 = GRAPHICS_HEIGHT / 2
+
+Graphics 800,600,32,60
+
+SetVirtualResolution GRAPHICS_WIDTH,GRAPHICS_HEIGHT
+
+' Pre-calc half of graphics width...
+
+' -----------------------------------------------------------------------------
+' Include media...
+' -----------------------------------------------------------------------------
+
+' Sounds (all from Yamaha RM1X!)...
+
+Incbin "sounds/shot.ogg" ' Player shot
+Incbin "sounds/fall.ogg" ' Block fall
+Incbin "sounds/hit.ogg" ' Block/player hit
+Incbin "sounds/beep.ogg" ' 'Press space' sound
+Incbin "sounds/gameover.ogg" ' Guess...
+
+' Graphics...
+
+Incbin "gfx/boing.png" ' Rocket
+Incbin "gfx/land.png" ' Background (used to be land, now a grid)
+Incbin "gfx/shot.png" ' Player's shot
+Incbin "gfx/block.png" ' Guess...
+
+' -----------------------------------------------------------------------------
+' Types (object definitions)...
+' -----------------------------------------------------------------------------
+
+' GravityItem: all objects affected by gravity are based upon this...
+
+Type GravityItem
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific globals...
+ ' ------------------------------------------------------------------------
+
+ ' Why not make these truly global? It's cleaner -- you can just copy and
+ ' paste the type definition into a completely different program without
+ ' having to remember which globals are related...
+
+ Global GCount ' Count of all GravityItems (for debugging)
+ Global GravityItemList:TList ' List used for all GravityItem objects
+ Global Gravity# = 0.05 ' Gravity applied to all GravityItems
+
+ ' ------------------------------------------------------------------------
+ ' Type fields...
+ ' ------------------------------------------------------------------------
+
+ Field x# ' x position of object
+ Field y# ' y position of object
+ Field xs# ' x speed of object
+ Field ys# ' y speed of object
+
+ Field width ' Width of object
+ Field height ' Height of object
+
+ Field damage ' Damage caused by this item if it hits player
+ Field fixed = False ' Is object fixed in place? Blocks are, at first...
+
+ Field r, g, b
+
+ ' ------------------------------------------------------------------------
+ ' Type functions...
+ ' ------------------------------------------------------------------------
+
+ Function UpdateAll ()
+ If GravityItemList = Null Then Return
+ If Shadows_On Then Block.DrawShadows ' Shadows_On is a global...
+ For g:GravityItem = EachIn GravityItemList
+ g.Update
+ g.Draw
+ Next
+ End Function
+
+ ' ------------------------------------------------------------------------
+ ' Type methods...
+ ' ------------------------------------------------------------------------
+
+ ' The New method is special -- Blitz calls it whenever a new object is
+ ' created...
+
+ ' Every time a new GravityItem is created -- including objects that extend
+ ' GravityItem -- this is called. In this case, it creates the type-global
+ ' list if it doesn't yet exist (only happens once), and adds the item to it...
+
+ Method New ()
+ If GravityItemList = Null Then GravityItemList = New TList
+ GravityItemList.AddLast Self
+ GCount = GCount + 1
+ End Method
+
+ ' Destroy current object and remove from list
+ Method Destroy ()
+ GravityItemList.Remove Self
+ GCount = GCount - 1
+ End Method
+
+ ' Rectangle-based collision test of current object and player.
+
+ ' 'The multiplier' parameter controls how much of an object's
+ ' 'damage' field applies to the player -- in the case of Block
+ ' objects, the more they're faded out, the less damage they do...
+
+ ' The 'posyonly' parameter is a hack to stop Shot objects damaging
+ ' the player while going up...
+
+ Method PlayerCollide (multiplier# = 1, posyonly = 0)
+
+ ' Offset x/y position of shots (all images' handles are centered)...
+
+ ox = x - width / 2
+ oy = y - height / 2
+
+ ' Offset x/y position of player...
+
+ opx = PlayerOne.x - PlayerOne.width / 2
+ opy = PlayerOne.y - PlayerOne.height / 2
+
+ ' Hack to stop Shot objects damaging player while going up...
+
+ check = 1
+
+ If posyonly
+ If ys < 0
+ check = 0
+ EndIf
+ EndIf
+
+ ' Test for collision, apply damage and make explosion...
+
+ If check
+ If OverLap (ox, oy, ox + width, oy + height, opx, opy, opx + PlayerOne.width, opy + PlayerOne.height)
+ PlayerOne.shields = PlayerOne.shields - damage * multiplier
+ ExplosionParticle.Explode x, y, damage * 5 * multiplier
+ PlayerOne.damaged = MilliSecs ()
+ Return True
+ EndIf
+ EndIf
+
+ End Method
+
+ ' There is no default Draw method here, as it's different for each extended type
+ ' of GravityItem, so I've defined it as Abstract...
+
+ ' Abstract forces every type that extends GravityItem to have a Draw () method defined
+ ' or the code simply won't compile...
+
+ ' One practical use for this is that you can call Draw from any random GravityItem,
+ ' regardless of which extended type it is, and this will call the correct Draw ()
+ ' for the type of object in question...
+
+ Method Draw () Abstract
+
+ ' Abstract Update method for GravityItems. See Draw () explanation...
+
+ Method Update () Abstract
+
+End Type
+
+' Particles created in an explosion. This type extends GravityItem, meaning all
+' properties of GravityItem apply, except where methods are over-ridden
+' (ie. re-defined) here...
+
+Type ExplosionParticle Extends GravityItem
+
+ ' ------------------------------------------------------------------------
+ ' Type fields...
+ ' ------------------------------------------------------------------------
+
+ ' No need to define, x, y, xs, ys, etc as they're part of the GravityItem definition...
+
+ Field alph# = 1.0 ' Alpha level of particle (translucency)
+
+ ' ------------------------------------------------------------------------
+ ' Type functions...
+ ' ------------------------------------------------------------------------
+
+ ' Create explosion of particles, and play sound...
+
+ Function Explode (x#, y#, particles)
+
+ ' NB. GW2 is a global set to half of GRAPHICS_WIDTH...
+
+ If Sounds_On
+ pan# = x / GW2 - 1.0
+ play = CueSound (hit)
+ SetChannelPan play, pan
+ ResumeChannel play
+ EndIf
+
+ For loop = 1 To particles
+ ExplosionParticle.Create (x, y)
+ Next
+
+ End Function
+
+ ' Create single explosion particle. Note that any items extending GravityItem
+ ' will call the New () method from GravityItem upon creation, so these will
+ ' be added to the GravityItem list automatically...
+
+ Function Create:ExplosionParticle (x, y)
+
+ e:ExplosionParticle = New ExplosionParticle
+ e.x = x
+ e.y = y
+ e.xs = Rnd (-8, 8)
+ e.ys = Rnd (-8, 8)
+
+ ' Random colour...
+
+ Select Rand (0, 3)
+ Case 0
+ e.r = 255
+ e.g = 255
+ e.b = 255
+ Case 1
+ e.r = 255
+ e.g = 127
+ e.b = 0
+ Case 2
+ e.r = 255
+ e.g = 255
+ e.b = 0
+ Case 3
+ e.r = 255
+ e.g = 0
+ e.b = 0
+ End Select
+
+ ' Random size...
+
+ size = Rand (1, 8)
+ e.width = size
+ e.height = size
+
+ End Function
+
+ ' ------------------------------------------------------------------------
+ ' Type methods...
+ ' ------------------------------------------------------------------------
+
+ ' Update () over-rides the GravityItem Update () method...
+
+ Method Update ()
+
+ ' Reduce alpha level of particle...
+
+ alph = alph - 0.01
+
+ ' Apply Gravity global (see GravityItem) to y speed...
+
+ ys = ys + Gravity
+
+ ' Move particle by current speed...
+
+ x = x + xs
+ y = y + ys
+
+ ' If off-screen or reduced to invisible, remove from list by
+ ' calling the Destroy method (inherited from GravityItem)...
+
+ If y > GRAPHICS_HEIGHT Or alph = 0 Then Destroy
+
+ End Method
+
+ ' Draw particle...
+
+ Method Draw ()
+
+ SetScale 1, 1
+
+ SetBlend ALPHABLEND
+
+ SetAlpha alph
+ SetColor r, g, b
+ DrawRect x, y, width, height
+
+ End Method
+
+End Type
+
+' Block definition. Again, Block is a kind of GravityItem...
+
+Type Block Extends GravityItem
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific globals...
+ ' ------------------------------------------------------------------------
+
+ Global BCount ' Number of blocks
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific fields...
+ ' ------------------------------------------------------------------------
+
+ Field alph# = 1.0 ' Alpha level of block
+ Field ang# ' Rotation of block
+ Field angspeed# ' Rotation speed of block
+ Field desty#
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific function...
+ ' ------------------------------------------------------------------------
+
+ ' Create a Block object (added to GravityItem list automatically)...
+
+ Function Create:Block (x, y)
+ blk:Block = New Block
+ blk.x = x
+ blk.y = y
+ blk.desty = y
+ blk.width = BLOCKWIDTH
+ blk.height = BLOCKHEIGHT
+ blk.fixed = True
+ blk.damage = 20
+ BCount = BCount + 1
+ Return blk
+ End Function
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific methods...
+ ' ------------------------------------------------------------------------
+
+ ' Update () method for Block objects...
+
+ Method Update ()
+
+ ' Check for collision (passing alpha level of block to apply
+ ' appropriate damage), and remove from GravityItem list if hit...
+
+ If PlayerCollide (alph) Then BCount = BCount - 1; Destroy; Return
+
+ ' If the block has been freed (by being hit), make it fall...
+
+ If Not fixed
+
+ alph = alph - 0.0075; If alph < 0 Then alph = 0
+ ang = ang + angspeed; If ang > 359 Then ang = 0
+ ys = ys + Gravity
+ x = x + xs
+ y = y + ys
+
+ If y > GRAPHICS_HEIGHT Or alph = 0 Destroy; BCount = BCount - 1
+
+ Else
+
+ ' When blocks are lowered in main loop, they are just set to 'desty',
+ ' their new y-position destination. This moves them towards that...
+
+ ydist# = desty - y
+ ys = ydist / 12.0
+ y = y + ys
+
+ EndIf
+
+ End Method
+
+ ' Block-specific Draw () method...
+
+ Method Draw ()
+
+ SetBlend ALPHABLEND
+ SetRotation ang
+
+ SetColor r, g, b
+ SetAlpha alph
+
+ DrawImage BlockImage, x, y
+
+ SetRotation 0
+
+ End Method
+
+ Function DrawShadows ()
+ SetBlend ALPHABLEND
+ For blk:Block = EachIn GravityItemList
+ SetRotation blk.ang
+ SetColor 0, 0, 0
+ SetAlpha blk.alph * 0.25
+ DrawImage BlockImage, blk.x + 8, blk.y + 8
+ Next
+ End Function
+
+End Type
+
+' Player object. Only one player possible right now, but this keeps everything
+' together for easy reference...
+
+Type Player
+
+ Field damaged ' Set to MilliSecs () when hit (used for flashing effect)
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific fields...
+ ' ------------------------------------------------------------------------
+
+ ' The shields field is a float so I can reduce by small amounts, but I use
+ ' Int (PlayerOne.shields) to display/evaluate it...
+
+ Field shields# = 100
+
+ Field x#
+ Field y#
+ Field xs#
+ Field ys#
+
+ Field image ' Player image...
+
+ Field width
+ Field height
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific functions...
+ ' ------------------------------------------------------------------------
+
+ ' Create () is a function that creates & returns a :Player type object...
+
+ Function Create:Player (x, y, image)
+ PlayerOne:Player = New Player
+ PlayerOne.image = image
+ PlayerOne.x = x
+ PlayerOne.y = y
+ PlayerOne.width = ImageWidth (PlayerOne.image) * 0.2 ' Image is scaled in Draw ()
+ PlayerOne.height = ImageHeight (PlayerOne.image) * 0.2
+ Return PlayerOne
+ End Function
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific methods...
+ ' ------------------------------------------------------------------------
+
+ ' This is passed the MouseX () and MouseY () positions in the main game
+ ' loop, and hence moves the player toward the mouse cursor...
+
+ Method Move (destx#, desty#, div#)
+ xdist# = destx - x
+ ydist# = desty - y
+ xs = xdist / div
+ ys = ydist / div
+ x = x + xs
+ y = y + ys
+ End Method
+
+ Method Draw (alpha# = 1, r = 255, g = 255, b = 255)
+
+ SetBlend ALPHABLEND
+ SetScale 0.2, 0.2
+
+ If Shadows_On
+ SetColor 0, 0, 0
+ SetAlpha alpha * 0.4
+ DrawImage image, x + 8, y + 8
+ EndIf
+
+ SetAlpha alpha
+
+ ' If player is damaged, rgb will be RED...
+
+ SetColor r, g, b
+ DrawImage image, x, y
+ SetScale 1, 1
+
+ End Method
+
+End Type
+
+Type Shot Extends GravityItem
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific functions...
+ ' ------------------------------------------------------------------------
+
+ Function Create:Shot (x#, y#, ys#, xs#, soundpan#)
+ If Sounds_On
+ play = CueSound (shoot)
+ SetChannelPan play, soundpan
+ ResumeChannel play
+ EndIf
+ s:Shot = New Shot
+ s.x = x
+ s.y = y
+ s.xs = xs
+ s.ys = ys
+ s.width = 6
+ s.height = 6
+ s.damage = 2
+ Return s
+ End Function
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific methods...
+ ' ------------------------------------------------------------------------
+
+ ' Over-ride standard GravityItem Update () method...
+
+ Method Update ()
+
+ ' Hit the player (note 'posyonly', 2nd parameter, of PlayerCollide)...
+
+ If PlayerCollide (1, 1) Then Destroy; Return
+
+ ys = ys + Gravity
+ x = x + xs
+ y = y + ys
+
+ ' Remove if below bottom of screen...
+
+ If y > GRAPHICS_HEIGHT
+
+ Destroy
+
+ Else
+
+ ' Check current Shot against all Blocks...
+
+ ' (Notice that this only checks Block objects in the list!)
+
+ For blk:Block = EachIn GravityItemList
+
+ ' Get x offset (rectangles are mid-handled)...
+
+ ox = x - width / 2
+ oy = y - height / 2
+
+ ogx = blk.x - blk.width / 2
+ ogy = blk.y - blk.width / 2
+
+ ' Check collision...
+
+ If OverLap (ox, oy, ox + width, oy + height, ogx, ogy, ogx + blk.width, ogy + blk.height)
+
+ ' If Block is already dead (ie. falling), reflect Shot, otherwise
+ ' un-fix block and create explosion...
+
+ ' Note: ys is current Shot object's y speed...
+
+ If blk.fixed = False
+ ys = -ys
+ Else
+ blk.fixed = False
+ blk.ys = ys / Rnd (1, 4)
+ blk.angspeed = Rnd (-4, 4)
+ ExplosionParticle.Explode ogx, ogy, 4
+ EndIf
+
+ EndIf
+
+ Next
+
+ EndIf
+
+ End Method
+
+ Method Draw ()
+ SetBlend MASKBLEND
+ SetAlpha 1
+ SetColor 255, 255, 255
+ DrawImage ShotImage, x, y
+ End Method
+
+End Type
+
+' The random debris that falls 'down' the screen...
+
+Type DebrisItem
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific fields...
+ ' ------------------------------------------------------------------------
+
+ Field x# = Rand (0, GRAPHICS_WIDTH - 1)
+ Field y# = Rand (0, GRAPHICS_HEIGHT - 1)
+ Field ys# = Rnd (0.01, 8)
+ Field size = Rand (1, 2)
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific methods...
+ ' ------------------------------------------------------------------------
+
+ Method Update ()
+ If y > GRAPHICS_HEIGHT y = 0
+ y = y + ys
+ SetColor Rnd (127, 255), Rnd (127, 255), 255
+ SetBlend SOLIDBLEND
+ DrawRect x, y, size, size
+ End Method
+
+End Type
+
+' -----------------------------------------------------------------------------
+' Functions...
+' -----------------------------------------------------------------------------
+
+' Draw simple text with shadow...
+
+Function DrawShadowText (t$, x, y)
+ SetHandle 0,0
+ SetRotation 0
+ SetColor 0, 0, 0
+ DrawText t$, x + 1, y + 1
+ SetColor 255, 255, 255
+ DrawText t$, x, y
+ SetHandle .5,.5
+End Function
+
+' Draw stuff in wireframe mode...
+
+Function WireFrame (enable = True)
+ If enable
+ glPolygonMode (GL_FRONT_AND_BACK, GL_LINE)
+ Else
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL)
+ EndIf
+End Function
+
+' Draw stuff in point mode...
+
+Function Point (enable = True)
+ If enable
+ glPolygonMode (GL_FRONT_AND_BACK, GL_POINT)
+ Else
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL)
+ EndIf
+End Function
+
+' Returns "Off" if 'status' is 0, otherwise "On"...
+
+Function OnOff$ (status)
+ If status Then Return "On" Else Return "Off"
+End Function
+
+' Phew! Thanks, Birdie! Rectangular overlap function. Should have been so easy...
+
+Function OverLap (x0, y0, x1, y1, x2, y2, x3, y3)
+ If x0 > x3 Or x1 < x2 Then Return False
+ If y0 > y3 Or y1 < y2 Then Return False
+ Return True
+End Function
+
+' Distance between two points...
+
+Function Dist# (x0#, y0#, x1#, y1#)
+ Return Sqr (((x1 - x0) * (x1 - x0)) + ((y1 - y0) * (y1 - y0)))
+End Function
+
+' -----------------------------------------------------------------------------
+' Main game. This is where it all goes pear-shaped!
+' -----------------------------------------------------------------------------
+
+' Open display...
+
+' Set Cls colour (used when background turned off)...
+
+SetClsColor 64, 96, 128
+
+' All images' and rectangles' handles should be set to the centre...
+
+AutoMidHandle True
+SetHandle 0.5, 0.5
+
+' All images mipped...
+
+AutoImageFlags MASKEDIMAGE|FILTEREDIMAGE|MIPMAPPEDIMAGE
+
+' Mask colour for loaded images (will be transparent)...
+
+SetMaskColor 255, 0, 255
+
+' Mouse position -- used in some type methods and functions, hence global...
+
+Global mx, my
+
+' Player object...
+
+Global PlayerOne:Player
+
+' Draw shadows?
+
+Global Shadows_On = True
+
+' Turn off sound?
+
+Global Sounds_On = True
+
+' Load media -- sounds, from included binaries (see start of code)...
+
+Global shoot = LoadSound ("incbin::sounds/shot.ogg")
+Global hit = LoadSound ("incbin::sounds/hit.ogg")
+Global beep = LoadSound ("incbin::sounds/beep.ogg")
+Global over = LoadSound ("incbin::sounds/gameover.ogg")
+
+' Load media -- images, from included binaries...
+
+' Shots...
+
+Global ShotImage = LoadImage ("incbin::gfx/shot.png")
+
+' Blocks...
+
+' Note there is only one image for all blocks -- they are altered by SetColorRGB before
+' drawing (WHITE gives normal image)...
+
+Global BlockImage = LoadImage ("incbin::gfx/block.png")
+
+' Player...
+
+pimage = LoadImage ("incbin::gfx/boing.png") ',MASKEDIMAGE|MIPMAPPEDIMAGE)
+
+' Background...
+
+' Note that bgscale stores the length of the screen diagonal, and this value is used
+' for the image height, so it doesn't get chopped when it rotates...
+
+bg = LoadImage ("incbin::gfx/land.png")
+bgscale# = Dist (0, 0, GRAPHICS_WIDTH - 1, GRAPHICS_HEIGHT - 1) / ImageHeight (bg)
+
+' Background angle/speed of rotation...
+
+bgang# = 0
+bgangspeed# = 0
+
+' Create an array of 100 debris particles...
+
+Local debris:DebrisItem [100]
+For loop = 0 Until 100
+ debris [loop] = New DebrisItem
+Next
+
+' This should probably read 'rows' -- the number of rows of blocks at startup...
+
+layers = 5
+
+' Toggle variables for drawing background, debris and wireframe mode...
+
+bgtoggle = 1
+debristoggle = 1
+wftoggle = 0
+
+' Delay before adding another row of blocks (this is decreased as the game progresses)...
+
+rowdelay = 10000 ' 10 seconds (10000 milliseconds)...
+
+' Background colour and first target colour...
+
+backr# = 64
+backg# = 96
+backb# = 180
+
+backtr# = 128
+backtg# = 32
+backtb# = 48
+
+' Delay between colour increments...
+
+backstep# = 5000
+
+' Colour increments...
+
+backstepr# = (backtr - backr) / backstep
+backstepg# = (backtg - backg) / backstep
+backstepb# = (backtb - backb) / backstep
+
+' Direction of increment to target colour...
+
+backsgn = Sgn (backtr - backr)
+
+' This is the point where the game is re-started from, whenever a level is completed or game ended...
+
+#ResetLevel ' $name signifies a label now...
+
+' Increase level number (level is 0 on startup, so becomes 1 for first level)...
+
+level = level + 1
+
+' Reset the 'new row' delay timer to the current time...
+
+rowtimer = MilliSecs ()
+
+' Set fire rate limiter to current time...
+
+firetimer = MilliSecs ()
+
+' Create rows of blocks...
+
+For y = 0 To layers - 1
+ For x = 0 Until GRAPHICS_WIDTH Step BLOCKWIDTH
+ b:Block = Block.Create (x + BLOCKWIDTH / 2, (y * BLOCKHEIGHT) + BLOCKHEIGHT / 2)
+ b.r = Rnd (127, 255)
+ b.g = Rnd (127, 255)
+ b.b = Rnd (127, 255)
+ Next
+Next
+
+' Minimum number of blocks left before we *stop* dropping them down (equivalent of 2 rows)...
+
+lowblocks = 2 * (GRAPHICS_WIDTH / BLOCKWIDTH)
+
+' When a level is completed, we delete the player object for simplicity of resetting
+' all its values. This creates it (same for game startup)...
+
+If PlayerOne = Null Then PlayerOne = Player.Create (VirtualMouseX (), VirtualMouseY (), pimage)
+
+' Don't show the mouse pointer; gonna draw our own...
+
+HideMouse
+
+' Game text and precalculated x offsets...
+
+go$ = "G A M E O V E R -- H I T S P A C E O R R M B"
+wd$ = "W E L L D O N E -- H I T S P A C E O R R M B"
+
+gox = (GRAPHICS_WIDTH / 2) - (TextWidth (go$) / 2)
+wdx = (GRAPHICS_WIDTH / 2) - (TextWidth (wd$) / 2)
+
+Repeat
+
+ ' Clear the screen...
+
+ Cls
+
+ ' Store mouse position in these global variables...
+
+ mx = VirtualMouseX ()
+ my = VirtualMouseY ()
+
+ ' -----------------------------------------------------------------------
+ ' Toggles...
+ ' -----------------------------------------------------------------------
+
+ If KeyHit (KEY_F1) Then bgtoggle = 1 - bgtoggle ' Background
+ If KeyHit (KEY_F2) Then debristoggle = 1 - debristoggle ' Debris
+ If KeyHit (KEY_F3) Then wftoggle = 1 - wftoggle; WireFrame wftoggle ' Wireframe *
+ If KeyHit (KEY_F4) Then Sounds_On = 1 - Sounds_On ' Sound
+ If KeyHit (KEY_F5) Then Shadows_On = 1 - Shadows_On ' Shadows
+
+ ' * Wireframe worked when the game used rectangles instead of images for blocks!
+
+ ' -----------------------------------------------------------------------
+ ' Background...
+ ' -----------------------------------------------------------------------
+
+ ' Update background rotation...
+
+ bgang = bgang + bgangspeed; If bgang > 359 - bgangspeed Then bgang = 0
+ bgangspeed = bgangspeed + 0.0001
+
+ ' The bgtoggle variable controls whether the background should be drawn or not...
+
+ If bgtoggle
+
+ ' Turn off wireframe mode (if it's on)...
+
+ If wftoggle Then WireFrame False
+
+ ' Change colour by pre-calculated increment...
+
+ backr = backr + backstepr
+ backg = backg + backstepg
+ backb = backb + backstepb
+
+ ' Reached target colour? Set a new target/increments/increment-direction...
+
+ If backr => backtr * backsgn
+ backtr = Rnd (255)
+ backtg = Rnd (255)
+ backtb = Rnd (255)
+ backstepr# = (backtr - backr) / backstep
+ backstepg# = (backtg - backg) / backstep
+ backstepb# = (backtb - backb) / backstep
+ backsgn = Sgn (backtr - backr)
+ EndIf
+
+ ' Set colour of background image (applied to the greyscale default)...
+
+ SetColor backr, backg, backb
+
+ ' Set the background's pre-calculated scale...
+
+ SetScale bgscale, bgscale
+ SetRotation bgang
+ SetAlpha 1
+
+ DrawImage bg, GW2, GH2
+
+ ' Reset this stuff so next drawn items don't have to...
+
+ SetRotation 0
+ SetScale 1, 1
+
+ ' Put back to wireframe/non-wireframe mode, depending on value of 'wftoggle'...
+
+ WireFrame wftoggle
+
+ EndIf
+
+ ' -----------------------------------------------------------------------
+ ' Debris...
+ ' -----------------------------------------------------------------------
+
+ ' Draw debris particles if 'debristoggle' is True...
+
+ If debristoggle
+ For loop = 0 Until 100
+ debris [loop].Update
+ Next
+ EndIf
+
+ ' -----------------------------------------------------------------------
+ ' Cursor...
+ ' -----------------------------------------------------------------------
+
+ SetColor 255, 255, 255
+ DrawLine mx - 8, my, mx + 8, my
+ DrawLine mx, my - 8, mx, my + 8
+
+ ' -----------------------------------------------------------------------
+ ' Move and draw player...
+ ' -----------------------------------------------------------------------
+
+ ' Move the player object based on mouse position (the '20' controls the
+ ' speed at which the player moves toward the mouse -- play with it;
+ ' lower is faster)...
+
+ PlayerOne.Move (mx, my, 12)
+
+ ' Turn wireframe off if still on, before drawing player...
+
+ If wftoggle Then WireFrame False
+
+ ' When the player is hit, the 'damaged' field is set to the current time.
+ ' This code checks if a second has passed since 'damaged'. If so, it draws
+ ' the player normally; if not, the player is drawn in red, with varying
+ ' transparency...
+
+ If MilliSecs () > PlayerOne.damaged + 1000 ' Damage timeout has passed...
+
+ ' Draw normally...
+
+ PlayerOne.damaged = 0 ' Resetting damage time...
+ alpha# = 1
+ rcol = 255; gcol = 255; bcol = 255
+
+ Else
+
+ ' Flash player for 1 second if hit...
+
+ alpha# = Sin (MilliSecs ())
+ rcol = 255; gcol = 0; bcol = 0
+
+ EndIf
+
+ ' Draw the player using the above values...
+
+ PlayerOne.Draw (alpha, rcol, gcol, bcol)
+
+ ' Reset the wireframe mode according to 'w'...
+
+ WireFrame wftoggle
+
+ ' -----------------------------------------------------------------------
+ ' If player is alive, do stuff...
+ ' -----------------------------------------------------------------------
+
+ If Int (PlayerOne.shields) > 0 ' Shields is a float, so gotta round it...
+
+ ' Player is alive...
+
+ ' If more than 'lowblocks' left on screen, add a row and lower all
+ ' blocks. Reset drop-down timer and reduce delay for next drop-down...
+
+ If Block.BCount > lowblocks And MilliSecs () > rowtimer + rowdelay
+
+ ' Add a row of blocks, above top of screen...
+
+ For x = 0 Until GRAPHICS_WIDTH Step BLOCKWIDTH
+ b:Block = Block.Create (x + BLOCKWIDTH / 2, -BLOCKHEIGHT / 2)
+ b.r = Rnd (127, 255)
+ b.g = Rnd (127, 255)
+ b.b = Rnd (127, 255)
+ Next
+
+ ' Set all blocks' target y position down by block height. When blocks
+ ' are updated, they get moved towards this new position...
+
+ For b:Block = EachIn GravityItem.GravityItemList
+ b.desty = b.desty + BLOCKHEIGHT
+ Next
+
+ ' Reset timeout until a new row is added...
+
+ rowtimer = MilliSecs ()
+
+ ' Reduce row-down timeout a bit...
+
+ If rowdelay => 1100 Then rowdelay = rowdelay - 100 ' Minimum 1 sec interval!
+
+ EndIf
+
+ ' --------------------------------------------------------------------
+ ' Fire shot (maximum fire rate 75 milliseconds)...
+ ' --------------------------------------------------------------------
+
+ If MouseDown (1)
+ If MilliSecs () > firetimer + 75
+ pan# = PlayerOne.x / GW2 - 1.0
+ s:Shot = Shot.Create (PlayerOne.x, PlayerOne.y - (PlayerOne.height / 2 + 5), -5, PlayerOne.xs / 5.0, pan)
+ firetimer = MilliSecs ()
+ EndIf
+ EndIf
+
+ ' --------------------------------------------------------------------
+ ' No blocks left?
+ ' --------------------------------------------------------------------
+
+ ' Set 'welldone' flag (text shows "Well done" below if True);
+
+ ' If there are no blocks and Space is hit, delete all GravityItems,
+ ' reduce block count, increase number of block rows and reset level...
+
+ If Block.BCount = 0
+
+ ' Will display 'well done' message further down...
+
+ welldone = True
+
+ ' Remove all items (and reduce block count)...
+
+ For g:GravityItem = EachIn GravityItem.GravityItemList
+ If Block (g) Then Block.BCount = Block.BCount - 1
+ g.Destroy
+ Next
+
+ ' If space is hit, add some layers and reset everything (new level)...
+
+ If KeyHit (KEY_SPACE) Or MouseHit (2)
+ If Sounds_On Then PlaySound beep
+ layers = layers + 3
+ Goto ResetLevel
+ EndIf
+
+ EndIf
+
+ Else
+
+ If gameoverplayed = 0
+ If Sounds_On Then PlaySound over
+ gameoverplayed = 1
+ EndIf
+
+ ' Player is dead...
+
+ PlayerOne.shields = 0 ' Force to zero as can be reduced after game is over...
+
+ gameover = True
+
+ ' Remove all blocks...
+
+ For b:Block = EachIn GravityItem.GravityItemList
+ b.Destroy
+ Block.BCount = Block.BCount - 1
+ Next
+
+ ' -------------------------------------------------------------------
+ ' If player is dead and Space hit...
+ ' -------------------------------------------------------------------
+
+ ' Space hit... remove everything else and reset to inital settings...
+
+ If KeyHit (KEY_SPACE) Or MouseHit (2)
+
+ If Sounds_On Then PlaySound beep
+
+ For g:GravityItem = EachIn GravityItem.GravityItemList
+ g.Destroy
+ Next
+
+ ' Delete player (recreated when level is reset)...
+
+ PlayerOne = Null
+ layers = 5
+ bgang = 0
+ bgangspeed = 0
+ level = 0
+ rowdelay = 10000
+
+ gameoverplayed = 0
+
+ Goto ResetLevel
+
+ Else
+
+ ' Quit if ESC hit...
+
+ If KeyHit (KEY_ESCAPE) Then End
+
+ EndIf
+
+ EndIf
+
+ ' -----------------------------------------------------------------------
+ ' Update everything...
+ ' -----------------------------------------------------------------------
+
+ GravityItem.UpdateAll
+
+ ' -----------------------------------------------------------------------
+ ' Draw text on top of everything...
+ ' -----------------------------------------------------------------------
+
+ SetAlpha 1
+
+ DrawShadowText "Level: " + level + " | Shields: " + Int (PlayerOne.shields) + "%", 20, GRAPHICS_HEIGHT - 80
+ DrawShadowText "GCMemAlloced:"+GCMemAlloced(),20,GRAPHICS_HEIGHT - 60
+ DrawShadowText "F1: Toggle background | F3: Wireframe mode | F5: Toggle shadows", 20, GRAPHICS_HEIGHT - 40
+ DrawShadowText "F2: Toggle debris | F4: Toggle audio (" + OnOff (Sounds_On) + ")", 20, GRAPHICS_HEIGHT - 20
+
+ ' Draw extra text if appropriate...
+
+ If gameover
+ DrawShadowText go$, gox, GRAPHICS_HEIGHT / 2
+ gameover = False
+ Else
+ If welldone
+ DrawShadowText wd$, wdx, GRAPHICS_HEIGHT / 2
+ welldone = False
+ EndIf
+ EndIf
+
+ ' Display everything that's been drawn to the hidden back buffer...
+
+ GCCollect
+ Flip True
+
+Until KeyHit (KEY_ESCAPE)
+
+End
--- /dev/null
+
+' Hacked-into-GUI version! It's not pretty, but still, it's only meant to
+' demonstrate a windowed game with GUI!
+
+' -----------------------------------------------------------------------------
+' RockOut -- Rocket BlockOut
+' -----------------------------------------------------------------------------
+' Public domain source code by James L Boyd (support @ blitzbasic . com)
+' -----------------------------------------------------------------------------
+' Rocket image © 2004 James L Boyd, with permission granted for freeware/PD use,
+' not that anyone'd really want it anyway...!
+' -----------------------------------------------------------------------------
+
+' -----------------------------------------------------------------------------
+' Constants...
+' -----------------------------------------------------------------------------
+
+' Sizes used for blocks...
+Import MaxGUI.Drivers
+
+Const BLOCKWIDTH = 32
+Const BLOCKHEIGHT = 16
+
+' -----------------------------------------------------------------------------
+' Include media...
+' -----------------------------------------------------------------------------
+
+' Sounds (all from Yamaha RM1X!)...
+
+Incbin "sounds/shot.ogg" ' Player shot
+Incbin "sounds/fall.ogg" ' Block fall
+Incbin "sounds/hit.ogg" ' Block/player hit
+Incbin "sounds/beep.ogg" ' 'Press space' sound
+Incbin "sounds/gameover.ogg" ' Guess...
+
+' Graphics...
+
+Incbin "gfx/boing.png" ' Rocket
+Incbin "gfx/land.png" ' Background (used to be land, now a grid)
+Incbin "gfx/shot.png" ' Player's shot
+Incbin "gfx/block.png" ' Guess...
+
+' -----------------------------------------------------------------------------
+' Types (object definitions)...
+' -----------------------------------------------------------------------------
+
+' GravityItem: all objects affected by gravity are based upon this...
+
+Type GravityItem
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific globals...
+ ' ------------------------------------------------------------------------
+
+ ' Why not make these truly global? It's cleaner -- you can just copy and
+ ' paste the type definition into a completely different program without
+ ' having to remember which globals are related...
+
+ Global GCount ' Count of all GravityItems (for debugging)
+ Global GravityItemList:TList ' List used for all GravityItem objects
+ Global Gravity# = 0.05 ' Gravity applied to all GravityItems
+
+ ' ------------------------------------------------------------------------
+ ' Type fields...
+ ' ------------------------------------------------------------------------
+
+ Field x# ' x position of object
+ Field y# ' y position of object
+ Field xs# ' x speed of object
+ Field ys# ' y speed of object
+
+ Field width ' Width of object
+ Field height ' Height of object
+
+ Field damage ' Damage caused by this item if it hits player
+ Field fixed = False ' Is object fixed in place? Blocks are, at first...
+
+ Field r, g, b
+
+ ' ------------------------------------------------------------------------
+ ' Type functions...
+ ' ------------------------------------------------------------------------
+
+ Function UpdateAll ()
+ If GravityItemList = Null Then Return
+ If Shadows_On Then Block.DrawShadows ' Shadows_On is a global...
+ For g:GravityItem = EachIn GravityItemList
+ g.Update
+ g.Draw
+ Next
+ End Function
+
+' Function DrawAll ()
+' If Shadows_On Then Block.DrawShadows ' Shadows_On is a global...
+' For g:GravityItem = EachIn GravityItemList
+' g.Draw
+' Next
+' End Function
+
+ ' ------------------------------------------------------------------------
+ ' Type methods...
+ ' ------------------------------------------------------------------------
+
+ ' The New method is special -- Blitz calls it whenever a new object is
+ ' created...
+
+ ' Every time a new GravityItem is created -- including objects that extend
+ ' GravityItem -- this is called. In this case, it creates the type-global
+ ' list if it doesn't yet exist (only happens once), and adds the item to it...
+
+ Method New ()
+ If GravityItemList = Null Then GravityItemList = New TList
+ GravityItemList.AddLast Self
+ GCount = GCount + 1
+ End Method
+
+ ' Destroy current object and remove from list
+ Method Destroy ()
+ GravityItemList.Remove Self
+ GCount = GCount - 1
+ End Method
+
+ ' Rectangle-based collision test of current object and player.
+
+ ' 'The multiplier' parameter controls how much of an object's
+ ' 'damage' field applies to the player -- in the case of Block
+ ' objects, the more they're faded out, the less damage they do...
+
+ ' The 'posyonly' parameter is a hack to stop Shot objects damaging
+ ' the player while going up...
+
+ Method PlayerCollide (multiplier# = 1, posyonly = 0)
+
+ ' Offset x/y position of shots (all images' handles are centered)...
+
+ ox = x - width / 2
+ oy = y - height / 2
+
+ ' Offset x/y position of player...
+
+ opx = PlayerOne.x - PlayerOne.width / 2
+ opy = PlayerOne.y - PlayerOne.height / 2
+
+ ' Hack to stop Shot objects damaging player while going up...
+
+ check = 1
+
+ If posyonly
+ If ys < 0
+ check = 0
+ EndIf
+ EndIf
+
+ ' Test for collision, apply damage and make explosion...
+
+ If check
+ If OverLap (ox, oy, ox + width, oy + height, opx, opy, opx + PlayerOne.width, opy + PlayerOne.height)
+ PlayerOne.shields = PlayerOne.shields - damage * multiplier
+ ExplosionParticle.Explode x, y, damage * 5 * multiplier
+ PlayerOne.damaged = MilliSecs ()
+ Return True
+ EndIf
+ EndIf
+
+ End Method
+
+ ' There is no default Draw method here, as it's different for each extended type
+ ' of GravityItem, so I've defined it as Abstract...
+
+ ' Abstract forces every type that extends GravityItem to have a Draw () method defined
+ ' or the code simply won't compile...
+
+ ' One practical use for this is that you can call Draw from any random GravityItem,
+ ' regardless of which extended type it is, and this will call the correct Draw ()
+ ' for the type of object in question...
+
+ Method Draw () Abstract
+
+ ' Abstract Update method for GravityItems. See Draw () explanation...
+
+ Method Update () Abstract
+
+End Type
+
+' Particles created in an explosion. This type extends GravityItem, meaning all
+' properties of GravityItem apply, except where methods are over-ridden
+' (ie. re-defined) here...
+
+Type ExplosionParticle Extends GravityItem
+
+ ' ------------------------------------------------------------------------
+ ' Type fields...
+ ' ------------------------------------------------------------------------
+
+ ' No need to define, x, y, xs, ys, etc as they're part of the GravityItem definition...
+
+ Field alph# = 1.0 ' Alpha level of particle (translucency)
+
+ ' ------------------------------------------------------------------------
+ ' Type functions...
+ ' ------------------------------------------------------------------------
+
+ ' Create explosion of particles, and play sound...
+
+ Function Explode (x#, y#, particles)
+
+ ' NB. GW2 is a global set to half of GraphicsWidth ()...
+
+ If Sounds_On
+ pan# = x / GW2 - 1.0
+ play = CueSound (hit)
+ SetChannelPan play, pan
+ ResumeChannel play
+ EndIf
+
+ For loop = 1 To particles
+ ExplosionParticle.Create (x, y)
+ Next
+
+ End Function
+
+ ' Create single explosion particle. Note that any items extending GravityItem
+ ' will call the New () method from GravityItem upon creation, so these will
+ ' be added to the GravityItem list automatically...
+
+ Function Create:ExplosionParticle (x, y)
+
+ e:ExplosionParticle = New ExplosionParticle
+ e.x = x
+ e.y = y
+ e.xs = Rnd (-8, 8)
+ e.ys = Rnd (-8, 8)
+
+ ' Random colour...
+
+ Select Rand (0, 3)
+ Case 0
+ e.r = 255
+ e.g = 255
+ e.b = 255
+ Case 1
+ e.r = 255
+ e.g = 127
+ e.b = 0
+ Case 2
+ e.r = 255
+ e.g = 255
+ e.b = 0
+ Case 3
+ e.r = 255
+ e.g = 0
+ e.b = 0
+ End Select
+
+ ' Random size...
+
+ size = Rand (1, 8)
+ e.width = size
+ e.height = size
+
+ End Function
+
+ ' ------------------------------------------------------------------------
+ ' Type methods...
+ ' ------------------------------------------------------------------------
+
+ ' Update () over-rides the GravityItem Update () method...
+
+ Method Update ()
+
+ ' Reduce alpha level of particle...
+
+ alph = alph - 0.01
+
+ ' Apply Gravity global (see GravityItem) to y speed...
+
+ ys = ys + Gravity
+
+ ' Move particle by current speed...
+
+ x = x + xs
+ y = y + ys
+
+ ' If off-screen or reduced to invisible, remove from list by
+ ' calling the Destroy method (inherited from GravityItem)...
+
+ If y > GraphicsHeight () Or alph = 0 Then Destroy
+
+ End Method
+
+ ' Draw particle...
+
+ Method Draw ()
+
+ SetScale 1, 1
+
+ SetBlend ALPHABLEND
+
+ SetAlpha alph
+ SetColor r, g, b
+ DrawRect x, y, width, height
+
+ End Method
+
+End Type
+
+' Block definition. Again, Block is a kind of GravityItem...
+
+Type Block Extends GravityItem
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific globals...
+ ' ------------------------------------------------------------------------
+
+ Global BCount ' Number of blocks
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific fields...
+ ' ------------------------------------------------------------------------
+
+ Field alph# = 1.0 ' Alpha level of block
+ Field ang# ' Rotation of block
+ Field angspeed# ' Rotation speed of block
+ Field desty#
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific function...
+ ' ------------------------------------------------------------------------
+
+ ' Create a Block object (added to GravityItem list automatically)...
+
+ Function Create:Block (x, y)
+ blk:Block = New Block
+ blk.x = x
+ blk.y = y
+ blk.desty = y
+ blk.width = BLOCKWIDTH
+ blk.height = BLOCKHEIGHT
+ blk.fixed = True
+ blk.damage = 20
+ BCount = BCount + 1
+ Return blk
+ End Function
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific methods...
+ ' ------------------------------------------------------------------------
+
+ ' Update () method for Block objects...
+
+ Method Update ()
+
+ ' Check for collision (passing alpha level of block to apply
+ ' appropriate damage), and remove from GravityItem list if hit...
+
+ If PlayerCollide (alph) Then BCount = BCount - 1; Destroy; Return
+
+ ' If the block has been freed (by being hit), make it fall...
+
+ If Not fixed
+
+ alph = alph - 0.0075; If alph < 0 Then alph = 0
+ ang = ang + angspeed; If ang > 359 Then ang = 0
+ ys = ys + Gravity
+ x = x + xs
+ y = y + ys
+
+ If y > GraphicsHeight () Or alph = 0 Destroy; BCount = BCount - 1
+
+ Else
+
+ ' When blocks are lowered in main loop, they are just set to 'desty',
+ ' their new y-position destination. This moves them towards that...
+
+ ydist# = desty - y
+ ys = ydist / 12.0
+ y = y + ys
+
+ EndIf
+
+ End Method
+
+ ' Block-specific Draw () method...
+
+ Method Draw ()
+
+ SetBlend ALPHABLEND
+ SetRotation ang
+
+ SetColor r, g, b
+ SetAlpha alph
+
+ DrawImage BlockImage, x, y
+
+ SetRotation 0
+
+ End Method
+
+ Function DrawShadows ()
+ SetBlend ALPHABLEND
+ For blk:Block = EachIn GravityItemList
+ SetRotation blk.ang
+ SetColor 0, 0, 0
+ SetAlpha blk.alph * 0.25
+ DrawImage BlockImage, blk.x + 8, blk.y + 8
+ Next
+ End Function
+
+End Type
+
+' Player object. Only one player possible right now, but this keeps everything
+' together for easy reference...
+
+Type Player
+
+ Field damaged ' Set to MilliSecs () when hit (used for flashing effect)
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific fields...
+ ' ------------------------------------------------------------------------
+
+ ' The shields field is a float so I can reduce by small amounts, but I use
+ ' Int (PlayerOne.shields) to display/evaluate it...
+
+ Field shields# = 100
+
+ Field x#
+ Field y#
+ Field xs#
+ Field ys#
+
+ Field image ' Player image...
+
+ Field width
+ Field height
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific functions...
+ ' ------------------------------------------------------------------------
+
+ ' Create () is a function that creates & returns a :Player type object...
+
+ Function Create:Player (x, y, image)
+ PlayerOne:Player = New Player
+ PlayerOne.image = image
+ PlayerOne.x = x
+ PlayerOne.y = y
+ PlayerOne.width = ImageWidth (PlayerOne.image) * 0.2 ' Image is scaled in Draw ()
+ PlayerOne.height = ImageHeight (PlayerOne.image) * 0.2
+ Return PlayerOne
+ End Function
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific methods...
+ ' ------------------------------------------------------------------------
+
+ ' This is passed the MouseX () and MouseY () positions in the main game
+ ' loop, and hence moves the player toward the mouse cursor...
+
+ Method Move (destx#, desty#, div#)
+ xdist# = destx - x
+ ydist# = desty - y
+ xs = xdist / div
+ ys = ydist / div
+ x = x + xs
+ y = y + ys
+ End Method
+
+ Method Draw (alpha# = 1, r = 255, g = 255, b = 255)
+
+ SetBlend ALPHABLEND
+ SetScale 0.2, 0.2
+
+ If Shadows_On
+ SetColor 0, 0, 0
+ SetAlpha alpha * 0.4
+ DrawImage image, x + 8, y + 8
+ EndIf
+
+ SetAlpha alpha
+
+ ' If player is damaged, rgb will be RED...
+
+ SetColor r, g, b
+ DrawImage image, x, y
+ SetScale 1, 1
+
+ End Method
+
+End Type
+
+Type Shot Extends GravityItem
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific functions...
+ ' ------------------------------------------------------------------------
+
+ Function Create:Shot (x#, y#, ys#, xs#, soundpan#)
+ If Sounds_On
+ play = CueSound (shoot)
+ SetChannelPan play, soundpan
+ ResumeChannel play
+ EndIf
+ s:Shot = New Shot
+ s.x = x
+ s.y = y
+ s.xs = xs
+ s.ys = ys
+ s.width = 6
+ s.height = 6
+ s.damage = 2
+ Return s
+ End Function
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific methods...
+ ' ------------------------------------------------------------------------
+
+ ' Over-ride standard GravityItem Update () method...
+
+ Method Update ()
+
+ ' Hit the player (note 'posyonly', 2nd parameter, of PlayerCollide)...
+
+ If PlayerCollide (1, 1) Then Destroy; Return
+
+ ys = ys + Gravity
+ x = x + xs
+ y = y + ys
+
+ ' Remove if below bottom of screen...
+
+ If y > GraphicsHeight ()
+
+ Destroy
+
+ Else
+
+ ' Check current Shot against all Blocks...
+
+ ' (Notice that this only checks Block objects in the list!)
+
+ For blk:Block = EachIn GravityItemList
+
+ ' Get x offset (rectangles are mid-handled)...
+
+ ox = x - width / 2
+ oy = y - height / 2
+
+ ogx = blk.x - blk.width / 2
+ ogy = blk.y - blk.width / 2
+
+ ' Check collision...
+
+ If OverLap (ox, oy, ox + width, oy + height, ogx, ogy, ogx + blk.width, ogy + blk.height)
+
+ ' If Block is already dead (ie. falling), reflect Shot, otherwise
+ ' un-fix block and create explosion...
+
+ ' Note: ys is current Shot object's y speed...
+
+ If blk.fixed = False
+ ys = -ys
+ Else
+ blk.fixed = False
+ blk.ys = ys / Rnd (1, 4)
+ blk.angspeed = Rnd (-4, 4)
+ ExplosionParticle.Explode ogx, ogy, 4
+ EndIf
+
+ EndIf
+
+ Next
+
+ EndIf
+
+ End Method
+
+ Method Draw ()
+ SetBlend MASKBLEND
+ SetAlpha 1
+ SetColor 255, 255, 255
+ DrawImage ShotImage, x, y
+ End Method
+
+End Type
+
+' The random debris that falls 'down' the screen...
+
+Type DebrisItem
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific fields...
+ ' ------------------------------------------------------------------------
+
+ Field x# = Rand (0, GraphicsWidth () - 1)
+ Field y# = Rand (0, GraphicsHeight () - 1)
+ Field ys# = Rnd (0.01, 8)
+ Field size = Rand (1, 2)
+
+ ' ------------------------------------------------------------------------
+ ' Type-specific methods...
+ ' ------------------------------------------------------------------------
+
+ Method Update ()
+ If y > GraphicsHeight () y = 0
+ y = y + ys
+ End Method
+
+ Method Draw ()
+ SetColor Rnd (127, 255), Rnd (127, 255), 255
+ SetBlend SOLIDBLEND
+ DrawRect x, y, size, size
+ End Method
+
+End Type
+
+' -----------------------------------------------------------------------------
+' Functions...
+' -----------------------------------------------------------------------------
+
+' Draw simple text with shadow...
+
+Function DrawShadowText (t$, x, y)
+ SetColor 0, 0, 0
+ DrawText t$, x + 1, y + 1
+ SetColor 255, 255, 255
+ DrawText t$, x, y
+End Function
+
+' Returns "Off" if 'status' is 0, otherwise "On"...
+
+Function OnOff$ (status)
+ If status Then Return "On" Else Return "Off"
+End Function
+
+' Phew! Thanks, Birdie! Rectangular overlap function. Should have been so easy...
+
+Function OverLap (x0, y0, x1, y1, x2, y2, x3, y3)
+ If x0 > x3 Or x1 < x2 Then Return False
+ If y0 > y3 Or y1 < y2 Then Return False
+ Return True
+End Function
+
+' Distance between two points...
+
+Function Dist# (x0#, y0#, x1#, y1#)
+ Return Sqr (((x1 - x0) * (x1 - x0)) + ((y1 - y0) * (y1 - y0)))
+End Function
+
+' -----------------------------------------------------------------------------
+' Main game. This is where it all goes pear-shaped!
+' -----------------------------------------------------------------------------
+
+' Open display...
+
+'Graphics 640, 480, 32
+
+x = GadgetWidth (Desktop ()) / 2 - 320
+y = GadgetHeight (Desktop ()) / 2 - 240
+
+window = CreateWindow ("RockOut GUI", x, y, 640, 480, Null, WINDOW_TITLEBAR)
+If Not window Notify "Couldn't open window!"; End
+
+canvas:TGadget = CreateCanvas (0, 0, ClientWidth (window), ClientHeight (window) - 48, window)
+If Not canvas Notify "Couldn't create canvas!"; End
+
+SetGraphics CanvasGraphics (canvas)
+
+' Buttons...
+
+y = ClientHeight (window) - 24
+width = ClientWidth (window) / 4
+
+tbackground:TGadget = CreateButton ("Toggle background", 0, y, width, 24, window)
+tdebris:TGadget = CreateButton ("Toggle debris", width, y, width, 24, window)
+tshadows:TGadget = CreateButton ("Toggle shadows", width * 2, y, width, 24, window)
+tsounds:TGadget = CreateButton ("Toggle sounds", width * 3, y, width + 2, 24, window)
+
+tslabel:TGadget = CreateLabel ("Shields:", 20, y - 22, 60, 20, window, LABEL_CENTER)' | LABEL_FRAME)
+tshields:TGadget = CreateProgBar (100, y - 24, ClientWidth (window) - 100, 20, window)
+
+' Pre-calc half of graphics width...
+
+Global GW2 = GraphicsWidth () / 2
+Global GH2 = GraphicsHeight () / 2
+
+' Set Cls colour (used when background turned off)...
+
+SetClsColor 64, 96, 128
+
+' All images' and rectangles' handles should be set to the centre...
+
+AutoMidHandle True
+SetHandle 0.5, 0.5
+
+' All images unfiltered...
+
+'AutoImageFlags MASKEDIMAGE
+
+' Mask colour for loaded images (will be transparent)...
+
+SetMaskColor 255, 0, 255
+
+' Mouse position -- used in some type methods and functions, hence global...
+
+Global mx, my
+
+' Player object...
+
+Global PlayerOne:Player
+
+' Draw shadows?
+
+Global Shadows_On = True
+
+' Turn off sound?
+
+Global Sounds_On = True
+
+' Load media -- sounds, from included binaries (see start of code)...
+
+Global shoot = LoadSound ("incbin::sounds/shot.ogg")
+Global hit = LoadSound ("incbin::sounds/hit.ogg")
+Global beep = LoadSound ("incbin::sounds/beep.ogg")
+Global over = LoadSound ("incbin::sounds/gameover.ogg")
+
+' Load media -- images, from included binaries...
+
+' Shots...
+
+Global ShotImage = LoadImage ("incbin::gfx/shot.png")
+
+' Blocks...
+
+' Note there is only one image for all blocks -- they are altered by SetColorRGB before
+' drawing (WHITE gives normal image)...
+
+Global BlockImage = LoadImage ("incbin::gfx/block.png")
+
+' Player...
+
+pimage = LoadImage ("incbin::gfx/boing.png",MASKEDIMAGE|MIPMAPPEDIMAGE)
+
+' Background...
+
+' Note that bgscale stores the length of the screen diagonal, and this value is used
+' for the image height, so it doesn't get chopped when it rotates...
+
+bg = LoadImage ("incbin::gfx/land.png")
+bgscale# = Dist (0, 0, GraphicsWidth () - 1, GraphicsHeight () - 1) / ImageHeight (bg)
+
+' Background angle/speed of rotation...
+
+bgang# = 0
+bgangspeed# = 0
+
+' Create an array of 100 debris particles...
+
+Local debris:DebrisItem [100]
+For loop = 0 Until 100
+ debris [loop] = New DebrisItem
+Next
+
+' This should probably read 'rows' -- the number of rows of blocks at startup...
+
+layers = 5
+
+' Toggle variables for drawing background, debris and wireframe mode...
+
+bgtoggle = 1
+debristoggle = 1
+wftoggle = 0
+
+' Delay before adding another row of blocks (this is decreased as the game progresses)...
+
+rowdelay = 10000 ' 10 seconds (10000 milliseconds)...
+
+' Background colour and first target colour...
+
+backr# = 64
+backg# = 96
+backb# = 180
+
+backtr# = 128
+backtg# = 32
+backtb# = 48
+
+' Delay between colour increments...
+
+backstep# = 5000
+
+' Colour increments...
+
+backstepr# = (backtr - backr) / backstep
+backstepg# = (backtg - backg) / backstep
+backstepb# = (backtb - backb) / backstep
+
+' Direction of increment to target colour...
+
+backsgn = Sgn (backtr - backr)
+
+mx = GraphicsWidth () / 2
+my = GraphicsHeight () / 2
+MoveMouse mx, my
+
+timer = CreateTimer (60)
+
+' This is the point where the game is re-started from, whenever a level is completed or game ended...
+
+#ResetLevel ' $name signifies a label now...
+
+' Increase level number (level is 0 on startup, so becomes 1 for first level)...
+
+level = level + 1
+
+' Reset the 'new row' delay timer to the current time...
+
+rowtimer = MilliSecs ()
+
+' Set fire rate limiter to current time...
+
+firetimer = MilliSecs ()
+
+' Create rows of blocks...
+
+For y = 0 To layers - 1
+ For x = 0 Until GraphicsWidth () Step BLOCKWIDTH
+ b:Block = Block.Create (x + BLOCKWIDTH / 2, (y * BLOCKHEIGHT) + BLOCKHEIGHT / 2)
+ b.r = Rnd (127, 255)
+ b.g = Rnd (127, 255)
+ b.b = Rnd (127, 255)
+ Next
+Next
+
+' Minimum number of blocks left before we *stop* dropping them down (equivalent of 2 rows)...
+
+lowblocks = 2 * (GraphicsWidth () / BLOCKWIDTH)
+
+' When a level is completed, we delete the player object for simplicity of resetting
+' all its values. This creates it (same for game startup)...
+
+If PlayerOne = Null Then PlayerOne = Player.Create (mx, my, pimage)
+
+' Game text and precalculated x offsets...
+
+go$ = "G A M E O V E R -- H I T S P A C E O R R M B"
+wd$ = "W E L L D O N E -- H I T S P A C E O R R M B"
+
+gox = (GraphicsWidth () / 2) - (TextWidth (go$) / 2)
+wdx = (GraphicsWidth () / 2) - (TextWidth (wd$) / 2)
+
+ActivateGadget canvas
+
+firing = 0
+HideMouse
+
+firstgo = 1
+
+Repeat
+
+ ' Mac compatibility fix...
+
+' SetGraphics CanvasGraphics (canvas)
+
+ WaitEvent
+
+ ' Clear the screen...
+
+ Cls
+
+ ' Store mouse position in these global variables...
+
+ Select EventID ()
+
+ Case EVENT_MOUSEENTER
+ If EventSource () = canvas Then HideMouse
+
+ Case EVENT_MOUSELEAVE
+ If EventSource () = canvas Then ShowMouse
+
+ Case EVENT_TIMERTICK
+ 'Print "Tick!"
+ RedrawGadget canvas
+
+ Case EVENT_GADGETPAINT
+ draw = 1
+
+ Case EVENT_MOUSEDOWN
+ If EventData () = 1
+ firing = 1
+ EndIf
+
+ Case EVENT_MOUSEUP
+ firing = 0
+ If EventData () = 2
+ spacehit = 1
+ EndIf
+
+ Case EVENT_MOUSEMOVE
+ mx = EventX ()
+ my = EventY ()
+
+ Case EVENT_KEYUP
+
+ ' -----------------------------------------------------------------------
+ ' Toggles...
+ ' -----------------------------------------------------------------------
+
+ Select EventData ()
+ Case KEY_ESCAPE
+ End
+ Case KEY_SPACE
+ spacehit = 1
+ End Select
+
+ Case EVENT_GADGETACTION
+
+ Select EventSource ()
+ Case tbackground:TGadget
+ bgtoggle = 1 - bgtoggle
+ Case tdebris:TGadget
+ debristoggle = 1 - debristoggle
+ Case tshadows:TGadget
+ Shadows_On = 1 - Shadows_On
+ Case tsounds:TGadget
+ Sounds_On = 1 - Sounds_On
+ End Select
+
+ ActivateGadget canvas ' Get event focus back!
+
+ End Select
+
+ ' -----------------------------------------------------------------------
+ ' Background...
+ ' -----------------------------------------------------------------------
+
+ If draw ' EVENT_GADGETPAINT received...
+
+ ' Update background rotation...
+
+ bgang = bgang + bgangspeed; If bgang > 359 - bgangspeed Then bgang = 0
+ bgangspeed = bgangspeed + 0.0001
+
+ ' The bgtoggle variable controls whether the background should be drawn or not...
+
+ If bgtoggle
+
+ ' Change colour by pre-calculated increment...
+
+ backr = backr + backstepr
+ backg = backg + backstepg
+ backb = backb + backstepb
+
+ ' Reached target colour? Set a new target/increments/increment-direction...
+
+ If backr => backtr * backsgn
+ backtr = Rnd (255)
+ backtg = Rnd (255)
+ backtb = Rnd (255)
+ backstepr# = (backtr - backr) / backstep
+ backstepg# = (backtg - backg) / backstep
+ backstepb# = (backtb - backb) / backstep
+ backsgn = Sgn (backtr - backr)
+ EndIf
+
+ EndIf
+
+ ' When the player is hit, the 'damaged' field is set to the current time.
+ ' This code checks if a second has passed since 'damaged'. If so, it draws
+ ' the player normally; if not, the player is drawn in red, with varying
+ ' transparency...
+
+ If MilliSecs () > PlayerOne.damaged + 1000 ' Damage timeout has passed...
+
+ ' Draw normally...
+
+ PlayerOne.damaged = 0 ' Resetting damage time...
+ alpha# = 1
+ rcol = 255; gcol = 255; bcol = 255
+
+ Else
+
+ ' Flash player for 1 second if hit...
+
+ alpha# = Sin (MilliSecs ())
+ rcol = 255; gcol = 0; bcol = 0
+
+ EndIf
+
+ ' -----------------------------------------------------------------------
+ ' If player is alive, do stuff...
+ ' -----------------------------------------------------------------------
+
+ If Int (PlayerOne.shields) > 0 ' Shields is a float, so gotta round it...
+
+ ' Player is alive...
+
+ ' If more than 'lowblocks' left on screen, add a row and lower all
+ ' blocks. Reset drop-down timer and reduce delay for next drop-down...
+
+ If Block.BCount > lowblocks And MilliSecs () > rowtimer + rowdelay
+
+ ' Add a row of blocks, above top of screen...
+
+ For x = 0 Until GraphicsWidth () Step BLOCKWIDTH
+ b:Block = Block.Create (x + BLOCKWIDTH / 2, -BLOCKHEIGHT / 2)
+ b.r = Rnd (127, 255)
+ b.g = Rnd (127, 255)
+ b.b = Rnd (127, 255)
+ Next
+
+ ' Set all blocks' target y position down by block height. When blocks
+ ' are updated, they get moved towards this new position...
+
+ For b:Block = EachIn GravityItem.GravityItemList
+ b.desty = b.desty + BLOCKHEIGHT
+ Next
+
+ ' Reset timeout until a new row is added...
+
+ rowtimer = MilliSecs ()
+
+ ' Reduce row-down timeout a bit...
+
+ If rowdelay => 1100 Then rowdelay = rowdelay - 100 ' Minimum 1 sec interval!
+
+ EndIf
+
+ ' --------------------------------------------------------------------
+ ' Fire shot (maximum fire rate 75 milliseconds)...
+ ' --------------------------------------------------------------------
+
+ If firing 'MouseDown (1)
+ If MilliSecs () > firetimer + 75
+ pan# = PlayerOne.x / GW2 - 1.0
+ s:Shot = Shot.Create (PlayerOne.x, PlayerOne.y - (PlayerOne.height / 2 + 5), -5, PlayerOne.xs / 5.0, pan)
+ firetimer = MilliSecs ()
+ EndIf
+ EndIf
+
+ ' --------------------------------------------------------------------
+ ' No blocks left?
+ ' --------------------------------------------------------------------
+
+ ' Set 'welldone' flag (text shows "Well done" below if True);
+
+ ' If there are no blocks and Space is hit, delete all GravityItems,
+ ' reduce block count, increase number of block rows and reset level...
+
+ If Block.BCount = 0
+
+ ' Will display 'well done' message further down...
+
+ welldone = True
+
+ ' Remove all items (and reduce block count)...
+
+ For g:GravityItem = EachIn GravityItem.GravityItemList
+ If Block (g) Then Block.BCount = Block.BCount - 1
+ g.Destroy
+ Next
+
+ ' If space is hit, add some layers and reset everything (new level)...
+
+ If spacehit'KeyHit (KEY_SPACE) Or MouseHit (2)
+ If Sounds_On Then PlaySound beep
+ layers = layers + 3
+ Goto ResetLevel
+ EndIf
+
+ EndIf
+
+ Else
+
+ If gameoverplayed = 0
+ If Sounds_On Then PlaySound over
+ gameoverplayed = 1
+ EndIf
+
+ ' Player is dead...
+
+ PlayerOne.shields = 0 ' Force to zero as can be reduced after game is over...
+
+ gameover = True
+
+ ' Remove all blocks...
+
+ For b:Block = EachIn GravityItem.GravityItemList
+ b.Destroy
+ Block.BCount = Block.BCount - 1
+ Next
+
+ ' -------------------------------------------------------------------
+ ' If player is dead and Space hit...
+ ' -------------------------------------------------------------------
+
+ ' Space hit... remove everything else and reset to initial settings...
+
+ If spacehit'KeyHit (KEY_SPACE) Or MouseHit (2)
+
+ If Sounds_On Then PlaySound beep
+
+ For g:GravityItem = EachIn GravityItem.GravityItemList
+ g.Destroy
+ Next
+
+ ' Delete player (recreated when level is reset)...
+
+ PlayerOne = Null
+ layers = 5
+ bgang = 0
+ bgangspeed = 0
+ level = 0
+ rowdelay = 10000
+
+ gameoverplayed = 0
+
+ Goto ResetLevel
+
+ EndIf
+
+ EndIf
+
+ If debristoggle
+ For loop = 0 Until 100
+ debris [loop].Update
+ Next
+ EndIf
+
+
+
+ If bgtoggle
+ ' Set colour of background image (applied to the greyscale default)...
+
+ SetColor backr, backg, backb
+
+ ' Set the background's pre-calculated scale...
+
+ SetScale bgscale, bgscale
+ SetRotation bgang
+ SetAlpha 1
+
+ DrawImage bg, GW2, GH2
+
+ ' Reset this stuff so next drawn items don't have to...
+
+ SetRotation 0
+ SetScale 1, 1
+
+ ' Put back to wireframe/non-wireframe mode, depending on value of 'wftoggle'...
+
+ ' WireFrame wftoggle
+ EndIf
+
+ ' -----------------------------------------------------------------------
+ ' Debris...
+ ' -----------------------------------------------------------------------
+
+ ' Draw debris particles if 'debristoggle' is True...
+
+ If debristoggle
+ For loop = 0 Until 100
+ ' debris [loop].Update
+ debris [loop].Draw
+ Next
+ EndIf
+
+ ' -----------------------------------------------------------------------
+ ' Cursor...
+ ' -----------------------------------------------------------------------
+
+ SetColor 255, 255, 255
+ DrawLine mx - 8, my, mx + 8, my
+ DrawLine mx, my - 8, mx, my + 8
+
+ ' -----------------------------------------------------------------------
+ ' Move and draw player...
+ ' -----------------------------------------------------------------------
+
+ ' Move the player object based on mouse position (the '20' controls the
+ ' speed at which the player moves toward the mouse -- play with it;
+ ' lower is faster)...
+
+ PlayerOne.Move (mx, my, 12)
+
+ ' Draw the player using the above values...
+
+ PlayerOne.Draw (alpha, rcol, gcol, bcol)
+
+ ' -----------------------------------------------------------------------
+ ' Update everything...
+ ' -----------------------------------------------------------------------
+
+ GravityItem.UpdateAll
+
+ ' -----------------------------------------------------------------------
+ ' Draw text on top of everything...
+ ' -----------------------------------------------------------------------
+
+ SetAlpha 1
+
+ DrawShadowText "Level: " + level, 20, GraphicsHeight () - 40
+ ' + " | Shields: " + Int (PlayerOne.shields) + "%"
+ UpdateProgBar tshields, Int (PlayerOne.Shields) / 100.0
+ ' Draw extra text if appropriate...
+
+ If gameover
+ DrawShadowText go$, gox, GraphicsHeight () / 2
+ gameover = False
+ Else
+ If welldone
+ DrawShadowText wd$, wdx, GraphicsHeight () / 2
+ welldone = False
+ EndIf
+ EndIf
+
+ ' Display everything that's been drawn to the hidden back buffer...
+
+ Flip
+
+ spacehit = 0
+ draw = 0
+
+ EndIf
+
+Until EventID () = EVENT_WINDOWCLOSE
+
+End
--- /dev/null
+
+' Rockets rotating and casting alpha-blended, pseudo light-sourced shadows on each other...
+
+MAXNUM = 500
+
+Graphics 640, 480, 32
+
+AutoImageFlags MASKEDIMAGE
+
+SetMaskColor 255, 0, 255
+
+rocket = LoadImage ("gfx/boing.png",MASKEDIMAGE|MIPMAPPEDIMAGE)
+grass = LoadImage ("gfx/grass.png")
+
+MidHandleImage rocket
+
+scale# = 0.5
+trans# = 0.5
+
+NUM = MAXNUM
+
+Local x [NUM], y [NUM]
+Local xs [NUM], ys [NUM]
+Local ang# [NUM], angstep# [NUM]
+
+For loop = 0 To NUM - 1
+ x [loop] = Rand (0, GraphicsWidth () - 1)
+ y [loop] = Rand (0, GraphicsHeight () - 1)
+ xs [loop] = Rand (1, 5)
+ ys [loop] = Rand (1, 5)
+ ang [loop] = Rand (0, 359)
+ angstep [loop] = Rnd (1, 5)
+Next
+
+NUM = 1
+
+Repeat
+
+ Cls
+
+ If KeyHit (KEY_RIGHT) Or MouseHit (2)
+ If NUM < MAXNUM Then NUM = NUM + 1
+ Else
+ If KeyHit (KEY_LEFT) Or MouseHit (1)
+ If NUM > 1 Then NUM = NUM - 1
+ EndIf
+ EndIf
+
+ mx = MouseX ()
+ my = MouseY ()
+
+ SetScale scale, scale
+ SetRotation 0
+ TileImage grass
+
+ For loop = 0 To NUM - 1
+
+ x [loop] = x [loop] + xs [loop]
+ y [loop] = y [loop] + ys [loop]
+
+ If x [loop] < 0 Or x [loop] > GraphicsWidth () - 1
+ xs [loop] = -xs [loop]; x [loop] = x [loop] + xs [loop]
+ angstep [loop] = -angstep [loop]
+ EndIf
+
+ If y [loop] < 0 Or y [loop] > GraphicsHeight () - 1
+ ys [loop] = -ys [loop]; y [loop] = y [loop] + ys [loop]
+ angstep [loop] = -angstep [loop]
+ EndIf
+
+ ang [loop] = ang [loop] + angstep [loop]
+ If ang [loop] > 360 - angstep [loop] Then ang [loop] = 0
+ SetRotation ang [loop]
+
+ offx = -(mx - x [loop]) / 8
+ offy = -(my - y [loop]) / 8
+ DrawShadowedImage rocket, x [loop], y [loop], offx, offy, trans
+
+ Next
+
+ SetScale 1, 1
+ DrawShadowText "Use left/right cursors or mouse buttons to add/remove rockets", 20, 20
+ DrawShadowText "Move mouse to change light direction", 20, 40
+ DrawShadowText "Number of rockets: " + NUM, 20, 80
+
+ Flip
+
+Until KeyHit (KEY_ESCAPE)
+
+End
+
+Function DrawShadowText (t$, x, y)
+ SetRotation 0
+ SetColor 0, 0, 0
+ DrawText t$, x + 1, y + 1
+ SetColor 255, 255, 255
+ DrawText t$, x, y
+End Function
+
+Function DrawShadowedImage (image, x#, y#, xoff#, yoff#, level#)
+
+ SetBlend ALPHABLEND
+ SetColor 0, 0, 0
+ SetAlpha level
+ DrawImage image, x + xoff, y + yoff
+
+ SetBlend MASKBLEND
+ SetColor 255, 255, 255
+ SetAlpha 1
+ DrawImage image, x, y
+
+End Function
+
--- /dev/null
+
+' Adding objects to an object-specific list...
+
+Type Particle
+
+ Global ParticleList:TList ' The list for all objects of this type...
+ Global Gravity# = 0.1
+
+ Field x#
+ Field y#
+ Field xs#
+ Field ys#
+
+ ' The New method is called whenever one of these objects is created. If
+ ' the list hasn't yet been created, it's created here. The object is then
+ ' added to the list...
+
+ Method New ()
+ If ParticleList = Null
+ ParticleList = New TList
+ EndIf
+ ParticleList.AddLast Self
+ End Method
+
+ Function Create:Particle (x, y)
+ p:Particle = New Particle
+ p.x = x
+ p.y = y
+ p.xs = Rnd (-4, 4)
+ p.ys = 0
+ Return p
+ End Function
+
+ Function UpdateAll ()
+
+ ' Better check the list exists before trying to use it...
+
+ If ParticleList = Null Return
+
+ ' Iterate through list...
+
+ For p:Particle = EachIn ParticleList
+ p.ys = p.ys + Gravity
+ p.x = p.x + p.xs
+ p.y = p.y + p.ys
+ DrawRect p.x, p.y, 8, 8
+ If p.y > GraphicsHeight () p = Null
+ Next
+
+ End Function
+
+End Type
+
+' D E M O . . .
+
+Graphics 640, 480
+
+Repeat
+
+ Cls
+
+ ' Create a Particle every now and then...
+
+ If Rand (100) > 50
+ p:Particle = Particle.Create (MouseX (), MouseY ())
+ EndIf
+
+ ' Update all Particle objects...
+
+ Particle.UpdateAll ()
+
+ Flip
+
+Until KeyHit (KEY_ESCAPE)
+
+End
+
+
--- /dev/null
+Const PARTICLE_GRAVITY# = 0.05
+
+Global ParticleCounter
+Global ParticleList:TList = New TList
+
+' Core abstract type...
+
+Type Atom
+
+ Field image
+ Field x#
+ Field y#
+ Field xs#
+ Field ys#
+ Field ALPHA# = 1
+ Field size#
+
+ Method Update () Abstract
+
+End Type
+
+' Generic particle type that holds creation/update functions, based
+' on abstract type Atom...
+
+Type Particle Extends Atom
+
+ Function Create:Particle (image, x#, y#, xs#, ys#)
+ p:Rocket = New Rocket
+ p.image = image
+ p.x = x
+ p.y = y
+ p.xs = xs
+ p.ys = ys
+ ParticleList.AddLast p
+ ParticleCounter = ParticleCounter + 1
+ Return p
+ End Function
+
+ Function UpdateAll ()
+ Local p:Atom
+ For p=EachIn ParticleList
+ p.Update ()
+ Next
+ End Function
+
+End Type
+
+' Types based on Particle, all to be created using EXTENDED_TYPE.Create ()...
+
+Type Rocket Extends Particle
+
+ Method Update ()
+ If ALPHA > 0.01
+ ALPHA = ALPHA - 0.005
+ SetAlpha ALPHA
+ ys = ys + PARTICLE_GRAVITY
+ x = x + xs
+ y = y + ys
+ ang# = ATan2 (xs, -ys)
+ SetRotation ang
+ DrawImage image, x, y
+ If x < 0 Or x > GraphicsWidth () Or y > GraphicsHeight ()
+ ParticleList.Remove Self
+ ParticleCounter = ParticleCounter - 1
+ EndIf
+ Else
+ ParticleList.Remove Self
+ ParticleCounter = ParticleCounter - 1
+ EndIf
+ End Method
+
+End Type
+
+' --------------------------------------------------------------------------------
+
+Incbin "gfx/boing.png"
+
+Const GAME_WIDTH = 640
+Const GAME_HEIGHT = 480
+
+Const GRAPHICS_WIDTH = 1024
+Const GRAPHICS_HEIGHT = 768
+
+Graphics GRAPHICS_WIDTH,GRAPHICS_HEIGHT,32
+
+SetVirtualResolution GAME_WIDTH,GAME_HEIGHT
+
+SetClsColor 64, 96, 180
+
+SetMaskColor 255, 0, 255
+AutoImageFlags MASKEDIMAGE ' Disable for filtered rockets...
+
+image = LoadImage ("incbin::gfx/boing.png")
+MidHandleImage image
+
+lastmousex = VirtualMouseX ()
+lastmousey = VirtualMouseY ()
+
+Repeat
+
+ x = VirtualMouseX ()
+ y = VirtualMouseY ()
+
+ mxs# = VirtualMouseXSpeed ()'# = x - lastmousex
+ mys# = VirtualMouseYSpeed ()'# = y - lastmousey
+
+ Cls
+
+ xs# = mxs / 10 + Rnd (-0.1, 0.1)
+ ys# = mys / 10 + Rnd (-0.1, 0.1)
+
+ If MouseDown (1) And (mxs Or mys)
+ Rocket.Create (image, x, y, xs, ys)
+ EndIf
+
+ SetScale 0.2, 0.2
+ SetBlend ALPHABLEND
+ Particle.UpdateAll ()
+
+ SetScale 1, 1
+ SetRotation 0
+
+ DrawText "Click and drag mouse to throw rockets!", 10, 10
+ DrawText "Rockets: " + ParticleCounter, 10, 25
+
+ Flip
+
+ lastmousex = x
+ lastmousey = y
+
+Until KeyHit (KEY_ESCAPE)
+
+End
--- /dev/null
+
+' Ugly -- just playing around!
+
+Global FlameList:TList = New TList
+Global ShotList:TList = New TList
+
+Type Flame
+ Field x#, y#
+ Field image
+ Field ALPHA#
+ Field scale#
+ Field ys#
+End Type
+
+Type Shot
+ Field x#, y#
+ Field xs#, ys#
+ Field ALPHA#
+End Type
+
+Graphics 640, 480
+
+AutoImageFlags MASKEDIMAGE
+
+SetClsColor 16, 32, 64
+
+SetMaskColor 255, 0, 255
+
+player = LoadImage ("gfx/boing.png")
+MidHandleImage player
+
+flm = LoadImage ("gfx/flame.png")
+MidHandleImage flm
+
+sky = LoadImage ("gfx/sky.png")
+grass = LoadImage ("gfx/grass.png")
+rock = LoadImage ("gfx/rock.png")
+water = LoadImage ("gfx/water.png")
+
+MidHandleImage grass
+MidHandleImage rock
+MidHandleImage water
+
+x# = GraphicsWidth () / 2
+y# = GraphicsHeight () / 2
+
+speed# = 0
+
+playerscale# = 0.25
+
+' Map tiles...
+
+MAPXS = 50
+MAPYS = 30
+
+Local map [MAPXS, MAPYS]
+
+For mapx = 0 To MAPXS - 1
+
+ For mapy = 0 To MAPYS - 1
+
+ Select Rand (0, 3)
+ Case 0
+ image = 0
+ Case 1
+ image = grass
+ Case 2
+ image = rock
+ Case 3
+ image = water
+ End Select
+
+ map [mapx, mapy] = image
+
+ Next
+
+Next
+
+GW2 = GraphicsWidth () / 2
+GH2 = GraphicsHeight () / 2
+
+ShotSpeed# = 4
+
+Repeat
+
+ mx = MouseX ()
+ my = MouseY ()
+
+ Cls
+
+ SetBlend SOLIDBLEND
+ TileImage sky
+
+ ang# = ATan2 (my - GH2, mx - GW2)
+
+ x = x + Cos (ang) * speed
+ y = y + Sin (ang) * speed
+
+ If KeyHit (KEY_SPACE)
+ s:Shot = New Shot
+ s.x = (x + Cos (ang) * (ImageWidth (player) * playerscale) / 2)
+ s.y = (y + Sin (ang) * (ImageHeight (player) * playerscale)/ 2)
+ s.xs = Cos (ang) * (speed + ShotSpeed)
+ s.ys = Sin (ang) * (speed + ShotSpeed)
+ s.ALPHA = 1
+ ShotList.AddLast s
+ EndIf
+
+ If MouseDown (1)
+ If speed < 10 Then speed = speed + 0.1
+ If Rand (1, 100) > (100 - speed * 2.5)
+ f:Flame = New Flame
+ f.x = Rand (1, 8) + (x - Cos (ang) * (ImageWidth (player) * playerscale) / 2)
+ f.y = Rand (1, 8) + (y - Sin (ang) * (ImageHeight (player) * playerscale)/ 2)
+ f.ys = 0
+ f.image = flm
+ f.ALPHA = 1
+ f.scale = 0.5
+ FlameList.AddLast f
+ EndIf
+ EndIf
+
+ SetBlend MASKBLEND
+
+ SetRotation 0
+ SetScale 1, 1
+
+ For mapx = 0 To MAPXS - 1
+ For mapy = 0 To MAPYS - 1
+ If map [mapx, mapy]
+ DrawImage map [mapx, mapy], mapx * 128 - x, mapy * 128 - y
+ EndIf
+ Next
+ Next
+
+ SetColor 255, 255, 255
+ SetBlend ALPHABLEND
+
+ Local p:Flame
+ For p=EachIn FlameList
+ p.ys = p.ys - 0.05
+ p.y = p.y + p.ys
+ SetAlpha p.ALPHA
+ SetScale p.scale, p.scale
+ SetRotation 0
+ DrawImage p.image, GW2 + p.x - x, GH2 + p.y - y
+ p.ALPHA = p.ALPHA - 0.01
+ p.scale = p.scale + Rnd (0.0025, 0.05)
+ If p.ALPHA < 0
+ FlameList.remove p
+ EndIf
+ Next
+
+ SetColor 255, 255, 0
+
+ For s=EachIn ShotList
+ s.x = s.x + s.xs
+ s.y = s.y + s.ys
+ SetAlpha s.ALPHA
+ SetRotation 0
+ SetScale 1, 1
+ DrawRect GW2 + s.x - x, GH2 + s.y - y, 8, 8
+ s.ALPHA = s.ALPHA - 0.01
+ If s.ALPHA < 0
+ ShotList.remove s
+ EndIf
+ Next
+
+ SetBlend ALPHABLEND
+ SetAlpha 0.25
+ SetScale 0.25, 0.25
+ SetColor 0, 0, 0
+ sp2 = speed * 2
+ DrawPointedImage player, GW2 + sp2, GH2 + sp2, mx + sp2, my + sp2
+
+ SetColor 255, 255, 255
+ SetBlend MASKBLEND
+ SetScale 0.25, 0.25
+ SetAlpha 1
+ DrawPointedImage player, GW2, GH2, mx, my
+
+ speed = speed - 0.075; If speed < 0 Then speed = 0
+
+ SetRotation 0
+ SetScale 1, 1
+ SetColor 0, 0, 0
+ DrawText "Hold left mouse button to move and hit space to fire...", 21, 21
+ SetColor 255, 255, 255
+ DrawText "Hold left mouse button to move and hit space to fire...", 20, 20
+
+ Flip
+
+Until KeyHit (KEY_ESCAPE)
+
+End
+
+' Assumes image is oriented 'upwards' by default -- fiddle with
+' the "+ 90" part on the first line if not!
+
+Function DrawPointedImage (image, x#, y#, targetx#, targety# )
+ ang# = ATan2 (targety - y, targetx - x) + 90
+ SetRotation ang
+ DrawImage image, x, y
+End Function
+
--- /dev/null
+
+Incbin "gfx/bg.png"
+Incbin "gfx/boing.png"
+
+Graphics 640, 480 , 32
+
+AutoImageFlags MASKEDIMAGE|FILTEREDIMAGE
+
+SetMaskColor 255, 0, 255
+
+bg = LoadImage ("incbin::gfx/bg.png")
+bgw# = GraphicsWidth () / Float (ImageWidth (bg))
+bgh# = GraphicsHeight () / Float (ImageHeight (bg))
+
+image = LoadImage ("incbin::gfx/boing.png") ' My example is 256 x 256
+MidHandleImage image
+
+rotstep# = 1
+
+Repeat
+
+ mx = MouseX (); my = MouseY ()
+
+ SetViewport 0, 0, GraphicsWidth (), GraphicsHeight ()
+ Cls
+
+ SetViewport mx - 200, my - 150, 400, 300
+ Cls
+
+ ' ---------------------------------------------------------------
+ ' Draw background...
+ ' ---------------------------------------------------------------
+
+ SetAlpha 1
+ SetBlend MASKBLEND
+
+ SetRotation 0; SetScale bgw, bgh
+ DrawImage bg, 0, 0
+
+ ' ---------------------------------------------------------------
+ ' Draw image...
+ ' ---------------------------------------------------------------
+
+ rot# = rot + rotstep; If rot > 360 - rotstep Then rot = 0
+ SetRotation rot
+
+ scale# = 0.1 + Sin (rot / 2); If scale < 0 Then scale = -scale
+ SetScale scale, scale
+
+ SetBlend ALPHABLEND
+ SetAlpha scale
+
+ DrawImage image, mx, my
+
+ Flip
+
+Until KeyHit (KEY_ESCAPE)
+
+End
+
+
+
+
+
+
--- /dev/null
+
+Strict
+
+Import BRL.GNet
+
+AppTitle="GNet Test2"
+
+Local host:TGNetHost=CreateGNetHost()
+
+Local me:TGNetObject
+Local chat$,info$
+
+Graphics 800,600,0,15
+
+Repeat
+
+ Local c=GetChar()
+
+ Select c
+ Case 8
+ If chat chat=chat[..chat.length-1]
+ Case 27
+ If Confirm( "Quit?" )
+ CloseGNetHost host
+ End
+ EndIf
+ Case 13
+ If chat.find("/")=0
+ chat=chat[1..]
+ Local cmd$=chat
+ Local arg$
+ Local i=chat.find(" ")
+ If i<>-1
+ cmd=chat[..i]
+ arg=chat[i+1..]
+ EndIf
+ Select cmd
+ Case "create"
+ If me
+ info="Already created"
+ Else
+ me=CreateGNetObject( host )
+ SetGNetString me,0,arg
+ SetGNetString me,1,"Ready"
+ EndIf
+ Case "close"
+ If me
+ CloseGNetObject me
+ me=Null
+ Else
+ info="Not created"
+ EndIf
+ Case "quit","exit"
+ CloseGNetHost host
+ End
+ Case "nick"
+ If arg
+ If me SetGNetString me,0,arg
+ info="Nick changed to "+arg
+ Else
+ info="Expecting arg"
+ EndIf
+ Case "listen"
+ Local port=12345
+ If arg port=Int(arg)
+ If GNetListen( host,port )
+ info="Listening on port "+port
+ Else
+ info="Listen failed"
+ EndIf
+ Case "connect"
+ If arg
+ Local addr$=arg
+ Local port=12345
+ Local i=arg.find(":")
+ If i<>-1
+ addr=arg[..i]
+ port=Int(arg[i+1..])
+ EndIf
+ If GNetConnect( host,addr,port )
+ info="Connected to "+addr+":"+port
+ Else
+ info="Failed to connect to "+addr+":"+port
+ EndIf
+ Else
+ info="Expecting arg"
+ EndIf
+ Default
+ info="Unrecognized command '"+cmd+"'"
+ End Select
+ Else
+ If me SetGNetString me,1,chat
+ EndIf
+ chat=""
+ Default
+ If c>31 And c<127 chat:+Chr(c)
+ End Select
+
+ GNetSync host
+
+ Cls
+
+ Local y,h=GraphicsHeight()
+
+ For Local obj:TGNetObject=EachIn GNetObjects( host,GNET_ALL )
+ If obj.state()=GNET_CLOSED Continue
+ If obj=me
+ SetColor 255,255,255
+ Else
+ SetColor 0,128,255
+ EndIf
+ DrawText GetGNetString( obj,0 )+":"+GetGNetString( obj,1 ),0,y
+ y:+16
+ Next
+
+ SetColor 255,255,0
+ DrawText info,0,h-32
+
+ SetColor 0,255,0
+ DrawText ">"+chat,0,h-16
+ DrawRect TextWidth(">"+chat),h-16,8,16
+ DrawText "/create nick /listen /connect host /quit /nick newnick",0,h-48
+
+ Flip
+
+Forever
--- /dev/null
+
+Strict
+
+?Win32
+Framework BRL.D3D7Max2D
+?MacOS
+Framework BRL.GLMax2D
+?
+Import BRL.GNet
+Import BRL.BASIC
+Import BRL.PNGLoader
+
+Const GAMEPORT=12345
+
+Const SLOT_TYPE=0
+Const SLOT_NAME=1
+Const SLOT_CHAT=2
+Const SlOT_SCORE=3
+Const SLOT_X=4
+Const SLOT_Y=5
+Const SLOT_VX=6
+Const SLOT_VY=7
+Const SLOT_ROT=8
+Const SLOT_TIMEOUT=9
+Const SLOT_HIT=10
+
+Local GWIDTH=640
+Local GHEIGHT=480
+Local GDEPTH=0
+Local GHERTZ=30
+
+Graphics GWIDTH,GHEIGHT,GDEPTH,GHERTZ
+
+AutoMidHandle True
+Local playerImage:TImage=LoadImage( "ship.png" )
+Local bulletImage:TImage=LoadImage( "bullet1.png" )
+Local warpImage:TImage=LoadImage( "sparkle.png" )
+
+Local host:TGNetHost=CreateGNetHost()
+
+SeedRnd MilliSecs()
+
+Local playerName$="Player"
+Local playerChat$=""
+Local playerX#=Rnd(GWIDTH-64)+32
+Local playerY#=Rnd(GHEIGHT-64)+32
+Local playerVx#=0
+Local playerVy#=0
+Local playerRot#=0
+Local playerScore=0
+Local playerHit#=0
+Local playerShot=0
+
+'create local player
+Local localPlayer:TGNetObject=CreateGNetObject( host )
+
+SetGNetString localPlayer,SLOT_TYPE,"player"
+SetGNetString localPlayer,SLOT_NAME,playerName
+SetGNetString localPlayer,SLOT_CHAT,"Ready"
+SetGNetFloat localPlayer,SLOT_X,playerX
+SetGNetFloat localPlayer,SLOT_Y,playerY
+SetGNetFloat localPlayer,SLOT_ROT,playerRot
+SetGNetFloat localPlayer,SLOT_HIT,playerHit
+SetGNetInt localPlayer,SLOT_SCORE,playerScore
+
+While Not KeyHit( KEY_ESCAPE )
+
+ Local c=GetChar()
+ Select c
+ Case 8
+ If playerChat playerChat=playerChat[..playerChat.length-1]
+ Case 13
+ If playerChat
+ If playerChat[..1]="/"
+ Local cmd$=playerChat[1..]
+ Local i=cmd.Find(" "),arg$
+ If i<>-1
+ arg=cmd[i+1..]
+ cmd=cmd[..i]
+ EndIf
+ Select cmd.ToLower()
+ Case "nick"
+ If arg
+ playerName=arg
+ SetGNetString localPlayer,SLOT_NAME,playerName
+ EndIf
+ Case "listen"
+ If Not GNetListen( host,GAMEPORT ) Notify "Listen failed"
+ Case "connect"
+ If Not arg arg="localhost"
+ If Not GNetConnect( host,arg,GAMEPORT ) Notify "Connect failed"
+ End Select
+ Else
+ SetGNetString localPlayer,SLOT_CHAT,playerChat
+ EndIf
+ playerChat=""
+ EndIf
+ Default
+ If c>31 And c<127 playerChat:+Chr(c)
+ End Select
+
+ If KeyDown( KEY_LEFT )
+ playerRot:-5
+ If playerRot<-180 playerRot:+360
+ SetGNetFloat localPlayer,SLOT_ROT,playerRot
+ Else If KeyDown( KEY_RIGHT )
+ playerRot:+5
+ If playerRot>=180 playerRot:-360
+ SetGNetFloat localPlayer,SLOT_ROT,playerRot
+ EndIf
+
+ If KeyDown( KEY_UP )
+ playerVx:+Cos(playerRot)*.15
+ playerVy:+Sin(playerRot)*.15
+ Else
+ playerVx:*.99
+ If Abs(playerVx)<.1 playerVx=0
+ playerVy:*.99
+ If Abs(playerVy)<.1 playerVy=0
+ EndIf
+
+ If playerVx
+ playerX:+playerVx
+ If playerX<-8 playerX:+GWIDTH+16 Else If playerX>=GWIDTH+8 playerX:-GWIDTH+16
+ SetGNetFloat localPlayer,SLOT_X,playerX
+ EndIf
+
+ If playerVy
+ playerY:+playerVy
+ If playerY<-8 playerY:+GHEIGHT+16 Else If playerY>=GHEIGHT+8 playerY:-GHEIGHT+16
+ SetGNetFloat localPlayer,SLOT_Y,playerY
+ EndIf
+
+ If playerShot playerShot:-1
+
+ If KeyHit( KEY_LALT ) And Not playerShot
+ Local obj:TGnetObject=CreateGNetObject( host )
+ SetGNetString obj,SLOT_TYPE,"bullet"
+ SetGNetFloat obj,SLOT_X,playerX
+ SetGNetFloat obj,SLOT_Y,playerY
+ SetGNetFloat obj,SLOT_VX,playerVx+Cos(playerRot)*10
+ SetGNetFloat obj,SLOT_VY,playerVy+Sin(playerRot)*10
+ SetGNetInt obj,SLOT_TIMEOUT,60
+ playerShot=5
+ EndIf
+
+ 'update bullets
+ For Local obj:TGNetObject=EachIn GNetObjects( host )
+
+ If obj.State()=GNET_CLOSED Continue
+
+ Local typ$=GetGNetString( obj,SLOT_TYPE )
+ If typ<>"bullet" Continue
+
+ Local x#=GetGNetFloat( obj,SLOT_X )
+ Local y#=GetGNetFloat( obj,SLOT_Y )
+
+ If GNetObjectRemote( obj )
+ 'remote bullet? Check for collision...
+ Local dx#=x-playerX,dy#=y-playerY
+ If dx*dx+dy*dy<256'144
+ Local msg:TGNetObject=CreateGNetMessage( host )
+ If playerHit
+ SetGNetString msg,SLOT_TYPE,"gotme"
+ Else
+ SetGNetString msg,SLOT_TYPE,"hurtme"
+ playerHit=1
+ EndIf
+ SendGNetMessage msg,obj
+ EndIf
+ Else
+ 'local bullet? Update...
+ Local t=GetGNetInt( obj,SLOT_TIMEOUT )
+
+ If Not t
+ CloseGNetObject obj
+ Continue
+ EndIf
+
+ Local vx#=GetGNetFloat( obj,SLOT_VX )
+ Local vy#=GetGNetFloat( obj,SLOT_VY )
+
+ Local dx#=x-GWIDTH/2
+ Local dy#=y-GHEIGHT/2
+ Local rot#=ATan2(dy,dx)
+ Local accel#=1/(dx*dx+dy*dy)*2000
+ vx:-Cos(rot)*accel
+ vy:-Sin(rot)*accel
+ x:+vx
+ y:+vy
+
+ SetGNetFloat obj,SLOT_X,x
+ SetGNetFloat obj,SLOT_Y,y
+ SetGNetFloat obj,SLOT_VX,vx
+ SetGNetFloat obj,SLOT_VY,vy
+ SetGNetInt obj,SLOT_TIMEOUT,t-1
+ EndIf
+ Next
+
+ If playerHit
+ playerHit:-.05
+ If playerHit<0 playerHit=0
+ SetGNetFloat localPlayer,SLOT_HIT,playerHit
+ EndIf
+
+ GNetSync host
+
+ For Local msg:TGNetObject=EachIn GNetMessages( host )
+ Local typ$=GetGNetString( msg,SLOT_TYPE )
+ Select typ
+ Case "gotme","hurtme"
+ Local obj:TGNetObject=GNetMessageObject(msg)
+ If obj.State()<>GNET_CLOSED
+ If typ="hurtme"
+ playerScore:+1
+ SetGNetInt localPlayer,SLOT_SCORE,playerScore
+ EndIf
+ CloseGNetObject obj
+ EndIf
+ End Select
+ Next
+
+ Cls
+
+ Local ty
+ For Local obj:TGNetObject=EachIn GNetObjects( host )
+
+ If obj.State()=GNET_CLOSED Continue
+
+ Local typ$=GetGNetString( obj,SLOT_TYPE )
+ Local x#=GetGNetFloat( obj,SLOT_X )
+ Local y#=GetGNetFloat( obj,SLOT_Y )
+ Select typ
+ Case "bullet"
+ SetBlend LIGHTBLEND
+ SetColor 255,255,255
+ DrawImage bulletImage,x,y
+ SetBlend MASKBLEND
+ Case "player"
+ Local rot#=GetGNetFloat( obj,SLOT_ROT )
+ Local name$=GetGNetString( obj,SLOT_NAME )
+ Local chat$=GetGNetString( obj,SLOT_CHAT )
+ Local score=GetGNetInt( obj,SLOT_SCORE )
+ Local hit#=GetGNetFloat( obj,SLOT_HIT )
+ SetRotation rot
+ SetColor 255,255,255
+ DrawImage playerImage,x,y
+ If hit
+ SetAlpha hit
+ SetBlend LIGHTBLEND
+ DrawImage playerImage,x,y
+ SetBlend MASKBLEND
+ SetAlpha 1
+ SetColor 255,255,255
+ EndIf
+ SetRotation 0
+ DrawText name+":"+score,x,y+16
+ If obj=localPlayer SetColor 255,255,255 Else SetColor 0,128,255
+ DrawText name+":"+chat,0,ty
+ ty:+16
+ End Select
+ Next
+
+ If playerChat
+ SetColor 255,255,0
+ DrawText ">"+playerChat,0,GHEIGHT-16
+ SetColor 0,255,0
+ DrawRect TextWidth(">"+playerChat),GHEIGHT-16,8,16
+ EndIf
+
+ SetColor 255,255,255
+ Local txt$="MemAllocd:"+GCMemAlloced()
+ DrawText txt,GWIDTH-TextWidth(txt),0
+
+ SetBlend LIGHTBLEND
+ SetRotation Rnd(360)
+ SetScale Rnd(2,2.125),Rnd(2,2.125)
+ DrawImage warpImage,GWIDTH/2,GHEIGHT/2
+ SetScale 1,1
+ SetRotation 0
+ SetBlend MASKBLEND
+
+ Flip
+
+Wend
+
+CloseGNetHost host
--- /dev/null
+
+Strict
+
+Import MaxGui.Drivers
+
+Repeat
+
+ Local w,h,d,r
+
+ If Not RequestGraphicsMode( w,h,d,r ) End
+
+ Graphics w,h,d,r
+
+ DrawText "Graphics Mode:"+w+","+h+","+d+" "+r+"Hz",0,0
+ DrawText "Hit any key",0,16
+
+ Flip
+
+ WaitKey
+
+ EndGraphics
+
+Forever
+
+Function ListModes( list:TGadget )
+ ClearGadgetItems list
+ For Local t:TGraphicsMode=EachIn GraphicsModes()
+ AddGadgetItem list,t.ToString()
+ Next
+ SelectGadgetItem list,0
+End Function
+
+Function RequestGraphicsMode( width Var,height Var,depth Var,hertz Var )
+
+ Local w=ClientWidth( Desktop() ),h=ClientHeight( Desktop() )
+
+ Local window:TGadget=CreateWindow( "Select graphics Mode",w/2-160,h/2-160,320,320,Null,WINDOW_TITLEBAR )
+
+ Local panel:TGadget=CreatePanel( 0,0,ClientWidth(window),ClientHeight(window),window )
+
+ w=ClientWidth(panel) ; h=ClientHeight(panel)
+
+ Local okay:TGadget=CreateButton( "Okay",w-104,h-32,96,24,panel,BUTTON_OK )
+
+ CreateLabel "Graphics Mode:",8,16,w-16,16,panel
+ Local list1:TGadget=CreateListBox( 8,32,w-16,h-128,panel )
+
+ CreateLabel "Graphics Driver:",8,h-80,w-16,16,panel
+ Local combo1:TGadget=CreateComboBox( 8,h-64,w-16,24,panel )
+ DisableGadget combo1
+ AddGadgetItem combo1,"OpenGL"
+ SelectGadgetItem combo1,0
+ SetGraphicsDriver GLMax2DDriver()
+?Win32
+ EnableGadget combo1
+ AddGadgetItem combo1,"Direct3D7"
+ SelectGadgetItem combo1,1
+ SetGraphicsDriver D3D7Max2DDriver()
+?
+ Local cancel:TGadget=CreateButton( "Cancel",8,h-32,96,24,panel,BUTTON_CANCEL )
+
+ ListModes list1
+ ActivateGadget list1
+
+ Local ret=False
+
+ While WaitEvent()<>EVENT_WINDOWCLOSE
+ Select EventID()
+ Case EVENT_GADGETACTION
+ Select EventSource()
+ Case okay
+ Local t:TGraphicsMode=GraphicsModes()[ SelectedGadgetItem( list1 ) ]
+ width=t.width
+ height=t.height
+ depth=t.depth
+ hertz=t.hertz
+ ret=True
+ Exit
+ Case cancel
+ Exit
+ Case combo1
+ Select SelectedGadgetItem( combo1 )
+ Case 0
+ SetGraphicsDriver GLMax2DDriver()
+?Win32
+ Case 1
+ SetGraphicsDriver D3D7Max2DDriver()
+?
+ End Select
+ ListModes list1
+ End Select
+ Case EVENT_WINDOWCLOSE
+ Exit
+ End Select
+ Wend
+
+ FreeGadget window
+
+ Return ret
+
+End Function
+
--- /dev/null
+Import MaxGui.Drivers
+
+AppTitle = "BigSearch"
+
+Const Views = 1'3 ' Update this if you add more searches in DefData lines below!
+
+Global HTML:TGadget [Views]
+
+Global HTMLTitle$ [Views]
+Global HTMLURL$ [Views]
+
+gadheight = 24
+
+For site = 0 To Views - 1
+ ReadData HTMLTitle (site)
+ ReadData HTMLURL (site)
+Next
+
+' Some random examples...
+
+'DefData "Google", "http://www.google.com/search?q="
+'DefData "Yahoo", "http://search.yahoo.com/search?p="
+DefData "Merriam-Webster Dictionary", "http://www.m-w.com/cgi-bin/dictionary?"
+
+Load = ReadFile ("window.dat")
+If Load
+ x = Int (ReadLine (Load))
+ y = Int (ReadLine (Load))
+ width = Int (ReadLine (Load))
+ height = Int (ReadLine (Load))
+EndIf
+
+If x < 0 Then x = 0
+If x > GadgetWidth (Desktop ()) x = 0
+
+If y < 0 Then x = 0
+If y > GadgetHeight (Desktop ()) x = 0
+
+If width <= 0 width = 640
+If height <= 0 height = 480
+
+window:TGadget = CreateWindow ("BigSearch", x, y, width, height)
+
+htmlheight = (ClientHeight (window) - gadheight) / Views
+
+If window
+
+ file:TGadget = CreateMenu ("&File", 0, WindowMenu (window))
+ xit:TGadget = CreateMenu ("E&xit", 1, file)
+
+ help:TGadget = CreateMenu ("&Help", 2, WindowMenu (window))
+ about:TGadget = CreateMenu ("&About...", 3, help)
+
+ UpdateWindowMenu window
+
+ search:TGadget = CreateTextField (4, 4, ClientWidth (window) - 152, gadheight, window)
+
+ go:TGadget = CreateButton ("Search!", ClientWidth (window) - 144, 4, 140, gadheight, window, BUTTON_OK)
+
+ SetGadgetLayout search, 1, 1, 1, 0
+ SetGadgetLayout go, 0, 1, 1, 0
+
+ tab:TGadget = CreateTabber (0, gadheight+4, ClientWidth (window), ClientHeight (window) - gadheight - 4, window)
+ panel:TGadget = CreatePanel (0, 0, ClientWidth (tab), ClientHeight (tab), tab)
+
+ SetGadgetLayout tab, 1, 1, 1, 1
+ SetGadgetLayout panel, 1, 1, 1, 1
+
+ For loop = 0 To Views - 1
+ HTML (loop) = CreateHTMLView (0, 0, ClientWidth (panel), ClientHeight (panel), panel)
+ SetGadgetLayout HTML (loop), 1, 1, 1, 1
+ HideGadget HTML (loop)
+ AddGadgetItem (tab, HTMLTitle (loop))
+ Next
+
+ ShowGadget HTML (0)
+ SetStatusText window, "Done"
+
+ urltimer:TTimer = CreateTimer (1)
+
+ ActivateGadget search
+
+ Repeat
+
+ Select WaitEvent ()
+' Print currentevent.toString()
+' Select EventID()
+
+ Case EVENT_GADGETACTION
+ If (EventSource () = go)' Or ((EventSource () = search) And (EventData () = KEY_ENTER))
+ For site = 0 To Views - 1
+ HtmlViewGo HTML (site), HTMLURL (site) + TextFieldText (search)
+ Next
+ EndIf
+
+ If EventSource () = tab
+ For loop = 0 To Views - 1
+ HideGadget HTML (loop)
+ Next
+ ShowGadget HTML (SelectedGadgetItem (tab))
+ ActivateGadget HTML (SelectedGadgetItem (tab))
+ EndIf
+
+ Case EVENT_WINDOWCLOSE
+ SaveWindow (window); End
+
+' ****** Not working!
+
+ Case EVENT_TIMERTICK
+' DebugLog HtmlViewStatus (HTML (SelectedGadgetItem (tab)))
+ If HtmlViewStatus (HTML (SelectedGadgetItem (tab)))
+ SetStatusText window, "Loading..."
+ Else
+ SetStatusText window, "Done"
+ EndIf
+
+' Case EVENT_GADGETDONE
+' DebugLog HtmlViewStatus (HTML (SelectedGadgetItem (tab)))
+
+ Case EVENT_MENUACTION
+ If EventData () = 1 Then SaveWindow (window); End
+ If EventData () = 3 Then Notify ("BigSearch: it searches [tm]")
+
+ End Select
+
+ Forever
+
+EndIf
+
+Function SaveWindow (window:TGadget)
+ If window
+ save = WriteFile ("window.dat")
+ If save
+ WriteLine save, GadgetX (window)
+ WriteLine save, GadgetY (window)
+ WriteLine save, GadgetWidth (window)
+ WriteLine save, GadgetHeight (window)
+ CloseFile save
+ EndIf
+ EndIf
+End Function
+
--- /dev/null
+
+'Simple GL cube demo
+'Written by Birdie
+
+Import MaxGui.Drivers
+
+Strict
+SetGraphicsDriver GLGraphicsDriver(),GRAPHICS_BACKBUFFER|GRAPHICS_DEPTHBUFFER
+
+Global ax#, ay#,tim#
+
+Local w:TGadget = CreateWindow("Easy GL Cube in a GUI window", 10, 10, 512, 512 )
+
+Local c:TGadget = CreateCanvas(0,0,w.ClientWidth(),w.ClientHeight(),w,0)
+c.setlayout 1,1,1,1
+CreateTimer( 60 )
+
+While True
+ WaitEvent()
+ Select EventID()
+ Case EVENT_WINDOWCLOSE
+ End
+ Case EVENT_TIMERTICK
+ RedrawGadget c
+
+ Case EVENT_GADGETPAINT
+ SetGraphics CanvasGraphics( c )
+ Local wid = c.ClientWidth()
+ Local hgt = c.ClientHeight()
+ Local asp# = Float(wid)/Float(hgt)
+
+ glViewport 0,0,wid,hgt
+ glMatrixMode GL_PROJECTION
+ glLoadIdentity
+ gluPerspective 45, asp, 1, 100
+ gltranslatef 0,0,-50+tim
+ tim=20*Cos(MilliSecs()/10.0)
+
+ glMatrixMode GL_MODELVIEW
+ glLoadIdentity
+
+ Local global_ambient#[]=[0.6#, 0.5#, 0.3#, 1.0#]
+ Local light0pos#[]= [0.0#, 5.0#, 10.0#, 1.0#]
+ Local light0ambient#[]= [0.5#, 0.5#, 0.5#, 1.0#]
+ Local light0diffuse#[]= [0.3#, 0.3#, 0.3#, 1.0#]
+ Local light0specular#[]=[0.8#, 0.8#, 0.8#, 1.0#]
+
+ Local lmodel_ambient#[]=[ 0.2#,0.2#,0.2#,1.0#]
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT,lmodel_ambient)
+
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient)
+ glLightfv(GL_LIGHT0, GL_POSITION, light0pos)
+ glLightfv(GL_LIGHT0, GL_AMBIENT, light0ambient)
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, light0diffuse)
+ glLightfv(GL_LIGHT0, GL_SPECULAR, light0specular)
+ glEnable(GL_LIGHTING)
+ glEnable(GL_LIGHT0)
+ glShadeModel(GL_SMOOTH)
+ glMateriali(GL_FRONT, GL_SHININESS, 128)
+
+
+ glClearColor 0,0,0.5,1
+ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+
+ glEnable(GL_DEPTH_TEST)
+
+ glRotatef ax,1,0,0
+ glRotatef ay,0,1,0
+ ax:+1
+ ay:+5
+ DrawSizeCube(7)
+
+ Flip
+
+ EndSelect
+Wend
+
+
+
+Function DrawSizeCube(size#)
+ size=-size
+ 'Front Face
+ glBegin(GL_TRIANGLE_STRIP)
+ glNormal3f( 0.0, 0.0, 1.0)
+ glVertex3f( size, size,-size)
+ glNormal3f( 0.0, 0.0, 1.0)
+ glVertex3f(-size, size,-size)
+ glNormal3f( 0.0, 0.0, 1.0)
+ glVertex3f( size,-size,-size)
+ glNormal3f( 0.0, 0.0, 1.0)
+ glVertex3f(-size,-size,-size)
+ glEnd
+ 'Back Face
+ glNormal3f( 0.0, 0.0, -1.0)
+ glBegin(GL_TRIANGLE_STRIP)
+ glVertex3f(-size, size, size)
+ glVertex3f( size, size, size)
+ glVertex3f(-size,-size, size)
+ glVertex3f( size,-size, size)
+ glEnd
+ 'Right Face
+ glNormal3f( 1.0, 0.0, 0.0)
+ glBegin(GL_TRIANGLE_STRIP)
+ glVertex3f(-size, size,-size)
+ glVertex3f(-size, size, size)
+ glVertex3f(-size,-size,-size)
+ glVertex3f(-size,-size, size)
+ glEnd
+ 'Left Face
+ glNormal3f( -1.0, 0.0, 0.0)
+ glBegin(GL_TRIANGLE_STRIP)
+ glVertex3f( size, size, size)
+ glVertex3f( size, size,-size)
+ glVertex3f( size,-size, size)
+ glVertex3f( size,-size,-size)
+ glEnd
+ 'Bottom Face
+ glNormal3f( 0.0, -1.0, 0.0)
+ glBegin(GL_TRIANGLE_STRIP)
+ glVertex3f( size, size,-size)
+ glVertex3f( size, size, size)
+ glVertex3f(-size, size,-size)
+ glVertex3f(-size, size, size)
+ glEnd
+ 'Top Face
+ glNormal3f( 0.0, 1.0, 0.0)
+ glBegin(GL_TRIANGLE_STRIP)
+ glVertex3f( size,-size,-size)
+ glVertex3f(-size,-size,-size)
+ glVertex3f( size,-size, size)
+ glVertex3f(-size,-size, size)
+ glEnd
+End Function
+
--- /dev/null
+Strict
+
+Import BRL.Stream
+Import BRL.EndianStream
+
+Type PNGHeader
+
+ Field signiture:String
+ Field chunksize:Int
+ Field chunkID:String
+ Field width:Int
+ Field height:Int
+ Global PNG_ID:String = Chr($89) + Chr($50) + Chr($4E) + Chr($47) + Chr($0D) + Chr($0A) + Chr($1A) + Chr($0A)
+
+ Function fromFile:PNGHeader( url:Object )
+ Local myStream:TStream = ReadStream( url )
+ Local temp:PNGHeader
+ If StreamSize (myStream) > 24
+ temp = New PNGHeader
+ Local eStream:TStream = BigEndianStream(myStream)
+ temp.signiture = ReadString (eStream , 8)
+ temp.chunksize = Readint (eStream)
+ temp.chunkID = ReadString (eStream , 4)
+ temp.width = Readint (eStream)
+ temp.height = Readint (eStream)
+ CloseStream eStream
+ EndIf
+ CloseStream myStream
+ Return temp
+ EndFunction
+
+ Function fromPtr:PNGHeader( Pointer:Byte Ptr )
+ Local temp:PNGHeader = New PNGHeader
+ temp.signiture = Chr(Pointer[0]) + Chr(Pointer[1]) + Chr(Pointer[2]) + Chr(Pointer[3]) + Chr(Pointer[4]) + Chr(Pointer[5]) + Chr(Pointer[6]) + Chr(Pointer[7])
+ temp.chunksize = Pointer[8] Shl 24 | Pointer[9] Shl 16 | Pointer[10] Shl 8 | Pointer[11]
+ temp.chunkID = Chr(Pointer[12]) + Chr(Pointer[13]) + Chr(Pointer[14]) + Chr(Pointer[15])
+ temp.width = Pointer[16] Shl 24 | Pointer[17] Shl 16 | Pointer[18] Shl 8 | Pointer[19]
+ temp.height = Pointer[20] Shl 24 | Pointer[21] Shl 16 | Pointer[22] Shl 8 | Pointer[23]
+ Return temp
+ EndFunction
+
+ Method isPNG:Int()
+ If signiture = PNG_ID
+ Return True
+ EndIf
+ Return False
+ EndMethod
+
+ Method toString:String()
+ Local temp:String = "isPng: "
+ If isPNG()
+ temp:+"True "
+ Else
+ temp:+"False "
+ EndIf
+ temp:+"Width: " + width + " Height: " + height
+
+ Return temp
+ EndMethod
+EndType
\ No newline at end of file
--- /dev/null
+Strict
+
+Import MaxGui.Drivers
+
+Import "PNGHeader.bmx"
+Incbin "bmxlogo.png"
+
+Type TAR
+ Field width:Int
+ Field height:Int
+
+ Function Create:TAR( width:Int , height:Int )
+ Local temp:TAR = New TAR
+ temp.width = width
+ temp.height = height
+ Return temp
+ EndFunction
+ Method ToString:String()
+ Return width + ":" + height
+ EndMethod
+EndType
+
+Type TLauncher
+ Field angle = 0
+ Field sync:TTimer
+ Field myWindow:TGadget
+ Field myCanvas:TGadget
+ Field myLogo:TImage
+ Field myLB:TGadget
+ Field resMap:TMap
+ Field btnLaunch:TGadget
+ Field btnAbort:TGadget
+ Field terminate = False
+ Field selected:TGraphicsMode
+ Field isDisposed = True
+ Field aspectRatios:TList
+ Field pngInfo:PNGHeader
+
+ Method initGUI()
+ myWindow = CreateWindow("Arise GUI Launcher - BETA", Desktop().width/2 - 100, Desktop().height/2 - 100, 200, 200, Null, WINDOW_TITLEBAR | WINDOW_CLIENTCOORDS | WINDOW_HIDDEN)
+ myCanvas:TGadget = CreateCanvas ( 0 , 0 , 200 , 87 , myWindow)
+ pngInfo:PNGHeader = PNGHeader.fromPtr(IncbinPtr( "bmxlogo.png" ))
+ If pngInfo.isPNG()
+ myLogo:TImage = LoadAnimImage( "incbin::bmxlogo.png",pngInfo.width,1,0,pngInfo.height )
+ EndIf
+ myLB:TGadget = CreateListBox(0,88,200,93,myWindow)
+ For Local i:String = EachIn resMap.Keys()
+ AddGadgetItem myLB , i
+ Next
+
+ 'SelectGadgetItem myLB , CountGadgetItems(myLB) -1
+ sync = CreateTimer(100)
+ btnLaunch = CreateButton ("LAUNCH!" , 0,181,100,20,myWindow)
+ btnAbort = CreateButton ("ABORT!" , 100,181,100,20,myWindow)
+ isDisposed = False
+ EndMethod
+
+ Function Create:TLauncher()
+ Local temp:TLauncher = New TLauncher
+ temp.addAspectRatio( 4 , 3 )
+ temp.populateModes
+ Return temp
+ End Function
+
+ Method populateModes()
+ If aspectRatios = Null
+ addAspectRatio( 4 , 3 )
+ EndIf
+ resMap = New TMap
+
+ For Local i:TGraphicsMode = EachIn GraphicsModes()
+ For Local j:TAR = EachIn aspectRatios
+ If i.width / j.width = i.height / j.height And i.depth > 8
+ Local res:String = i.width + " x " + i.height + " - " + i.depth + " bits @ " + i.hertz + "Hz"
+ resMap.insert res , i
+ Exit
+ EndIf
+ Next
+ Next
+ EndMethod
+
+ Method addAspectRatio( width:Int , height:Int )
+ If aspectRatios = Null
+ aspectRatios = New TList
+ EndIf
+ aspectRatios.addLast( TAR.Create( width , height ))
+ EndMethod
+
+ Method show()
+ If Not isDisposed
+ ShowGadget myWindow
+ EndIf
+ EndMethod
+
+ Method hide()
+ If Not isDisposed
+ HideGadget myWindow
+ EndIf
+ EndMethod
+
+ Method dispose()
+ FreeGadget myWindow
+ FreeGadget myCanvas
+ FreeGadget btnLaunch
+ FreeGadget btnAbort
+ FreeGadget myLB
+
+ myLogo = Null
+ resMap = Null
+ myWindow = Null
+ myCanvas = Null
+ btnLaunch = Null
+ btnAbort = Null
+ myLB = Null
+ isDisposed = True
+ EndMethod
+
+ Method getSelectedMode:TGraphicsMode()
+ Return selected
+ EndMethod
+
+ Method main()
+ If Not isDisposed
+ WaitEvent
+ If CurrentEvent.id = EVENT_TIMERTICK
+ angle = TimerTicks(sync) Mod 360
+ SetGraphics CanvasGraphics( myCanvas )
+ SetClsColor 255 , 255 , 255
+ Cls
+ For Local i:Int = 0 Until pngInfo.height
+ DrawImage myLogo,24 + Sin(angle+i*2) * 48,i,i
+ Next
+ Flip
+ ElseIf CurrentEvent.id = EVENT_APPTERMINATE Or CurrentEvent.id = EVENT_WINDOWCLOSE Or..
+ ( CurrentEvent.id = EVENT_GADGETACTION And EventSource() = btnAbort )
+ terminate = True
+ ElseIf ( CurrentEvent.id = EVENT_GADGETACTION And EventSource() = btnLaunch )
+ terminate = True
+ If SelectedGadgetItem(myLB)<>-1
+ selected = TGraphicsMode(resMap.ValueForKey(GadgetItemText(myLB,SelectedGadgetItem(myLB))))
+ EndIf
+ EndIf
+ EndIf
+ EndMethod
+EndType
--- /dev/null
+Strict
+
+Import "TLauncher.bmx"
+
+Local myL:TLauncher
+Local myMode:TGraphicsMode
+
+'myL = TLauncher.Create() <- The quick way, will create a standard requester with only a 4:3 aspect ratio.
+
+myL = New TLauncher
+
+myL.addAspectRatio( 4 , 3 )
+myL.addAspectRatio( 16 , 9 )
+myL.populateModes
+
+myL.initGUI()
+myL.show()
+While Not myL.terminate
+ myL.main
+EndWhile
+If myL.selected <> Null
+ Print myL.selected.toString()
+EndIf
+
--- /dev/null
+' file drag & drop example
+
+Import MaxGui.Drivers
+
+Strict
+
+Const MENU_EXIT=105
+Const MENU_ABOUT=109
+
+'
+' A window
+Local win:TGadget = CreateWindow("Drag & Drop!",100,100,400,400,Null,WINDOW_TITLEBAR|WINDOW_RESIZABLE|WINDOW_MENU|WINDOW_STATUS|WINDOW_CLIENTCOORDS|WINDOW_ACCEPTFILES)
+
+'
+' A simple menu
+Local filemenu:TGadget = CreateMenu("&File",0,WindowMenu(win))
+CreateMenu"E&xit",MENU_EXIT,filemenu
+
+Local helpmenu:TGadget = CreateMenu("&Help",0,WindowMenu(win))
+CreateMenu "&About",MENU_ABOUT,helpmenu
+
+UpdateWindowMenu win
+
+'
+' A canvas gadget to display the image
+Local can:TGadget = CreateCanvas(0,0,400,400,win,1)
+SetGadgetLayout can,1,1,1,1
+
+'
+' A few bits and pieces
+Local image:Timage
+Local file:String = "Drag an image file onto window"
+SetStatusText win,file
+
+
+'
+' Main loop
+While WaitEvent()
+ Select EventID()
+
+ Case EVENT_GADGETPAINT
+ Select EventSource()
+ Case can
+ '
+ ' Draw to the canvas
+ SetGraphics CanvasGraphics(can)
+
+ '
+ ' Make sure it has the correct dimensions
+ SetViewport 0,0,GadgetWidth(can),GadgetHeight(can)
+
+ '
+ ' Draw the checker background
+ SetBlend SOLIDBLEND
+ Local a:Int = 1
+ Local b:Int = 1
+ For Local x = 0 To GadgetWidth(can) Step 16
+ b = Not b
+ a = b
+ For Local y = 0 To GadgetHeight(can) Step 16
+ a = Not a
+ SetColor 160-a*20,160-a*20,160-a*20
+ DrawRect x,y,16,16
+ Next
+ Next
+
+ '
+ ' Draw the image
+ If image
+ SetBlend ALPHABLEND
+ SetColor 255,255,255
+
+ Local scale:Float = Min(1.0,Min(Float(ClientWidth(can))/ImageWidth(image),Float(ClientHeight(can))/ImageHeight(image)))
+
+ Local w = ImageWidth(image)*scale
+ Local h = ImageHeight(image)*scale
+ Local x = (ClientWidth(can)-w)/2
+ Local y = (ClientHeight(can)-h)/2
+
+ SetScale scale,scale
+ DrawImage image,x,y
+
+ SetScale 1,1
+ SetBlend SHADEBLEND
+ SetColor 170,170,170
+ DrawRect x-1,0,1,ClientHeight(can)
+ DrawRect x+w,0,1,ClientHeight(can)
+ DrawRect 0,y-1,ClientWidth(can),1
+ DrawRect 0,y+h,ClientWidth(can),1
+
+ SetStatusText win,file+" @ "+Int(scale*100)+"%"
+ EndIf
+
+ Flip
+ EndSelect
+
+ Case EVENT_WINDOWCLOSE
+ '
+ ' Quit
+ End
+
+ Case EVENT_WINDOWACCEPT
+ '
+ ' A file has been dragged and dropped on the window
+ file = EventExtra().tostring()
+
+ '
+ ' Try loading the file as an image
+ image = LoadImage(file)
+ If image = Null
+ file = "Invalid file format!"
+ Else
+ file = file+" ("+(FileSize(file)/1024)+"Kb) "+ImageWidth(image)+"x"+ImageHeight(image)
+ EndIf
+ SetStatusText win,file
+ RedrawGadget can
+
+ Case EVENT_MENUACTION
+ '
+ ' Menu stuff
+ Select EventData()
+ Case MENU_EXIT
+ End
+ Case MENU_ABOUT
+ Notify "File drag & drop example!~nBy Mikkel Fredborg"
+ End Select
+
+ Default
+ '
+ ' Uncomment this to show what other events occur
+ ' Print CurrentEvent.toString()
+
+ EndSelect
+
+Wend
--- /dev/null
+'
+' www.sublimegames.com
+' Silly picture tool by Stephen Greener (aka Shagwana)
+'
+'
+
+Strict
+Import MaxGUI.Drivers
+
+
+'_/ Consts \_______________________________________________________________________________________________________________
+
+
+Const iLEVEL_X_PIXEL_SIZE:Int = 32
+Const iLEVEL_Y_PIXEL_SIZE:Int = 32
+
+Const iLEVEL_X_SIZE:Int = 512 / iLEVEL_X_PIXEL_SIZE
+Const iLEVEL_Y_SIZE:Int = 512 / iLEVEL_Y_PIXEL_SIZE
+
+
+'_/ Includes \_______________________________________________________________________________________________________________
+
+
+
+'Image files
+Incbin "sourcepic.jpg" '512x512
+
+
+'_/ Types \_______________________________________________________________________________________________________________
+
+
+
+Type tLevelInfo
+
+ Field pImage:TImage[iLEVEL_X_SIZE,iLEVEL_Y_SIZE]
+ Field pMapping:TImage[iLEVEL_X_SIZE,iLEVEL_Y_SIZE]
+
+
+ Method Reset()
+ For Local iX:Int=0 To iLEVEL_X_SIZE-1
+ For Local iY:Int=0 To iLEVEL_Y_SIZE-1
+ pMapping[iX,iY]=pImage[iX,iY]
+ Next
+ Next
+ End Method
+
+ Method Load(sFilename:String)
+ 'Load the image
+ Local pPixmap:TPixmap=LoadPixmap(sFilename)
+ pPixmap:TPixmap=ConvertPixmap(pPixmap:TPixmap,PF_BGR888)
+ For Local iX:Int=0 To iLEVEL_X_SIZE-1
+ For Local iY:Int=0 To iLEVEL_Y_SIZE-1
+ Local tWinPixMap:TPixmap=PixmapWindow(pPixmap:TPixmap,iX*iLEVEL_X_PIXEL_SIZE,iY*iLEVEL_Y_PIXEL_SIZE,iLEVEL_X_PIXEL_SIZE,iLEVEL_Y_PIXEL_SIZE)
+ pImage:TImage[iX,iY]=LoadImage(tWinPixMap:TPixmap,MASKEDIMAGE) 'Chop up the image into tiles
+ pMapping[iX,iY]=pImage:TImage[iX,iY]
+ Next
+ Next
+ End Method
+
+ Method Draw()
+ SetBlend SOLIDBLEND
+ For Local iX:Int=0 To iLEVEL_X_SIZE-1
+ For Local iY:Int=0 To iLEVEL_Y_SIZE-1
+ DrawImage pMapping[iX,iY],(iX*32),(iY*32)
+ Next
+ Next
+ Flip
+ End Method
+
+ End Type
+
+
+
+
+'_/ Setup \_______________________________________________________________________________________________________________
+
+Local pMainWindow:TGadget = CreateWindow("Picture sliding thing v1.0",(GadgetWidth(Desktop())-600)/2,(GadgetHeight(Desktop())-600)/2,600,600,Null,WINDOW_TITLEBAR | WINDOW_CLIENTCOORDS)
+Local pCanvas:TGadget = CreateCanvas(44,44,512,512,pMainWindow:TGadget)
+
+Local pLeftButton:TGadget[16]
+Local pRightButton:TGadget[16]
+Local pTopButton:TGadget[16]
+Local pBottomButton:TGadget[16]
+
+For Local iLoop:Int=0 To 15
+ pRightButton[iLoop]=CreateButton("+",512+44,44+(iLoop*32),32,32,pMainWindow,BUTTON_PUSH)
+ pLeftButton[iLoop]=CreateButton("+",44-32,44+(iLoop*32),32,32,pMainWindow,BUTTON_PUSH)
+ pTopButton[iLoop]=CreateButton("+",44+(iLoop*32),44-32,32,32,pMainWindow,BUTTON_PUSH)
+ pBottomButton[iLoop]=CreateButton("+",44+(iLoop*32),512+44,32,32,pMainWindow,BUTTON_PUSH)
+ Next
+
+
+Global tGameLevel:tLevelInfo = New tLevelInfo
+tGameLevel.Load("incbin::sourcepic.jpg") 'Load it from the included file
+
+Local bQuitProgram=False
+
+'_/ Main program \_______________________________________________________________________________________________________________
+
+
+
+Repeat
+
+ gccollect
+
+ WaitEvent()
+
+ Local pGadgetResposible:TGadget=Null
+ Local iDir:Int=-1
+ Local iXLine:Int=-1
+ Local iYLine:Int=-1
+
+ For Local iLoop:Int=0 To 15
+ If EventSource()=pLeftButton[iLoop]
+ pGadgetResposible=pLeftButton[iLoop]
+ iXLine=-1
+ iYLine=iLoop
+ iDir=1
+ EndIf
+ If EventSource()=pRightButton[iLoop]
+ pGadgetResposible=pRightButton[iLoop]
+ iXLine=-1
+ iYLine=iLoop
+ iDir=-1
+ EndIf
+ If EventSource()=pTopButton[iLoop]
+ pGadgetResposible=pTopButton[iLoop]
+ iYLine=-1
+ iXLine=iLoop
+ iDir=1
+ EndIf
+ If EventSource()=pBottomButton[iLoop]
+ pGadgetResposible=pBottomButton[iLoop]
+ iYLine=-1
+ iXLine=iLoop
+ iDir=-1
+ EndIf
+ Next
+
+
+
+ If pGadgetResposible<>Null
+ 'Was a slider gadget ? ...
+
+ If iYLine<>-1
+ 'X line to scroll
+ If iDir=1
+ 'Scroll map one way
+ Local pTemp:TImage=tGameLevel.pMapping[0,iYLine]
+ For Local iP:Int=0 To iLEVEL_Y_SIZE-2
+ tGameLevel.pMapping[iP,iYLine]=tGameLevel.pMapping[iP+1,iYLine]
+ Next
+ tGameLevel.pMapping[iLEVEL_X_SIZE-1,iYLine]=pTemp:TImage
+ Else
+ 'Scroll map the other way
+ Local pTemp:TImage=tGameLevel.pMapping[iLEVEL_X_SIZE-1,iYLine]
+ For Local iP:Int=iLEVEL_Y_SIZE-2 To 0 Step -1
+ tGameLevel.pMapping[iP+1,iYLine]=tGameLevel.pMapping[iP,iYLine]
+ Next
+ tGameLevel.pMapping[0,iYLine]=pTemp:TImage
+ EndIf
+
+ Else
+ 'Presume that iXLine<>-1
+ If iDir=1
+ 'Scroll map one way
+ Local pTemp:TImage=tGameLevel.pMapping[iXLine,0]
+ For Local iP:Int=0 To iLEVEL_X_SIZE-2
+ tGameLevel.pMapping[iXLine,iP]=tGameLevel.pMapping[iXLine,iP+1]
+ Next
+ tGameLevel.pMapping[iXLine,iLEVEL_X_SIZE-1]=pTemp:TImage
+ Else
+ 'Scroll map the other way
+ Local pTemp:TImage=tGameLevel.pMapping[iXLine,iLEVEL_Y_SIZE-1]
+ For Local iP:Int=iLEVEL_X_SIZE-2 To 0 Step -1
+ tGameLevel.pMapping[iXLine,iP+1]=tGameLevel.pMapping[iXLine,iP]
+ Next
+ tGameLevel.pMapping[iXLine,0]=pTemp:TImage
+ EndIf
+
+ EndIf
+
+ 'Redraw the display cause we have moved it
+ SetGraphics CanvasGraphics(pCanvas)
+ Cls
+ tGameLevel.Draw()
+
+
+
+ Else
+
+ 'Not one of the main game control gadgets
+ Select EventID()
+
+ Case EVENT_GADGETPAINT
+ 'Redraw the main display
+ SetGraphics CanvasGraphics(pCanvas)
+ Cls
+ tGameLevel.Draw()
+
+ Case EVENT_WINDOWCLOSE
+ 'Quit the program
+ bQuitProgram=True
+
+ End Select
+ EndIf
+
+
+ Until bQuitProgram=True
+
+End
\ No newline at end of file
--- /dev/null
+'===============================================================================
+' Little Shooty Test Thing
+' Code & Stuff by Richard Olpin (rik@olpin.net)
+'===============================================================================
+' main
+'===============================================================================
+' A little shooty thing! Needs a lot of work as I only hacked it together in
+' about three hours one night and it's a right mix of both procedural and OO
+' but it may inspire someone to do something..
+'===============================================================================
+
+Include "globals.bmx"
+Include "player.bmx"
+Include "playershots.bmx"
+Include "enemies.bmx"
+Include "background.bmx"
+Include "particles.bmx"
+Include "sound.bmx"
+Include "init.bmx"
+Include "titles.bmx"
+Include "gfont.bmx"
+
+' -----------------------------------------------------------------------------
+
+Global tm, ms, old
+
+AppTitle$="Choose Screen Mode"
+Graphics 320,240,0,60
+DrawText "(W)indowed or (F)ullscreen?",0,120 ; Flip
+
+Repeat
+Until (KeyDown(KEY_F) Or KeyDown(KEY_W)) Or JoyDown(2)
+
+AppTitle$="Little Shooty Test by RiK (ESC To quit)"
+
+If KeyDown(KEY_W) Or JoyDown(2) Then
+ Graphics WIDTH, HEIGHT,0,60
+Else
+ Graphics WIDTH, HEIGHT,32,60
+EndIf
+
+AutoMidHandle True
+SeedRnd(MilliSecs())
+
+' -----------------------------------------------------------------------------
+' init
+' -----------------------------------------------------------------------------
+
+init()
+Gfont.Init()
+HideMouse()
+ShowTitlePage()
+
+' -----------------------------------------------------------------------------
+' Main game loop
+' -----------------------------------------------------------------------------
+
+' Just stick an OGG called "music.ogg" in the sounds dir
+' PlaySound(music,musicchannel)
+
+'SetChannelVolume SoundChannel,1
+SetChannelVolume MusicChannel,0.75
+
+While Not KeyHit(KEY_ESCAPE)
+ While JoyDown(5)
+ Wend
+
+ Cls
+ ResetCollisions
+
+ ' -------------------------------------------------------------------------
+ ' Spawn a random spikey thing, joypad/keyboard check are for testing.
+ ' -------------------------------------------------------------------------
+
+ If Spawntimer<=0 Or JoyDown(3) Or KeyDown(key_e) Then
+ Local nx=Rand(0,800)
+ If Abs(player.x-nx)<200 Then nx=nx+400 ' dont spawn on top of player!
+ Local ny=Rand(0,600)
+ TEnemy.CreateEnemy (nx,ny)
+ Spawntimer=60
+ EndIf
+
+ spawntimer:-1
+
+ ' -------------------------------------------------------------------------
+ ' Update Everything
+ ' -------------------------------------------------------------------------
+
+ UpdateEntities()
+
+ ' -------------------------------------------------------------------------
+ ' HUD/Score/whatever
+ ' -------------------------------------------------------------------------
+
+ SetColor 255,255,255; SetRotation 0; SetAlpha 1
+
+ DrawText player.invincible,0,0 ' just for debug purposes
+ DrawText "Little Shooty Test by RiK (ESC to quit)",20,0
+
+ GFont.DrawString 800,16,score,-1,0
+
+ If player.state=2 Then GFont.Drawstring 400,300,"YOU SUCK!",1,1
+
+ Flip
+Wend
+
+ShowMouse()
+
+' -----------------------------------------------------------------------------
+' Update/draw everything
+'
+' I've only stuck in out here as I like to keep the main loop small for clarity
+'
+' -----------------------------------------------------------------------------
+
+Function UpdateEntities( )
+
+ MoveBG()
+ RenderBackGround(0,player.y)
+
+ SetScale 1,1
+ For Local b:TBullet =EachIn bullets
+ b.Update
+ Next
+
+ SetScale 1,1
+ For Local e:TEnemy =EachIn enemies
+ e.Update
+ Next
+
+ For Local p:TParticle =EachIn particles
+ p.Update
+ Next
+
+ player.update()
+
+ ' Reset everything
+ SetBlend ALPHABLEND
+ SetScale 1,1
+ SetAlpha 1
+ SetRotation 0
+
+End Function
+
+' -----------------------------------------------------------------------------
+' End
+' -----------------------------------------------------------------------------
+
--- /dev/null
+'===============================================================================
+' Little Shooty Test Thing
+' Code & Stuff by Richard Olpin (rik@olpin.net)
+'===============================================================================
+' Background.bmx
+'
+' Module for tilemapped background layer
+'
+'===============================================================================
+
+Global bgtileset, bgwidth=256
+Global skyfade, cloud
+Global direction=1
+Global map:Int[6,1000,2]
+Global worldpos
+
+'==============================================================================
+' Init BG
+'==============================================================================
+
+Function CreateBG()
+ bgtileset=LoadAnimImage:TImage("gfx/hill_1.png",256,256,0,9, MASKEDIMAGE )
+ skyfade=LoadAnimImage:TImage("gfx/sky32.png",32,32,0,48 )
+ cloud = LoadImage:TImage("gfx/bigcloud.png")
+ RandomMap()
+EndFunction
+
+'==============================================================================
+' Note:
+'==============================================================================
+
+Function RenderBackground( worldx, worldy)
+
+ Local sframe,soffset,lx
+ Local layerpos, mappos, offset
+
+ '-------------------------------
+ ' Sky
+ '-------------------------------
+
+ SetBlend ALPHABLEND
+ SetAlpha 1
+ SetRotation 0
+ SetScale 1,1
+
+ If y_offset<0 Then y_offset=0
+ If y_offset>600 Then y_offset=600
+
+ sframe = Int(y_offset/32) ; If sframe>24 Then sframe=24
+ soffset = y_offset Mod 32
+
+ For x = 0 To WIDTH Step 32
+ For y=0 To 18
+ DrawImage skyfade,x,(y*32)-soffset,sframe+y
+ Next
+ Next
+
+ '-------------------------------
+ ' Clouds
+ '-------------------------------
+
+ SetBlend ALPHABLEND
+ SetAlpha 0.25
+ DrawImage cloud,sky_pos, 100
+
+ '-------------------------------
+ ' Hills
+ '-------------------------------
+ SetAlpha 1
+
+ ' layerpos = scaled from worldpos for parallax
+ ' map_pos = offset into Map
+ ' offset = shift for
+
+ layerpos = worldpos/8
+ mappos = Int(layerpos/bgwidth)
+ offset = layerpos Mod bgwidth
+
+ For x = 0 To 5
+ DrawImage bgtileset,(x*bgwidth)-offset,380-(y_offset/12), map[5,mappos+x,0]
+ Next
+
+ layerpos = worldpos/6
+ mappos = Int(layerpos/bgwidth)
+ offset = layerpos Mod bgwidth
+
+ For x = 0 To 5
+ DrawImage bgtileset,(x*bgwidth)-offset,430-(y_offset/11), map[4,mappos+x,0]
+ Next
+
+ layerpos = worldpos/4
+ mappos = Int(layerpos/bgwidth)
+ offset = layerpos Mod bgwidth
+
+ For x = 0 To 5
+ DrawImage bgtileset,(x*bgwidth)-offset,480-(y_offset/10), map[3,mappos+x,0]
+ Next
+
+ layerpos = worldpos/2
+ mappos = Int(layerpos/bgwidth)
+ offset = layerpos Mod bgwidth
+
+ For x = 0 To 5
+ DrawImage bgtileset,(x*bgwidth)-offset,530-(y_offset/9), map[2,mappos+x,0]
+ Next
+
+ '----------------------------------
+ ' Layer 1 Hills with collisions
+ '----------------------------------
+
+ SetAlpha 1
+
+ mappos = Int(worldpos/bgwidth)
+ offset = worldpos Mod bgwidth
+
+ For x = 0 To 5
+ DrawImage bgtileset,(x*bgwidth)-offset,480, map[1,mappos+x,0]
+' CollideImage bgtileset,(x*bgwidth)-offset,480, map[1,mappos+x,0],0,2
+ Next
+
+End Function
+
+
+'===============================================================================
+' Description:
+'
+' Quick hack to test the concept. Need to sort this out fully.
+'
+'===============================================================================
+
+Function MoveBg()
+
+ sky_pos = sky_pos - (scroll_speed/32)
+ If sky_pos<-256 Then sky_pos=1000
+
+ worldpos=worldpos + scroll_speed
+
+End Function
+
+'==============================================================================
+' Random Map
+'
+' Test function only to randomise map data
+'==============================================================================
+
+Function RandomMap()
+ Local h=0, l=0,pos=0
+
+ For h=0 To 5
+
+ pos=0
+
+ While pos<980
+
+ '--------------------------
+ ' Hill
+ map(h,pos,0)=1 ' Hill ramp up
+ pos:+1
+ l=Rand(5)
+ If h=1 Then l=Rand(10)
+ If h=5 Then l=Rand(20)
+
+ While l>0
+ map(h,pos,0)=Rand(2,7)
+ pos:+1
+ l:-1
+ Wend
+
+ map(h,pos,0)=8 ' Hill ramp down
+ pos:+1
+
+ '--------------------------
+ ' Gap
+
+ l=Rand(3)
+ While l>0
+ If pos<1000 map(h,pos,0)=0
+ pos:+1
+ l:-1
+ Wend
+
+
+ Wend
+
+ Next
+
+End Function
+
+
+'===============================================================================
+' END OF FILE
+'===============================================================================
--- /dev/null
+'===============================================================================
+' Little Shooty Test Thing
+' Code & Stuff by Richard Olpin (rik@olpin.net)
+'===============================================================================
+' enemies.bmx
+'===============================================================================
+
+Global enemies:TList=New TList
+Global num_enemies=0
+Global alien_img
+
+Type TEnemy
+ Field link:TLink
+ Field x#,y#
+ Field xs#,ys#
+ Field ang#, rs#;
+ Field alpha#,img
+ Field expl
+ Field frame
+
+ '-------------------------------------------------------------------
+ ' Move / Draw
+ '-------------------------------------------------------------------
+
+ Method Update()
+ x:+xs ; If x>WIDTH Or x<0 Then xs:*-1
+ y:+ys ; If y>HEIGHT Or y<0 Then ys:*-1
+ frame=(frame+1)&63
+
+ 'SetRotation ang ; ang:+rs;
+ SetAlpha 1
+ SetBlend ALPHABLEND
+ DrawImage img,x,y, frame/4
+ CollideImage img,x,y,frame/4,0,1
+ End Method
+
+ Method hit()
+' PlaySound explode, SoundChannel
+ PlaySound explode
+ TParticle.CreateExplosion(x,y)
+ TParticle.ShowBonus(x,y,1)
+' TParticle.ShowMult(x,y,Rand(9))
+ enemies.remove(Self)
+ num_enemies:-1
+ End Method
+
+ '-------------------------------------------------------------------
+
+ Function CreateEnemy:TEnemy( x#,y# )
+ Local enemy:TEnemy=New TEnemy
+ enemy.x=x
+ enemy.y=y
+ enemy.alpha=0.1
+ enemy.ang=0.0
+ enemy.rs#=Rnd(0,4)
+ enemy.Expl=0
+ enemy.img=alien_img
+ Repeat
+ enemy.xs=Rnd(-6,6)
+ Until enemy.xs<>0
+
+ Repeat
+ enemy.ys=Rnd(-6,6)
+ Until enemy.ys<>0
+ enemies.AddLast enemy
+ num_enemies:+1
+ End Function
+
+ Function Init()
+ SetMaskColor(255,0,255)
+ alien_img=LoadAnimImage("gfx/spikeyball.png",64,64,0,16,MASKEDIMAGE)
+ EndFunction
+
+End Type
+
+'----------------------------------------------------------------------
+' End of file
+'---------------------------------------------------------------------
\ No newline at end of file
--- /dev/null
+'===============================================================================
+' Little Shooty Test Thing
+' Code & Stuff by Richard Olpin (rik@olpin.net)
+'==============================================================================
+' Graphic Font
+'==============================================================================
+Type GFont
+
+ Global FontImg, f
+ Global TypeStr$
+
+ ' ---------------------------------------------------------------------------
+ ' Init() - Load Font Image
+ ' ---------------------------------------------------------------------------
+
+ Function Init()
+ FontImg = LoadAnimImage("gfx/abduction.png",32,32,0,49, MASKEDIMAGE)
+ End Function
+
+ ' ---------------------------------------------------------------------------
+ ' DrawString(MsgX,MsgY,Message$)
+ ' ---------------------------------------------------------------------------
+
+ Function DrawString(MsgX,MsgY,Message$,centrex, centrey)
+ Local MsgCount = Len(Message$)
+
+ SetBlend MASKBLEND
+ SetScale 1,1
+ SetAlpha 1
+ SetRotation 0
+
+ length =Len(message$)*30
+ x=msgx
+
+ If centrex=1 Then x=msgx-(length/2)
+ If centrex=-1 Then x=msgx-length
+
+ If centrey=1
+ y=msgy-16
+ Else
+ y=msgy
+ EndIf
+
+ For f=0 To MsgCount-1
+ FontChar = Asc(Lower$(Mid$(Message$,f+1,1)))
+ imgchar= sortchar(fontchar)
+
+ DrawImage FontImg,x+(f*30),MsgY,ImgChar
+ Next
+
+ End Function
+
+ ' ---------------------------------------------------------------------------
+ ' Sortchar
+ ' ---------------------------------------------------------------------------
+
+ Function sortchar(char)
+ ' Letters
+ If char>=97 And char<=122 Then c=char-97
+
+ ' Numbers
+ If char>=48 And char<=57 Then c=char-22
+
+ ' Special characters
+ Select Char
+ Case 63 c = 36
+ Case 46 c= 37
+ Case 44 c= 38
+ Case 39 c= 39
+ Case 34 c= 40
+ Case 33 c= 41
+ Case 40 c= 42
+ Case 41 c= 43
+ Case 45 c= 44
+ Case 58 c= 45
+ Case 59 c= 46
+ Case 32 c= 48
+ End Select
+
+ Return c
+ End Function
+
+End Type
\ No newline at end of file
--- /dev/null
+'===============================================================================
+' Little Shooty Test Thing
+' Code & Stuff by Richard Olpin (rik@olpin.net)
+'===============================================================================
+' Constants
+'===============================================================================
+
+Const WIDTH=800,HEIGHT=600
+Const DEPTH=16,HERTZ=60
+
+'===============================================================================
+' Global Vars
+'===============================================================================
+
+Global spawntimer=0
+Global sky, ay
+Global bg_layer_1
+Global bp1
+Global bx1
+Global sky_pos#=800
+Global scroll_speed#=2.0
+Const horizon = 200
+Global upd=0
+
+'===============================================================================
+' Controls
+'===============================================================================
+
+' Default key mappings for PS2 dualshock through USB adaptor
+'
+' For a full game obviously this stuff would be configurable from an options
+' screen, or at the very least read from a text file.
+'
+
+Const Joy_L = 15
+Const Joy_R = 13
+Const Joy_U = 12
+Const Joy_D = 14
+
+Const Joy_Fire1 = 2
+Const Joy_Fire2 = 3
+Const Joy_Fire3 = 1
+Const Joy_Fire4 = 0
+
+Const Joy_Start = 9
+Const Joy_Select = 8
--- /dev/null
+'===============================================================================
+' Little Shooty Test Thing
+' Code & Stuff by Richard Olpin (rik@olpin.net)
+'===============================================================================
+' Initialisation
+'===============================================================================
+
+Function Init()
+
+ HideMouse()
+ JoyCount()
+
+ TPlayer.CreatePlayer()
+ Tbullet.image=LoadImage("gfx/player_shot.png", MASKEDIMAGE)
+
+ CreateBG()
+ TParticle.Init()
+ TEnemy.Init()
+ InitSound()
+
+EndFunction
--- /dev/null
+'===============================================================================
+' Little Shooty Test Thing
+' Code & Stuff by Richard Olpin (rik@olpin.net)
+'===============================================================================
+' particles.bmx
+'===============================================================================
+
+Global particles:TList=New TList
+Global particle_density=8
+Global spark1,spark2,spark3,explosion, smoke
+Global mult_img, score_decal
+
+Type TParticle
+ Field link:TLink
+ Field img
+ Field life
+ Field frame
+ Field x#,y#,xs#,ys#
+ Field scale#, ss#
+ Field speed#,gravity#=0
+ Field bounce=0
+ Field ang#, rs#=0.0;
+ Field alpha#=1.0, as#=0.0
+
+ '-------------------------------------------------------------------
+ ' Move / Draw
+ '-------------------------------------------------------------------
+
+ Method Update()
+ ' Move ---------------------------------------------------------
+ x:+xs ; If x<0 Or x>WIDTH Then kill()
+ y:+ys ; If y<0 Or y>HEIGHT Then kill()
+ ys:+gravity
+ ang:+rs;
+ alpha:+as
+ scale:+ss
+
+ ' Draw ---------------------------------------------------------
+ SetScale scale, scale
+ SetRotation ang
+ SetAlpha alpha
+ SetBlend ALPHABLEND
+ DrawImage img,x,y,frame
+
+ life:-1
+ If life<=0 Then particles.remove(Self)
+ End Method
+
+ Method hit()
+ expl=1
+ particles.remove(Self)
+ End Method
+
+ Method kill()
+ particles.remove(Self)
+ End Method
+
+ '-------------------------------------------------------------------
+
+ Function Init()
+ spark1=LoadImage("gfx/spark1.png", MASKEDIMAGE|FILTEREDIMAGE)
+ spark2=LoadImage("gfx/spark2.png", MASKEDIMAGE|FILTEREDIMAGE)
+ spark3=LoadImage("gfx/spark3.png", MASKEDIMAGE|FILTEREDIMAGE)
+ explosion=LoadImage("gfx/expl.png", MASKEDIMAGE|FILTEREDIMAGE)
+ smoke=LoadImage("gfx/smoke.png", MASKEDIMAGE|FILTEREDIMAGE)
+ score_decal = LoadAnimImage("gfx/bonusdecals.png",128,64,0,5, MASKEDIMAGE|FILTEREDIMAGE)
+ mult_img = LoadAnimImage("gfx/multdecals.png",128,64,0,10, MASKEDIMAGE|FILTEREDIMAGE)
+ End Function
+
+ '-------------------------------------------------------------------
+
+ Function CreateExplosion(x,y)
+ For Local s = 0 To particle_density
+ TParticle.CreateSpark(explosion,x,y, 0.5 ,0 ,6 ,0.1 , Rand(50,100) )
+ TParticle.CreateSpark(spark1, x,y, 0.5 ,0 ,7 ,0.2 , Rand(30,80) )
+ TParticle.CreateSpark(spark2, x,y, 0.25,0 ,10 ,0.3, Rand(50,100))
+ Next
+ TParticle.Createspark(explosion, x,y,0.5,0.1,0,0, Rand(50,100))
+ EndFunction
+
+ Function PlayerExplosion(x,y)
+ For Local s = 0 To particle_density*2
+ TParticle.CreateSpark(explosion,x,y, 0.5 ,0 ,10 ,0.1 , Rand(150,200) )
+ TParticle.CreateSpark(spark1, x,y, 0.5 ,0 ,12 ,0.2 , Rand(130,180) )
+ TParticle.CreateSpark(spark2, x,y, 0.25,0 ,14 ,0.3, Rand(150,200))
+ Next
+
+ For Local i=1 To 6
+ TParticle.Createspark(explosion, x+Rand(-64,64),y+Rand(-64,64),Rnd(0.25,1),0.2,Rand(4),0, Rand(150,300))
+ Next
+ EndFunction
+
+
+
+ Function CreateSpark:Tparticle(img,x#,y#,sc#,si#,sp#,gr#, lf)
+ Local particle:Tparticle=New Tparticle
+ particle.img=img
+
+ particle.x=x
+ particle.y=y
+
+ particle.alpha=1
+ particle.as=-0.03
+
+ particle.scale=sc
+ particle.ss=si
+
+ particle.speed=sp
+
+ particle.ang = Rnd(360)
+ particle.xs = Sin(particle.ang)*sp
+ particle.ys = Cos(particle.ang)*sp
+
+ particle.rs#=Rnd(0,2)
+ particle.life=lf
+ particle.gravity = gr
+
+ particles.AddLast particle
+ End Function
+
+ Function ShowBonus:Tparticle( x#,y#,frame )
+ Local particle:Tparticle=New Tparticle
+ particle.img=score_decal
+ particle.frame=frame
+ particle.x=x
+ particle.y=y
+ particle.alpha=1
+ particle.as=-0.02
+ particle.scale=0.5
+ particle.ss=0.01
+ particle.ang = 0
+ particle.xs = 0
+ particle.ys = -1
+
+ particle.rs#=0
+ particle.life=60
+ particle.gravity=0
+
+ particles.AddLast particle
+ End Function
+
+ Function ShowMult:Tparticle( x#,y#,frame )
+ Local particle:Tparticle=New Tparticle
+ particle.img=mult_img
+ particle.frame=frame
+ particle.x=x
+ particle.y=y
+ particle.alpha=1
+ particle.as=-0.02
+ particle.scale=0.1
+ particle.ss=0.2
+ particle.ang = 0
+ particle.xs = Rnd(-4,4)
+ particle.ys = Rnd(-4,4)
+
+ particle.rs#=Rnd(6)
+ particle.life=100
+ particle.gravity=0
+
+ particles.AddLast particle
+ End Function
+
+
+End Type
+
+'----------------------------------------------------------------------
+' End of file
+'---------------------------------------------------------------------
\ No newline at end of file
--- /dev/null
+'===============================================================================
+' Little Shooty Test Thing
+' Code & Stuff by Richard Olpin (rik@olpin.net)
+'===============================================================================
+' Player.bmx
+'===============================================================================
+
+Global score=0, oldswitch=0
+Global mx, mn, damping#
+Global player:TPlayer
+
+
+Type TPlayer
+ Field x#,y#,xs#,ys#
+ Field rot#,alpha#,img,frame=2
+ Field primary_weapon, secondary_weapon
+ Field pshot_timer=0
+ Field shield = 0
+ Field invincible =0
+ Field state=0, lives
+
+ '---------------------------------------
+
+ Function CreatePlayer()
+ player=New TPlayer
+ player.x=320; xs#=0
+ player.y=240; ys#=0
+ player.img=LoadAnimImage:TImage("gfx/playera.png",80,64,0,5, MASKEDIMAGE|FILTEREDIMAGE )
+ mx=6
+ mn=-6
+ damping#=0.8
+ player.rot=0
+ player.state=1
+ player.lives=3
+ player.shield=180
+ player.invincible=0
+ player.primary_weapon=WPN_DEFLASER
+ EndFunction
+
+ '---------------------------------------
+
+ Method Update()
+ shield:-1
+ If state=2 Then Goto pskip
+
+ '---------------------------------------------
+ ' move
+
+ If JoyDown(5) Then
+ If oldswitch=0 Then
+ direction=-direction
+ oldswitch=1
+ Else
+ oldswitch=0
+ EndIf
+ EndIf
+
+
+ If KeyHit(KEY_I) Then invincible=1-invincible
+
+ If KeyDown(KEY_RIGHT)Or KeyDown(KEY_D) Then xs:+1
+ If KeyDown(KEY_LEFT)Or KeyDown(KEY_A) Then xs:-1
+ If Abs(JoyX(0))>0.2 Then xs:+JoyX(0)
+ If (xs > mx) Then xs=mx
+ If (xs < mn) Then xs=mn
+ xs:*damping# ; If Abs(pvx)<0.5 Then pvx=0
+
+ If KeyDown(KEY_UP)Or KeyDown(KEY_W) Then ys:-1
+ If KeyDown(KEY_DOWN) Or KeyDown(KEY_S) Then ys:+1
+ If Abs(JoyY(0))>0.2 Then ys:+JoyY(0)
+
+ If ys > mx Then ys=mx
+ If ys < mn Then ys=mn
+
+ ys:*damping# ; If Abs(pvy)<0.5 Then pvy=0
+ frame = 4-(Int(ys/1.5)+2)
+
+ x:+xs ; If x>=WIDTH Then x=WIDTH ; If x<=0 Then x=0
+ y:+ys ; If y>=HEIGHT Then y=HEIGHT ; If y<=0 Then y=0
+
+ '---------------------------------------------
+ ' shoot?
+
+ pshot_timer:-1
+ If (JoyDown(2) Or KeyDown(KEY_SPACE)) And pshot_timer<0 Then fire()
+
+#pskip If state=2 Then
+ If rot<60 Then rot=rot+0.25
+ ys=ys+0.05
+ y=y+ys
+ x=x+1
+ ' img,x#,y#,sc#,si#,sp#,gr#, lf
+ If (Int(y)&3)=1 Then TParticle.Createspark(smoke, x,y,0.25,0.02,Rand(-0.5,0.5),0, Rand(125,175) )
+
+ If y>HEIGHT Then
+ StopChannel TempChannel
+ PlaySound playerdie
+ TParticle.PlayerExplosion(x,y)
+ rot=0
+ state=1
+ y=HEIGHT/2
+ ys=0
+ shield=180
+ lives=lives-1
+ EndIf
+ EndIf
+
+ '---------------------------------------------
+ ' Draw
+
+ SetBlend ALPHABLEND
+ SetScale 1,1
+ SetAlpha 1
+ SetRotation rot
+
+ If shield>0 Then SetAlpha Rnd(1)
+
+ DrawImage img,x,y,frame
+
+ '---------------------------------------------
+ ' Collisions
+ If (shield <0) Then
+ If CollideImage(img,x,y,frame,1,0) Then hit() ' Player/Alien Collision
+' If CollideImage(img,x,y,frame,2,0) Then hit() ' Player/Ground Collision
+ EndIf
+ '---------------------------------------------
+
+ End Method
+
+ Method fire()
+
+ Select primary_weapon
+
+ Case WPN_NORMAL
+ TBullet.CreateBullet bull_img,x+24,y+2+(ys*2),(16+spd)*direction
+ pshot_timer=5
+ PlaySound player_shot, SoundChannel
+
+ Case WPN_DEFLASER
+
+ For Local sp=1 To 8
+ TBullet.CreateBullet bull_img,x+24,y+2+(ys*2),(16+sp)*direction
+ Next
+ pshot_timer=5
+ PlaySound player_shot, SoundChannel
+
+ End Select
+
+ End Method
+
+
+ Method hit()
+ If invincible Then Return
+ If state=1 Then
+' TParticle.PlayerExplosion(x,y)
+ TParticle.CreateExplosion(x,y)
+ TempChannel=AllocChannel()
+ PlaySound bombfall, TempChannel
+ state=2
+ EndIf
+ End Method
+
+End Type
+
+
+'----------------------------------------------------------------------
+' End of file
+'---------------------------------------------------------------------
\ No newline at end of file
--- /dev/null
+'===============================================================================
+' Little Shooty Test Thing
+' Code & Stuff by Richard Olpin (rik@olpin.net)
+'===============================================================================
+' Player shots
+'===============================================================================
+
+Global bullets:TList=New TList
+
+Const WPN_NORMAL=0
+Const WPN_DEFLASER=1
+
+Type TBullet
+ Global image
+
+ Field link:TLink
+ Field x#,y#,xs#,ys#
+ Field rot#,alpha#,img
+
+ Method Update()
+ x:+xs
+ alpha:+0.02
+ If x>WIDTH
+ ' remove
+ Return
+ EndIf
+
+ For Local e:TEnemy =EachIn enemies
+ If ( x>(e.x-16) And x<(e.x+16) And y>(e.y-16) And y<(e.y+16) ) Then
+ e.hit()
+ score:+100
+ EndIf
+ Next
+
+ SetScale 1,1
+ If player.primary_weapon = WPN_DEFLASER Then SetScale 1,0.5
+ SetBlend ALPHABLEND
+ DrawImage img,x,y
+
+ End Method
+
+ Function CreateBullet:TBullet( img, x#,y#, xs# )
+ Local bullet:TBullet=New TBullet
+ bullet.x=x
+ bullet.y=y
+ bullet.xs=xs
+ bullet.alpha=0.1
+ bullet.img=image
+ bullets.AddLast bullet
+ End Function
+
+End Type
+
+'===============================================================================
+'
+'===============================================================================
\ No newline at end of file
--- /dev/null
+'===============================================================================
+' Little Shooty Test Thing
+' Code & Stuff by Richard Olpin (rik@olpin.net)
+'===============================================================================
+' Sound & Music
+'===============================================================================
+
+Global player_shot, playerdie
+Global bombfall
+Global explode, SoundChannel, TempChannel
+Global music, MusicChannel
+
+Function InitSound()
+ player_shot = LoadSound("sound/shot-1.wav", 0)
+ playerdie = LoadSound("sound/explosion1.wav", 0)
+ explode = LoadSound("sound/explode.wav", 0)
+ bombfall = LoadSound("sound/bombfall.wav", 0)
+ SoundChannel=0'AllocChannel()
+
+ MusicChannel=AllocChannel()
+' music=LoadSound("sound/music.ogg",1)
+EndFunction
+
+'===============================================================================
+'
+'===============================================================================
\ No newline at end of file
--- /dev/null
+'===============================================================================
+' Little Shooty Test Thing
+' Code & Stuff by Richard Olpin (rik@olpin.net)
+'==============================================================================
+' Titles
+'==============================================================================
+
+Function ShowTitlePage()
+ Local title, i#, a#, s#
+ Local state, quit, time
+
+ title=LoadImage ("gfx/titlepage.png",MASKEDIMAGE|FILTEREDIMAGE)
+ a#=0.01
+ s=1.0
+ state=0
+ time=100
+
+ SetBlend ALPHABLEND
+
+ Repeat
+ Cls
+
+ Select state
+
+ Case 0 ' Fade in
+ a#:+0.01
+ time:-1
+ If time<0 Then state=1
+
+ Case 1 ' Static, wait for quit
+
+ Case 2 ' Fade Out
+ a#:-0.02
+ s:+0.2
+ time:-1
+ If time<0 Then state=-1
+
+ End Select
+
+ If JoyDown(Joy_Fire1) Or KeyHit(KEY_SPACE) Then
+ State=2
+ time=100
+ EndIf
+
+ SetAlpha a#
+ SetScale s,s
+
+ DrawImage title,400,300
+ Flip
+ Until state=-1
+
+End Function
\ No newline at end of file
--- /dev/null
+' Fireworks by simonh (si@si-design.co.uk)
+
+Strict
+
+Global width=800
+Global height=600
+
+Graphics width,height,16
+
+' Create a spark type
+Type spark
+ Field x#,y#,z#,vy#,xd#,yd#,zd#,r#,g#,b#,alpha#
+End Type
+
+' Load spark image
+Global sparki:TImage=LoadImage("spark.png")
+
+' Set no. of sparks to be created per firework
+Global no_sparks=500
+
+' Create spark list
+Global spark_list:TList=New TList
+
+' Load and set font
+Global font:TImageFont=LoadImageFont("Arial.ttf",1)
+SetImageFont font
+
+' Start main loop
+While Not KeyHit(KEY_ESCAPE)
+
+ ' If space key pressed then create new set of sparks (new firework)
+
+ If KeyHit(KEY_SPACE)
+
+ Local x#=Rand(-100,100)
+ Local y#=Rand(-100,100)
+ Local z#=200
+
+ Local r#=Rand(255)
+ Local g#=Rand(255)
+ Local b#=Rand(255)
+
+ For Local i=1 To no_sparks
+
+ Local speed# = 0.1
+
+ Local ang1# = Rnd!(360)
+ Local ang2# = Rnd!(360)
+
+ Local sp:spark=New Spark
+ spark_list.AddLast sp
+
+ sp.x=x#
+ sp.y=y#
+ sp.z=z#
+
+ sp.xd=Cos(ang1#)*Cos(ang2#)*speed#
+ sp.yd=Cos(ang1#)*Sin(ang2#)*speed#
+ sp.zd=Sin(ang1#)*speed#
+
+ sp.r=r
+ sp.g=g
+ sp.b=b
+
+ sp.alpha=1
+
+ Next
+
+ EndIf
+
+ ' Draw all sparks
+
+ For Local sp:spark=EachIn spark_list
+
+ ' If spark alpha is above 0 then draw it...
+
+ If sp.alpha>0
+
+ sp.x=sp.x+sp.xd*10.0
+ sp.y=sp.y+sp.yd*10.0
+ sp.z=sp.z+sp.zd*10.0
+ sp.y=sp.y+sp.vy#
+ sp.vy=sp.vy+0.02
+
+ ' Calculate x and y draw values based on x,y,z co-ordinates
+ Local x#=(width/2.0)+((sp.x/sp.z)*500)
+ Local y#=(height/2.0)+((sp.y/sp.z)*500)
+
+ sp.alpha=sp.alpha-0.01
+
+ SetColor sp.r#,sp.g#,sp.b#
+ SetBlend LIGHTBLEND
+ SetAlpha sp.alpha
+ SetScale 20/sp.z,20/sp.z
+ DrawImage sparki,x#,y#
+
+ '...else remove spark from spark list
+
+ Else
+
+ spark_list.Remove sp
+
+ EndIf
+
+ Next
+
+ SetBlend SOLIDBLEND
+ SetScale 1,1
+ SetColor 255,255,255
+ DrawText "Press space to ignite firework",0,0
+
+ Flip
+ Cls
+
+Wend
--- /dev/null
+' Snowfall by simonh (si@si-design.co.uk)
+
+Strict
+
+Global width=800
+Global height=600
+
+Graphics width,height,16
+
+' Load snowflake image
+Global flakei:TImage=LoadImage("flake.png",MIPMAPPEDIMAGE)
+
+' Set no. of snowflakes to be created
+Global no_flakes=1000
+
+' Create a snowflake type
+Type flake
+ Field x#,y#,size#,speed#,sway#,phase
+End Type
+
+' Create snowflake list
+Global flake_list:TList=New TList
+
+' Initialise snowflakes
+
+For Local i=1 To no_flakes
+
+ Local fl:flake=New flake
+ flake_list.AddLast fl
+
+ fl.size=Rnd!(0.01,0.1)
+ fl.speed=Rnd!(1,2)
+ fl.sway=Rnd!(1,2)
+ fl.phase=Rand(45)
+ fl.x=Rand(-10,width+10)
+ fl.y=Rand(height)-height-10
+
+Next
+
+' Main loop
+
+While Not KeyHit(KEY_ESCAPE)
+
+ ' Begin loop in which we will update values of all snowflakes and draw them
+
+ For Local wind=1 To 360 Step 5
+
+ ' Iterate through our snowflake list
+
+ For Local fl:flake=EachIn flake_list
+
+ ' Just update the snowflake position values to try and make them move convincingly!
+ fl.y=fl.y+fl.speed
+ fl.x#=fl.x#+(Sin(wind+(fl.phase)))*fl.sway
+
+ ' If snowflake has not yet reached the bottom of screen...
+ If fl.y<height+10
+
+ ' ...then draw snowflake.
+ SetBlend LIGHTBLEND
+ SetScale fl.size,fl.size
+ DrawImage flakei,fl.x,fl.y
+
+ '...else if snowflake has reached bottom of screen...
+ Else
+
+ '...reset snowflake values so it appears as new snowflake at top of screen.
+ fl.speed=Rnd!(1,2)
+ fl.sway=Rnd!(1,2)
+ fl.phase=Rand(45)
+ fl.x=Rand(-10,width+10)
+ fl.y=-10
+
+ EndIf
+
+ Next
+
+ Flip
+ Cls
+
+ Next
+
+Wend
--- /dev/null
+
+Graphics 640,480
+
+t$="***** Some spinny text *****"
+w=TextWidth(t)
+h=TextHeight(t)
+
+r#=0
+
+While Not KeyHit( KEY_ESCAPE )
+
+ Cls
+
+ r:+3
+ SetOrigin 320,240
+ SetHandle w/2,h/2
+ SetTransform r,3,5
+
+ SetColor 0,0,255
+ DrawRect 0,0,w,h
+ SetColor 255,255,255
+ DrawText t,0,0
+
+ SetOrigin 0,0
+ SetHandle 0,0
+ SetTransform 0,1,1
+
+ Flip
+
+Wend
+
\ No newline at end of file
--- /dev/null
+Strict
+Const WIDTH=640,HEIGHT=480,DEPTH=32
+Const Star_Count = 1000 ' Stars Count
+Const MAX_SIZE = 12 ' Maximum starts
+Const MAX_ROTSPD# = 1.5 ' How much rotation goin on
+
+Global Delta_X#,Delta_Y#, Delta_Ang#=0 ,tick#=0
+
+Type TEntity
+ Field x#,y#
+ Method Update() Abstract
+EndType
+
+Type Star Extends TEntity
+ Field s#
+ Field size#
+ Field col#,alp#
+ Field rot#
+ Field tcol[3]
+ Field vtype
+
+ Method Update()
+ Local cs# , sn#
+ Local tx# , ty#
+
+ x:+ ( x-319.99999 ) / s
+ y:+ ( y-239.99999 ) / s
+
+ x=x-320
+ y=y-240
+
+ cs = Cos(Delta_Ang)
+ sn = Sin(Delta_Ang)
+
+ tx = x
+ ty = y
+
+ x = tx * cs - ty * sn
+ y = tx * sn + ty * Cs
+
+ x=x +320
+ y=y +240
+
+ 'Pitch Horiz and Verti
+ x = x + Delta_X / s
+ y = y + Delta_Y / s
+
+ If x<0 Or x>WIDTH
+ x=Rnd(WIDTH)
+ alp=0
+ EndIf
+ If y<0 Or y>HEIGHT
+ y=Rnd(HEIGHT)
+ alp=0
+ EndIf
+ If alp<1
+ alp = alp + .05
+ EndIf
+ SetBlend LIGHTBLEND
+ SetRotation rot
+ SetAlpha alp
+ rot=rot+5
+ SetColor tcol[0],tcol[1],tcol[2]
+ Select vtype
+ Case 0
+ SetHandle size*.5,.5
+ DrawRect x,y,size,1
+ SetHandle .5,size*.5
+ DrawRect x,y,1,size
+ SetHandle 0,0
+ Case 1
+ SetHandle size*.5,size*.5
+ DrawRect x,y,size,size
+ SetHandle 0,0
+ End Select
+ End Method
+
+ Function CreateStar:Star()
+ Local s:Star = New Star
+ Local r =Rand(128)
+ s.x=Rnd(640)
+ s.y=Rnd(480)
+ s.s=Rnd(150,250)
+ s.tcol=[r,r,r]
+ s.size = Rnd(1,MAX_SIZE)
+ s.vtype = Rnd(1)
+ Return s
+ EndFunction
+End Type
+
+Function UpdateEntities( list:TList )
+ Delta_X = 400*Cos(tick)
+ Delta_Y = 400*Sin(tick)
+
+ Delta_Ang = MAX_ROTSPD*Cos( tick )
+ tick=tick+.5
+ Local c:TEntity
+ For c=EachIn list
+ c.Update
+ Next
+End Function
+
+Graphics WIDTH,HEIGHT,DEPTH
+HideMouse
+
+Local StarList:TList = New TList
+Local a
+
+Local px1#=30,py1#
+Local px2#=WIDTH-30,py2#
+Local bx#=WIDTH/2, by#=HEIGHT/2
+Local bdx#=Rnd(-8,4)
+Local bdy#=3
+Local sc1,sc2
+
+
+For a= 0 To Star_Count-1
+ StarList.AddLast( star.CreateStar() )
+Next
+
+While Not KeyHit( KEY_ESCAPE )
+ Cls
+ UpdateEntities StarList
+
+ py1=MouseY()
+ If py1<40 py1=40
+ If py1>HEIGHT-40 py1=HEIGHT-40
+
+ SetBlend SOLIDBLEND
+ SetColor 255,0,0
+ SetRotation 0
+ SetHandle 5,40
+ DrawRect px1,py1,10,80
+
+ DrawRect px2,py2,10,80
+ SetHandle 0,0
+ SetColor 0,0,255
+ SetHandle 2.5,2.5
+ DrawRect bx,by,5,5
+ SetHandle 0,0
+
+
+ bx=bx+bdx
+ by=by+bdy
+ If by<3 bdy=-bdy
+ If by>HEIGHT-3 bdy=-bdy
+
+ 'check players paddle
+ If bx<px1+10
+ If by>py1-40 And by<py1+40
+ bdx=-bdx*Rnd(1.1,1.2)
+ bdy=-bdy+Rnd(-1,1)
+ EndIf
+
+ EndIf
+
+ If bx>px2-10 And bx<px2+10
+ If by>py2-40 And by<py2+40
+ bdx=-bdx*Rnd(1.1,1.2)
+ bdy=-bdy+Rnd(-1,1)
+ EndIf
+ EndIf
+
+ If bx>WIDTH-3 Or bx<3
+ bdx= Rnd(-8,8)
+ bdy= Rnd(-8,8)
+ If bx>Width-3
+ sc1:+1
+ Else
+ sc2:+1
+ EndIf
+ bx=width/2
+ by=height/2
+ EndIf
+
+ If py2<by
+ If py2<HEIGHT-40
+ py2=py2+3
+ EndIf
+ EndIf
+ If py2>by
+ If py2>40
+ py2=py2-3
+ EndIf
+ EndIf
+ DrawText sc1,width/2-40,0
+ DrawText sc2,width/2+40,0
+
+ Flip
+Wend
--- /dev/null
+' The UTAH Teapot. See <a href="http://sjbaker.org/teapot/" target="_blank">sjbaker.org/teapot/</a> For more information
+' This Function returns an OpenGL display list.
+' BlitzMax port by Peter Scheutz 2004.12.18
+Function ogld_TeaPot(grid%)
+
+ Local x#,y#,z#
+ Local i, n
+ Local verts
+ Local teaList
+ Local rimbank
+ Local bodybank1
+ Local bodybank2
+ Local lidbank1
+ Local lidbank2
+ Local handlebank1
+ Local handlebank2
+ Local spoutbank1
+ Local spoutbank2
+
+
+ verts=CreateBank(3*4*119)
+
+
+ RestoreData teaPotVerts
+ For i=0 To 118
+ ReadData x#
+ ReadData y#
+ ReadData z#
+
+ PokeFloat verts,i*12,x
+ PokeFloat verts,i*12+4,y
+ PokeFloat verts,i*12+8,z
+
+ Next
+
+ rimbank=CreateBank(16*4*3)
+
+ bodybank1=CreateBank(16*4*3)
+ bodybank2=CreateBank(16*4*3)
+
+ lidbank1=CreateBank(16*4*3)
+ lidbank2=CreateBank(16*4*3)
+
+
+ handlebank1=CreateBank(16*4*3)
+ handlebank2=CreateBank(16*4*3)
+
+ spoutbank1=CreateBank(16*4*3)
+ spoutbank2=CreateBank(16*4*3)
+
+
+ ' rim
+ RestoreData teaPotRim
+ For n=0 To 15
+ ReadData i
+ PokeFloat rimbank,n*12,PeekFloat(verts,i*12)
+ PokeFloat rimbank,n*12+4,PeekFloat(verts,i*12+4)
+ PokeFloat rimbank,n*12+8,PeekFloat(verts,i*12+8)
+ Next
+
+ ' body
+ RestoreData teaPotBody
+ For n=0 To 15
+ ReadData i
+ PokeFloat bodybank1,n*12,PeekFloat(verts,i*12)
+ PokeFloat bodybank1,n*12+4,PeekFloat(verts,i*12+4)
+ PokeFloat bodybank1,n*12+8,PeekFloat(verts,i*12+8)
+ Next
+ For n=0 To 15
+ ReadData i
+ PokeFloat bodybank2,n*12,PeekFloat(verts,i*12)
+ PokeFloat bodybank2,n*12+4,PeekFloat(verts,i*12+4)
+ PokeFloat bodybank2,n*12+8,PeekFloat(verts,i*12+8)
+ Next
+
+ ' lid
+ RestoreData teaPotLid
+ For n=0 To 15
+ ReadData i
+ PokeFloat lidbank1,n*12,PeekFloat(verts,i*12)
+ PokeFloat lidbank1,n*12+4,PeekFloat(verts,i*12+4)
+ PokeFloat lidbank1,n*12+8,PeekFloat(verts,i*12+8)
+ Next
+ For n=0 To 15
+ ReadData i
+ PokeFloat lidbank2,n*12,PeekFloat(verts,i*12)
+ PokeFloat lidbank2,n*12+4,PeekFloat(verts,i*12+4)
+ PokeFloat lidbank2,n*12+8,PeekFloat(verts,i*12+8)
+ Next
+
+ ' handle
+ RestoreData teaPotHandle
+ For n=0 To 15
+ ReadData i
+ PokeFloat handlebank1,n*12,PeekFloat(verts,i*12)
+ PokeFloat handlebank1,n*12+4,PeekFloat(verts,i*12+4)
+ PokeFloat handlebank1,n*12+8,PeekFloat(verts,i*12+8)
+ Next
+ For n=0 To 15
+ ReadData i
+ PokeFloat handlebank2,n*12,PeekFloat(verts,i*12)
+ PokeFloat handlebank2,n*12+4,PeekFloat(verts,i*12+4)
+ PokeFloat handlebank2,n*12+8,PeekFloat(verts,i*12+8)
+ Next
+
+ ' Spout
+ RestoreData teaPotSpout
+ For n=0 To 15
+ ReadData i
+ PokeFloat spoutbank1,n*12,PeekFloat(verts,i*12)
+ PokeFloat spoutbank1,n*12+4,PeekFloat(verts,i*12+4)
+ PokeFloat spoutbank1,n*12+8,PeekFloat(verts,i*12+8)
+ Next
+ For n=0 To 15
+ ReadData i
+ PokeFloat spoutbank2,n*12,PeekFloat(verts,i*12)
+ PokeFloat spoutbank2,n*12+4,PeekFloat(verts,i*12+4)
+ PokeFloat spoutbank2,n*12+8,PeekFloat(verts,i*12+8)
+ Next
+
+
+
+ teaList = glGenLists(1)
+ glNewList teaList, GL_COMPILE
+ glPushMatrix
+
+ glRotatef 270, 1, 0, 0
+
+ glEnable GL_MAP2_VERTEX_3
+ glMapGrid2f grid, 0, 1, grid, 0, 1
+
+
+
+ For i=0 To 3
+
+ glMap2f GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, Float Ptr( BankBuf(rimbank))
+ glEvalMesh2 GL_FILL, 0, grid, 0, grid
+
+ glMap2f GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, Float Ptr( BankBuf(bodybank1))
+ glEvalMesh2 GL_FILL, 0, grid, 0, grid
+
+ glMap2f GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, Float Ptr( BankBuf(bodybank2))
+ glEvalMesh2 GL_FILL, 0, grid, 0, grid
+
+ glMap2f GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, Float Ptr( BankBuf(lidbank1))
+ glEvalMesh2 GL_FILL, 0, grid, 0, grid
+
+ glMap2f GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, Float Ptr( BankBuf(lidbank2))
+ glEvalMesh2 GL_FILL, 0, grid, 0, grid
+
+ glRotatef 90, 0, 0, 1
+
+
+ Next
+
+
+ For i=0 To 1
+
+ glMap2f GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, Float Ptr( BankBuf(handlebank1))
+ glEvalMesh2 GL_FILL, 0, grid, 0, grid
+
+ glMap2f GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, Float Ptr( BankBuf(handlebank2))
+ glEvalMesh2 GL_FILL, 0, grid, 0, grid
+
+
+ glMap2f GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, Float Ptr( BankBuf(spoutbank1))
+ glEvalMesh2 GL_FILL, 0, grid, 0, grid
+
+ glMap2f GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, Float Ptr( BankBuf(spoutbank2))
+ glEvalMesh2 GL_FILL, 0, grid, 0, grid
+
+ glScalef 1,-1,1
+
+
+ Next
+
+
+ glDisable GL_MAP2_VERTEX_3
+ glPopMatrix
+ glEndList
+
+ Return teaList
+
+End Function
+
+
+#teaPotRim
+DefData 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+#teaPotBody
+DefData 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27
+DefData 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40
+#teaPotLid
+DefData 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3
+DefData 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117
+#teaPotHandle
+DefData 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56
+DefData 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67
+#teaPotSpout
+DefData 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83
+DefData 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95
+
+
+#teaPotVerts
+DefData 0.2000, 0.0000, 2.70000 , 0.2000, -0.1120, 2.70000 , 0.1120, -0.2000, 2.70000
+DefData 0.0000, -0.2000, 2.70000 , 1.3375, 0.0000, 2.53125 , 1.3375, -0.7490, 2.53125
+DefData 0.7490, -1.3375, 2.53125 , 0.0000, -1.3375, 2.53125 , 1.4375, 0.0000, 2.53125
+DefData 1.4375, -0.8050, 2.53125 , 0.8050, -1.4375, 2.53125 , 0.0000, -1.4375, 2.53125
+DefData 1.5000, 0.0000, 2.40000 , 1.5000, -0.8400, 2.40000 , 0.8400, -1.5000, 2.40000
+DefData 0.0000, -1.5000, 2.40000 , 1.7500, 0.0000, 1.87500 , 1.7500, -0.9800, 1.87500
+DefData 0.9800, -1.7500, 1.87500 , 0.0000, -1.7500, 1.87500 , 2.0000, 0.0000, 1.35000
+DefData 2.0000, -1.1200, 1.35000 , 1.1200, -2.0000, 1.35000 , 0.0000, -2.0000, 1.35000
+DefData 2.0000, 0.0000, 0.90000 , 2.0000, -1.1200, 0.90000 , 1.1200, -2.0000, 0.90000
+DefData 0.0000, -2.0000, 0.90000 , -2.0000, 0.0000, 0.90000 , 2.0000, 0.0000, 0.45000
+DefData 2.0000, -1.1200, 0.45000 , 1.1200, -2.0000, 0.45000 , 0.0000, -2.0000, 0.45000
+DefData 1.5000, 0.0000, 0.22500 , 1.5000, -0.8400, 0.22500 , 0.8400, -1.5000, 0.22500
+DefData 0.0000, -1.5000, 0.22500 , 1.5000, 0.0000, 0.15000 , 1.5000, -0.8400, 0.15000
+DefData 0.8400, -1.5000, 0.15000 , 0.0000, -1.5000, 0.15000 , -1.6000, 0.0000, 2.02500
+DefData -1.6000, -0.3000, 2.02500 , -1.5000, -0.3000, 2.25000 , -1.5000, 0.0000, 2.25000
+DefData -2.3000, 0.0000, 2.02500 , -2.3000, -0.3000, 2.02500 , -2.5000, -0.3000, 2.25000
+DefData -2.5000, 0.0000, 2.25000 , -2.7000, 0.0000, 2.02500 , -2.7000, -0.3000, 2.02500
+DefData -3.0000, -0.3000, 2.25000 , -3.0000, 0.0000, 2.25000 , -2.7000, 0.0000, 1.80000
+DefData -2.7000, -0.3000, 1.80000 , -3.0000, -0.3000, 1.80000 , -3.0000, 0.0000, 1.80000
+DefData -2.7000, 0.0000, 1.57500 , -2.7000, -0.3000, 1.57500 , -3.0000, -0.3000, 1.35000
+DefData -3.0000, 0.0000, 1.35000 , -2.5000, 0.0000, 1.12500 , -2.5000, -0.3000, 1.12500
+DefData -2.6500, -0.3000, 0.93750 , -2.6500, 0.0000, 0.93750 , -2.0000, -0.3000, 0.90000
+DefData -1.9000, -0.3000, 0.60000 , -1.9000, 0.0000, 0.60000 , 1.7000, 0.0000, 1.42500
+DefData 1.7000, -0.6600, 1.42500 , 1.7000, -0.6600, 0.60000 , 1.7000, 0.0000, 0.60000
+DefData 2.6000, 0.0000, 1.42500 , 2.6000, -0.6600, 1.42500 , 3.1000, -0.6600, 0.82500
+DefData 3.1000, 0.0000, 0.82500 , 2.3000, 0.0000, 2.10000 , 2.3000, -0.2500, 2.10000
+DefData 2.4000, -0.2500, 2.02500 , 2.4000, 0.0000, 2.02500 , 2.7000, 0.0000, 2.40000
+DefData 2.7000, -0.2500, 2.40000 , 3.3000, -0.2500, 2.40000 , 3.3000, 0.0000, 2.40000
+DefData 2.8000, 0.0000, 2.47500 , 2.8000, -0.2500, 2.47500 , 3.5250, -0.2500, 2.49375
+DefData 3.5250, 0.0000, 2.49375 , 2.9000, 0.0000, 2.47500 , 2.9000, -0.1500, 2.47500
+DefData 3.4500, -0.1500, 2.51250 , 3.4500, 0.0000, 2.51250 , 2.8000, 0.0000, 2.40000
+DefData 2.8000, -0.1500, 2.40000 , 3.2000, -0.1500, 2.40000 , 3.2000, 0.0000, 2.40000
+DefData 0.0000, 0.0000, 3.15000 , 0.8000, 0.0000, 3.15000 , 0.8000, -0.4500, 3.15000
+DefData 0.4500, -0.8000, 3.15000 , 0.0000, -0.8000, 3.15000 , 0.0000, 0.0000, 2.85000
+DefData 1.4000, 0.0000, 2.40000 , 1.4000, -0.7840, 2.40000 , 0.7840, -1.4000, 2.40000
+DefData 0.0000, -1.4000, 2.40000 , 0.4000, 0.0000, 2.55000 , 0.4000, -0.2240, 2.55000
+DefData 0.2240, -0.4000, 2.55000 , 0.0000, -0.4000, 2.55000 , 1.3000, 0.0000, 2.55000
+DefData 1.3000, -0.7280, 2.55000 , 0.7280, -1.3000, 2.55000 , 0.0000, -1.3000, 2.55000
+DefData 1.3000, 0.0000, 2.40000 , 1.3000, -0.7280, 2.40000 , 0.7280, -1.3000, 2.40000
+DefData 0.0000, -1.3000, 2.40000 , 0.0000, 0.0000, 0.00000
--- /dev/null
+
+' Test program for the UTAH Teapot.
+' BlitzMax versio by Peter Scheutz 2004.12.18
+
+Strict
+
+Import "teapot.bmx"
+
+Type ogld_color4f
+ Field r#,g#,b#,a#
+End Type
+
+Type ogld_pos4f
+ Field x#,y#,z#,w#
+End Type
+
+
+Local teapot
+Local z#=-4
+Local xrot#=0
+Local yrot#=0
+
+
+GLGraphics 800,600
+
+ glClearColor(0.2, 0.0, 0.4, 0.0)
+ glEnable(GL_DEPTH_TEST)
+
+ glEnable GL_AUTO_NORMAL
+ glEnable GL_NORMALIZE
+
+ teapot= ogld_TeaPot(16)
+
+ ResizeViewport 800,600
+
+ initlights
+
+ gldisable(GL_CULL_FACE)
+
+
+
+While Not KeyHit(Key_Escape)
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+
+ glMatrixMode GL_MODELVIEW
+ glLoadIdentity
+
+ glTranslatef 0,0,-8
+ glRotatef yrot,1,0,0
+ glRotatef xrot,0,1,0
+
+
+ glTranslatef 0.0,-1.0,0.0
+ glCallList teapot
+
+ Flip
+
+ xrot = xrot +1
+ yrot = yrot +.1
+
+Wend
+
+Function ResizeViewport(w,h)
+
+ Local aspect#
+
+
+ If w = 0 Then h = 1
+
+ glViewport 0,0,w,h
+
+ glMatrixMode GL_PROJECTION
+ glLoadIdentity
+ aspect#=Float(w)/Float(h)
+
+
+ gluPerspective 45.0,aspect,1.0,100.0
+ glMatrixMode GL_MODELVIEW
+
+
+End Function
+
+Function initlights()
+
+ Local ambient:ogld_color4f = New ogld_color4f
+ Local position:ogld_pos4f = New ogld_pos4f
+
+ Local mat_ambient:ogld_color4f = New ogld_color4f
+ Local mat_diffuse:ogld_color4f = New ogld_color4f
+ Local mat_specular:ogld_color4f = New ogld_color4f
+ Local mat_shininess:ogld_color4f = New ogld_color4f
+
+
+
+ ambient.r=0.2
+ ambient.g=0.2
+ ambient.b=0.2
+ ambient.a=1
+
+ position.x=-2
+ position.y=5
+ position.z=0
+ position.w=1
+
+ mat_ambient.r=1
+ mat_ambient.g=0
+ mat_ambient.b=0
+ mat_diffuse.a=1
+
+ mat_diffuse.r=1
+ mat_diffuse.g=0
+ mat_diffuse.b=0
+ mat_diffuse.a=1
+
+
+
+ mat_specular.r=1
+ mat_specular.g=1
+ mat_specular.b=1
+ mat_specular.a=1
+
+
+ mat_shininess.r=50.0
+ mat_shininess.g=50.0
+ mat_shininess.b=50.0
+ mat_shininess.a=0
+
+
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+
+ glLightfv GL_LIGHT0, GL_AMBIENT, Varptr(ambient.r)
+ glLightfv GL_LIGHT0, GL_POSITION, Varptr(position.x)
+
+
+ glMaterialfv GL_FRONT, GL_DIFFUSE, Varptr(mat_diffuse.r)
+ glMaterialfv GL_FRONT, GL_AMBIENT, Varptr(mat_ambient.r)
+ glMaterialfv GL_FRONT, GL_SPECULAR, Varptr(mat_specular.r)
+ glMaterialfv GL_FRONT, GL_SHININESS, Varptr(mat_shininess.r)
+
+End Function
\ No newline at end of file
--- /dev/null
+Tempest 1.5
+---------------------------------------------------------------------------
+Get latest version here: http:\\www.incitti.com\Blitz\
+
+
+History:
+---------------------------------------------------------------------------
+Version 1.5 - July 18
+ added 16 more tubes
+ tweaked the difficulty and enemy release rates
+ changed many of the scaling calcs to TFormSZ()
+
+Version 1.1 - July 7
+ fixed egg that were hatching to act 7 (non-existent case
+ - they disappeared)
+ changed fuseballs - only kill when off the edge w>1 or w<7
+ added Tempest Tubes boards
+
+Version 1.0 - June 20, 2005
+ Completed by Mark Incitti (Mark1nc)
+
+Version 0.1 -
+ Started by David Bird (Birdie) - BlitzMax 1.10 Samples
+
+
+
+
+Instructions:
+------------------------------------------------------------------------------
+Run Tempest.exe
+Shoot everything.
+
+
+Controls:
+------------------------------------------------------------------------------
+CTRL - Fire!
+SPACE - Superzapper
+L/R - Move
+ESC - Quit
+T - select tube set (3 sets of 16 tubes)
+
+
+
+Building using BMax:
+------------------------------------------------------------------------------
+Run boardgen.bmx first - it creates the file boarddata.bmx
+which contains the tube co-ords.
+This file will be included into Tempest.bmx
+Compile Tempest.bmx
+Feel free to modify and tweak - please let me know of any
+cool changes.
+
--- /dev/null
+' Continuous, CenterY, YOFFSET, x1,y1,...x16,y16 - created by boardgen
+'Level 1 - circle
+DefData 1,400,-80,200,0,184,76,141,141,76,184,0,200,-76,184,-141,141,-184,76,-200,0,-184,-76,-141,-141,-76,-184,0,-200,76,-184,141,-141,184,-76
+'Level 2 - square
+DefData 1,400,-80,-200,200,-200,100,-200,0,-200,-100,-200,-200,-100,-200,0,-200,100,-200,200,-200,200,-100,200,0,200,100,200,200,100,200,0,200,-100,200
+'Level 3 - plus
+DefData 1,400,-80,-100,100,-200,100,-200,0,-200,-100,-100,-100,-100,-200,0,-200,100,-200,100,-100,200,-100,200,0,200,100,100,100,100,200,0,200,-100,200
+'Level 4 - binoculars
+DefData 1,380,-75,-40,105,-120,150,-220,120,-260,60,-260,-30,-220,-90,-120,-120,-40,-75,40,-75,120,-120,220,-90,260,-30,260,60,220,120,120,150,40,105
+'Level 5 - cross
+DefData 1,415,-90,-30,210,-60,120,-120,60,-210,30,-210,-30,-120,-60,-60,-120,-30,-210,30,-210,60,-120,120,-60,210,-30,210,30,120,60,60,120,30,210
+'Level 6 - triangle
+DefData 1,390,-40,-119,168,-175,168,-140,84,-105,0,-70,-84,-35,-168,0,-252,35,-168,70,-84,105,0,140,84,175,168,119,168,56,168,0,168,-56,168
+'Level 7 - clover
+DefData 1,310,-10,-179,179,-200,50,-100,0,-200,-50,-179,-179,-50,-200,0,-100,50,-200,179,-179,200,-50,100,0,200,50,179,179,50,200,0,100,-50,200
+'Level 8 - Vee
+DefData 0,240,90,240,-220,210,-165,180,-110,150,-55,120,0,90,55,60,110,30,165,-30,165,-60,110,-90,55,-120,0,-150,-55,-180,-110,-210,-165,-240,-220
+'Level 9 - steps
+DefData 0,200,100,280,-108,280,-36,200,-36,200,36,120,36,120,108,40,108,40,180,-40,180,-40,108,-120,108,-120,36,-200,36,-200,-36,-280,-36,-280,-108
+'Level 10 - U
+DefData 0,500,-200,210,-175,210,-105,210,-35,210,35,204,105,165,175,105,227,30,245,-30,245,-105,227,-165,175,-204,105,-210,35,-210,-35,-210,-105,-210,-175
+'Level 11 - horiz line
+DefData 0,230,70,300,160,260,160,220,160,180,160,140,160,100,160,60,160,20,160,-20,160,-60,160,-100,160,-140,160,-180,160,-220,160,-260,160,-300,160
+'Level 12 - heart
+DefData 1,540,-210,60,-180,150,-170,180,-60,180,60,150,150,90,210,0,240,-90,210,-150,150,-180,60,-180,-60,-150,-170,-60,-180,-15,-90,0,30,15,-90
+'Level 13 - star
+DefData 1,415,-95,-120,129,-200,100,-160,0,-200,-100,-120,-129,-80,-220,0,-170,80,-220,120,-129,200,-100,160,0,200,100,120,129,80,220,0,170,-80,220
+'Level 14 - W
+DefData 0,140,120,280,-105,245,-35,234,52,213,129,164,181,94,181,52,140,21,70,-21,70,-52,140,-94,181,-164,181,-213,129,-234,52,-245,-35,-280,-105
+'Level 15 - broken V
+DefData 0,240,75,280,-240,262,-162,245,-90,227,-6,140,-20,97,45,73,120,38,165,-35,150,-87,195,-129,120,-157,60,-175,-30,-192,-90,-227,-150,-280,-210
+'Level 16 - infinity
+DefData 1,310,0,0,0,48,-110,144,-165,240,-110,288,0,240,110,144,165,48,110,0,0,-48,-110,-144,-165,-240,-110,-288,0,-240,110,-144,165,-48,110
+'Level 17 - Tubes 1 - octagon
+DefData 1,400,-80,-120,120,-180,80,-180,0,-180,-80,-120,-120,-60,-160,0,-160,60,-160,120,-120,180,-80,180,0,180,80,120,120,60,160,0,160,-60,160
+'Level 18 - Tubes 2 - tear
+DefData 1,390,-40,-122,150,-157,90,-157,0,-122,-60,-70,-120,-35,-180,0,-240,35,-180,70,-120,122,-60,157,0,157,90,122,150,56,180,0,180,-56,180
+'Level 19 - Tubes 3 - false closed V
+DefData 0,400,0,0,-210,75,-210,150,-175,225,-125,225,-50,150,-25,95,45,50,110,-50,110,-95,45,-150,-25,-225,-50,-225,-125,-150,-175,-75,-210,0,-210
+'Level 20 - Tubes 4 - bowtie
+DefData 1,300,0,-150,75,-250,150,-250,75,-250,-75,-250,-150,-150,-75,-50,-50,50,-50,150,-75,250,-150,250,-75,250,75,250,150,150,75,50,50,-50,50
+'Level 21 - Tubes 5 - vert bent line
+DefData 0,280,0,210,-210,210,-180,210,-150,210,-120,210,-90,195,-60,180,-30,150,0,120,30,105,60,90,90,90,120,90,150,90,180,90,210,90,240
+'Level 22 - Tubes 6 - thin rectangle
+DefData 1,300,0,-30,150,-30,90,-30,30,-30,-30,-30,-90,-30,-150,-30,-210,30,-210,30,-150,30,-90,30,-30,30,30,30,90,30,150,30,210,-30,210
+'Level 23 - Tubes 7 - spiral
+DefData 0,300,0,-210,0,-140,-70,-70,-140,0,-210,70,-140,140,-70,210,0,140,70,70,140,0,210,-70,140,-140,70,-70,0,0,-70,70,0,0,70
+'Level 24 - Tubes 8 - ^U^
+DefData 0,100,90,240,-30,180,-60,120,-60,60,-30,30,30,30,90,30,150,30,210,-30,210,-30,150,-30,90,-30,30,-60,-30,-120,-60,-180,-60,-240,-30
+'Level 25 - Tubes 9 - half spade
+DefData 0,350,0,30,-270,30,-225,60,-180,120,-135,180,-90,210,-45,240,0,270,45,240,112,180,180,120,180,60,112,30,45,30,90,30,135,30,180
+'Level 26 - Tubes 10 - diagonal line
+DefData 0,250,90,300,-200,260,-175,220,-150,180,-125,140,-100,100,-75,60,-50,20,-25,-20,0,-60,25,-100,50,-140,75,-180,100,-220,125,-260,150,-300,175
+'Level 27 - Tubes 11 - jagged horz line
+DefData 0,200,200,280,0,240,25,200,0,160,25,120,0,80,25,40,0,0,25,0,-25,-40,0,-80,-25,-120,0,-160,-25,-200,0,-240,-25,-280,0
+'Level 28 - Tubes 12 - star/cross
+DefData 1,400,-80,-100,100,-200,100,-150,0,-200,-100,-100,-100,-100,-200,0,-150,100,-200,100,-100,200,-100,150,0,200,100,100,100,100,200,0,150,-100,200
+'Level 29 - Tubes 13 - claw
+DefData 1,400,-100,200,-35,150,-140,225,-70,300,0,200,70,100,140,0,210,-100,140,-200,70,-300,0,-225,-70,-150,-140,-200,-35,-100,0,0,35,100,0
+'Level 30 - Tubes 14 - ^-^
+DefData 0,200,150,267,-10,227,-37,187,-37,148,-12,120,25,100,75,60,75,20,75,-20,75,-60,75,-100,75,-120,25,-148,-12,-187,-37,-227,-37,-267,-10
+'Level 31 - Tubes 15 - bent steps
+DefData 0,200,80,245,-150,210,-50,280,0,210,50,210,150,140,100,70,200,35,100,-35,100,-70,200,-140,100,-210,150,-210,50,-280,0,-210,-50,-245,-150
+'Level 32 - Tubes 16 - triple infinity
+DefData 1,310,0,35,105,105,0,175,-105,280,-35,280,35,175,105,105,0,35,-105,-35,-105,-105,0,-175,105,-280,35,-280,-35,-175,-105,-105,0,-35,105
+'Level 33 - rainbow
+DefData 0,480,-80,-199,-23,-191,-69,-176,-113,-154,-152,-126,-185,-94,-211,-58,-229,-19,-238,19,-238,58,-229,94,-211,126,-185,154,-152,176,-113,191,-69,199,-23
+'Level 34 - pointy square
+DefData 1,400,-80,-200,200,-170,100,-130,0,-170,-100,-200,-200,-100,-170,0,-130,100,-170,200,-200,170,-100,130,0,170,100,200,200,100,170,0,130,-100,170
+'Level 35 - 3 leaf clover
+DefData 1,375,0,0,0,170,0,260,66,190,144,75,118,0,0,-85,-128,-80,-228,0,-270,80,-228,85,-128,0,0,-75,118,-190,144,-260,66,-170,0
+'Level 36 - lips
+DefData 1,400,-80,290,0,244,76,171,141,86,164,0,160,-86,164,-171,141,-244,76,-290,0,-244,-76,-171,-141,-76,-154,0,-100,76,-154,171,-141,244,-76
+'Level 37 - /\/
+DefData 0,310,0,280,-60,280,21,270,108,240,170,170,198,100,173,60,117,20,41,-20,-41,-60,-117,-100,-173,-170,-198,-240,-170,-270,-108,-280,-21,-280,60
+'Level 38 - cat
+DefData 1,400,-80,0,-100,66,-124,131,-111,204,-186,220,-60,214,46,161,121,86,174,0,180,-86,174,-161,121,-214,46,-220,-60,-204,-186,-131,-111,-66,-124
+'Level 39 - rocket
+DefData 1,390,-40,-119,168,-175,108,-120,64,-65,0,-30,-74,-15,-158,0,-242,15,-158,30,-74,65,0,110,64,175,108,119,168,56,128,0,168,-56,128
+'Level 40 - pontiac
+DefData 1,370,-50,160,-150,250,-190,200,-110,150,-30,100,50,50,130,0,210,-50,130,-100,50,-150,-30,-200,-110,-250,-190,-160,-150,-75,-110,0,-70,75,-110
+'Level 41 - Ev3
+DefData 0,220,40,170,-188,270,-136,230,-56,270,36,190,46,190,148,100,148,40,220,-40,220,-100,148,-190,148,-190,56,-270,36,-230,-56,-270,-136,-170,-188
+'Level 42 - \O/
+DefData 0,270,30,300,130,200,160,100,160,0,140,-100,90,-150,0,-130,-100,-50,-170,50,-170,130,-100,150,0,100,90,0,140,-100,160,-200,160,-300,130
+'Level 43 - yakhorns
+DefData 0,200,80,120,-210,200,-185,240,-100,200,-25,120,0,90,65,60,130,30,195,-30,195,-60,130,-90,65,-120,0,-200,-25,-240,-100,-200,-185,-120,-210
+'Level 44 - asteroid
+DefData 1,300,40,-150,90,-220,30,-220,-90,-140,-90,-60,-90,-100,-200,0,-200,100,-200,160,-140,230,-70,100,-30,160,20,220,70,130,170,70,100,-70,160
+'Level 45 - broken house
+DefData 0,140,90,70,-138,150,-116,200,-46,200,36,200,116,230,228,120,178,40,210,-40,210,-120,178,-230,228,-200,116,-200,36,-200,-46,-150,-116,-70,-138
+'Level 46 - overlap star
+DefData 1,300,0,-80,80,-100,10,-200,0,-100,-10,-80,-80,-10,-100,0,-200,10,-100,80,-80,100,-10,200,0,100,10,80,80,10,100,0,200,-10,100
+'Level 47 - pentagon
+DefData 1,440,-130,160,-80,250,-20,220,60,190,140,160,220,80,220,0,220,-80,220,-160,220,-190,140,-220,60,-250,-20,-160,-80,-75,-140,0,-190,75,-140
+'Level 48 - skull
+DefData 1,400,-80,180,40,124,86,111,151,66,204,0,220,-66,204,-111,151,-124,86,-180,40,-194,-46,-161,-131,-86,-184,0,-200,86,-184,161,-131,194,-46
--- /dev/null
+Strict
+
+' this generates the web coordinates for tempest
+
+Local fh = WriteFile("boarddata.bmx")
+Local a#
+
+WriteLine fh,"' Continuous, CenterY, YOFFSET, x1,y1,...x16,y16 - created by boardgen"
+
+For Local b = 1 To 48
+Select b
+Case 1 'circle (level 1)
+
+ WriteLine fh,"'Level 1 - circle"
+ Local s$ = "DefData "+True+",400"+",-80,"
+
+ For a#=0 Until 16 '360 Step 22.5 '30
+ s$ = s$ + Int(Cos(a*22.5)*200) +","+ Int(Sin(a*22.5)*200)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 2 ' square (level 2)
+ Local stepsx#[] = [2.0,2.0,2.0,2.0,2.0,1.0,0.0,-1.0,-2.0,-2.0,-2.0,-2.0,-2.0,-1.0,0.0,1.0]
+ Local stepsy#[] = [2.0,1.0,0.0,-1.0,-2.0,-2.0,-2.0,-2.0,-2.0,-1.0,0.0,1.0,2.0,2.0,2.0,2.0]
+
+ WriteLine fh,"'Level 2 - square"
+ Local s$ = "DefData "+True+",400"+",-80,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*100) +","+ Int(stepsy[a]*100)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 3 ' plus (level 3)
+ Local stepsx#[] = [1.0,2.0,2.0,2.0,1.0,1.0,0.0,-1.0,-1.0,-2.0,-2.0,-2.0,-1.0,-1.0,0.0,1.0]
+ Local stepsy#[] = [1.0,1.0,0.0,-1.0,-1.0,-2.0,-2.0,-2.0,-1.0,-1.0,0.0,1.0,1.0,2.0,2.0,2.0]
+
+ WriteLine fh,"'Level 3 - plus"
+ Local s$ = "DefData "+True+",400"+",-80,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*100) +","+ Int(stepsy[a]*100)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 4 'binoculars - level 4
+ Local stepsx#[] = [ 1.0, 3.0, 5.5, 6.5, 6.5, 5.5, 3.0, 1.0,-1.0,-3.0,-5.5,-6.5,-6.5,-5.5,-3.0,-1.0]
+ Local stepsy#[] = [-3.5,-5.0,-4.0,-2.0, 1.0, 3.0, 4.0, 2.5, 2.5, 4.0, 3.0, 1.0,-2.0,-4.0,-5.0,-3.5]
+
+ WriteLine fh,"'Level 4 - binoculars"
+ Local s$ = "DefData "+True+",380"+",-75,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*40) +","+ Int(-stepsy[a]*30)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 5 ' cross level 5
+ Local stepsx#[] = [ 1.0, 2.0, 4.0, 7.0, 7.0, 4.0, 2.0, 1.0,-1.0,-2.0,-4.0,-7.0,-7.0,-4.0,-2.0,-1.0]
+ Local stepsy#[] = [-7.0,-4.0,-2.0,-1.0, 1.0, 2.0, 4.0, 7.0, 7.0, 4.0, 2.0, 1.0,-1.0,-2.0,-4.0,-7.0]
+
+ WriteLine fh,"'Level 5 - cross"
+ Local s$ = "DefData "+True+",415"+",-90,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*30) +","+ Int(-stepsy[a]*30)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 6 ' triangle level 6
+ Local stepsx#[] = [ 3.4, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0,-1.0,-2.0,-3.0,-4.0,-5.0,-3.4,-1.6, 0.0, 1.6]
+ Local stepsy#[] = [ 6.0, 6.0, 3.0, 0.0,-3.0,-6.0,-9.0,-6.0,-3.0, 0.0, 3.0, 6.0, 6.0, 6.0, 6.0, 6.0]
+
+ WriteLine fh,"'Level 6 - triangle"
+ Local s$ = "DefData "+True+",390"+",-40,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*35) +","+ Int(stepsy[a]*28)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 7 'clover (level 7)
+ Local stepsx#[] = [1.8, 2.0, 1.0, 2.0, 1.8, 0.5, 0.0,-0.5,-1.8,-2.0,-1.0,-2.0,-1.8,-0.5, 0.0, 0.5]
+ Local stepsy#[] = [1.8, 0.5, 0.0,-0.5,-1.8,-2.0,-1.0,-2.0,-1.8,-0.5, 0.0, 0.5, 1.8, 2.0, 1.0, 2.0]
+
+ WriteLine fh,"'Level 7 - clover"
+ Local s$ = "DefData "+True+",310"+",-10,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*100) +","+ Int(stepsy[a]*100)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 8 'V (level 8)
+ Local stepsx#[] = [ 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0,-1.0,-2.0,-3.0,-4.0,-5.0,-6.0,-7.0,-8.0]
+ Local stepsy#[] = [ 4.0, 3.0, 2.0, 1.0, 0.0,-1.0,-2.0,-3.0,-3.0,-2.0,-1.0, 0.0, 1.0, 2.0, 3.0, 4.0]
+
+ WriteLine fh,"'Level 8 - Vee"
+ Local s$ = "DefData "+False+",240"+",90,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(stepsx[a]*30) +","+ Int(-stepsy[a]*55)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+\vCase 9 'steps (level 9)
+ Local stepsx#[] = [ 7.0, 7.0, 5.0, 5.0, 3.0, 3.0, 1.0, 1.0,-1.0,-1.0,-3.0,-3.0,-5.0,-5.0,-7.0,-7.0]
+ Local stepsy#[] = [-3.0,-1.0,-1.0, 1.0, 1.0, 3.0, 3.0, 5.0, 5.0, 3.0, 3.0, 1.0, 1.0,-1.0,-1.0,-3.0]
+
+ WriteLine fh,"'Level 9 - steps"
+ Local s$ = "DefData "+False+",200"+",100,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(stepsx[a]*40) +","+ Int(stepsy[a]*36)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 10 'U (level 10)
+ Local stepsx#[] = [ 7.0, 7.0, 7.0, 7.0, 6.8, 5.5, 3.5, 1.0,-1.0,-3.5,-5.5,-6.8,-7.0,-7.0,-7.0,-7.0]
+ Local stepsy#[] = [-5.0,-3.0,-1.0, 1.0, 3.0, 5.0, 6.5, 7.0, 7.0, 6.5, 5.0, 3.0, 1.0,-1.0,-3.0,-5.0]
+
+ WriteLine fh,"'Level 10 - U"
+ Local s$ = "DefData "+False+",500"+",-200,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(stepsx[a]*30) +","+ Int(stepsy[a]*35)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 11 'line (level 11)
+ Local x# = -7.5
+
+ WriteLine fh,"'Level 11 - horiz line"
+ Local s$ = "DefData "+False+",230"+",70,"
+ For a#=0 Until 16
+ s$ = s$ + Int(-x*40) +","+"160"
+ x:+1.0
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 12 'heart (level 12)
+ Local stepsx#[] = [2.0, 5.0, 6.0, 6.0, 5.0, 3.0, 0.0,-3.0,-5.0,-6.0,-6.0,-5.0,-2.0,-0.5, 0.0, 0.5]
+ Local stepsy#[] = [6.0, 5.7, 2.0,-2.0,-5.0,-7.0,-8.0,-7.0,-5.0,-2.0, 2.0, 5.7, 6.0, 3.0,-1.0, 3.0]
+
+ WriteLine fh,"'Level 12 - heart"
+ Local s$ = "DefData "+True+",540"+",-210,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(stepsx[a]*30) +","+ Int(-stepsy[a]*30)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 13 ' star (level 13)
+ Local stepsx#[] = [1.5,2.5,2.0, 2.5, 1.5, 1.0, 0.0,-1.0,-1.5,-2.5,-2.0,-2.5,-1.5,-1.0,0.0,1.0]
+ Local stepsy#[] = [1.3,1.0,0.0,-1.0,-1.3,-2.2,-1.7,-2.2,-1.3,-1.0, 0.0, 1.0, 1.3, 2.2,1.7,2.2]
+
+ WriteLine fh,"'Level 13 - star"
+ Local s$ = "DefData "+True+",415"+",-95,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*80) +","+ Int(stepsy[a]*100)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 14 'W (level 14)
+ Local stepsx#[] = [ 8.0, 7.0, 6.7, 6.1, 4.7, 2.7, 1.5, 0.6,-0.6,-1.5,-2.7,-4.7,-6.1,-6.7,-7.0,-8.0]
+ Local stepsy#[] = [-3.0,-1.0, 1.5, 3.7, 5.2, 5.2, 4.0, 2.0, 2.0, 4.0, 5.2, 5.2, 3.7, 1.5,-1.0,-3.0]
+
+ WriteLine fh,"'Level 14 - W"
+ Local s$ = "DefData "+False+",140"+",120,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(stepsx[a]*35) +","+ Int(stepsy[a]*35)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 15 ' broken v (level 15)
+
+ Local stepsx#[] = [ -8.0, -7.5, -7.0, -6.5, -4.0, -2.8, -2.1, -1.1, 1.0, 2.5, 3.7, 4.5, 5.0, 5.5, 6.5,8.0]
+ Local stepsy#[] = [ 8.0, 5.4, 3.0, 0.2, 0.7,-1.5,-4.0,-5.5,-5.0,-6.5,-4.0,-2.0, 1.0, 3.0, 5.0, 7.0]
+
+ WriteLine fh,"'Level 15 - broken V"
+ Local s$ = "DefData "+False+",240"+",75,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*35) +","+ Int(-stepsy[a]*30)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 16 'level 16 infinity
+ Local stepsx#[] = [0.0,-1.0,-3.0,-5.0,-6.0,-5.0,-3.0,-1.0, 0.0, 1.0, 3.0, 5.0, 6.0, 5.0, 3.0, 1.0]
+ Local stepsy#[] = [0.0, 2.0, 3.0, 2.0, 0.0,-2.0,-3.0,-2.0, 0.0, 2.0, 3.0, 2.0, 0.0,-2.0,-3.0,-2.0]
+
+ WriteLine fh,"'Level 16 - infinity"
+ Local s$ = "DefData "+True+",310"+",0,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*48) +","+ Int(-stepsy[a]*55)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+
+'Tubes
+Case 17 ' octagon (level 1)
+ Local stepsx#[] = [2.0, 3.0, 3.0, 3.0, 2.0, 1.0, 0.0,-1.0,-2.0,-3.0,-3.0,-3.0,-2.0,-1.0,0.0,1.0]
+ Local stepsy#[] = [1.5, 1.0, 0.0,-1.0,-1.5,-2.0,-2.0,-2.0,-1.5,-1.0, 0.0, 1.0, 1.5, 2.0,2.0,2.0]
+
+ WriteLine fh,"'Level 17 - Tubes 1 - octagon"
+ Local s$ = "DefData "+True+",400"+",-80,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*60) +","+ Int(stepsy[a]*80)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 18 ' tear (level 2)
+ Local stepsx#[] = [ 3.5, 4.5, 4.5, 3.5, 2.0, 1.0, 0.0,-1.0,-2.0,-3.5,-4.5,-4.5,-3.5,-1.6, 0.0, 1.6]
+ Local stepsy#[] = [ 5.0, 3.0, 0.0,-2.0,-4.0,-6.0,-8.0,-6.0,-4.0,-2.0, 0.0, 3.0, 5.0, 6.0, 6.0, 6.0]
+
+ WriteLine fh,"'Level 18 - Tubes 2 - tear"
+ Local s$ = "DefData "+True+",390"+",-40,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*35) +","+ Int(stepsy[a]*30)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 19 'closed V (level 3)
+ WriteLine fh,"'Level 19 - Tubes 3 - false closed V"
+ WriteLine fh,"DefData 0,400,0,0,-210,75,-210,150,-175,225,-125,225,-50,150,-25,95,45,50,110,-50,110,-95,45,-150,-25,-225,-50,-225,-125,-150,-175,-75,-210,0,-210"
+
+Case 20 ' bowtie (level 4)
+ Local stepsx#[] = [3.0, 5.0, 5.0, 5.0, 5.0, 3.0, 1.0,-1.0,-3.0,-5.0,-5.0,-5.0,-5.0,-3.0,-1.0, 1.0]
+ Local stepsy#[] = [1.5, 3.0, 1.5,-1.5,-3.0,-1.5,-1.0,-1.0,-1.5,-3.0,-1.5, 1.5, 3.0, 1.5, 1.0, 1.0]
+
+ WriteLine fh,"'Level 20 - Tubes 4 - bowtie"
+ Local s$ = "DefData "+True+",300"+",0,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*50) +","+ Int(stepsy[a]*50)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 21 ' | (level 5)
+ Local stepsx#[] = [ 7.0, 7.0, 7.0, 7.0, 7.0, 6.5, 6.0, 5.0, 4.0, 3.5, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0]
+ Local stepsy#[] = [ 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0,-1.0,-2.0,-3.0,-4.0,-5.0,-6.0,-7.0,-8.0]
+
+ WriteLine fh,"'Level 21 - Tubes 5 - vert bent line"
+ Local s$ = "DefData "+False+",280"+",0,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(stepsx[a]*30) +","+ Int(-stepsy[a]*30)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 22 ' [] (level 6)
+ Local stepsx#[] = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0, 1.0]
+ Local stepsy#[] = [5.0, 3.0, 1.0,-1.0,-3.0,-5.0,-7.0,-7.0,-5.0,-3.0,-1.0, 1.0, 3.0, 5.0, 7.0, 7.0]
+
+ WriteLine fh,"'Level 22 - Tubes 6 - thin rectangle"
+ Local s$ = "DefData "+True+",300"+",0,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*30) +","+ Int(stepsy[a]*30)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+
+Case 23 ' @ (level 7)
+ Local stepsx#[] = [ 3.0, 2.0, 1.0, 0.0,-1.0,-2.0,-3.0,-2.0,-1.0, 0.0, 1.0, 2.0, 1.0, 0.0,-1.0, 0.0]
+ Local stepsy#[] = [ 0.0, 1.0, 2.0, 3.0, 2.0, 1.0, 0.0,-1.0,-2.0,-3.0,-2.0,-1.0, 0.0, 1.0, 0.0,-1.0]
+
+ WriteLine fh,"'Level 23 - Tubes 7 - spiral"
+ Local s$ = "DefData "+False+",300"+",0,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*70) +","+ Int(-stepsy[a]*70)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+
+Case 24 ' ^U^ (level 8)
+ Local stepsx#[] = [8.0, 6.0, 4.0, 2.0, 1.0, 1.0, 1.0, 1.0,-1.0,-1.0,-1.0,-1.0,-2.0,-4.0,-6.0,-8.0]
+ Local stepsy#[] = [1.0, 2.0, 2.0, 1.0,-1.0,-3.0,-5.0,-7.0,-7.0,-5.0,-3.0,-1.0, 1.0, 2.0, 2.0, 1.0]
+
+ WriteLine fh,"'Level 24 - Tubes 8 - ^U^"
+ Local s$ = "DefData "+False+",100"+",90,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(stepsx[a]*30) +","+ Int(-stepsy[a]*30)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+
+Case 25 ' half spade (level 9)
+ Local stepsx#[] = [ 1.0, 1.0, 2.0, 4.0, 6.0, 7.0, 8.0, 9.0, 8.0, 6.0, 4.0, 2.0, 1.0, 1.0, 1.0, 1.0]
+ Local stepsy#[] = [ 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0,-1.0,-2.5,-4.0,-4.0,-2.5,-1.0,-2.0,-3.0,-4.0]
+
+ WriteLine fh,"'Level 25 - Tubes 9 - half spade"
+ Local s$ = "DefData "+False+",350"+",0,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(stepsx[a]*30) +","+ Int(-stepsy[a]*45)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 26 ' / (level 10)
+ Local stepsx#[] = [-7.5,-6.5,-5.5,-4.5,-3.5,-2.5,-1.5,-0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5]
+ Local stepsy#[] = [ 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0,-1.0,-2.0,-3.0,-4.0,-5.0,-6.0,-7.0]
+
+ WriteLine fh,"'Level 26 - Tubes 10 - diagonal line"
+ Local s$ = "DefData "+False+",250"+",90,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*40) +","+ Int(-stepsy[a]*25)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 27 ' --- (level 11)
+ Local stepsx#[] = [-7.0,-6.0,-5.0,-4.0,-3.0,-2.0,-1.0, 0.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]
+ Local stepsy#[] = [ 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0,-1.0,-0.0,-1.0,-0.0,-1.0,-0.0,-1.0,-0.0]
+
+ WriteLine fh,"'Level 27 - Tubes 11 - jagged horz line"
+ Local s$ = "DefData "+False+",200"+",200,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*40) +","+ Int(stepsy[a]*25)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 28 ' star (level 12)
+ Local stepsx#[] = [1.0, 2.0, 1.5, 2.0, 1.0, 1.0, 0.0,-1.0,-1.0,-2.0,-1.5,-2.0,-1.0,-1.0, 0.0, 1.0]
+ Local stepsy#[] = [1.0, 1.0, 0.0,-1.0,-1.0,-2.0,-1.5,-2.0,-1.0,-1.0, 0.0, 1.0, 1.0, 2.0, 1.5, 2.0]
+
+ WriteLine fh,"'Level 28 - Tubes 12 - star/cross"
+ Local s$ = "DefData "+True+",400"+",-80,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-stepsx[a]*100) +","+ Int(stepsy[a]*100)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 29 ' claw (level 13)
+ Local stepsx#[] = [4.0, 3.0, 4.5, 6.0, 4.0, 2.0, 0.0,-2.0,-4.0,-6.0,-4.5,-3.0,-4.0,-2.0, 0.0, 2.0]
+ Local stepsy#[] = [1.0, 4.0, 2.0, 0.0,-2.0,-4.0,-6.0,-4.0,-2.0, 0.0, 2.0, 4.0, 1.0, 0.0,-1.0, 0.0]
+
+ WriteLine fh,"'Level 29 - Tubes 13 - claw"
+ Local s$ = "DefData "+True+",400"+",-100,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(stepsx[a]*50) +","+ Int(-stepsy[a]*35)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 30 ' ^-^ (level 14)
+ Local stepsx#[] = [6.7, 5.7, 4.7, 3.7, 3.0, 2.5, 1.5, 0.5,-0.5,-1.5,-2.5,-3.0,-3.7,-4.7,-5.7,-6.7]
+ Local stepsy#[] = [0.4, 1.5, 1.5, 0.5,-1.0,-3.0,-3.0,-3.0,-3.0,-3.0,-3.0,-1.0, 0.5, 1.5, 1.5, 0.4]
+
+ WriteLine fh,"'Level 30 - Tubes 14 - ^-^"
+ Local s$ = "DefData "+False+",200"+",150,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(stepsx[a]*40) +","+ Int(-stepsy[a]*25)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 31 ' bent steps (level 15)
+ Local stepsx#[] = [ 7.0, 6.0, 8.0, 6.0, 6.0, 4.0, 2.0, 1.0,-1.0,-2.0,-4.0,-6.0,-6.0,-8.0,-6.0,-7.0]
+ Local stepsy#[] = [-3.0,-1.0, 0.0, 1.0, 3.0, 2.0, 4.0, 2.0, 2.0, 4.0, 2.0, 3.0, 1.0, 0.0,-1.0,-3.0]
+
+ WriteLine fh,"'Level 31 - Tubes 15 - bent steps"
+ Local s$ = "DefData "+False+",200"+",80,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(stepsx[a]*35) +","+ Int(stepsy[a]*50)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 32 'triple infinity <><><> (level 16)
+ Local stepsx#[] = [ 1.0, 3.0, 5.0, 8.0, 8.0, 5.0, 3.0, 1.0,-1.0,-3.0,-5.0,-8.0,-8.0,-5.0,-3.0,-1.0]
+ Local stepsy#[] = [-3.0, 0.0, 3.0, 1.0,-1.0,-3.0, 0.0, 3.0, 3.0, 0.0,-3.0,-1.0, 1.0, 3.0, 0.0,-3.0]
+
+ WriteLine fh,"'Level 32 - Tubes 16 - triple infinity"
+ Local s$ = "DefData "+True+",310"+",0,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(stepsx[a]*35) +","+ Int(-stepsy[a]*35)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 33 'arc
+ WriteLine fh,"'Level 33 - rainbow"
+ Local s$ = "DefData "+False+",480"+",-80,"
+
+ For a#=0 Until 16
+ s$ = s$ + Int(-Cos((a+.5)*11.25)*200) +","+ Int(-Sin((a+.5)*11.25)*240)
+ If a < 15 Then s$=s$+","
+ Next
+ WriteLine fh,s$
+
+Case 34 ' pointy square
+ WriteLine fh,"'Level 34 - pointy square"
+ WriteLine fh,"DefData 1,400,-80,-200,200,-170,100,-130,0,-170,-100,-200,-200,-100,-170,0,-130,100,-170,200,-200,170,-100,130,0,170,100,200,200,100,170,0,130,-100,170"
+
+Case 35 ' 3 leaf
+ WriteLine fh,"'Level 35 - 3 leaf clover"
+ WriteLine fh,"DefData 1,375,0,0,0,170,0,260,66,190,144,75,118,0,0,-85,-128,-80,-228,0,-270,80,-228,85,-128,0,0,-75,118,-190,144,-260,66,-170,0"
+
+Case 36 ' lips
+ WriteLine fh,"'Level 36 - lips"
+ WriteLine fh,"DefData 1,400,-80,290,0,244,76,171,141,86,164,0,160,-86,164,-171,141,-244,76,-290,0,-244,-76,-171,-141,-76,-154,0,-100,76,-154,171,-141,244,-76"
+
+Case 37 ' ~
+ WriteLine fh,"'Level 37 - /\/"
+ WriteLine fh,"DefData 0,310,0,280,-60,280,21,270,108,240,170,170,198,100,173,60,117,20,41,-20,-41,-60,-117,-100,-173,-170,-198,-240,-170,-270,-108,-280,-21,-280,60"
+
+Case 38 ' cat
+ WriteLine fh,"'Level 38 - cat"
+ WriteLine fh,"DefData 1,400,-80,0,-100,66,-124,131,-111,204,-186,220,-60,214,46,161,121,86,174,0,180,-86,174,-161,121,-214,46,-220,-60,-204,-186,-131,-111,-66,-124"
+
+Case 39 ' rocket
+ WriteLine fh,"'Level 39 - rocket"
+ WriteLine fh,"DefData 1,390,-40,-119,168,-175,108,-120,64,-65,0,-30,-74,-15,-158,0,-242,15,-158,30,-74,65,0,110,64,175,108,119,168,56,128,0,168,-56,128"
+
+Case 40 ' pontiac
+ WriteLine fh,"'Level 40 - pontiac"
+ WriteLine fh,"DefData 1,370,-50,160,-150,250,-190,200,-110,150,-30,100,50,50,130,0,210,-50,130,-100,50,-150,-30,-200,-110,-250,-190,-160,-150,-75,-110,0,-70,75,-110"
+
+Case 41 ' Ev3
+ WriteLine fh,"'Level 41 - Ev3"
+ WriteLine fh,"DefData 0,220,40,170,-188,270,-136,230,-56,270,36,190,46,190,148,100,148,40,220,-40,220,-100,148,-190,148,-190,56,-270,36,-230,-56,-270,-136,-170,-188"
+
+Case 42 ' \O/
+ WriteLine fh,"'Level 42 - \O/"
+ WriteLine fh,"DefData 0,270,30,300,130,200,160,100,160,0,140,-100,90,-150,0,-130,-100,-50,-170,50,-170,130,-100,150,0,100,90,0,140,-100,160,-200,160,-300,130"
+
+Case 43 ' yakhorns
+ WriteLine fh,"'Level 43 - yakhorns"
+ WriteLine fh, "DefData 0,200,80,120,-210,200,-185,240,-100,200,-25,120,0,90,65,60,130,30,195,-30,195,-60,130,-90,65,-120,0,-200,-25,-240,-100,-200,-185,-120,-210"
+
+Case 44 ' asteroid
+ WriteLine fh,"'Level 44 - asteroid"
+ WriteLine fh,"DefData 1,300,40,-150,90,-220,30,-220,-90,-140,-90,-60,-90,-100,-200,0,-200,100,-200,160,-140,230,-70,100,-30,160,20,220,70,130,170,70,100,-70,160"
+
+Case 45 ' broken house
+ WriteLine fh,"'Level 45 - broken house"
+ WriteLine fh, "DefData 0,140,90,70,-138,150,-116,200,-46,200,36,200,116,230,228,120,178,40,210,-40,210,-120,178,-230,228,-200,116,-200,36,-200,-46,-150,-116,-70,-138"
+
+Case 46 ' overlap star +
+ WriteLine fh,"'Level 46 - overlap star"
+ WriteLine fh,"DefData 1,300,0,-80,80,-100,10,-200,0,-100,-10,-80,-80,-10,-100,0,-200,10,-100,80,-80,100,-10,200,0,100,10,80,80,10,100,0,200,-10,100"
+
+Case 47 ' pentagon
+ WriteLine fh,"'Level 47 - pentagon"
+ WriteLine fh,"DefData 1,440,-130,160,-80,250,-20,220,60,190,140,160,220,80,220,0,220,-80,220,-160,220,-190,140,-220,60,-250,-20,-160,-80,-75,-140,0,-190,75,-140"
+
+Case 48 ' skull
+ WriteLine fh,"'Level 48 - skull"
+ WriteLine fh,"DefData 1,400,-80,180,40,124,86,111,151,66,204,0,220,-66,204,-111,151,-124,86,-180,40,-194,-46,-161,-131,-86,-184,0,-200,86,-184,161,-131,194,-46"
+
+End Select
+Next
+
+CloseFile(fh)
+
--- /dev/null
+Strict
+
+Import BRL.FreeAudioAudio
+Import BRL.Wavloader
+
+
+' sfx
+Global ticksfx:TSound
+Global bulletsfx:TSound
+Global shotsfx:TSound
+Global zapsfx:TSound
+Global zoominsfx:TSound
+Global zoomoutsfx:TSound
+
+Global pulsesfx:TSound
+
+Global killedbybulletsfx:TSound
+Global killedbyflippersfx:TSound
+Global killedbyspikesfx:TSound
+Global killedbypulsarsfx:TSound
+Global killedbyfuseballsfx:TSound
+
+Global flippershotsfx:TSound
+Global spikeshotsfx:TSound
+Global pulsarshotsfx:TSound
+Global fuseballshotsfx:TSound
+Global tankershotsfx:TSound
+Global spinnershotsfx:TSound
+
+Global bonusmansfx:TSound
+
+Incbin "sfx/tick.wav"
+Incbin "sfx/bullet.wav"
+Incbin "sfx/shot.wav"
+Incbin "sfx/zap.wav"
+Incbin "sfx/zoomin.wav"
+Incbin "sfx/zoomout.wav"
+Incbin "sfx/pulse.wav"
+Incbin "sfx/killedbybullet.wav"
+Incbin "sfx/killedbyflipper.wav"
+Incbin "sfx/flippershot.wav"
+Incbin "sfx/spikeshot.wav"
+Incbin "sfx/bonus.wav"
+
+Function LoadSfx()
+
+ ticksfx = LoadSound("incbin::sfx/tick.wav")
+ bulletsfx = LoadSound("incbin::sfx/bullet.wav")
+ shotsfx = LoadSound("incbin::sfx/shot.wav")
+ zapsfx = LoadSound("incbin::sfx/zap.wav")
+ zoominsfx = LoadSound("incbin::sfx/zoomin.wav")
+ zoomoutsfx = LoadSound("incbin::sfx/zoomout.wav")
+
+ pulsesfx = LoadSound("incbin::sfx/pulse.wav")
+
+ killedbybulletsfx = LoadSound("incbin::sfx/killedbybullet.wav")
+ killedbyflippersfx = LoadSound("incbin::sfx/killedbyflipper.wav")
+ killedbyspikesfx = LoadSound("incbin::sfx/killedbyflipper.wav")'*reused
+ killedbypulsarsfx = LoadSound("incbin::sfx/killedbyflipper.wav")'*reused
+ killedbyfuseballsfx = LoadSound("incbin::sfx/killedbyflipper.wav")'*reused
+
+ flippershotsfx = LoadSound("incbin::sfx/flippershot.wav")
+ spikeshotsfx = LoadSound("incbin::sfx/spikeshot.wav")
+ pulsarshotsfx = LoadSound("incbin::sfx/flippershot.wav")'*reused
+ fuseballshotsfx = LoadSound("incbin::sfx/flippershot.wav")'*reused
+ tankershotsfx = LoadSound("incbin::sfx/flippershot.wav")'*reused
+ spinnershotsfx = LoadSound("incbin::sfx/flippershot.wav")'*reused
+
+ bonusmansfx = LoadSound("incbin::sfx/bonus.wav")
+
+End Function
\ No newline at end of file
--- /dev/null
+'Tempest
+'Started by David Bird (Birdie) - BlitzMax 1.10 Samples
+
+'Completed by Mark Incitti (Mark1nc) - June 2005
+
+' Version 1.5 - July 18
+' added 16 more tubes
+' tweaked the difficulty and release rates
+
+' Version 1.1 - July 7
+' fixed egg hatching to act 7 (non-existant case - disappeared)
+' changed fuseballs - only kill when off the edge w>1 or w<7
+' added Tempest Tubes boards
+
+' CTRL - Fire!
+' SPACE - Superzapper
+' L/R - Move
+' ESC - Quit
+' T - select tube set
+
+Strict
+
+Import "transformfunctions.bmx"
+Import "vectorfont.bmx"
+Import "sfx.bmx"
+
+'Setup Graphics mode
+Graphics CWidth,CHeight,32 ',0 windowed ',32 'fullscreen
+HideMouse
+SeedRnd(MilliSecs())
+
+
+'defined colours index
+Const COL_BULLETS = 0
+Const COL_CLAW = 1
+Const COL_TANKERS = 2
+Const COL_FLIPPERS = 3
+Const COL_PULSARS = 4
+Const COL_SPIKERS = 5
+Const COL_LEVEL = 6
+Const COL_INFO = 7
+
+'death types
+Const KILLED_BY_BULLET = 0
+Const KILLED_BY_PULSAR = 2
+Const KILLED_BY_SPIKE = 3
+Const KILLED_BY_FLIPPER = 4
+Const KILLED_BY_FUSEBALL = 5
+
+'level states
+Const LEVEL_BEGIN = 0
+Const LEVEL_COMPLETE = 1
+Const LEVEL_READY = 2
+Const LEVEL_PLAYER_DYING = 3
+Const LEVEL_ZOOMING = 4
+Const LEVEL_START_ZOOM = 5
+Const LEVEL_REVERSE_ZOOM = 6
+Const LEVEL_INTO_VORTEX = 7
+
+'baddie types
+Const BAD_SPIKE = 1
+Const BAD_BULLET = -1
+Const BAD_SPINNER = 2
+Const BAD_FLIPPER = 3
+Const BAD_TANKER = 4
+Const BAD_PULSAR = 5
+Const BAD_FUSEBALL = 6
+
+Const PLAYERSPEED# = 0.25 '5 positions per edge 0,.25,.5,.75,1
+
+
+' lists of objects
+Global POINT_LIST:TList = New TList
+Global FUSEPOINT_LIST:TList = New TList
+Global EGG_LIST:TList = New TList
+Global EXPLOSION_LIST:TList = New TList
+Global SHOT_LIST:TList = New TList
+Global BADDIE_LIST:TList = New TList
+
+
+Global theLevel:Level
+Global current_level = 0
+Global current_color = 0
+Global current_board = 0
+Global tubes = 0
+Global hiscore = 0
+
+Global leveldata[64,3+32] ' Closed/Open, YCenter, YOFFSET, 16 x,y pairs
+
+Global eggsleft
+Global maxeggs
+Global enemiesleft
+Global hatchingeggs
+
+Global MainPlayer:Player
+Global superzapper = 2
+Global superzapperdisplay = 0
+Global startbonus = 0
+Global zapchan:TChannel = AllocChannel()
+
+Global canflip = True
+Global flipflipspeed = 15
+Global pulseflipspeed = 15
+Global fuseclimbspeed = 1
+Global tankerclimbspeed = 1
+
+Global pulsecount = 0
+Global pulsespeed = 1
+Global pulsing = False
+Global pulse_zh#
+Global pulsesalive = False
+
+Global fusex#[5,6,7]
+Global fusey#[5,6,7]
+Global fuseball_count
+Global fuseball_frame
+
+Global globalclock
+Global enemyreleaserate = 30
+Global hatchrate = 1
+Global rimit = False
+Global onrim = False
+
+Global showdebug
+
+LoadSfx()
+ReadFuseballData()
+ReadLevelData()
+
+theLevel = Level.Create()
+MainPlayer = Player.Create()
+
+Game()
+
+'BoardMaker()
+
+
+
+Function BoardMaker()
+
+ Local showcord = False
+ Local index = 0
+ Local cb = 0
+ Local xp[16]
+ Local yp[16]
+ Local c
+ While Not KeyHit(key_escape)
+
+ c = leveldata[cb,0]
+ k = 70
+ CCenterY = leveldata[cb,1]
+ YOFFSET = leveldata[cb,2]
+
+
+ For Local a = 0 Until 16
+ xp[a] = leveldata[cb,3+a*2]+400
+ yp[a] = leveldata[cb,3+a*2+1]+300
+ If index = a
+ DrawOval xp[a]-4,yp[a]-4,8,8
+ Else
+ DrawOval xp[a]-2,yp[a]-2,4,4
+ EndIf
+
+ If a > 0
+ DrawLine xp[a-1],yp[a-1],xp[a],yp[a]
+ EndIf
+ If showcord Then DrawText "("+(xp[a]-400)+","+(yp[a]-300)+")",xp[a]-30,yp[a]-20
+ Next
+ If c
+ DrawLine xp[15],yp[15],xp[0],yp[0]
+ EndIf
+
+ If KeyHit(key_c) Then showcord = 1-showcord
+ If KeyHit(key_b) Then cb = cb + 1 ; If cb > 47 Then cb = 0
+ If KeyHit(key_v) Then cb = cb - 1 ; If cb < 0 Then cb = 47
+
+ If KeyHit(key_COMMA) Then index = index - 1;If index < 0 Then index = 15
+ If KeyHit(key_PERIOD) Then index = index + 1;If index > 15 Then index = 0
+
+ If KeyHit(key_left) Then leveldata[cb,3+index*2]:-10
+ If KeyHit(key_right) Then leveldata[cb,3+index*2]:+10
+ If KeyHit(key_up) Then leveldata[cb,3+index*2+1]:-10
+ If KeyHit(key_down) Then leveldata[cb,3+index*2+1]:+10
+
+ DrawText cb,10,10
+
+ If KeyHit(key_s)
+ DebugLog "'Level "+cb
+ Local s$ = "DefData "+c+","+leveldata[cb,1]+","+leveldata[cb,2]+","
+ For Local a = 0 Until 16
+ s$ = s$ + Int(leveldata[cb,3+a*2]) +","+ Int(leveldata[cb,3+a*2+1])
+ If a < 15 Then s$=s$+","
+ Next
+ DebugLog s$
+ EndIf
+ Flip
+ Delay 30
+ Cls
+ Wend
+
+End Function
+
+
+
+
+
+
+
+Type Player
+ Field e_Index 'the edge the player is on
+ Field zPos ' player height
+ Field shottimer = 3 ' delay between shots timer
+ Field score
+ Field oldscore
+ Field bonusmencnt = 0 ' keep track of when bonus man is due
+ Field dying
+ Field men = 3
+ Field deathcount
+ Field deathtype
+ Field bonusdisplay
+ Field scl#=0.5 ' where on the edge 0.0, 0.25, 0.5, 0.75, 1.0
+ Field schan:TChannel = Null ' send all shot audio through this channel
+
+
+ Method SetEdge(index)
+ e_index = index
+ EndMethod
+
+
+ Method AddShot()
+ Shot.Create(theLevel.edges[e_Index],zPos)
+ EndMethod
+
+ Method ShiftLeft()
+ PlaySound(ticksfx)
+ If e_Index=0
+ If theLevel.continuous
+ e_Index = theLevel.e_Cnt-1
+ EndIf
+ Else
+ e_Index:-1
+ EndIf
+ EndMethod
+
+ Method ShiftRight()
+ PlaySound(ticksfx)
+ If e_Index=theLevel.e_cnt-1
+ If theLevel.continuous
+ e_Index = 0
+ EndIf
+ Else
+ e_Index:+1
+ EndIf
+ EndMethod
+
+ Method Update()
+
+ ' fire!
+ If KeyDown(KEY_LCONTROL)
+ If shottimer > 2
+ If deathcount = 0 ' no control when dying
+ If CountList(SHOT_LIST) < 12
+ Self.AddShot()
+ PlaySound(shotsfx, schan)
+ shottimer = 0
+ EndIf
+ EndIf
+ EndIf
+ EndIf
+ shottimer:+1
+
+ ' superzapper
+ If KeyHit(KEY_SPACE)
+ If deathcount = 0
+ If superzapper > 0
+ baddies.superZapit(superzapper)
+ superzapper:-1
+ EndIf
+ EndIf
+ EndIf
+
+ ' rotate
+ If KeyDown(KEY_LEFT)
+ If deathcount = 0
+ If theLevel.continuous
+ scl:-PLAYERSPEED
+ If scl<0 Then
+ Self.ShiftLeft()
+ scl = 1
+ EndIf
+ Else
+ If e_index > 0
+ scl:-PLAYERSPEED
+ If scl<0 Then
+ Self.ShiftLeft()
+ scl = 1
+ EndIf
+ Else
+ scl:-PLAYERSPEED
+ If scl < 0 Then
+ scl = 0
+ EndIf
+ EndIf
+ EndIf
+ EndIf
+ EndIf
+
+ ' rotate other direction
+ If KeyDown(KEY_RIGHT)
+ If deathcount = 0
+ If theLevel.continuous
+ scl:+PLAYERSPEED
+ If scl>1 Then
+ Self.ShiftRight()
+ scl = 0
+ EndIf
+ Else
+ If e_index < theLevel.e_cnt-1
+ scl:+PLAYERSPEED
+ If scl > 1 Then
+ Self.ShiftRight()
+ scl = 0
+ EndIf
+ Else
+ scl:+PLAYERSPEED
+ If scl > 1 Then
+ scl = 1
+ EndIf
+ EndIf
+ EndIf
+ EndIf
+ EndIf
+
+ ' this edge will be player colour
+ theLevel.edges[e_Index].hasplayer = True
+
+ 'we're dying if deathcount has been set to > 0
+ If deathcount > 0
+ deathcount:-1
+ If deathcount = 0
+ men:-1
+ deathtype = 0
+ EndIf
+ EndIf
+
+ 'check score for bonus
+ If score <> oldscore
+ If Int(score/10000) > bonusmencnt
+ men:+1
+ bonusdisplay = 100
+ bonusmencnt = score/10000
+ PlaySound(bonusmansfx)
+ EndIf
+ ' keep track of high score
+ If score > hiscore
+ hiscore = score
+ EndIf
+ EndIf
+
+ 'time to show rainbow pattern - for bonus man
+ If bonusdisplay > 0
+ If theLevel.state <> LEVEL_BEGIN
+ bonusdisplay:-1
+ EndIf
+ EndIf
+
+ EndMethod
+
+
+ Method Draw()
+ SetRotation 0
+ 'Draw it
+ Local zz#,zh#
+ Local x#[8],y#[8]
+ Select theLevel.state
+ Case LEVEL_BEGIN
+ zPos = theLevel.depth
+ zz = theLevel.position+theLevel.depth-zPos
+ zh# = (10.0)*(1.0-zz/1500.0)
+ Case LEVEL_READY
+ zPos = theLevel.depth
+ zz = theLevel.position+theLevel.depth-zPos
+ zh# = 20.0+10-Abs(scl-0.5)*10.0
+ Case LEVEL_PLAYER_DYING
+ Select deathtype
+ Case KILLED_BY_FLIPPER
+ zz = theLevel.position+theLevel.depth-zPos' +180-deathcount*3
+ zh# = 20.0+10-Abs(scl-0.5)*10.0
+ zh = zh * (deathcount*3)/180.0
+ Case KILLED_BY_PULSAR
+ zz = theLevel.position - Sin(deathcount*1.5)*100 +100
+ zh# = 20.0+10-Abs(scl-0.5)*10.0
+ Case KILLED_BY_BULLET
+ zz = theLevel.position + Sin(deathcount*1.5)*20 -20
+ zh# = 20.0+10-Abs(scl-0.5)*10.0
+ Default
+ zPos = theLevel.depth
+ zz = theLevel.position+theLevel.depth-zPos
+ zh# = 2+zPos/10-Abs(scl-0.5)*zPos/40
+ End Select
+ Case LEVEL_COMPLETE
+ zPos = theLevel.depth
+ zz = theLevel.position+theLevel.depth-zPos
+ zh# = 2+zPos/10-Abs(scl-0.5)*zPos/40
+ Case LEVEL_START_ZOOM
+ zPos = theLevel.depth
+ zz = theLevel.position+theLevel.depth-zPos
+ zh# = 2+zPos/10-Abs(scl-0.5)*zPos/40
+ Case LEVEL_ZOOMING
+ 'zPos = theLevel.depth
+ zz = theLevel.position+theLevel.depth-zPos
+ zh# = 30.0-Abs(scl-0.5)*zPos/40
+ Case LEVEL_REVERSE_ZOOM
+ zz = theLevel.position+theLevel.depth-zPos
+ zh# = 2+zPos/10-Abs(scl-0.5)*zPos/40
+ EndSelect
+ Local e:Edge = theLevel.edges[e_Index]
+ TForm(e.p1.x, e.p1.y,zz,x[0],y[0])
+ TForm(e.p2.x, e.p2.y,zz,x[1],y[1])
+
+ Local xn#
+ Local yn#
+
+ Local xd# = x[1]-x[0]
+ Local yd# = y[1]-y[0]
+ Local sz# = Sqr(xd*xd + yd*yd)
+ ' find a perpendicular line to the outside edge of the web
+ If sz = 0
+ xn# = 0
+ yn# = 0
+ Else
+ xn# = -yd/sz
+ yn# = xd/sz
+ EndIf
+ If scl > 0.0 And scl < 1.0
+ x[4] = x[1]-(xd)*scl + xn*zh
+ y[4] = y[1]-(yd)*scl + yn*zh
+
+ x[2] = x[1]-(xd)*0.3 - xn*zh/2
+ y[2] = y[1]-(yd)*0.3 - yn*zh/2
+
+ x[3] = x[1]-(xd)*0.6 - xn*zh/2
+ y[3] = y[1]-(yd)*0.6 - yn*zh/2
+
+ x[5] = x[1]-(xd)*scl + xn*zh/2
+ y[5] = y[1]-(yd)*scl + yn*zh/2
+
+ x[6] = x[1]-(xd)*0.8
+ y[6] = y[1]-(yd)*0.8
+
+ x[7] = x[1]-(xd)*0.2
+ y[7] = y[1]-(yd)*0.2
+
+ Else
+ ' extreme ends of motion
+ If scl >.5
+ x[4] = x[1]-(xd)*scl + xn*zh
+ y[4] = y[1]-(yd)*scl + yn*zh
+
+ x[2] = x[1]-(xd)*0.3 - xn*zh/2
+ y[2] = y[1]-(yd)*0.3 - yn*zh/2
+
+ x[3] = x[1]-(xd)*0.6 - xn*zh/2
+ y[3] = y[1]-(yd)*0.6 - yn*zh/2
+
+ x[6] = x[1]-(xd)*0.9' + xn*zh/8
+ y[6] = y[1]-(yd)*0.9' + yn*zh/8
+
+ x[5] = x[1]-(xd)*0.9 + xn*zh/2
+ y[5] = y[1]-(yd)*0.9 + yn*zh/2
+
+ x[7] = x[1]-(xd)*0.8 + xn*zh
+ y[7] = y[1]-(yd)*0.8 + yn*zh
+
+ x[1] = x[1]-(xd)*0.7 + xn*zh*1.5
+ y[1] = y[1]-(yd)*0.7 + yn*zh*1.5
+ Else
+ x[4] = x[1]-(xd)*scl + xn*zh
+ y[4] = y[1]-(yd)*scl + yn*zh
+
+ x[2] = x[1]-(xd)*0.3 - xn*zh/2
+ y[2] = y[1]-(yd)*0.3 - yn*zh/2
+
+ x[3] = x[1]-(xd)*0.6 - xn*zh/2
+ y[3] = y[1]-(yd)*0.6 - yn*zh/2
+
+ x[7] = x[1]-(xd)*0.1' + xn*zh/8
+ y[7] = y[1]-(yd)*0.1' + yn*zh/8
+
+ x[5] = x[1]-(xd)*0.1 + xn*zh/2
+ y[5] = y[1]-(yd)*0.1 + yn*zh/2
+
+ x[6] = x[1]-(xd)*0.2 + xn*zh
+ y[6] = y[1]-(yd)*0.2 + yn*zh
+
+ x[0] = x[1]-(xd)*0.3 + xn*zh*1.5
+ y[0] = y[1]-(yd)*0.3 + yn*zh*1.5
+ EndIf
+ EndIf
+ Color(COL_CLAW)
+ If deathtype <> 5
+ ' normal claw draw
+ DrawLine x[0],y[0],x[4],y[4]
+ DrawLine x[1],y[1],x[4],y[4]
+ DrawLine x[0],y[0],x[3],y[3]
+ DrawLine x[1],y[1],x[2],y[2]
+
+ DrawLine x[5],y[5],x[6],y[6]
+ DrawLine x[5],y[5],x[7],y[7]
+ DrawLine x[3],y[3],x[6],y[6]
+ DrawLine x[2],y[2],x[7],y[7]
+ Else
+ 'death by fuseball
+ If (deathcount/4)Mod 4 >1 SetColor 255,255,255
+ For Local i = 0 To 7
+ Local rv = Rand(0,360)
+ Local sr = Rnd(4,40)*(60-deathcount)/60
+ DrawRect (x[2]+x[3])/2+Cos(rv)*sr,(y[2]+y[3])/2+Sin(rv)*sr,2,2
+ Next
+ EndIf
+
+ If showdebug
+ DrawText zPos+" "+zh,600,10
+ EndIf
+
+ EndMethod
+
+
+ Method DrawMenLeft()
+
+ Local zz#,zh#
+ Local x#[8],y#[8]
+ zh# = 10.0
+ x[0] = 30
+ y[0] = 60
+ x[1] = 0
+ y[1] = 60
+
+ Local xn#
+ Local yn#
+
+ Local xd# = -30
+ Local yd# = 0
+ Local sz# = 30
+ xn# = 0
+ yn# = -1
+ x[4] = x[1]-(xd)*.5 + xn*zh
+ y[4] = y[1]-(yd)*.5 + yn*zh
+
+ x[2] = x[1]-(xd)*0.3 - xn*zh/2
+ y[2] = y[1]-(yd)*0.3 - yn*zh/2
+
+ x[3] = x[1]-(xd)*0.6 - xn*zh/2
+ y[3] = y[1]-(yd)*0.6 - yn*zh/2
+
+ x[5] = x[1]-(xd)*.5 + xn*zh/2
+ y[5] = y[1]-(yd)*.5 + yn*zh/2
+
+ x[6] = x[1]-(xd)*0.8' + xn*zh/8
+ y[6] = y[1]-(yd)*0.8' + yn*zh/8
+
+ x[7] = x[1]-(xd)*0.2' + xn*zh/8
+ y[7] = y[1]-(yd)*0.2' + yn*zh/8
+
+ Color(COL_CLAW)
+ Local m = men;If m > 5 Then m = 5
+ For Local t = 1 To m
+ DrawLine x[0]+t*40-30,y[0],x[4]+t*40-30,y[4]
+ DrawLine x[1]+t*40-30,y[1],x[4]+t*40-30,y[4]
+ DrawLine x[0]+t*40-30,y[0],x[3]+t*40-30,y[3]
+ DrawLine x[1]+t*40-30,y[1],x[2]+t*40-30,y[2]
+
+ DrawLine x[5]+t*40-30,y[5],x[6]+t*40-30,y[6]
+ DrawLine x[5]+t*40-30,y[5],x[7]+t*40-30,y[7]
+ DrawLine x[3]+t*40-30,y[3],x[6]+t*40-30,y[6]
+ DrawLine x[2]+t*40-30,y[2],x[7]+t*40-30,y[7]
+ Next
+ If men > 5
+ DrawString( men,6*40+5-30,50,2.0)
+ EndIf
+ EndMethod
+
+ Function Create:Player()
+ Local p:Player = New Player
+ p.zPos = 400
+ p.schan = AllocChannel()
+ Return p
+ EndFunction
+
+EndType
+
+
+
+
+Type Point
+ Field x#,y#,xd#,yd#
+ Field xtarget#,ytarget#
+ Field xrate#,yrate#
+ Field xoriginal#,yoriginal#
+
+ Field e0:edge
+ Field e1:edge
+
+ Function Create:Point( x#, y# )
+ Local p:Point = New Point
+ p.x=x
+ p.y=y
+ p.xoriginal# = x
+ p.yoriginal# = y
+ p.xtarget# = x
+ p.ytarget# = y
+ POINT_LIST.AddLast( p )
+ Return p
+ EndFunction
+
+ Method SetPointXY( x#, y# )
+ x=x
+ y=y
+ xoriginal# = x
+ yoriginal# = y
+ EndMethod
+
+ Method Update()
+ If Abs(xd) > 0.001
+ xd = xd - xrate
+ Else
+ xd = 0
+ xtarget = x
+ xrate = 0
+ EndIf
+ x = x + xrate
+ If Abs(yd) > 0.001
+ yd = yd - yrate
+ Else
+ yd = 0
+ ytarget = y
+ xrate = 0
+ EndIf
+ y = y + yrate
+ End Method
+
+ Method ResetPoint()
+ x=xoriginal
+ y=yoriginal
+ xtarget = xoriginal#
+ ytarget = yoriginal#
+ xrate = 0
+ yrate = 0
+ xd = 0
+ yd = 0
+ End Method
+
+ Method MorphPoint(xt#,yt#,xr#,yr#)
+ x = xtarget
+ y = ytarget
+ xtarget = xt
+ ytarget = yt
+ xd = xtarget-x
+ yd = ytarget-y
+ xrate = Abs(xr)*Sgn(xd)
+ yrate = Abs(yr)*Sgn(yd)
+ End Method
+
+ Function UpdatePoints()
+ Local p:Point
+ For p=EachIn POINT_LIST
+ p.Update()
+ Next
+ EndFunction
+
+ Function ResetPoints()
+ Local p:Point
+ For p=EachIn POINT_LIST
+ p.ResetPoint()
+ Next
+ EndFunction
+
+EndType
+
+
+
+
+
+
+
+
+
+Type Edge
+ Field index
+ Field p1:point
+ Field p2:point
+ Field angle
+ Field pulsing
+ Field haspulser = False
+ Field hasplayer = False
+ Field bcol
+
+ Field spike:Spikes
+
+ Field xx#,yy#
+
+ Method Draw( zd1#, zd2#, layer )
+ If zd1<1 zd1=1
+ If zd2<1 zd2=1
+
+ 'draw the edge at zero position,
+ 'the depth line and the far point
+ Local x#[4],y#[4]
+ TForm p1.x,p1.y,zd1, x[0],y[0]
+ TForm p1.x,p1.y,zd2, x[1],y[1]
+
+ TForm p2.x,p2.y,zd1, x[2],y[2]
+ TForm p2.x,p2.y,zd2, x[3],y[3]
+
+ If pulsing
+ 'SetBlend LIGHTBLEND
+
+ SetLineWidth 2
+ If (pulsecount) Mod 8 > 3
+ Color(COL_PULSARS)
+ Else
+ Color(COL_BULLETS)
+ EndIf
+ DrawLine x[0],y[0],x[1],y[1]
+ DrawLine x[3],y[3],x[2],y[2]
+
+ Else
+
+ 'SetBlend LIGHTBLEND
+ SetLineWidth 2
+
+ If hasplayer
+ Color(COL_CLAW)
+ Else
+ If (superzapperdisplay/2) Mod 4 > 1
+ Color(COL_TANKERS)
+ Else
+ Color(COL_LEVEL)
+ EndIf
+ EndIf
+ If layer = 0 And mainPlayer.bonusdisplay > 0
+ Color(bcol)
+ EndIf
+ If layer = 0 Or hasplayer
+ DrawLine x[0],y[0],x[1],y[1]
+ DrawLine x[3],y[3],x[2],y[2]
+ EndIf
+
+ If (superzapperdisplay/2) Mod 4 > 1
+ Color(COL_TANKERS)
+ Else
+ Color(COL_LEVEL)
+ EndIf
+ DrawLine x[1],y[1],x[3],y[3]'bottom
+ If haspulser = False
+ DrawLine x[0],y[0],x[2],y[2]'top
+ EndIf
+ EndIf
+ SetLineWidth 1
+
+ If showdebug Then DrawText angle,x[0],y[0]
+ EndMethod
+
+ Function Create:Edge(p1:Point, p2:Point)
+ Local e:Edge = New edge
+
+ 'assign the points
+ e.p1=p1
+ e.p2=p2
+
+ 'linkem up
+ p1.e1=e
+ p2.e0=e
+
+ 'store the midpoint for speeding up
+ e.xx =( ( p2.x - p1.x ) / 2.0 ) + p1.x
+ e.yy =( ( p2.y - p1.y ) / 2.0 ) + p1.y
+
+ Return e
+ EndFunction
+EndType
+
+
+
+Type Level
+ Field depth# = 400
+ Field position# = 1500
+ Field state = LEVEL_BEGIN
+ Field continuous = True
+ Field cnt
+ Field hasspikes = False
+ Field hasflippers = False
+ Field hastankers = False
+ Field hastankersp = False
+ Field hastankersf = False
+ Field hasfuseballs = False
+ Field haspulsars = False
+
+ 'Field points:TList
+ Field edges:Edge[20]
+ Field e_cnt
+
+ Method AddPoint:Point(x#,y#)
+ Local p:Point = Point.Create( x, y )
+ 'points.AddLast( p )
+ Return p
+ EndMethod
+
+ Method AddEdge:Edge( p1:Point, p2:point )
+ Local e:Edge = Edge.Create( p1, p2 )
+ edges[e_cnt] = e
+ e_cnt:+1
+ e.index = e_cnt
+ Return e
+ EndMethod
+
+ Method Update()
+ Select state
+ Case LEVEL_BEGIN
+ If cnt = 0 Then PlaySound(zoominsfx)
+ cnt = cnt + 1
+ If position>50
+ position:-10
+ CCenterY = -(position-50)/5 + leveldata[current_board+tubes,1]
+ Else
+ state=LEVEL_READY
+ CCenterY = leveldata[current_board+tubes,1]
+ EndIf
+ Case LEVEL_READY
+ ZOFFSET = 5
+ mainplayer.zPos = depth
+ Case LEVEL_PLAYER_DYING
+ ZOFFSET = 5
+ If mainplayer.deathcount = 1
+ state=LEVEL_INTO_VORTEX
+ cnt = 0
+ EndIf
+ Case LEVEL_INTO_VORTEX
+ cnt:+1
+ ZOFFSET:+3
+ If cnt > 60
+ state=LEVEL_READY
+ Baddies.ConvertBaddieToEggs()
+ cnt = 0
+ ZOFFSET=5
+ EndIf
+ Case LEVEL_START_ZOOM
+ k = 70
+ ZOFFSET = 5
+ mainplayer.zPos = depth
+ cnt = cnt + 1
+ If cnt > 20
+ state = LEVEL_ZOOMING
+ mainPlayer.deathcount = 0
+ cnt = 0
+ PlaySound(zoomoutsfx)
+ EndIf
+ Case LEVEL_ZOOMING
+ If ZOFFSET < -40
+ k = k + 2
+ If k > 70+30 Then k = k + 2
+ If mainplayer.zPos < 10
+ state = LEVEL_COMPLETE
+ ZOFFSET = 5
+ cnt = 0
+ Else
+ mainplayer.zPos:-3
+ EndIf
+ Else
+ ZOFFSET = ZOFFSET - 2
+ mainplayer.zPos:-3
+ EndIf
+ Case LEVEL_REVERSE_ZOOM
+ k=k-4
+ If k<70 Then k = 70
+ ZOFFSET:+1
+ If ZOFFSET > 5
+ ZOFFSET = 5
+ cnt = cnt + 1
+ If cnt > 20
+ If CountList(EGG_LIST) = 0
+ state = LEVEL_START_ZOOM
+ Else
+ state = LEVEL_READY
+ EndIf
+ cnt = 0
+ k=70
+ mainplayer.deathcount = 0
+ EndIf
+ mainplayer.zPos = depth
+ EndIf
+ Case LEVEL_COMPLETE
+ cnt = cnt + 1
+ If cnt > 60
+ If startbonus > 0
+ mainplayer.score:+startbonus
+ startbonus = 0
+ EndIf
+
+ ZOFFSET = 5
+ mainplayer.zPos = depth
+ state = LEVEL_BEGIN
+ ClearLevel()
+ current_level:+1
+ If current_level > 16*6-1
+ current_level = 0
+ tubes = tubes+16
+ If tubes > 32 Then tubes = 0
+ EndIf
+ current_board = current_level Mod 16
+ current_color = current_level/16
+ SetUpLevel()
+ SetUpEnemies()
+ superzapper = 2
+ MainPlayer.SetEdge( 7 )
+ cnt = 0
+ position = 1500
+ CCenterY = -(position-50)/5 + leveldata[current_board+tubes,1]
+ FlushKeys()
+ EndIf
+
+ EndSelect
+ For Local a=0 Until e_cnt
+ edges[a].pulsing = False
+ edges[a].haspulser = False
+ edges[a].hasplayer = False
+ If mainplayer.bonusdisplay Mod 4 = 3
+ edges[a].bcol = Rand(0,5)
+ EndIf
+ Next
+ If superzapperdisplay > 0 Then superzapperdisplay:-1
+ EndMethod
+
+
+ Method UpdateAngles()
+ Local a
+ For a=0 Until e_cnt
+ edges[a].angle = GetAngle#( edges[a].p2.x, edges[a].p2.y, edges[a].p1.x, edges[a].p1.y, edges[(a+1)Mod e_cnt].p1.x, edges[(a+1)Mod e_cnt].p1.y)
+ Next
+ If theLevel.continuous = False
+ edges[14].angle = 0
+ EndIf
+ For a=0 Until e_cnt
+ edges[a].xx =( ( edges[a].p2.x - edges[a].p1.x ) / 2.0 ) + edges[a].p1.x
+ edges[a].yy =( ( edges[a].p2.y - edges[a].p1.y ) / 2.0 ) + edges[a].p1.y
+ Next
+ a=0
+ If theLevel.continuous = False
+ edges[a].xx =( ( edges[a].p2.x - edges[a].p1.x ) / 2.0 ) + edges[a].p1.x
+ edges[a].yy =( ( edges[a].p2.y - edges[a].p1.y ) / 2.0 ) + edges[a].p1.y
+ EndIf
+ End Method
+
+
+ Method Draw()
+ Local a=0
+ Local oldz
+ Select state
+ Case LEVEL_INTO_VORTEX
+ oldz = ZOFFSET
+ ZOFFSET = 5
+ EndSelect
+
+ For a=0 Until e_cnt
+ edges[a].Draw(position,position+depth,0)
+ Next
+ For a=0 Until e_cnt
+ edges[a].Draw(position,position+depth,1)
+ Next
+
+ Select state
+ Case LEVEL_INTO_VORTEX
+ ZOFFSET = oldz
+ EndSelect
+
+ EndMethod
+
+ Function Create:Level()
+ Local l:Level = New Level
+ 'l.points = New TList
+ Return l
+ EndFunction
+
+ Method mutate()
+ Local a=0
+ For a=0 Until e_cnt
+ Local xt# = Rnd(-1,1)
+ Local yt# = Rnd(-1,1)
+ Local xr# = xt/16
+ Local yr# = yt/16
+ edges[a].p1.Morphpoint(xt+edges[a].p1.x,yt+edges[a].p1.y,xr,yr)
+ Next
+ a=0
+ If theLevel.continuous = False
+ Local xt# = Rnd(-1,1)
+ Local yt# = Rnd(-1,1)
+ Local xr# = xt/16
+ Local yr# = yt/16
+ edges[a].p2.Morphpoint(xt+edges[a].p2.x,yt+edges[a].p2.y,xr,yr)
+ EndIf
+ EndMethod
+
+ Function LevelSelect()
+
+ Local done = False
+ Local lv = -1
+ Local index = current_level/2
+ If index > 42 Then index = 42
+ Local sel = 0
+
+ While lv = -1
+ If KeyHit(key_left)
+ sel:-1
+ If sel < 0
+ sel = 0
+ index:-1
+ If index < 0 Then index = 0
+ EndIf
+ PlaySound(ticksfx)
+ EndIf
+ If KeyHit(KEY_RIGHT)
+ sel:+1
+ If sel > 4
+ sel = 4
+ index:+1
+ If index > 43 Then index = 43
+ EndIf
+ PlaySound(ticksfx)
+ EndIf
+
+ Cls
+ SetColor 255,255,0
+ DrawString("SELECT STARTING LEVEL",100,10,6.0)
+ DrawString("HI SCORE "+hiscore,180,500,6.0)
+
+ For Local br = 0 To 4
+ DrawSmallLevel((index+br)*2,190+br*120,300,br=sel)
+ Next
+ SetColor 0,255,0
+ DrawString("LEVEL",80,300-50,2.0)
+
+ SetColor 255,0,0
+ DrawString("BONUS",80,300+50,2.0)
+
+ If KeyHit(KEY_LCONTROL)
+ lv = (index+sel)*2
+ Local col = lv/16
+ startbonus = (lv)*(3000+col*1000)+col*7000+((lv)>3)*(lv)*4000
+ EndIf
+
+ If KeyHit(key_t) Then tubes:+16; If tubes > 32 Then tubes = 0
+
+ If KeyHit(KEY_ESCAPE) Then done = True;lv=-2
+ Globalclock:+1
+ Flip
+ Delay 16
+ Wend
+
+ If lv > -1
+ current_level = lv
+ current_board = lv Mod 16
+ current_color = lv/16
+
+ SetUpLevel()
+ SetUpEnemies()
+
+ superzapper = 2
+ MainPlayer.SetEdge(7)
+ theLevel.state = LEVEL_BEGIN
+ EndIf
+
+ Return done
+
+ End Function
+
+
+ Function DrawSmallLevel(lv,xoff,yoff,h)
+
+ Local col = lv/16
+ current_color = col
+ Local b = lv Mod 16
+
+ Local sc# = 1.0
+ Local x1,x2,y1,y2
+ Local c = leveldata[b+tubes,0]
+ CCenterY = leveldata[b+tubes,1]
+ YOFFSET = leveldata[b+tubes,2]
+
+ Color(COL_LEVEL)
+ If h And (Globalclock Mod 30 < 16) Then Color(Rand(0,7));sc = (Globalclock Mod 30)/20.0
+
+ For Local a = 0 Until 15
+ x1 = Float(leveldata[b+tubes,3+a*2])/8*sc
+ y1 = Float(leveldata[b+tubes,3+a*2+1])/8*sc
+ x2 = Float(leveldata[b+tubes,3+a*2+2])/8*sc
+ y2 = Float(leveldata[b+tubes,3+a*2+3])/8*sc
+ DrawLine x1+xoff,y1+yoff,x2+xoff,y2+yoff
+ Next
+ If c
+ x1 = Float(leveldata[b+tubes,3])/8*sc
+ y1 = Float(leveldata[b+tubes,3+1])/8*sc
+ DrawLine x1+xoff,y1+yoff,x2+xoff,y2+yoff
+ EndIf
+
+ SetColor 0,255,0
+ DrawString(b+1+16*col,xoff-5,yoff-50,2.0)
+
+ SetColor 255,0,0
+ Local l$ = (lv)*(3000+col*1000)+col*7000+((lv)>3)*(lv)*4000
+ Local ln = Len(l$)*5
+ DrawString(l$,xoff-ln,yoff+50,2.0)
+
+ End Function
+
+
+
+EndType
+
+
+
+Type Shot
+ Field e:edge ' the edge its on
+ Field z# ' its position
+ Field r# ' rotation
+ Field xx#,yy#
+
+ Method Draw()
+ Color(COL_BULLETS)
+
+ Local zz# = theLevel.depth-z+theLevel.position
+ Local sz# = TFormSZ(4,zz)
+ Local pxx#,pyy#
+
+ xx = e.xx
+ yy = e.yy
+
+ TForm(xx,yy,zz,pxx,pyy)
+
+ For Local a=0 Until 360 Step 45
+ SetRotation r+a
+ DrawLine pxx+sz,pyy+sz,pxx-sz,pyy-sz
+ Next
+ r:+15
+ SetRotation 0
+ EndMethod
+
+ Method Update()
+ z:-6
+ Local bad:Baddies
+ If z<0
+ SHOT_LIST.Remove(Self)
+ Return
+ Else
+ 'check for collisions
+ For bad = EachIn BADDIE_LIST
+ If bad.CheckColl(e,z)
+ SHOT_LIST.Remove(Self)
+ Return
+ EndIf
+ Next
+ EndIf
+ EndMethod
+
+ Function Create:Shot(e:Edge, zPos)
+ Local ns:Shot = New Shot
+ ns.e = e
+ ns.xx =e.xx
+ ns.yy =e.yy
+ ns.z = zPos '+5
+ SHOT_LIST.AddLast( ns )
+ Return ns
+ EndFunction
+
+ Function UpdateShots()
+ Local s:shot
+ For s=EachIn SHOT_LIST
+ s.Update()
+ Next
+ EndFunction
+
+ Function DrawShots()
+ Local s:shot
+ For s=EachIn SHOT_LIST
+ s.Draw()
+ Next
+ EndFunction
+
+EndType
+
+
+
+
+
+
+
+
+Type Explosion
+ Field x,y
+ Field cnt '60
+ Field scale#
+
+ Method Draw()
+ Color(COL_BULLETS)
+ For Local t = 0 To 7
+ Local xd = Sin(t*45)*Cos(cnt*3)*scale
+ Local yd = Cos(t*45)*Cos(cnt*3)*scale
+ DrawLine x-xd,y-yd,x+xd,y+yd
+ Next
+ EndMethod
+
+ Method Update()
+ cnt:-3
+ If cnt < 0 Then EXPLOSION_LIST.Remove(Self)
+ EndMethod
+
+ Function Create:Explosion(x,y,height)
+ Local ex:Explosion = New Explosion
+ ex.x = x
+ ex.y =y
+ ex.cnt = 30
+ ex.scale# = height/20
+ EXPLOSION_LIST.AddLast( ex )
+ Return ex
+ EndFunction
+
+ Function UpdateDrawExplosions()
+ Local s:Explosion
+ For s=EachIn EXPLOSION_LIST
+ s.Update()
+ Next
+ For s=EachIn EXPLOSION_LIST
+ s.Draw()
+ Next
+ EndFunction
+
+EndType
+
+
+
+
+
+' points displayed when a fuseball is shot
+' 250/500/750/1000
+Type fusepoint
+ Field x,y
+ Field cnt '60
+ Field scale#
+ Field pts$
+
+ Method Draw()
+ Color(COL_BULLETS)
+ DrawString(pts,x,y,scale)
+ EndMethod
+
+ Method Update()
+ cnt:-1
+ If cnt < 0 Then FUSEPOINT_LIST.Remove(Self)
+ EndMethod
+
+ Function Create:fusepoint(x,y,height,pt)
+ Local ex:fusepoint = New fusepoint
+ ex.cnt = 30
+ ex.pts = pt
+ ex.scale# = height/64+.5
+ ex.x = x -ex.scale*1.5
+ ex.y = y
+ FUSEPOINT_LIST.AddLast( ex )
+ Return ex
+ EndFunction
+
+ Function UpdateDrawFusePoints()
+ Local s:fusepoint
+ For s=EachIn FUSEPOINT_LIST
+ s.Update()
+ Next
+ For s=EachIn FUSEPOINT_LIST
+ s.Draw()
+ Next
+ EndFunction
+
+EndType
+
+
+
+
+
+' enemies in the vortex!
+Type Egg
+ Field e_index
+ Field height
+ Field act
+ Field cnt
+ Field hdir
+ Field dir
+
+ Method DrawStats(x,y)
+ DrawText "i= "+e_Index+" a="+act+" h="+Int(height)+" c="+cnt+" d="+hdir,x,y
+ End Method
+
+ Method Hatch()
+ Local typ = -1
+ While typ < 0
+ ' turn it into a baddie
+ typ = Rand(0,(7-(enemiesleft=0)*3))
+ Select typ
+ Case 0'flipper
+ If theLevel.hasflippers
+ flipper.Create(e_Index,0,0)
+ Else
+ typ = -1
+ EndIf
+ Case 1'tank
+ If theLevel.hastankers
+ Local car = -1
+ While car < 0
+ car = Rand(0,2)
+ Select car
+ Case 0
+ ' carry flippers
+ If theLevel.hasflippers
+ Tanker.Create(e_Index, Rand(1,2),0,0)
+ Else
+ car = -1
+ EndIf
+ Case 1
+ ' carry fuseballs
+ If theLevel.hastankersf
+ Tanker.Create(e_Index, 0,0,Rand(1,2))
+ Else
+ car = -1
+ EndIf
+ Case 2
+ ' carry pulsars
+ If theLevel.hastankersp
+ Tanker.Create(e_Index, 0,Rand(1,2),0)
+ Else
+ car = -1
+ EndIf
+ End Select
+ Wend
+ Else
+ typ = -1
+ EndIf
+ Case 4,5,6,7'spinner
+ If theLevel.hasspikes
+ spinner.Create( e_Index ,Rand(2.0,4.0))
+ Else
+ typ = -1
+ EndIf
+ Case 2'fuseball
+ If theLevel.hasfuseballs
+ fuseball.Create(e_Index,0,0)
+ Else
+ typ = -1
+ EndIf
+ Case 3'pulsar
+ If theLevel.haspulsars
+ Pulsar.Create(e_Index,0,0)
+ Else
+ typ = -1
+ EndIf
+ End Select
+ Wend
+ EGG_LIST.Remove(Self)
+ End Method
+
+ Method Update()
+
+ Select act
+ Case 0 'move from edge to edge
+ cnt:+1
+ If cnt > 7
+ height:+hdir
+ If height >= -1 Or height <= -20 Or Rand(0,100) > 95 Then hdir=-hdir
+ cnt = 0
+ If dir = 1
+ If e_Index=theLevel.e_cnt-1
+ If theLevel.continuous
+ e_Index = 0
+ Else
+ dir = 1-dir
+ EndIf
+ Else
+ e_Index:+1
+ EndIf
+ Else
+ If e_Index = 0
+ If theLevel.continuous
+ e_Index = theLevel.e_Cnt-1
+ Else
+ dir = 1-dir
+ EndIf
+ Else
+ e_Index:-1
+ EndIf
+ EndIf
+ If theLevel.state=LEVEL_READY
+ If Rand(0,100) > 95-hatchrate
+ If enemiesleft + hatchingeggs < 12
+ act = 1
+ hatchingeggs:+1
+ EndIf
+ EndIf
+ EndIf
+ EndIf
+ Case 1 'move towards web
+ cnt:+1
+ If cnt > 7
+ height:+1
+ If height >= 0 Then act = 2
+ cnt=0
+ EndIf
+ Case 2 'hatch
+ hatch()
+ hatchingeggs:-1
+ End Select
+
+ End Method
+
+ Method Draw()
+
+ Local zz# = theLevel.depth-height*20+theLevel.position
+ Local pxx#,pyy#
+ Local xx# = theLevel.Edges[e_Index].xx
+ Local yy# = theLevel.Edges[e_Index].yy
+
+ TForm(xx,yy,zz,pxx,pyy)
+ Color(COL_FLIPPERS)
+ Plot(pxx,pyy)
+
+ End Method
+
+ Function Create:Egg(index)
+ Local e:Egg = New Egg
+ e.e_index = index
+ e.height = -10
+ e.cnt = 0
+ e.act = 0
+ e.hdir = 1
+ e.dir = Rand(0,1)
+ EGG_LIST.AddLast( e )
+ Return e
+ EndFunction
+
+ Function UpdateEggs()
+ Local e:egg
+ For e = EachIn EGG_LIST
+ e.Update()
+ Next
+ EndFunction
+
+ Function DrawEggs()
+ Local e:egg
+ Local cnt = 0
+ For e = EachIn EGG_LIST
+ e.Draw()
+ If showdebug Then e.DrawStats(400,cnt*12)
+ cnt = cnt + 1
+ Next
+ EndFunction
+
+End Type
+
+
+
+
+
+
+
+
+Type Baddies
+ Field OnEdge:Edge
+ Field e_Index 'used to traverse theLevel edgelist
+ Field baddietype = 0
+ Field dietime = 0
+ Field height#
+
+ Method Draw() Abstract
+ Method DrawStats(x,y) Abstract
+ Method Zapit(t) Abstract
+ Method Update() Abstract
+ Method CheckColl(e:edge,z#) Abstract 'shots->enemy
+ Method CheckPlayerColl() Abstract 'enemy->player
+
+ Function UpdateBaddies()
+ onrim = True
+ Local b:Baddies
+ For b = EachIn BADDIE_LIST
+ If theLevel.state = LEVEL_ZOOMING Or theLevel.state = LEVEL_REVERSE_ZOOM
+ If b.baddietype = BAD_SPIKE Then b.Update() ' only spikes
+ Else
+ b.Update()
+ If b.baddietype > BAD_SPIKE
+ If b.height < 400
+ onrim = False
+ EndIf
+ EndIf
+ EndIf
+ Next
+ For b = EachIn BADDIE_LIST
+ If theLevel.state = LEVEL_ZOOMING Or theLevel.state = LEVEL_REVERSE_ZOOM
+ If b.baddietype = BAD_SPIKE Then b.CheckPlayerColl()' only spikes
+ Else
+ If theLevel.state <> LEVEL_PLAYER_DYING Then b.CheckPlayerColl()
+ EndIf
+ Next
+ EndFunction
+
+ Function DrawBaddies()
+ Local b:Baddies
+ Local cnt = 0
+ For b = EachIn BADDIE_LIST
+ If theLevel.state = LEVEL_ZOOMING Or theLevel.state = LEVEL_REVERSE_ZOOM
+ If b.baddietype = BAD_SPIKE ' only spikes kill when zooming
+ b.Draw()
+ If showdebug Then b.DrawStats(10,cnt*12)
+ EndIf
+ Else
+ b.Draw()
+ If showdebug Then b.DrawStats(10,cnt*12)
+ EndIf
+ cnt:+1
+ Next
+ EndFunction
+
+ Function ConvertBaddieToEggs()
+ ' player just died - reset the enemies to eggs
+ enemiesleft = 0
+ Local b:Baddies
+ For b = EachIn BADDIE_LIST
+ If b.baddietype > BAD_SPIKE ' non spikes or bullets
+ BADDIE_LIST.Remove(b)
+ ' eggs yet to be created + live eggs
+ If eggsleft + CountList(EGG_LIST) < maxeggs
+ eggsleft:+1
+ EndIf
+ Else
+ If b.baddietype = BAD_BULLET 'remove bullets, keep spikes
+ BADDIE_LIST.Remove(b)
+ EndIf
+ EndIf
+ Next
+ ' only strat with max 16 eggs
+ If CountList(EGG_LIST) > 16
+ eggsleft:+(CountList(EGG_LIST)-16)
+ Local cnt = 0
+ For Local e:egg = EachIn EGG_LIST
+ cnt:+1
+ If cnt > 16
+ EGG_LIST.Remove(e)
+ EndIf
+ Next
+ EndIf
+ End Function
+
+ Function SuperZapit(strength)
+
+ Local b:Baddies
+ Local cnt = 4
+ If strength = 2
+ ' zap them all
+ superzapperdisplay = 60
+ For b = EachIn BADDIE_LIST
+ If b.baddietype <> 1
+ If b.Zapit(cnt) Then cnt = cnt + 2
+ EndIf
+ Next
+ Else
+ ' find biggest threat and kill it
+ Local i = mainplayer.e_index
+ Local b_best:baddies = Null
+ Local best_d = 16
+ Local best_h = 0
+ Local best_b = 0
+ For b = EachIn BADDIE_LIST
+ If b.baddietype > 1
+ Local d
+ If theLevel.continuous
+ d = -(i - b.e_index) 'how far from player
+ If Abs(d) > 8 Then d = -d
+ Else
+ d = -(i - b.e_index)
+ EndIf
+ d = Abs(d)
+ If d < best_d+2
+ If b.height > best_h
+ b_best = b
+ best_d = d
+ best_h = b.height
+ best_b = b.baddietype
+' DebugLog("b="+b.baddietype+" i="+b.e_index+" d="+d+" h="+b.height)
+ Else
+ If d < best_d
+ b_best = b
+ best_d = d
+ best_h = b.height
+ best_b = b.baddietype
+' DebugLog("+ b="+b.baddietype+" i="+b.e_index+" d="+d+" h="+b.height)
+ Else
+ ' same d, pick the meaner one
+ If b.baddietype > best_b
+ b_best = b
+ best_d = d
+ best_h = b.height
+ best_b = b.baddietype
+' DebugLog("++ b="+b.baddietype+" i="+b.e_index+" d="+d+" h="+b.height)
+ EndIf
+ EndIf
+ EndIf
+ EndIf
+ EndIf
+ Next
+ If b_best <> Null
+' DebugLog("Zap b="+b_best.baddietype+" i="+b_best.e_index+" d="+best_d+" h="+best_h)
+ b_best.Zapit(4)
+ superzapperdisplay = 60
+ EndIf
+
+ EndIf
+
+ End Function
+
+EndType
+
+
+Type Bullets Extends Baddies
+ Field height# ' its position
+ Field r# ' rotation
+ Field xx#,yy#
+
+ Method Draw()
+ Color(COL_BULLETS)
+ Local zz# = theLevel.depth-height+theLevel.position
+ Local sz# = TFormSZ(6,zz)
+ Local pxx#,pyy#
+
+ xx = onEdge.xx
+ yy = onEdge.yy
+
+ TForm(xx,yy,zz,pxx,pyy)
+
+ For Local a=0 Until 360 Step 45
+ SetRotation r+a
+ DrawLine pxx+sz,pyy,pxx-sz,pyy
+ Next
+ r:+15
+ SetRotation 0
+ EndMethod
+
+ Method Update()
+ height:+3
+ If height > theLevel.depth
+ BADDIE_LIST.Remove(Self)
+ Return
+ EndIf
+ EndMethod
+
+ Method CheckColl(e:edge,z#)
+ If e = OnEdge
+ If z < height
+ MainPlayer.Score = MainPlayer.Score+25
+ BADDIE_LIST.Remove(Self)
+ Return True
+ EndIf
+ EndIf
+ Return False
+ EndMethod
+
+ Method CheckPlayerColl()
+ If MainPlayer.e_Index = e_index
+ If theLevel.depth-height < 8
+ PlaySound(killedbybulletsfx)
+ BADDIE_LIST.Remove(Self)
+ KillPlayer(KILLED_BY_BULLET)
+ Return
+ EndIf
+ EndIf
+ Return False
+ EndMethod
+
+ Method DrawStats(x,y)
+ End Method
+
+ Method ZapIt(t)
+ Return False
+ End Method
+
+ Function Create:Bullets(index, depth)
+ PlaySound(bulletsfx)
+ Local b:Bullets = New Bullets
+ b.OnEdge = theLevel.edges[index]
+ b.e_Index= index
+ b.xx = b.onEdge.xx
+ b.yy = b.onEdge.yy
+ b.height = depth
+ b.baddietype = BAD_BULLET
+ BADDIE_LIST.AddLast b
+ Return b
+ EndFunction
+
+EndType
+
+
+
+
+Type Tanker Extends Baddies
+ Field cargo_flipper 'flipper,pulsar,fuseball 1 or 2 of them
+ Field cargo_pulsar
+ Field cargo_fuseball
+ Field xx#,yy#
+ Field hasbullets
+
+ Method Draw()
+ Color(COL_TANKERS)
+ Local x#[2],y#[2]
+ Local zz# = theLevel.depth-height+theLevel.position
+ xx = onEdge.xx
+ yy = onEdge.yy
+
+ TForm(xx,yy,zz,x[0],y[0])
+ Local scale# '= height/theLevel.depth*24.0+1
+ scale = TFormSZ(26,zz)+1
+
+ Local scale3# = scale/3
+ DrawLine x[0],y[0]-scale3,x[0]-scale,y[0]
+ DrawLine x[0],y[0]-scale,x[0]+scale3,y[0]
+ DrawLine x[0],y[0]+scale,x[0]-scale3,y[0]
+ DrawLine x[0],y[0]+scale3,x[0]+scale,y[0]
+
+ DrawLine x[0],y[0]+scale,x[0]+scale,y[0]
+ DrawLine x[0],y[0]+scale,x[0]-scale,y[0]
+ DrawLine x[0],y[0]-scale,x[0]+scale,y[0]
+ DrawLine x[0],y[0]-scale,x[0]-scale,y[0]
+
+ DrawLine x[0],y[0]+scale3,x[0]+scale3,y[0]
+ DrawLine x[0],y[0]+scale3,x[0]-scale3,y[0]
+ DrawLine x[0],y[0]-scale3,x[0]+scale3,y[0]
+ DrawLine x[0],y[0]-scale3,x[0]-scale3,y[0]
+
+ DrawLine x[0],y[0]+scale3,x[0],y[0]+scale
+ DrawLine x[0],y[0]-scale3,x[0],y[0]-scale
+ DrawLine x[0]+scale3,y[0],x[0]+scale,y[0]
+ DrawLine x[0]-scale3,y[0],x[0]-scale,y[0]
+
+ If cargo_fuseball
+ 'draw fuse in the middle
+ Color(COL_FLIPPERS)
+ DrawLine x[0],y[0],x[0],y[0]+scale3
+ Color(COL_SPIKERS)
+ DrawLine x[0],y[0],x[0],y[0]-scale3
+ Color(COL_PULSARS)
+ DrawLine x[0],y[0],x[0]+scale3,y[0]
+ Color(COL_CLAW)
+ DrawLine x[0],y[0],x[0]-scale3,y[0]
+ Else
+ If cargo_pulsar
+ 'draw pulsar in the middle
+ Color(COL_PULSARS)
+ DrawLine x[0]-scale3,y[0],x[0],y[0]+scale3/2
+ DrawLine x[0],y[0]+scale3/2,x[0],y[0]-scale3/2
+ DrawLine x[0],y[0]-scale3/2,x[0]+scale3,y[0]
+ EndIf
+ EndIf
+ EndMethod
+
+ Method ReleaseCargo(stillborn)
+ If cargo_flipper > 0
+ Local dir = 1-Rand(0,1)*2
+ For Local d = 1 To cargo_flipper
+ Local e = e_index
+ If dir = 1
+ If e=0
+ If theLevel.continuous
+ e = theLevel.e_Cnt-1
+ EndIf
+ Else
+ e:-1
+ EndIf
+ Else
+ If e=theLevel.e_cnt-1
+ If theLevel.continuous
+ e = 0
+ EndIf
+ Else
+ e:+1
+ EndIf
+ EndIf
+ Local c:flipper = flipper.Create(e,1,height)
+ c.dir = -dir
+ If stillborn Then c.dietime = 8
+ dir = -dir
+ Next
+ EndIf
+ If cargo_fuseball > 0
+ Local dir = 1-Rand(0,1)*2
+ For Local d = 1 To cargo_fuseball
+ Local e = e_index
+ If dir = 1
+ If e=0
+ If theLevel.continuous
+ e = theLevel.e_Cnt-1
+ EndIf
+ Else
+ e:-1
+ EndIf
+ Else
+ If e=theLevel.e_cnt-1
+ If theLevel.continuous
+ e = 0
+ EndIf
+ Else
+ e:+1
+ EndIf
+ EndIf
+ Local c:fuseball= fuseball.Create(e,0,height)
+ c.dir = -dir
+ If stillborn Then c.dietime = 8
+ dir = -dir
+ Next
+ EndIf
+ If cargo_pulsar > 0
+ Local dir = 1-Rand(0,1)*2
+ For Local d = 1 To cargo_pulsar
+ Local e = e_index
+ If dir = 1
+ If e=0
+ If theLevel.continuous
+ e = theLevel.e_Cnt-1
+ EndIf
+ Else
+ e:-1
+ EndIf
+ Else
+ If e=theLevel.e_cnt-1
+ If theLevel.continuous
+ e = 0
+ EndIf
+ Else
+ e:+1
+ EndIf
+ EndIf
+ Local c:pulsar = pulsar.Create(e,0,height)
+ c.dir = -dir
+ If stillborn Then c.dietime = 8
+ dir = -dir
+ Next
+ EndIf
+ EndMethod
+
+ Method ZapIt(t)
+ dietime = t 'die after t tics
+ Return True
+ End Method
+
+ Method Update()
+ If dietime > 0
+ dietime:-1
+ If dietime = 0 'die
+ PlaySound(zapsfx,zapchan)
+ Local xx#,yy#,zz1#
+ MainPlayer.Score = MainPlayer.Score+200
+ ReleaseCargo(True) ' it's cargo will die too
+ zz1 = theLevel.position + theLevel.depth - height
+ TForm(OnEdge.xx,OnEdge.yy,zz1,xx,yy)
+ Explosion.Create(xx,yy,height)
+ BADDIE_LIST.Remove(Self)
+ enemiesleft:-1
+ Return
+ EndIf
+ EndIf
+ height:+2
+ If height < 380
+ If Rand(0,100) > 98 And hasbullets Then bullets.Create(e_index,height);hasbullets:-1
+ EndIf
+ If height > theLevel.depth-15
+ ReleaseCargo(False)
+ BADDIE_LIST.Remove(Self)
+ enemiesleft:-1
+ EndIf
+ EndMethod
+
+ Method CheckColl(e:edge,z#)
+ If e = OnEdge
+ If z < height
+ Local xx#,yy#,zz1#
+ MainPlayer.Score = MainPlayer.Score+200
+ ReleaseCargo(False)
+ zz1 = theLevel.position + theLevel.depth - height
+ TForm(OnEdge.xx,OnEdge.yy,zz1,xx,yy)
+ Explosion.Create(xx,yy,height)
+ PlaySound(tankershotsfx)
+ BADDIE_LIST.Remove(Self)
+ enemiesleft:-1
+ Return True
+ EndIf
+ EndIf
+ Return False
+ EndMethod
+
+ Method CheckPlayerColl()
+ Return False
+ EndMethod
+
+ Method DrawStats(x,y)
+ Color(COL_TANKERS)
+ DrawText "i= "+e_Index+" h="+Int(height)+" f="+cargo_flipper+" b="+cargo_fuseball+" p="+cargo_pulsar ,x,y
+ End Method
+
+ Function Create:Tanker(index, numc,nump,numf)
+ Local t:Tanker = New Tanker
+ t.OnEdge = theLevel.edges[index]
+ t.cargo_flipper = numc
+ t.cargo_pulsar = nump
+ t.cargo_fuseball = numf
+ t.e_Index= index
+ t.xx = t.onEdge.xx
+ t.yy = t.onEdge.yy
+ t.height = 10
+ t.hasbullets = Rand(1,2)
+ t.baddietype = BAD_TANKER
+ BADDIE_LIST.AddLast t
+ enemiesleft:+1
+ Return t
+ EndFunction
+
+EndType
+
+
+
+
+Type Flipper Extends Baddies
+ Field typ '0 just slides up the tube
+ '1 flips round the tube
+ '2 flipping on rim
+ '3 pausing
+ Field oldtyp
+ Field hasbullets
+
+ Field Pause 'the pause before changing edge
+ Field dir 'direction left or right
+ Field angle# 'the angle when changing lanes
+ Field totangle# ' the total angle to flip
+
+ Method DrawStats(x,y)
+ Color(COL_FLIPPERS)
+ DrawText "i= "+e_Index+" d="+dir+" h="+Int(height)+" a="+Int(angle)+" t="+Int(totangle) ,x,y
+ End Method
+
+ Method Draw()
+ Local xx#,yy#,zz1#,zz2#
+ Local x#[7],y#[7]
+ Select typ
+ Case 0 ' sliding up
+ zz1 = theLevel.position+theLevel.depth
+ zz2 = zz1 - height
+ TForm(OnEdge.p1.x,OnEdge.p1.y,zz2,x[0],y[0])
+ TForm(OnEdge.p2.x,OnEdge.p2.y,zz2,x[1],y[1])
+ Case 1,2 ' flipping
+ zz1 = theLevel.position+theLevel.depth
+ zz2 = zz1 - height
+ TForm(OnEdge.p1.x,OnEdge.p1.y,zz2,x[0],y[0])
+ TForm(OnEdge.p2.x,OnEdge.p2.y,zz2,x[1],y[1])
+ If dir = -1 'pivot around p1 of dest edge (current edge+1)
+ TFormR(x[1],y[1],-angle+totangle, x[0],y[0])
+ Else
+ TFormR(x[0],y[0],-(totangle-angle), x[1],y[1])
+ EndIf
+ Case 3,7 'delaying
+ zz1 = theLevel.position+theLevel.depth
+ zz2 = zz1 - height
+ TForm(OnEdge.p1.x,OnEdge.p1.y,zz2,x[0],y[0])
+ TForm(OnEdge.p2.x,OnEdge.p2.y,zz2,x[1],y[1])
+ End Select
+
+ Local xn#
+ Local yn#
+ Local zh#' = height/(theLevel.position+theLevel.depth)*24
+ zh = TFormSZ(20,zz2)
+
+ Local xd# = x[1]-x[0]
+ Local yd# = y[1]-y[0]
+ Local sz# = Sqr(xd*xd + yd*yd)
+ If sz = 0
+ xn# = 0
+ yn# = 0
+ Else
+ xn# = -yd/sz
+ yn# = xd/sz
+ EndIf
+ x[2] = x[1]-(xd)*0.3 - xn*zh/2
+ y[2] = y[1]-(yd)*0.3 - yn*zh/2
+
+ x[3] = x[1]-(xd)*0.1 - xn*zh
+ y[3] = y[1]-(yd)*0.1 - yn*zh
+
+ x[5] = x[1]-(xd)*0.9 - xn*zh
+ y[5] = y[1]-(yd)*0.9 - yn*zh
+
+ x[6] = x[1]-(xd)*0.7 - xn*zh/2
+ y[6] = y[1]-(yd)*0.7 - yn*zh/2
+
+ Color(COL_FLIPPERS)
+ DrawLine x[0],y[0],x[3],y[3]
+ DrawLine x[5],y[5],x[1],y[1]
+ DrawLine x[6],y[6],x[5],y[5]
+ DrawLine x[2],y[2],x[3],y[3]
+
+ DrawLine x[6],y[6],x[0],y[0]
+ DrawLine x[2],y[2],x[1],y[1]
+
+ EndMethod
+
+ Method ZapIt(t)
+ dietime = t 'die after t tics
+ Return True
+ End Method
+
+ Method Update()
+ If dietime > 0
+ dietime:-1
+ If dietime = 0 'die
+ PlaySound(zapsfx,zapchan)
+ Local xx#,yy#,zz1#
+ zz1 = theLevel.position + theLevel.depth - height
+ TForm(OnEdge.xx,OnEdge.yy,zz1,xx,yy)
+ Explosion.Create(xx,yy,height)
+ BADDIE_LIST.Remove(Self)
+ enemiesleft:-1
+ MainPlayer.Score = MainPlayer.Score+150
+ Return
+ EndIf
+ EndIf
+ Select typ
+ Case 0 ' sliding up
+ If onEdge.Spike
+ If height < OnEdge.Spike.height
+ height:+2
+ Else
+ oldtyp = 1
+ typ = 3
+ EndIf
+ Else
+ If height < theLevel.Depth
+ height:+1
+ If canflip Then oldtyp=1;typ = 3
+ Else
+ height = theLevel.Depth
+ oldtyp=2
+ typ = 3
+ EndIf
+ EndIf
+ If Rand(0,100) > 98 And hasbullets Then bullets.Create(e_index,height);hasbullets:-1
+ dir = FindShortDir(e_Index, MainPlayer.e_Index)
+ Case 1,2 ' flipping
+ angle = angle + flipflipspeed
+ If typ = 1
+ If height < theLevel.Depth
+ height:+1
+ Else
+ height = theLevel.Depth
+ EndIf
+ EndIf
+ totangle = theLevel.edges[e_Index].angle'-8
+ If dir = -1
+ Local ind = e_index-1
+ If ind < 0 Then ind = theLevel.e_cnt-1
+ totangle = theLevel.edges[ind].angle
+ EndIf
+ If angle >= totangle
+ angle = 0
+ If height < theLevel.Depth
+ oldtyp = 1
+ typ = 3
+ Else
+ oldtyp = 2 ' return to rim case, not climbing
+ typ = 3
+ EndIf
+ pause = 20
+ EndIf
+ Case 3 'delaying
+ pause :-1
+ If height < theLevel.Depth
+ height:+1
+ Else
+ height = theLevel.Depth
+ EndIf
+ If pause <= 0
+ typ = oldtyp
+
+ If height >= theLevel.depth Then dir = FindShortDir(e_Index, MainPlayer.e_Index)
+
+ If theLevel.continuous = False
+ If e_index - dir < 0 Or e_index - dir > theLevel.e_cnt-1
+ dir = -dir
+ EndIf
+ EndIf
+ 'move to next edge
+ If dir = 1
+ If e_Index=0
+ If theLevel.continuous
+ e_Index= theLevel.e_Cnt-1
+ Else
+ dir = -dir
+ EndIf
+ Else
+ e_Index:-1
+ EndIf
+ Else
+ If e_Index=theLevel.e_cnt-1
+ If theLevel.continuous
+ e_Index= 0
+ Else
+ dir = -dir
+ EndIf
+ Else
+ e_Index:+1
+ EndIf
+ EndIf
+ OnEdge = theLevel.edges[e_Index]
+ totangle = theLevel.edges[e_Index].angle'-8
+
+ If Rand(0,100) > 98 And hasbullets Then bullets.Create(e_index,height);hasbullets:-1
+
+ If dir = -1
+ Local ind = e_index-1
+ If ind < 0 Then ind = theLevel.e_cnt-1
+ totangle = theLevel.edges[ind].angle '-8
+ EndIf
+ If onEdge.Spike
+ If height < OnEdge.Spike.height
+ typ = 0
+ EndIf
+ Else
+ typ = oldtyp
+ EndIf
+
+ EndIf
+ Case 7 'killing player!
+ 'If height> 0 Then height:-3
+ End Select
+ EndMethod
+
+ Method CheckColl(e:edge,z#)
+ Local sp# = height-z
+ If e = OnEdge
+ If sp < 16 And sp > 0
+ Local xx#,yy#,zz1#,x#[2],y#[2]
+ If typ = 1 Or typ = 2
+ zz1 = theLevel.position+theLevel.depth-height
+ TForm(OnEdge.p1.x,OnEdge.p1.y,zz1,x[0],y[0])
+ TForm(OnEdge.p2.x,OnEdge.p2.y,zz1,x[1],y[1])
+ If dir = -1 'pivot around p1 of dest edge (current edge+1)
+ TFormR(x[1],y[1],-angle+totangle, x[0],y[0])
+ Else
+ TFormR(x[0],y[0],-(totangle-angle), x[1],y[1])
+ EndIf
+ xx = (x[0]+x[1])/2
+ yy = (y[0]+y[1])/2
+ Else
+ zz1 = theLevel.position + theLevel.depth - height
+ TForm(OnEdge.xx,OnEdge.yy,zz1,xx,yy)
+ EndIf
+ PlaySound(flippershotsfx)
+ Explosion.Create(xx,yy,height)
+ BADDIE_LIST.Remove(Self)
+ MainPlayer.Score = MainPlayer.Score+150
+ enemiesleft:-1
+ Return True
+ EndIf
+ EndIf
+ Return False
+ EndMethod
+
+ Method CheckPlayerColl()
+ If MainPlayer.e_Index = e_index
+ If theLevel.depth = height
+ If typ <> 1 And typ <> 2
+ typ=7
+ PlaySound(killedbyflippersfx)
+ KillPlayer(KILLED_BY_FLIPPER)
+ Return True
+ Else
+ If Abs(totangle-angle) < 30
+ typ=7
+ PlaySound(killedbyflippersfx)
+ KillPlayer(KILLED_BY_FLIPPER)
+ Return True
+ EndIf
+ EndIf
+ EndIf
+ EndIf
+ Return False
+ EndMethod
+
+ Function Create:flipper(index,typ = 0,height)
+ Local c:flipper = New flipper
+ c.OnEdge = theLevel.edges[index]
+ c.e_Index= index
+ c.typ = typ
+ c.height = height
+ c.hasbullets = Rand(1,3)
+ c.baddietype = BAD_FLIPPER
+ BADDIE_LIST.AddLast c
+ enemiesleft:+1
+ Return c
+ EndFunction
+
+EndType
+
+
+
+
+Type Pulsar Extends Baddies
+ Field typ
+ Field oldtyp
+ Field canflip = True
+ Field timetoflip = 0
+
+ Field Pause 'the pause before changing edge
+ Field dir 'direction left or right
+ Field hdir
+ Field angle# 'the angle when changing lanes
+ Field totangle#
+
+ Method DrawStats(x,y)
+ Color(COL_PULSARS)
+ DrawText "i= "+e_Index+" d="+dir+" h="+Int(height)+" a="+Int(typ)+" t="+Int(timetoflip) ,x,y
+ End Method
+
+ Method Draw()
+ Local xx#,yy#,zz1#,zz2#
+ Local x#[7],y#[7]
+ Select typ
+ Case 0,4 ' sliding up/down
+ zz1 = theLevel.position+theLevel.depth
+ zz2 = zz1 - height
+ TForm(OnEdge.p1.x,OnEdge.p1.y,zz2,x[0],y[0])
+ TForm(OnEdge.p2.x,OnEdge.p2.y,zz2,x[1],y[1])
+ Case 1,2 ' flipping
+ zz1 = theLevel.position+theLevel.depth
+ zz2 = zz1 - height
+ TForm(OnEdge.p1.x,OnEdge.p1.y,zz2,x[0],y[0])
+ TForm(OnEdge.p2.x,OnEdge.p2.y,zz2,x[1],y[1])
+ If dir = -1 'pivot around p1 of dest edge (current edge+1)
+ TFormR(x[1],y[1],-angle+totangle, x[0],y[0])
+ Else
+ TFormR(x[0],y[0],-(totangle-angle), x[1],y[1])
+ EndIf
+ Case 3,5,7 'delaying
+ zz1 = theLevel.position+theLevel.depth
+ zz2 = zz1 - height
+ TForm(OnEdge.p1.x,OnEdge.p1.y,zz2,x[0],y[0])
+ TForm(OnEdge.p2.x,OnEdge.p2.y,zz2,x[1],y[1])
+ End Select
+
+ Local xn#
+ Local yn#
+ Local zh# = pulse_zh#
+
+ Local xd# = x[1]-x[0]
+ Local yd# = y[1]-y[0]
+ Local sz# = Sqr(xd*xd + yd*yd)
+ If sz = 0
+ xn# = 0
+ yn# = 0
+ Else
+ xn# = -yd/sz
+ yn# = xd/sz
+ EndIf
+ x[2] = x[1]-(xd)*0.2 - xn*zh
+ y[2] = y[1]-(yd)*0.2 - yn*zh
+ x[3] = x[1]-(xd)*0.35 + xn*zh
+ y[3] = y[1]-(yd)*0.35 + yn*zh
+ x[4] = x[1]-(xd)*0.5 - xn*zh
+ y[4] = y[1]-(yd)*0.5 - yn*zh
+ x[5] = x[1]-(xd)*0.65 + xn*zh
+ y[5] = y[1]-(yd)*0.65 + yn*zh
+ x[6] = x[1]-(xd)*0.8 - xn*zh
+ y[6] = y[1]-(yd)*0.8 - yn*zh
+
+ If pulsing
+ Color(COL_BULLETS)
+ Else
+ Color(COL_PULSARS)
+ EndIf
+ DrawLine x[1],y[1],x[2],y[2]
+ DrawLine x[2],y[2],x[3],y[3]
+ DrawLine x[3],y[3],x[4],y[4]
+ DrawLine x[4],y[4],x[5],y[5]
+ DrawLine x[5],y[5],x[6],y[6]
+ DrawLine x[6],y[6],x[0],y[0]
+
+ EndMethod
+
+ Method ZapIt(t)
+ dietime = t 'die after t tics
+ Return True
+ End Method
+
+ Method Update()
+ If dietime > 0
+ dietime:-1
+ If dietime = 0 'die
+ PlaySound(zapsfx,zapchan)
+ Local xx#,yy#,zz1#
+ zz1 = theLevel.position + theLevel.depth - height
+ TForm(OnEdge.xx,OnEdge.yy,zz1,xx,yy)
+ Explosion.Create(xx,yy,height)
+ BADDIE_LIST.Remove(Self)
+ enemiesleft:-1
+ MainPlayer.Score = MainPlayer.Score+200
+ Return
+ EndIf
+ EndIf
+ onEdge.haspulser = True
+ OnEdge.pulsing = pulsing
+ timetoflip:+1
+ Select typ
+ Case 0 ' sliding up
+ If height < theLevel.Depth
+ height:+1
+ If timetoflip > 50
+ If Rand(0,1)
+ oldtyp=1;typ = 3
+ Else
+ oldtyp=0;typ = 5
+ EndIf
+ timetoflip = 0
+ EndIf
+ Else
+ ' reached the top , go down or flip?
+ height = theLevel.Depth
+ If Rand(0,1) Or rimit
+ 'flip
+ oldtyp=1;typ = 3
+ Else
+ 'go down
+ oldtyp=4;typ = 5
+ EndIf
+ timetoflip = 0
+ EndIf
+ pause = 20
+ dir = FindShortDir(e_Index, MainPlayer.e_Index)
+ Case 4 ' sliding down
+ If height > 0
+ height:-1
+ If timetoflip > 50
+ If Rand(0,1)
+ oldtyp=1;typ = 3
+ Else
+ oldtyp=4;typ = 5
+ EndIf
+ timetoflip = 0
+ EndIf
+ Else
+ ' reached the bottom
+ height = 0
+ If Rand(0,1)
+ 'flip
+ oldtyp=1;typ = 3
+ Else
+ 'go up
+ oldtyp=0;typ = 5
+ EndIf
+ timetoflip = 0
+ EndIf
+ pause = 20
+ dir = FindShortDir(e_Index, MainPlayer.e_Index)
+ Case 1,2 ' flipping
+ angle = angle + pulseflipspeed
+ If typ = 1
+ If height < theLevel.Depth
+ height:+1
+ Else
+ height = theLevel.Depth
+ EndIf
+ EndIf
+ totangle = theLevel.edges[e_Index].angle'-8
+ If dir = -1
+ Local ind = e_index-1
+ If ind < 0 Then ind = theLevel.e_cnt-1
+' If ind > theLevel.e_cnt-1 Then ind = 0
+ totangle = theLevel.edges[ind].angle '-8
+ EndIf
+ If angle >= totangle
+ angle = 0
+ If height < theLevel.Depth
+ oldtyp = 0
+ typ = 5
+ Else
+ oldtyp = 0 'made it to the rim
+ typ = 5
+ EndIf
+ pause = 20
+ EndIf
+ Case 5 'delaying 2
+ pause :-1
+ If pause <= 0
+ timetoflip = 0
+ typ = oldtyp
+ EndIf
+ Case 3 'delaying
+ pause :-1
+ If pause <= 0
+ timetoflip = 0
+ typ = oldtyp
+
+ If theLevel.continuous = False
+ If e_index - dir < 0 Or e_index - dir > theLevel.e_cnt-1
+ dir = -dir
+ EndIf
+ EndIf
+ 'move to next edge
+ If dir = 1
+ If e_Index=0
+ If theLevel.continuous
+ e_Index= theLevel.e_Cnt-1
+ Else
+ dir = -dir
+ EndIf
+ Else
+ e_Index:-1
+ EndIf
+ Else
+ If e_Index=theLevel.e_cnt-1
+ If theLevel.continuous
+ e_Index= 0
+ Else
+ dir = -dir
+ EndIf
+ Else
+ e_Index:+1
+ EndIf
+ EndIf
+ OnEdge = theLevel.edges[e_Index]
+ totangle = theLevel.edges[e_Index].angle'-8
+ If dir = -1
+ Local ind = e_index-1
+ If ind < 0 Then ind = theLevel.e_cnt-1
+ totangle = theLevel.edges[ind].angle '-8
+ EndIf
+ EndIf
+ Case 7 'killing player!
+ 'If height> 0 Then height:-3
+ End Select
+ pulsesalive = True
+ EndMethod
+
+ Method CheckColl(e:edge,z#)
+ Local sp# = height-z
+ If e = OnEdge
+ If sp < 16 And sp > 0
+ Local xx#,yy#,zz1#,x#[2],y#[2]
+ If typ = 1 Or typ = 2
+ zz1 = theLevel.position+theLevel.depth-height
+ TForm(OnEdge.p1.x,OnEdge.p1.y,zz1,x[0],y[0])
+ TForm(OnEdge.p2.x,OnEdge.p2.y,zz1,x[1],y[1])
+ If dir = -1 'pivot around p1 of dest edge (current edge+1)
+ TFormR(x[1],y[1],-angle+totangle, x[0],y[0])
+ Else
+ TFormR(x[0],y[0],-(totangle-angle), x[1],y[1])
+ EndIf
+ xx = (x[0]+x[1])/2
+ yy = (y[0]+y[1])/2
+ Else
+ zz1 = theLevel.position + theLevel.depth - height
+ TForm(OnEdge.xx,OnEdge.yy,zz1,xx,yy)
+ EndIf
+ Explosion.Create(xx,yy,height)
+ PlaySound(pulsarshotsfx)
+ BADDIE_LIST.Remove(Self)
+ MainPlayer.Score = MainPlayer.Score+200
+ enemiesleft:-1
+ Return True
+ EndIf
+ EndIf
+ Return False
+ EndMethod
+
+ Method CheckPlayerColl()
+ If MainPlayer.e_Index = e_index
+ If theLevel.depth-height < 8
+ If typ <> 1 And typ <> 2
+ PlaySound(killedbypulsarsfx)
+ KillPlayer(KILLED_BY_FLIPPER)
+ typ = 7
+ Return True
+ Else
+ If Abs(totangle-angle) < 30
+ PlaySound(killedbypulsarsfx)
+ KillPlayer(KILLED_BY_FLIPPER)
+ typ = 7
+ Return True
+ EndIf
+ EndIf
+ Else
+ If typ <> 1 And typ <> 2
+ If pulsing
+ PlaySound(killedbypulsarsfx)
+ KillPlayer(KILLED_BY_PULSAR)
+ Return True
+ EndIf
+ EndIf
+ EndIf
+ EndIf
+ Return False
+ EndMethod
+
+ Function Create:Pulsar(index,typ = 0,height)
+ Local p:Pulsar = New Pulsar
+ p.OnEdge = theLevel.edges[index]
+ p.e_Index= index
+ p.typ = typ
+ p.height = height
+ p.baddietype = BAD_PULSAR
+ BADDIE_LIST.AddLast p
+ enemiesleft:+1
+ Return p
+ EndFunction
+
+
+ Function UpdatePulseTimers()
+
+ pulsecount:+pulsespeed
+ If pulsecount > 180 Then pulsecount = 0
+ If pulsecount = 71 And pulsesalive Then PlaySound(pulsesfx)
+ If pulsecount > 70 And pulsecount < 110 Then pulsing = True Else pulsing = False
+
+ pulse_zh# = Abs((pulsecount Mod 90)-45)/4.0
+ If pulse_zh# < 2 Then pulse_zh# = 0
+ If pulse_zh# > 12 Then pulse_zh# = 12
+
+ pulsesalive = False
+ End Function
+
+EndType
+
+
+
+Type Fuseball Extends Baddies
+ Field act
+ Field cnt
+ Field dir
+ Field w ' position across the edge
+
+ Method DrawStats(x,y)
+ SetColor 255,255,255
+ DrawText "i="+e_Index+" h="+Int(height)+" a="+act+" w="+w+" d="+dir ,x,y
+ End Method
+
+ Method Draw()
+ Local xx#,yy#,zz1#,zz2#,x#[2],y#[2]
+ zz1# = theLevel.position+theLevel.depth
+ zz2# = zz1 - height
+ TForm(OnEdge.p1.x,OnEdge.p1.y,zz2,x[0],y[0])
+ TForm(OnEdge.p2.x,OnEdge.p2.y,zz2,x[1],y[1])
+ xx = x[1]+(x[0]-x[1]) *w/8
+ yy = y[1]+(y[0]-y[1]) *w/8
+ Local sc# '= height/theLevel.depth*7.0+1
+ sc = TFormSZ(8,zz2)+1
+
+ For Local arm = 0 To 5
+ Select arm
+ Case 0
+ Color(COL_CLAW)
+ Case 1
+ Color(COL_SPIKERS)
+ Case 2
+ Color(COL_TANKERS)
+ Case 3
+ Color(COL_PULSARS)
+ Case 4
+ Color(COL_FLIPPERS)
+ End Select
+ For Local i = 0 To 4
+ DrawLine xx+fusex[fuseball_frame,arm,i]*sc,yy+fusey[fuseball_frame,arm,i]*sc,xx+fusex[fuseball_frame,arm,i+1]*sc,yy+fusey[fuseball_frame,arm,i+1]*sc
+ Next
+ Next
+ EndMethod
+
+ Method ZapIt(t)
+ dietime = t 'die after t tics
+ Return True
+ End Method
+
+ Method Update()
+ If dietime > 0
+ dietime:-1
+ If dietime = 0 'die
+ PlaySound(zapsfx,zapchan)
+ Local xx#,yy#,zz1#
+ zz1 = theLevel.position + theLevel.depth - height
+ TForm(OnEdge.xx,OnEdge.yy,zz1,xx,yy)
+ Local sc = Int((theLevel.depth-height)/100+1)*250
+ fusepoint.Create(xx,yy,height,sc)
+ MainPlayer.Score = MainPlayer.Score+sc
+ BADDIE_LIST.Remove(Self)
+ enemiesleft:-1
+ Return
+ EndIf
+ EndIf
+ cnt = cnt + 1
+ Select act
+ Case 0
+ If cnt > 8
+ If Rand(0,100) > 90 Then dir = -FindShortDir(e_Index, MainPlayer.e_Index)
+ cnt = 0
+ 'move left or right....
+ Select dir
+ Case 1
+ w = w + 1
+ If w >= 8
+ w = 0
+ If e_Index=theLevel.e_cnt-1
+ If theLevel.continuous
+ e_Index= 0
+ Else
+ w = 8
+ dir = -dir
+ EndIf
+ Else
+ e_Index:+1
+ EndIf
+ EndIf
+
+ Case -1
+ w = w - 1
+ If w =< 0
+ w = 8
+ If e_Index=0
+ If theLevel.continuous
+ e_Index= theLevel.e_Cnt-1
+ Else
+ w = 0
+ dir = -dir
+ EndIf
+ Else
+ e_Index:-1
+ EndIf
+ EndIf
+ End Select
+ OnEdge = theLevel.edges[e_Index]
+ If w = 0 Or w = 8
+ act = Rand(1,2)
+ If rimit Or height = 0 Then act = 1
+ EndIf
+ EndIf
+ Case 1 ' going up
+ If height < theLevel.Depth
+ height:+4
+ If cnt > 25*(Rand(1,3))
+ act = 0
+ cnt = 0
+ EndIf
+ Else
+ height = theLevel.Depth
+ act = 0
+ cnt = 0
+ EndIf
+ Case 2 ' going down
+ If height >= 0
+ height:-4
+ If cnt > 25*(Rand(1,3))
+ act = 0
+ cnt = 0
+ EndIf
+ Else
+ act = 0
+ cnt = 0
+ height = 0
+ EndIf
+ Case 7
+ w = 3
+ 'killing player
+ End Select
+ EndMethod
+
+ Method CheckColl(e:edge,z#)
+
+ Local sp# = height-z
+ If e = OnEdge
+ If sp < 16 And sp > 0
+ If w >1 And w < 7
+ Local xx#,yy#,zz1#
+ zz1 = theLevel.position + theLevel.depth - height
+ TForm(OnEdge.xx,OnEdge.yy,zz1,xx,yy)
+ Local sc = Int((theLevel.depth-height)/100+1)*250
+ fusepoint.Create(xx,yy,height,sc)
+ MainPlayer.Score = MainPlayer.Score+sc
+ BADDIE_LIST.Remove(Self)
+ PlaySound(fuseballshotsfx)
+ enemiesleft:-1
+ Return True
+ EndIf
+ EndIf
+ EndIf
+ Return False
+ EndMethod
+
+ Method CheckPlayerColl()
+ If MainPlayer.e_Index = e_index
+ If act = 0 And height = theLevel.depth
+ If w > 1 And w < 7
+ act = 7
+ KillPlayer(KILLED_BY_FUSEBALL)
+ PlaySound(killedbyfuseballsfx)
+ Return True
+ EndIf
+ EndIf
+ EndIf
+ Return False
+ EndMethod
+
+ Function Create:fuseball(index,act,height)
+ Local f:fuseball= New fuseball
+ f.OnEdge=theLevel.Edges[index]
+ f.height=height
+ f.act = act
+ f.e_Index = index
+ f.cnt = 0
+ f.w = 0
+ f.dir = 1
+ f.baddietype = BAD_FUSEBALL
+ BADDIE_LIST.AddLast f
+ enemiesleft:+1
+ Return f
+ EndFunction
+
+ Function UpdateFuseballTimers()
+
+ fuseball_count:+1
+ fuseball_frame = (fuseball_count/4) Mod 4
+
+ End Function
+
+
+EndType
+
+
+
+
+Type Spikes Extends Baddies
+
+ Method DrawStats(x,y)
+ Color(COL_SPIKERS)
+ DrawText "i="+e_Index+" h="+Int(height),x,y
+ End Method
+
+ Method Draw()
+ Local oldz
+ If theLevel.state = LEVEL_INTO_VORTEX
+ oldz = ZOFFSET
+ ZOFFSET = 5
+ EndIf
+
+ Local xx#,yy#,zz1#,zz2#
+ zz1 = theLevel.position+theLevel.depth
+ zz2 = zz1 - height
+
+ Local x#[2],y#[2]
+ xx =( ( OnEdge.p2.x - OnEdge.p1.x ) / 2.0 ) + OnEdge.p1.x
+ yy =( ( OnEdge.p2.y - OnEdge.p1.y ) / 2.0 ) + OnEdge.p1.y
+ TForm(xx,yy,zz1,x[0],y[0])
+ TForm(xx,yy,zz2,x[1],y[1])
+ Color(COL_SPIKERS)
+ DrawLine x[0],y[0],x[1],y[1]
+ Color(COL_BULLETS)
+ Plot x[1],y[1]
+ If theLevel.state = LEVEL_INTO_VORTEX
+ ZOFFSET = oldz
+ EndIf
+
+ EndMethod
+
+ Method ZapIt(t)
+ Return False
+ End Method
+
+ Method Update()
+ EndMethod
+
+ Method CheckColl(e:edge,z#)
+ 'check to see if shot hits it
+ If e = OnEdge
+ If z < height
+ height:-8
+ MainPlayer.Score = MainPlayer.Score+1
+ PlaySound(spikeshotsfx)
+ If height =< 0
+ OnEdge.Spike = Null
+ BADDIE_LIST.Remove(Self)
+ Return True
+ EndIf
+ Return True
+ EndIf
+ EndIf
+ Return False
+ EndMethod
+
+ Method CheckPlayerColl()
+ If MainPlayer.e_Index = e_index
+ If height > mainplayer.zPos And mainplayer.deathcount = 0
+ PlaySound(killedbyspikesfx)
+ KillPlayer(KILLED_BY_SPIKE)
+ Return True
+ EndIf
+ EndIf
+ Return False
+ EndMethod
+
+ Function Create:Spikes(index,h#)
+ Local s:Spikes = New Spikes
+ s.OnEdge=theLevel.Edges[index]
+ theLevel.Edges[index].Spike = s
+ s.e_index = index
+ s.height=h
+ s.baddietype = BAD_SPIKE
+ BADDIE_LIST.AddLast s
+ Return s
+ EndFunction
+
+EndType
+
+
+
+
+
+Type Spinner Extends Baddies
+ Field grow_speed#
+ Field growth# = 0
+ Field r#
+ Field act
+
+ Method DrawStats(x,y)
+ Color(COL_SPIKERS)
+ DrawText "i="+e_Index+" h="+Int(height)+" a="+act ,x,y
+ End Method
+
+ Method Draw()
+ Local xx#,yy#,zz1#,zz2#
+ zz1# = theLevel.position+theLevel.depth
+ zz2# = zz1 - height
+ Local x#,y#,xd1#,yd1#,xd2#,yd2#
+ xx = OnEdge.xx
+ yy = OnEdge.yy
+ Color(COL_SPIKERS)
+ TForm(xx,yy,zz2,x,y)
+ Local scale#' = height/theLevel.depth*16.0+1
+ scale = TFormSZ(22,zz2)+2
+
+
+ For Local sc# = 0 To -13 Step -1
+ xd1 = Sin(r+sc*60)*sc*scale/16
+ yd1 = Cos(r+sc*60)*sc*scale/16
+ xd2 = Sin(r+(sc+1)*60)*(sc+1)*scale/16
+ yd2 = Cos(r+(sc+1)*60)*(sc+1)*scale/16
+ DrawLine x+xd1,y+yd1,x+xd2,y+yd2
+ Next
+ r = r+45
+ EndMethod
+
+ Method ZapIt(t)
+ dietime = t 'die after t tics
+ Return True
+ End Method
+
+ Method Update()
+ If dietime > 0
+ dietime:-1
+ If dietime = 0 'die
+ PlaySound(zapsfx,zapchan)
+ MainPlayer.Score = MainPlayer.Score+50
+ BADDIE_LIST.Remove(Self)
+ enemiesleft:-1
+ Return
+ EndIf
+ EndIf
+ Select act
+ Case 0
+ ' go directly to building spike
+ act = 1
+
+ Case 1 ' going up/building spike
+ If height < theLevel.Depth-4
+ height:+grow_speed
+ If onEdge.Spike
+ If height > onEdge.Spike.height
+ onEdge.Spike.height = height - 4
+ growth:+grow_speed
+ If growth > 100
+ If Rnd(0,140) > 88
+ act = 2
+ If Rnd(0,100) > 70 Then bullets.Create(e_index,height)
+ EndIf
+ EndIf
+ EndIf
+ Else
+ OnEdge.Spike = spikes.Create(e_index,10)
+ OnEdge.Spike.height = height
+ EndIf
+ Else
+ act = 2
+ EndIf
+ Case 2 ' going down spike
+ growth = 0
+ If height > 0
+ height:-grow_speed
+ Else
+ height = 0
+ act = 0
+ BADDIE_LIST.Remove(Self)
+ enemiesleft:-1
+ eggsleft:+1
+ EndIf
+ End Select
+ EndMethod
+
+ Method CheckColl(e:edge,z#)
+ 'check to see if any will hit
+ If e = OnEdge
+ If z < height
+ MainPlayer.Score = MainPlayer.Score+50
+ enemiesleft:-1
+ PlaySound(spinnershotsfx)
+ BADDIE_LIST.Remove(Self)
+ Return True
+ EndIf
+ EndIf
+ Return False
+ EndMethod
+
+ Method CheckPlayerColl()
+ ' never hits the player
+ Return False
+ EndMethod
+
+ Function Create:Spinner(index,speed#)
+ Local s:Spinner = New Spinner
+ s.OnEdge=theLevel.Edges[index]
+ s.height=0
+ s.act = 0
+ s.e_Index = index
+ s.grow_speed# = speed
+ s.baddietype = BAD_SPINNER
+ BADDIE_LIST.AddLast s
+ enemiesleft:+1
+ Return s
+ EndFunction
+
+EndType
+
+
+
+
+
+
+
+
+Function Game()
+
+ Local done = False
+
+ While Not done
+ FlushKeys()
+ done = theLevel.LevelSelect()
+ mainplayer.score = 0
+ mainplayer.men = 2
+ mainplayer.bonusmencnt = 0
+ FlushKeys()
+ If Not done
+ 'main loop
+ Local gamedone = False
+ While Not gamedone
+ If KeyHit(KEY_ESCAPE) Then gamedone = True
+ Local tim = MilliSecs()
+ globalclock = globalclock + 1
+
+ If (globalclock Mod enemyreleaserate = 0)
+ For Local a = 0 To 5
+ If eggsleft > 0
+ Egg.Create(Rand(0,theLevel.e_cnt-2+(theLevel.continuous)))
+ eggsleft:-1
+ EndIf
+ Next
+ EndIf
+
+ point.UpdatePoints()
+ theLevel.Update()
+ theLevel.UpdateAngles()
+ If theLevel.state <> LEVEL_INTO_VORTEX
+ pulsar.UpdatePulseTimers()
+ fuseball.UpdateFuseBallTimers()
+ shot.UpdateShots()
+ egg.UpdateEggs()
+ If theLevel.state <> LEVEL_COMPLETE Then Baddies.UpdateBaddies()
+ MainPlayer.Update()
+ EndIf
+
+ If theLevel.state <> LEVEL_COMPLETE
+ theLevel.Draw()
+ shot.DrawShots()
+ egg.DrawEggs()
+ Baddies.DrawBaddies()
+ explosion.UpdateDrawExplosions()
+ fusepoint.UpdateDrawFusePoints()
+
+ If theLevel.state <> LEVEL_INTO_VORTEX Then MainPlayer.Draw()
+ Else
+ If startbonus > 0
+ DrawString("BONUS",300,170,10)
+ Local l$ = startbonus
+ Local ln = Len(l$)
+ DrawString(startbonus,400-ln*20,270,10)
+ EndIf
+ DrawString("SUPERZAPPER RECHARGE",120,420,6)
+ EndIf
+
+ Color(COL_CLAW)
+ MainPlayer.DrawMenLeft()
+ DrawString(MainPlayer.Score,10,10,6.0)
+ Color(COL_INFO)
+ DrawString((current_level+1),400-6*(current_level>8),10,3.0)
+
+ tim = 16-(MilliSecs()-tim)
+ If tim > 0 Then Delay tim
+
+ If eggsleft = 0 And CountList(EGG_LIST) = 0
+ If enemiesleft = 0 Or onrim = True
+ If theLevel.state = LEVEL_READY
+ theLevel.state = LEVEL_START_ZOOM
+ EndIf
+ Else
+ rimit = True
+ EndIf
+ EndIf
+
+' If KeyHit(key_u) Then YOFFSET = YOFFSET - 10
+' If KeyHit(key_j) Then YOFFSET = YOFFSET + 10
+' DrawText YOFFSET ,700,0
+' If KeyHit(key_i) Then CCenterY = CCenterY - 10
+' If KeyHit(key_k) Then CCenterY = CCenterY + 10
+' DrawText CCenterY,700,20
+
+ If showdebug DrawString(theLevel.state+" ("+eggsleft+")("+CountList(EGG_LIST)+")("+enemiesleft+")",400,40,5.0)
+ If KeyDown(KEY_LSHIFT)
+ If KeyHit(key_s) Then superzapper = 2
+ If KeyHit(key_b) Then mainplayer.score:+10000
+ If KeyHit(key_z) Then theLevel.state = LEVEL_START_ZOOM
+ If KeyHit(key_m) Then theLevel.mutate()
+ If KeyHit(key_n) Then point.ResetPoints()
+ If KeyHit(key_t) Then tubes:+16; If tubes > 32 Then tubes = 0
+ If KeyHit(key_d) Then showdebug = 1-showdebug
+ EndIf
+
+ Local skip = False
+ While KeyDown(key_f1) And Not skip
+ Delay 100
+ If KeyHit(key_f2) Then skip = True
+ Wend
+
+
+
+ If mainplayer.men < 0 Then gameover();gamedone = True
+
+ Flip
+ Cls
+ Wend
+ ClearLevel()
+ EndIf
+ Wend
+End Function
+
+
+
+
+Function GameOver()
+
+ Local done = False
+ Local sc# = .2
+ Local scd# = .05
+ Local s$ = "GAME OVER"
+ While Not done
+
+ Cls
+ SetColor 0,255,0
+ DrawString(s$,400-sc*25,200,sc)
+
+ DrawString(MainPlayer.Score,10,10,6.0)
+ DrawString(current_level,400,10,3.0)
+
+ If KeyHit(KEY_SPACE) Then done = True
+
+ If KeyHit(KEY_ESCAPE) Then done = True
+
+ sc = sc + scd
+ If sc > 4 Or sc < .1 Then scd = -scd;If s$ = "GAME OVER" Then s$ = "PRESS ZAP" Else s$ = "GAME OVER"
+
+ Flip
+ Delay 16
+ Wend
+
+End Function
+
+
+
+
+
+Function KillPlayer(nme)
+
+ Select nme
+ Case KILLED_BY_BULLET
+ mainplayer.deathcount = 60
+ theLevel.state = LEVEL_PLAYER_DYING
+ mainplayer.deathtype = KILLED_BY_BULLET
+ Case KILLED_BY_PULSAR
+ mainplayer.deathcount = 60
+ theLevel.state = LEVEL_PLAYER_DYING
+ mainplayer.deathtype = KILLED_BY_PULSAR
+ Case KILLED_BY_SPIKE
+ mainplayer.deathcount = 60
+ theLevel.state = LEVEL_REVERSE_ZOOM
+ mainplayer.deathtype = KILLED_BY_SPIKE
+ Baddies.ConvertBaddieToEggs()
+ Case KILLED_BY_FLIPPER
+ mainplayer.deathcount = 60
+ theLevel.state = LEVEL_PLAYER_DYING
+ mainplayer.deathtype = KILLED_BY_FLIPPER
+ Case KILLED_BY_FUSEBALL
+ mainplayer.deathcount = 60
+ theLevel.state = LEVEL_PLAYER_DYING
+ mainplayer.deathtype = KILLED_BY_FUSEBALL
+ End Select
+
+End Function
+
+
+
+
+Function FindShortDir(from_i, to_i)
+
+ Local d
+ If theLevel.continuous
+' d = -(MainPlayer.e_Index - e_Index) 'how far from player
+ d = -(to_i - from_i) 'how far from player
+ If Abs(d) > 8 Then d = -Sgn(d) Else d = Sgn(d) 'take the shorter route
+ If d = 0 Then d = 1 - Rand(0,1)*2 '-1 or 1
+ Else
+ d = -Sgn(to_i - from_i)
+ If d = 0 Then d = 1 - Rand(0,1)*2 '-1 or 1
+ EndIf
+ Return d
+
+End Function
+
+
+
+
+Function Color(c)
+
+ Select current_color
+ Case 0 'level 1-16
+ Select c
+ Case COL_BULLETS
+ SetColor 255,255,255
+ Case COL_CLAW
+ SetColor 255,255,0
+ Case COL_TANKERS
+ SetColor 255,0,255
+ Case COL_FLIPPERS
+ SetColor 255,0,0
+ Case COL_PULSARS
+ SetColor 0,255,255
+ Case COL_SPIKERS
+ SetColor 0,255,0
+ Case COL_LEVEL
+ SetColor 0,0,255
+ Case COL_INFO
+ SetColor 0,0,255
+ End Select
+ Case 1 'level 17-32
+ Select c
+ Case COL_BULLETS
+ SetColor 255,255,255
+ Case COL_CLAW
+ SetColor 0,255,0
+ Case COL_TANKERS
+ SetColor 0,0,255
+ Case COL_FLIPPERS
+ SetColor 255,0,255
+ Case COL_PULSARS
+ SetColor 255,255,0
+ Case COL_SPIKERS
+ SetColor 0,255,255
+ Case COL_LEVEL
+ SetColor 255,0,0
+ Case COL_INFO
+ SetColor 255,0,0
+ End Select
+ Case 2 'level 33-48
+ Select c
+ Case COL_BULLETS
+ SetColor 255,255,255
+ Case COL_CLAW
+ SetColor 0,0,255
+ Case COL_TANKERS
+ SetColor 0,255,255
+ Case COL_FLIPPERS
+ SetColor 0,255,0
+ Case COL_PULSARS
+ SetColor 255,0,255
+ Case COL_SPIKERS
+ SetColor 255,0,0
+ Case COL_LEVEL
+ SetColor 255,255,0
+ Case COL_INFO
+ SetColor 255,255,0
+ End Select
+ Case 3 'level 49-64
+ Select c
+ Case COL_BULLETS
+ SetColor 255,255,255
+ Case COL_CLAW
+ SetColor 0,0,255
+ Case COL_TANKERS
+ SetColor 255,0,255
+ Case COL_FLIPPERS
+ SetColor 0,255,0
+ Case COL_PULSARS
+ SetColor 255,255,0
+ Case COL_SPIKERS
+ SetColor 255,0,0
+ Case COL_LEVEL
+ SetColor 0,255,255
+ Case COL_INFO
+ SetColor 0,255,255
+ End Select
+
+ Case 4 'level 65-80
+ Select c
+ Case COL_BULLETS
+ SetColor 255,255,255
+ Case COL_CLAW
+ SetColor 255,255,0
+ Case COL_TANKERS
+ SetColor 255,0,255
+ Case COL_FLIPPERS
+ SetColor 255,0,0
+ Case COL_PULSARS
+ SetColor 0,255,255
+ Case COL_SPIKERS
+ SetColor 0,255,0
+ Case COL_LEVEL
+ SetColor 0,0,0
+ Case COL_INFO
+ SetColor 0,0,255
+ End Select
+
+ Case 5 'level 81-96
+ Select c
+ Case COL_BULLETS
+ SetColor 255,255,255
+ Case COL_CLAW
+ SetColor 255,0,0
+ Case COL_TANKERS
+ SetColor 255,0,255
+ Case COL_FLIPPERS
+ SetColor 255,255,0
+ Case COL_PULSARS
+ SetColor 0,255,255
+ Case COL_SPIKERS
+ SetColor 0,0,255
+ Case COL_LEVEL
+ SetColor 0,255,0
+ Case COL_INFO
+ SetColor 0,255,0
+ End Select
+
+ End Select
+
+End Function
+
+
+
+
+
+
+
+
+Function ClearLevel()
+
+ ' delete all eggs and baddies
+ Local e:egg
+ For e = EachIn EGG_LIST
+ EGG_LIST.Remove(e)
+ Next
+
+ Local b:Baddies
+ For b = EachIn BADDIE_LIST
+ BADDIE_LIST.Remove(b)
+ Next
+
+ Local s:shot
+ For s=EachIn SHOT_LIST
+ SHOT_LIST.remove(s)
+ Next
+
+ Local p:Point
+ For p=EachIn POINT_LIST
+ POINT_LIST.Remove(p)
+ Next
+
+ For Local a = 0 Until 16
+ theLevel.Edges[a] = Null
+ Next
+ theLevel.E_cnt = 0
+
+ eggsleft = 0
+ enemiesleft = 0
+ hatchingeggs = 0
+
+End Function
+
+
+
+
+Function SetUpLevel()
+
+ Local p1:Point = Null
+ Local p2:Point = Null
+ Local fp:Point = Null
+
+ theLevel.continuous = leveldata[current_board+tubes,0]
+ k = 70
+ CCenterY = leveldata[current_board+tubes,1]
+ YOFFSET = leveldata[current_board+tubes,2]
+
+ For Local a = 0 Until 16
+ p1 = theLevel.AddPoint( Float(leveldata[current_board+tubes,3+a*2]),Float(leveldata[current_board+tubes,3+a*2+1]))
+ If p2
+ theLevel.AddEdge(p1,p2)
+ Else
+ fp = p1
+ EndIf
+ p2=p1
+ Next
+ If theLevel.continuous
+ Local lastedge:edge = theLevel.AddEdge(fp,p2)
+ EndIf
+
+End Function
+
+
+
+Function SetUpEnemies()
+
+ Local lv = current_level
+
+ enemiesleft = 0
+ eggsleft = 10+lv*2+current_board*2
+ If eggsleft > 100 Then eggsleft = 100
+ maxeggs = eggsleft
+ For Local t = 0 To 3
+ eggsleft:-1
+ Egg.Create(Rand(0,theLevel.e_cnt-2+(theLevel.continuous)))
+ Next
+
+ theLevel.hasflippers = True
+ theLevel.hastankers = False
+ theLevel.hasspikes = False
+ theLevel.hasfuseballs = False
+ theLevel.haspulsars = False
+ theLevel.hastankersp = False
+ theLevel.hastankersf = False
+
+ If current_board > 2 Or lv > 47 Then theLevel.hasspikes = True
+ If lv > 9 Then theLevel.hasfuseballs = True
+ If lv > 1 Then theLevel.hastankers = True
+ If lv > 15 Then theLevel.haspulsars = True
+ If lv > 20 Then theLevel.hastankersp = True
+ If lv > 31 Then theLevel.hastankersf = True
+
+ If theLevel.hasspikes
+ For Local a = 0 To theLevel.e_cnt-1
+ Spikes.Create(a,60.0)
+ Next
+ EndIf
+
+ enemyreleaserate = 60
+ hatchrate = lv/8
+ rimit = False
+
+ flipflipspeed = 15+lv/5 'degrees
+ pulseflipspeed = 15+lv/9
+ fuseclimbspeed = lv/2 ' not used yet
+
+ If lv = 0 Then canflip = False Else canflip = True
+
+ hatchingeggs = 0
+
+End Function
+
+
+
+
+
+
+
+
+
+
+Function ReadFuseballData()
+
+ Local x,y
+
+ For Local frame = 0 To 3
+ For Local arm = 0 To 4
+ For Local i = 0 To 5
+ ReadData x
+ ReadData y
+ fusex[frame,arm,i] = Float(x)/6.0
+ fusey[frame,arm,i] = Float(y)/6.0
+ Next
+ Next
+ Next
+
+End Function
+
+DefData 0,0, 7,-5, 6,-8, 9,-10, 7,-13, 11,-15
+DefData 0,0, 6,3, 6,6, 10,4, 12,4, 13,12
+DefData 0,0, 2,7, -4,10, 1,14, -4,17, 1,20
+DefData 0,0, -7,4, -7,1, -9,1, -11,6, -14,8
+DefData 0,0, -4,-5, -3,-10, -5,-13, -9,-13, -9,-15
+
+DefData 0,0, 6,-8, 5,-11, 9,-14, 7,-18, 6,-20
+DefData 0,0, 7,-3, 6,6, 10,6, 6,11, 11,16
+DefData 0,0, -3,8, -7,6, -5,12, -7,17, -3,21
+DefData 0,0, -6,3, -7,-4, -10,2, -11,-4, -18,4
+DefData 0,0, -3,-6, 2,-11, -4,-13, 2,-16, -2,-21
+
+DefData 0,0, 5,-8, 7,-7, 7,-12, 10,-16, 14,-14
+DefData 0,0, 7,1, 8,5, 11,6, 10,9, 14,10
+DefData 0,0, -5,6, -6,11, -3,11, -6,16, -6,19
+DefData 0,0, -6,1, -8,4, -11,3, -14,5, -16,6
+DefData 0,0, -2,-8, -7,-8, -5,-14, -8,-14, -10,-11
+
+DefData 0,0, 6,-6, 7,-12, 11,-6, 12,-12, 16,-12
+DefData 0,0, 6,5, 6,8, 11,9, 12,14, 16,14
+DefData 0,0, -3,6, -4,10, -9,11, -6,18, -9,21
+DefData 0,0, -4,2, -6,1, -7,2, -12,1, -15,6
+DefData 0,0, 1,-11, 3,-8, 5,-14, 3,-17, 6,-24
+
+
+
+
+Function ReadLevelData()
+
+ Local l,t,v
+
+ For Local l = 0 To 47
+ For Local t = 0 To 34
+ ReadData v
+ leveldata[l,t] = v
+ Next
+ Next
+
+End Function
+
+
+Include "boarddata.bmx"
--- /dev/null
+Strict
+
+' run in 800X600
+Const CWidth#=800
+Const CHeight#=600
+
+Global K# = 50
+
+Global CCenterX#=CWidth/2.0
+Global CCenterY#=CHeight/3.0
+
+Global YOFFSET = 128
+Global XOFFSET = 0
+Global ZOFFSET = 5
+
+
+
+' Return the dot product AB · BC.
+Function DotProduct#( Ax#,Ay#,Bx#,By#,Cx#,Cy#)
+
+ Local BAx#
+ Local BAy#
+ Local BCx#
+ Local BCy#
+
+ ' Get the vectors' coordinates.
+ BAx = Ax - Bx
+ BAy = Ay - By
+ BCx = Cx - Bx
+ BCy = Cy - By
+
+ ' Calculate the dot product.
+ Return (BAx * BCx + BAy * BCy)
+
+End Function
+
+
+
+
+' Return the cross product AB x BC.
+Function CrossProductLength#( Ax#,Ay#,Bx#,By#,Cx#,Cy#)
+
+ Local BAx#
+ Local BAy#
+ Local BCx#
+ Local BCy#
+
+ ' Get the vectors' coordinates.
+ BAx = Ax - Bx
+ BAy = Ay - By
+ BCx = Cx - Bx
+ BCy = Cy - By
+
+ ' Calculate the Z coordinate of the cross product.
+ Return(BAx * BCy - BAy * BCx)
+
+End Function
+
+
+
+' Return the angle ABC.
+Function GetAngle#( Ax#,Ay#,Bx#,By#,Cx#,Cy#)
+
+ Local dot_product#
+ Local cross_product#
+ Local angle#
+
+ ' Get the dot product and cross product.
+ dot_product = DotProduct(Ax, Ay, Bx, By, Cx, Cy)
+ cross_product = CrossProductLength(Ax, Ay, Bx, By, Cx, Cy)
+
+ ' Calculate the angle.
+ angle = MyATan2(cross_product, dot_product)
+ If angle = 0 Then angle = 180
+
+ ' ...handle if angle > 180 case
+ ' find point ax2,ay2 - rotated -90 degrees
+ ' find new angle2
+ ' if angle2 is > 90, then angle = 360-angle
+ If angle <> 180
+ Local px# = ax
+ Local py# = ay
+ Local rang = -1
+ If angle < 90 Then rang = -(179-angle)
+ TFormR(bx,by, rang, px#,py#)
+ dot_product = DotProduct(px, py, Bx, By, Cx, Cy)
+ cross_product = CrossProductLength(px, py, Bx, By, Cx, Cy)
+ ' Calculate the angle.
+ Local angle2 = MyATan2(cross_product, dot_product)
+ If angle2 > 90 Then angle=360-angle
+ EndIf
+
+ Return Abs(angle Mod 360)
+
+End Function
+
+
+
+
+' Return the angle with tangent opp/hyp.
+Function MyATan2#(opp#, adj#)
+
+ Local angle#
+
+ ' Get the basic angle.
+ If Abs(adj) < 0.0001 Then
+ angle = 90
+ Else
+ angle = Abs(ATan(opp / adj))
+ End If
+
+ ' See if we are in quadrant 2 or 3.
+ If adj < 0 Then
+ angle = 180-angle
+ End If
+
+ ' See if we are in quadrant 3 or 4.
+ If opp < 0 Then
+ angle = -angle
+ End If
+
+ ' Return the result.
+ Return angle
+
+End Function
+
+
+
+
+
+
+
+'scale
+Function TFormSZ#(x#, z#)
+ z:+ZOFFSET '50
+ Return (x/(z/K))
+EndFunction
+
+
+' rotate xr,yr around xc,yc
+Function TFormR(xc#,yc#, angle, xr# Var,yr# Var)
+ Local x# = (xr-xc)
+ Local y# = (yr-yc)
+ xr = Cos(angle)*x - Sin(angle)*y
+ yr = Sin(angle)*x + Cos(angle)*y
+ xr = xc+xr
+ yr = yc+yr
+End Function
+
+
+'scale based on z
+Function TForm(x#, y#, z#, x2d# Var, y2d# Var )
+ z:+ZOFFSET
+ y:+YOFFSET
+ x:+XOFFSET
+ x2d = CCenterX+(x/(z/K))
+ y2d = CCenterY+(y/(z/K))
+EndFunction
+
--- /dev/null
+Strict
+
+'Framework BRL.D3D7Max2D
+Import BRL.Retro
+
+
+Type bbdigit
+ Field x1#,y1#,x2#,y2#
+End Type
+
+Global letterlen[128]
+Global letters:bbdigit[128,8]
+SetUpVectorFont()
+
+
+
+'Test()
+Function Test()
+
+ Graphics 800,600,0
+
+ Local sc# = 3.0
+ Local dir = 1
+
+ While Not KeyHit(key_escape)
+
+ Cls
+ sc = sc + .1*dir
+ If sc > 10 Or sc < 1 Then dir = -dir
+ DrawString(" !"+Chr$(30)+"#$%&'()*+,-./",400-sc*40,200-sc*15,sc)
+ DrawString("0123456789:;<=>?",400-sc*40,225-sc*10,sc)
+ DrawString("@ABCDEFGHIJKLMNO",400-sc*40,250-sc*5,sc)
+ DrawString("PQRSTUVWXYZ[\]^_",400-sc*40,275+sc*5,sc)
+ DrawString("`abcdefghijklmno",400-sc*40,300+sc*10,sc)
+ DrawString("pqrstuvwxyz{|}~~" ,400-sc*40,325+sc*15,sc)
+ Flip
+
+ Delay 16+sc*5
+ Wend
+
+End Function
+
+
+
+
+
+
+
+Function SetUpVectorFont()
+
+ RestoreData letterdata
+
+ Local np,t,s
+
+ For t = 0 To 127
+ letterlen[t] = -1
+ Next
+
+ For t = 32 To 127
+ ReadData np 'number of lines in letter (max 6) x1,y1, x2,y2
+ letterlen[t] = np-1
+ For s = 0 To letterlen[t]
+ letters[t,s] = New bbdigit
+ ReadData letters[t,s].x1
+ ReadData letters[t,s].y1
+ ReadData letters[t,s].x2
+ ReadData letters[t,s].y2
+ Next
+ Next
+End Function
+
+
+Function DrawDigit(d,xd,yd,sc#)
+ Local t
+' If d > 32 And d < 128
+' If letterlen[d] > -1
+ For t = 0 To letterlen[d]
+ DrawLine letters[d,t].x1*sc+xd,letters[d,t].y1*sc+yd,letters[d,t].x2*sc+xd,letters[d,t].y2*sc+yd
+ Next
+' EndIf
+' EndIf
+End Function
+
+
+Function DrawString(st$,xd,yd,sc#)
+ Local s,d,ln,t
+
+ ln = Len(st$)
+ For s = 0 To ln-1
+ d = Asc(Mid$(st$,s+1,1))
+' If d > 32 And d < 128
+' If letterlen[d] > -1
+ For t = 0 To letterlen[d]
+ DrawLine letters[d,t].x1*sc+xd+sc*5*s,letters[d,t].y1*sc+yd,letters[d,t].x2*sc+xd+sc*5*s,letters[d,t].y2*sc+yd
+ Next
+' EndIf
+' EndIf
+ Next
+End Function
+
+
+
+
+' ************************** vector text data *******************************************
+' chars 32-127
+' spc!"#$%&'()*+`-,/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_"
+
+#letterdata
+' spc
+DefData 0
+' !
+DefData 2, 2,0, 2,4, 2,5, 2,6
+' ""
+DefData 2, 1,1, 1,3, 3,1, 3,3
+' #
+DefData 4, 1,0, 1,6, 3,0, 3,6, 0,2, 4,2, 0,4, 4,4
+' $
+DefData 6, 0,1, 0,3, 0,3, 4,3, 4,3, 4,5, 4,5, 0,5, 0,1, 4,1, 2,0, 2,6
+' %
+DefData 3, 3,0, 1,6, 1,1, 1,2, 3,4, 3,5
+' &
+DefData 6, 0,1, 4,5, 0,1, 1,0, 1,0, 2,1, 2,1, 0,4, 0,4, 2,6, 2,6, 4,4
+' '
+DefData 1, 3,1, 2,2
+' (
+DefData 3, 3,0, 1,2, 1,2, 1,4, 1,4, 3,6
+' )
+DefData 3, 1,0, 3,2, 3,2, 3,4, 3,4, 1,6
+' *
+DefData 3, 1,1, 3,5, 1,5, 3,1, 0,3, 4,3
+' +
+DefData 2, 2,2, 2,4, 1,3, 3,3
+' ,
+DefData 1, 2,5, 2,6
+' -
+DefData 1, 1,3, 3,3
+' .
+DefData 1, 2,5, 2,5
+' /
+DefData 1, 4,0, 0,6
+' 0
+DefData 4, 0,0, 0,6, 0,6, 4,6, 4,6, 4,0, 4,0, 0,0
+' 1
+DefData 1, 2,0, 2,6
+' 2
+DefData 5, 0,0, 4,0, 4,0, 4,3, 4,3, 0,3, 0,3, 0,6, 0,6, 4,6
+' 3
+DefData 4, 0,0, 4,0, 4,0, 4,6, 4,6, 0,6, 2,3, 4,3
+' 4
+DefData 3, 0,0, 0,3, 0,3, 4,3, 4,0, 4,6
+' 5
+DefData 5, 0,0, 4,0, 0,0, 0,3, 0,3, 4,3, 4,3, 4,6, 0,6, 4,6
+' 6
+DefData 4, 0,0, 0,6, 0,3, 4,3, 4,3, 4,6, 0,6, 4,6
+' 7
+DefData 2, 0,0, 4,0, 4,0, 4,6
+' 8
+DefData 5, 0,0, 0,6, 0,6, 4,6, 4,6, 4,0, 4,0, 0,0, 0,3, 4,3
+' 9
+DefData 4, 0,0, 4,0, 4,0, 4,6, 0,0, 0,3, 0,3, 4,3
+' :
+DefData 2, 2,1, 2,1, 2,5, 2,5
+' ;
+DefData 2, 2,1, 2,1, 2,5, 2,6
+' <
+DefData 2, 4,0, 1,3, 1,3, 4,6
+' =
+DefData 2, 1,2, 3,2, 1,4, 3,4
+' >
+DefData 2, 0,0, 3,3, 3,3, 0,6
+' ?
+DefData 6, 1,1, 1,0, 1,0, 3,0, 3,0, 3,2, 3,2, 2,3, 2,3, 2,4, 2,5, 2,6
+' @
+DefData 6, 2,2, 2,4, 2,4, 4,4, 4,4, 4,0, 4,0, 0,0, 0,0, 0,6, 0,6, 4,6
+' A (65)
+DefData 5, 2,0, 0,2, 2,0, 4,2, 0,2, 0,6, 4,2, 4,6, 0,3, 4,3
+' B
+DefData 6, 0,0, 0,6, 0,6, 4,6, 4,6, 4,3, 4,3, 0,3, 0,0, 3,0, 3,0, 3,3
+' C
+DefData 3, 0,0, 0,6, 0,6, 4,6, 0,0, 4,0
+' D
+DefData 6, 0,0, 0,6, 0,6, 2,6, 2,6, 4,4, 4,4, 4,2, 4,2, 2,0, 2,0, 0,0
+' E
+DefData 4, 0,0, 0,6, 0,6, 4,6, 0,0, 4,0, 0,3, 2,3
+' F
+DefData 3, 0,0, 0,6, 0,0, 4,0, 0,3, 2,3
+' G
+DefData 5, 0,0, 0,6, 0,6, 4,6, 0,0, 4,0, 4,6, 4,3, 4,3, 2,3
+' H
+DefData 3, 0,0, 0,6, 0,3, 4,3, 4,0, 4,6
+' I
+DefData 3, 0,0, 4,0, 0,6, 4,6, 2,0, 2,6
+' J
+DefData 4, 3,0, 4,0, 4,0, 4,6, 4,6, 2,6, 2,6, 1,4
+' K
+DefData 4, 0,0, 0,6, 0,3, 2,3, 2,3, 4,0, 2,3, 4,6
+' L
+DefData 2, 0,0, 0,6, 0,6, 4,6
+' M
+DefData 4, 0,0, 0,6, 0,0, 2,3, 2,3, 4,0, 4,0, 4,6
+' N
+DefData 3, 0,0, 0,6, 0,0, 4,6, 4,6, 4,0
+' O
+DefData 4, 0,0, 0,6, 0,6, 4,6, 4,6, 4,0, 4,0, 0,0
+' P
+DefData 4, 0,0, 0,6, 0,0, 4,0, 0,3, 4,3, 4,3, 4,0
+' Q
+DefData 6, 0,0, 0,6, 0,6, 2,6, 2,6, 4,4, 4,4, 4,0, 4,0, 0,0, 4,6, 2,4
+' R
+DefData 5, 0,0, 0,6, 0,0, 4,0, 0,3, 4,3, 4,0, 4,3, 2,3, 4,6
+' S
+DefData 5, 0,0, 0,3, 0,3, 4,3, 4,3, 4,6, 4,6, 0,6, 0,0, 4,0
+' T
+DefData 2, 0,0, 4,0, 2,0, 2,6
+' U
+DefData 3, 0,0, 0,6, 0,6, 4,6, 4,6, 4,0
+' V
+DefData 4, 0,0, 0,3, 0,3, 2,6, 2,6, 4,3, 4,3, 4,0
+' W
+DefData 4, 0,0, 1,6, 1,6, 2,4, 2,4, 3,6, 3,6, 4,0
+' X
+DefData 2, 0,0, 4,6, 0,6, 4,0
+' Y
+DefData 3, 0,0, 2,3, 2,3, 4,0, 2,3, 2,6
+' Z
+DefData 3, 0,0, 4,0, 4,0, 0,6, 0,6, 4,6
+' [
+DefData 3, 3,0, 1,0, 1,0, 1,6, 1,6, 3,6
+' \
+DefData 1, 0,0, 4,6
+' ]
+DefData 3, 1,0, 3,0, 3,0, 3,6, 3,6, 1,6
+' ^
+DefData 2, 1,2, 2,0, 2,0, 3,2
+' _
+DefData 1, 0,7, 4,7
+' `
+DefData 1, 1,1, 2,2
+' a (97)
+DefData 5, 0,2, 4,2, 4,2, 4,6, 4,6, 0,6, 0,6, 0,3, 0,3, 4,3
+' b
+DefData 4, 0,0, 0,6, 0,6, 4,6, 4,6, 4,2, 4,2, 0,2
+' c
+DefData 3, 0,2, 0,6, 0,6, 4,6, 0,2, 4,2
+' d
+DefData 4, 4,0, 4,6, 4,6, 0,6, 0,6, 0,2, 0,2, 4,2
+' e
+DefData 5, 4,6, 0,6, 0,6, 0,2, 0,2, 4,2, 4,2, 4,3, 4,3, 1,3
+' f
+DefData 3, 4,0, 2,0, 2,0, 2,6, 1,2, 3,2
+' g
+DefData 5, 0,7, 4,7, 4,7, 4,2, 4,2, 0,2, 0,2, 0,6, 0,6, 4,6
+' h
+DefData 3, 0,0, 0,6, 0,2, 4,2, 4,2, 4,6
+' i
+DefData 2, 2,2, 2,6, 2,1, 2,1
+' j
+DefData 4, 3,1, 3,1, 3,2, 3,7, 3,7, 0,7, 0,7, 0,5
+' k
+DefData 3, 0,0, 0,6, 0,4, 3,2, 0,4, 3,6
+' l
+DefData 1, 2,0, 2,6
+' m
+DefData 4, 0,6, 0,2, 0,2, 4,2, 2,2, 2,4, 4,2, 4,6
+' n
+DefData 4, 0,2, 0,6, 0,3, 1,2, 1,2, 4,2, 4,2, 4,6
+' o
+DefData 4, 0,2, 0,6, 0,6, 4,6, 4,6, 4,2, 4,2, 0,2
+' p
+DefData 4, 0,2, 0,7, 0,2, 4,2, 0,6, 4,6, 4,2, 4,6
+' q
+DefData 4, 0,2, 0,6, 0,6, 4,6, 4,2, 4,7, 0,2, 4,2
+' r
+DefData 3, 0,2, 0,6, 0,3, 1,2, 1,2, 4,2
+' s
+DefData 5, 4,2, 0,2, 0,2, 0,4, 0,4, 4,4, 4,4, 4,6, 4,6, 0,6
+' t
+DefData 2, 0,2, 4,2, 2,0, 2,6
+' u
+DefData 3, 0,2, 0,6, 0,6, 4,6, 4,6, 4,2
+' v
+DefData 2, 0,2, 2,6, 2,6, 4,2
+' w
+DefData 4, 0,2, 1,6, 1,6, 2,4, 2,4, 3,6, 3,6, 4,2
+' x
+DefData 2, 0,2, 4,6, 0,6, 4,2
+' y
+DefData 2, 0,2, 2,5, 4,2, 1,7
+' z
+DefData 3, 0,2, 4,2, 4,2, 0,6, 0,6, 4,6
+' {
+DefData 4, 3,0, 2,0, 2,0, 2,6, 2,6, 3,6, 1,3, 2,3
+' |
+DefData 1, 2,0, 2,6
+' }
+DefData 4, 1,0, 2,0, 2,0, 2,6, 2,6, 1,6, 2,3, 3,3
+' ~ (126)
+DefData 5, 0,3, 0,1, 0,1, 2,1, 2,1, 2,3, 2,3, 4,3, 4,3, 4,1
+
+' <-
+DefData 3, 3,1, 0,3, 0,3, 3,5, 0,3, 4,3
+' checkmark
+DefData 2, 0,4, 2,6, 2,6, 4,0
+' ->
+DefData 3, 1,1, 4,3, 4,3, 1,5, 0,3, 4,3
+
--- /dev/null
+
+
+
+
+
+' -----------------------------------------------------------------------------
+' MAKE SURE "Threaded Build" IS CHECKED IN THE Program -> Build Options menu!
+' -----------------------------------------------------------------------------
+
+
+
+
+
+' -----------------------------------------------------------------------------
+' Loading screen...
+' -----------------------------------------------------------------------------
+
+AppTitle = "Multi-threaded loading screen demo..."
+
+' How to display an animated loading screen while loading images...
+
+' Because only the main program thread can interact with DirectX/OpenGL,
+' we have to use BlitzMax TPixmaps in the threaded loading routine.
+
+' That's because BlitzMax's TImage is tied to the DirectX/OpenGL 'context',
+' while pixmaps are just blocks of memory that the CPU can manipulate.
+
+' After loading from disk, they can be 'loaded' from the in-memory TPixmap
+' into proper images via LoadImage.
+
+' You could just use DrawPixmap to skip this step, but you then can't use'
+' realtime scaling, rotation, etc.
+
+' The threaded function LoadPixmaps is at the bottom of this code...
+
+' -----------------------------------------------------------------------------
+' This is used to simulate slower loading in the LoadPixmaps thread...
+' -----------------------------------------------------------------------------
+
+Global TestDelay = 1000 ' Simulating more/larger images, 3D models, etc...
+
+' -----------------------------------------------------------------------------
+' Set up global TMap...
+' -----------------------------------------------------------------------------
+
+Global Pixmaps:TMap = CreateMap ()
+
+' -----------------------------------------------------------------------------
+' Add list of pixmap filenames to be added to the Pixmaps TMap...
+' -----------------------------------------------------------------------------
+
+AddPixmap ("bluboing.png")
+AddPixmap ("bluegem.png")
+AddPixmap ("boing.png")
+AddPixmap ("dead.png")
+AddPixmap ("greengem.png")
+AddPixmap ("redgem.png")
+
+' -----------------------------------------------------------------------------
+' Set up display...
+' -----------------------------------------------------------------------------
+
+Graphics 640, 480
+SetClsColor 32, 96, 128
+SetMaskColor 255, 0, 255
+AutoMidHandle True
+
+' -----------------------------------------------------------------------------
+' Start the LoadPixmaps thread...
+' -----------------------------------------------------------------------------
+
+thread:TThread = CreateThread (LoadPixmaps, Null)
+
+' -----------------------------------------------------------------------------
+' This is the loading screen! Some movement and colours while pixmaps load...
+' -----------------------------------------------------------------------------
+
+r = 0; g = 255; b = 127
+
+' -----------------------------------------------------------------------------
+' Do this routine until the thread has finished its work...
+' -----------------------------------------------------------------------------
+
+While ThreadRunning (thread) ' Important!
+
+ Cls
+
+ r = r + 8; If r > 255 Then r = 0
+ g = g - 4; If g > 255 Then g = 0
+ b = b + 2; If b > 255 Then b = 0
+
+ SetColor 0, 0, 0
+ DrawRect MouseX (), MouseY (), 32, 32
+
+ SetColor r, g, b
+ DrawRect MouseX (), MouseY (), 30, 30
+
+ SetColor 0, 0, 0
+ DrawText "Slow-ding, please wait...", 20, 20
+ SetColor 255, 255, 255
+ DrawText "Slow-ding, please wait...", 18, 18
+
+ Flip
+
+Wend
+
+' -----------------------------------------------------------------------------
+' Right, the thread has finished. Should have a nice TMap filled with pixmaps!
+' -----------------------------------------------------------------------------
+
+' Just re-setting colours, 'scuse me...
+
+r = 255; g = 255; b = 255
+SetColor r, g, b
+
+' -----------------------------------------------------------------------------
+' Create a list of TImage objects and load the pixmaps into them...
+' -----------------------------------------------------------------------------
+
+images:TList = CreateList ()
+
+For p$ = EachIn MapKeys (Pixmaps)
+ ListAddLast images, LoadImage (TPixmap (MapValueForKey (Pixmaps, p$)))
+Next
+
+' In reality, you would probably load each image based on the filename in the
+' map. You could just pass each filename you passed to AddPixmap at the start,
+' for example (untested)...
+
+' rocket:TImage = LoadImage (TPixmap (MapValueForKey (Pixmaps, "boing.png")))
+
+' -----------------------------------------------------------------------------
+' Free the map and all TPixmap objects it holds...
+' -----------------------------------------------------------------------------
+
+ClearMap Pixmaps
+
+' -----------------------------------------------------------------------------
+' Yay... into the main game! Woo! Fun!
+' -----------------------------------------------------------------------------
+
+Repeat
+
+ Cls
+
+ x = 0
+ y = 0
+
+ SetRotation ang#; ang = ang + 1; If ang > 360 Then ang = 0
+
+ ' Draw all images...
+
+ For i:TImage = EachIn images
+
+ DrawImage i, x, y
+ x = x + 96
+ y = y + 96
+ Next
+
+ SetRotation 0
+
+ SetColor 0, 0, 0
+ DrawRect MouseX (), MouseY (), 32, 32
+ SetColor 255, 255, 255
+ DrawRect MouseX (), MouseY (), 30, 30
+
+ SetColor 0, 0, 0
+ DrawText "All done! We're in-game now! Fun, fun, fun...", 20, 20
+ SetColor 255, 255, 255
+ DrawText "All done! We're in-game now! Fun, fun, fun...", 18, 18
+
+ Flip
+
+Until KeyHit (KEY_ESCAPE)
+
+End
+
+' -----------------------------------------------------------------------------
+' Helper function for anyone scared of maps...
+' -----------------------------------------------------------------------------
+
+Function AddPixmap (p$)
+
+ ' Maps are similar to lists, but associated two values with each other;
+ ' in this case, a filename and a TPixmap pointer, which is Null here.
+
+ ' The LoadPixmaps function will load the pixmap for each filename in the
+ ' map, and associated the resulting TPixmap with that filename.
+
+ MapInsert (Pixmaps, p$, New TPixmap)
+
+End Function
+
+' -----------------------------------------------------------------------------
+' The threaded pixmap loading function...
+' -----------------------------------------------------------------------------
+
+' No mutexes are needed here since the global Pixmaps:TMap is only accessed by
+' the main program after this thread is finished...
+
+Function LoadPixmaps:Object (data:Object)
+
+ ' Iterate through the global Map...
+
+ For p$ = EachIn MapKeys (Pixmaps)
+
+ ' Load pixmaps into the existing [Null] TPixmap slots for each
+ ' filename...
+
+ pix:TPixmap = LoadPixmap (p$)
+ MapInsert (Pixmaps, p$, pix)
+
+ ' Fake delay to simulate loading bigger images for this demo!
+
+ Delay TestDelay
+
+ Next
+
+End Function
+
--- /dev/null
+
+
+
+
+
+' -----------------------------------------------------------------------------
+' MAKE SURE "Threaded Build" IS CHECKED IN THE Program -> Build Options menu!
+' -----------------------------------------------------------------------------
+
+
+' You may have to tell your firewall to let this program through...
+
+
+
+' -----------------------------------------------------------------------------
+' Global mutex and abort 'signal' checked in thread...
+' -----------------------------------------------------------------------------
+Global abortmutex:TMutex = CreateMutex ()
+Global abort = 0
+
+' -----------------------------------------------------------------------------
+Global Bail = 3 ' How many times to attempt download of each image...
+' -----------------------------------------------------------------------------
+
+' Number of image URLs in array below...
+
+Const PicNum = 7
+Global Pic$ [PicNum]
+
+Pic [0] = ConvURL ("http://www.blitzbasic.com/img/platypus.jpg")
+Pic [1] = ConvURL ("http://www.blitzbasic.com/img/auto_cross_racing.jpg")
+Pic [2] = ConvURL ("http://www.blitzbasic.com/img/super_gerball.jpg")
+Pic [3] = ConvURL ("http://www.blitzbasic.com/img/tank_universal.jpg")
+Pic [4] = ConvURL ("http://www.blitzbasic.com/img/tecno.jpg")
+Pic [5] = ConvURL ("http://www.blitzbasic.com/img/master_of_defence.jpg")
+Pic [6] = ConvURL ("http://www.blitzbasic.com/img/kingdom_elemental_tactics.jpg")
+
+' -----------------------------------------------------------------------------
+' D E M O . . .
+' -----------------------------------------------------------------------------
+
+AppTitle = "Threaded image downloader..."
+
+Graphics 640, 480
+SetClsColor 64, 96, 128
+AutoMidHandle True
+
+' -----------------------------------------------------------------------------
+' Player type stores downloaded images and their co-ords...
+' -----------------------------------------------------------------------------
+
+Type Player
+
+ Field x:Float = Rnd (GraphicsWidth ())
+ Field y:Float = Rnd (GraphicsHeight ())
+
+ Field xs:Float = Rnd (10)
+ Field ys:Float = Rnd (10)
+
+ Field ang:Float = Rnd (360)
+ Field angspeed:Float = Rnd (-5, 5)
+
+ Field scale:Float = Rnd (1.0, 3.0)
+
+ Field image:TImage
+
+End Type
+
+' -----------------------------------------------------------------------------
+' List of Player objects...
+' -----------------------------------------------------------------------------
+
+PlayerList:TList = CreateList ()
+
+' -----------------------------------------------------------------------------
+' Spawn first thread...
+' -----------------------------------------------------------------------------
+
+index = 0 ' Picture index in Pic [] array...
+
+Print ""; Print "Downloading " + Pic [index]
+
+pixthread:TThread = CreateThread (DownloadPixmap, Pic [index])
+
+alldone = 0
+
+Repeat
+
+ ' This variable is set when all images have been downloaded:
+
+ If Not alldone
+
+ If Not ThreadRunning (pixthread)
+
+ ' WaitThread contains the result of the last thread (now finished)...
+
+ pix:TPixmap = TPixmap (WaitThread (pixthread))
+
+ ' If a valid pixmap was returned, create a new 'Player' object
+ ' and load the pixmap as an image...
+
+ If pix
+ p:Player = New Player
+ p.image = LoadImage (pix)
+ ListAddLast PlayerList, p
+ EndIf
+
+ ' Check we still have more images to load...
+
+ If index < PicNum - 1
+
+ ' OK... next!
+
+ index = index + 1
+
+ Print ""; Print "Downloading " + Pic [index]
+
+ ' Last image thread is done/processed, so create a new one!
+
+ pixthread = CreateThread (DownloadPixmap, Pic [index])
+
+ Else
+ Print ""; Print "All images loaded!"
+ alldone = 1 ' All images in array loaded!
+ EndIf
+
+ EndIf
+
+ EndIf
+
+ Cls
+
+ ' Draw all loaded images...
+
+ For p:Player = EachIn PlayerList
+
+ p.x = p.x + p.xs
+ If p.x < 0 Or p.x > GraphicsWidth () - 1
+ p.xs = -p.xs
+ EndIf
+
+ p.y = p.y + p.ys
+ If p.y < 0 Or p.y > GraphicsHeight () - 1
+ p.ys = -p.ys
+ EndIf
+
+ SetScale p.scale, p.scale
+
+ p.ang = p.ang + p.angspeed
+ SetRotation p.ang
+
+ DrawImage p.image, p.x, p.y
+
+ SetScale 1, 1
+ SetRotation 0
+
+ If alldone
+ txt$ = "All images loaded!"
+ Else
+ txt$ = "Loading images in background..."
+ EndIf
+
+ SetColor 0, 0, 0
+ DrawText txt$, 20, 20
+ SetColor 255, 255, 255
+ DrawText txt$, 18, 18
+
+ Next
+
+ Flip
+
+Until KeyHit (KEY_ESCAPE)
+
+' Lock the abort mutex so we can safely modify the global abort variable, which
+' is then checked by the thread to allow a safe exit...
+
+LockMutex abortmutex
+ abort = True
+UnlockMutex abortmutex
+
+' The mutex is unlocked by the DownloadPixmap thread before returning,
+' but it may already have exited. I think, hence the UnlockMutex above,
+' just in case...
+
+' Now wait to make sure the thread has finished...
+
+WaitThread pixthread
+
+' Done!
+
+End
+
+' -----------------------------------------------------------------------------
+' Threaded pixmap downloader...
+' -----------------------------------------------------------------------------
+
+Function DownloadPixmap:TPixmap (data:Object)
+
+ img$ = String (data)
+
+ Local pix:TPixmap ' Downloaded pixmap...
+
+ Local url:TStream ' Download stream...
+ Local copy:TStream ' Local copy of download...
+
+ Local count:Int ' Byte count during download...
+
+ Local retry:Int ' If download fails, retry 'Bail' times...
+ Local quit:Int ' Too many fails, exit...
+
+ Repeat
+
+ Print ""
+ Print "Attempting new download..."
+ Print ""
+
+ url = ReadStream (img$)
+
+ If url
+
+ ' Create local copy...
+
+ copy = WriteStream ("local.jpg")
+
+ If copy
+
+ ' Reset byte count...
+
+ count = 0
+
+ Repeat
+
+ ' Try to lock mutex, to check global abort variable, which
+ ' is set to True before exiting program...
+
+ LockMutex abortmutex
+
+ If abort
+
+ ' Aaaaaaahhhh!!! We're going down!!!
+
+ UnlockMutex abortmutex
+
+ ' Close all streams! Batten down the hatches!
+
+ CloseStream url
+ CloseStream copy
+
+ ' Abandon ship!
+
+ Return Null
+
+ Else
+
+ ' Not aborting, so unlock the mutex...
+
+ UnlockMutex abortmutex
+
+ ' Not every efficient (one byte at a time), but works as a demo...
+
+ WriteByte copy, ReadByte (url)
+
+ ' Count bytes downloaded...
+
+ count = count + 1
+ If count Mod 1024 = 0 Then Print count + " bytes downloaded in background"
+
+ EndIf
+
+ Until Eof (url)
+
+ CloseStream copy
+
+ ' Try to load pixmap...
+
+ pix = LoadPixmap ("local.jpg")
+
+ EndIf
+
+ CloseStream url
+
+ EndIf
+
+ ' Download failed? Retry 'Bail' times...
+
+ If pix = Null
+
+ retry = retry + 1
+
+ If retry = Bail
+
+ quit = True
+ Print "Failed to download after " + Bail + " attempts..."
+
+ Else
+
+ Print "Retrying..."
+
+ EndIf
+
+ EndIf
+
+ Until pix Or quit
+
+ If pix = Null
+ DebugLog "Oops... problem loading " + img$
+ EndIf
+
+ Return pix ' May still be Null after 'Bail' failed attempts...
+
+End Function
+
+' Convert real URL to BlitzMax stream-friendly URL...
+
+Function ConvURL$ (url$)
+ Return Replace (url$, "://", "::")
+End Function
--- /dev/null
+
+
+
+
+' -----------------------------------------------------------------------------
+' MAKE SURE "Threaded Build" IS CHECKED IN THE Program -> Build Options menu!
+' -----------------------------------------------------------------------------
+
+
+
+
+
+' -----------------------------------------------------------------------------
+' Simple thread demo...
+' -----------------------------------------------------------------------------
+
+' Mutexes are used to allow only one thread to access a variable or object in
+' memory.
+
+' A thread (including the main program's thread) can call LockMutex
+' on a mutex, and if another thread has already locked the mutex, it will
+' wait until the other thread has unlocked the mutex, then gain the lock. It's
+' important that a thread calls UnlockMutex when it's done what it needs to do!
+
+' The main program's thread and the spawned thread will both attempt to lock
+' this mutex throughout, waiting on each other if they can't lock it...
+
+Global Mutex = CreateMutex ()
+
+' Try commenting out the above line, then scroll back through the output of
+' the program, and you should see that the threads are fighting for access to
+' the output console, creating intermeshing, gibberish text...
+
+Print ""
+Print "NOTE: Output of the two threads may not always alternate between 'Main' and 'Thread'..."
+Print ""
+
+' Create thread using Test () function and pass Null parameter...
+
+thread:TThread = CreateThread (Test, Null)
+
+' The new thread has now started. Do some stuff in the main program...
+
+For a = 1 To 100
+
+ ' Other thread may be using the Print command (which isn't thread-friendly),
+ ' so LockMutex will wait until the thread has unlocked the mutex, and then
+ ' lock it so main program can call Print. It will then unlock the mutex so
+ ' the other thread can continue if it's ready (ie. waiting at its own
+ ' LockThread call)...
+
+ If Mutex Then LockMutex (Mutex)
+ Print "Main: " + a
+ If Mutex Then UnlockMutex (Mutex)
+
+ ' Note: You'd normally just do this like the Rem'd out code below! The
+ ' "If Mutex" check here is to allow you to comment out the line
+ ' "Global Mutex = CreateMutex ()" to see why mutexes are needed...
+
+ ' You would also not normally use LockMutex so heavily, as it will
+ ' slow things down if over-used...
+
+ Rem
+
+ LockMutex (Mutex)
+ Print "Main: " + a
+ UnlockMutex (Mutex)
+
+ End Rem
+
+Next
+
+' Other thread may still be running, so wait for it to end...
+
+WaitThread (thread)
+
+End
+
+' -----------------------------------------------------------------------------
+' Test function. Locks same mutex as main program, or waits until it
+' can do so, calls Print, then unlocks the mutex so main program can
+' lock it and proceed...
+' -----------------------------------------------------------------------------
+
+Function Test:Object (data:Object)
+
+ For a = 1 To 100
+
+ If Mutex Then LockMutex (Mutex)
+ Print "--------> Thread: " + a
+ If Mutex Then UnlockMutex (Mutex)
+
+ Next
+
+End Function
+