From: blitz-research Date: Wed, 22 Jan 2014 22:33:28 +0000 (+1300) Subject: Added samples. X-Git-Tag: v150~25 X-Git-Url: https://jcornell.net/gitweb/gitweb.cgi?a=commitdiff_plain;h=f3f749af43f449809bc44ad09ed665b1890bea4a;p=blitzmax.git Added samples. --- diff --git a/samples/aaronkoolen/AStar/Callback.bmx b/samples/aaronkoolen/AStar/Callback.bmx new file mode 100644 index 0000000..a00aab6 --- /dev/null +++ b/samples/aaronkoolen/AStar/Callback.bmx @@ -0,0 +1,6 @@ +' Simple object to handle callbacks +' Is there a way in BlitzMax to pass pointers to functions?? + +Type Callback + Method callback() Abstract +End Type diff --git a/samples/aaronkoolen/AStar/astar_demo.bmx b/samples/aaronkoolen/AStar/astar_demo.bmx new file mode 100644 index 0000000..d83034e --- /dev/null +++ b/samples/aaronkoolen/AStar/astar_demo.bmx @@ -0,0 +1,805 @@ +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 + + diff --git a/samples/aaronkoolen/AStar/astar_graph_walker.bmx b/samples/aaronkoolen/AStar/astar_graph_walker.bmx new file mode 100644 index 0000000..03e1319 --- /dev/null +++ b/samples/aaronkoolen/AStar/astar_graph_walker.bmx @@ -0,0 +1,259 @@ +' 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 diff --git a/samples/aaronkoolen/AStar/astar_node.bmx b/samples/aaronkoolen/AStar/astar_node.bmx new file mode 100644 index 0000000..234a4df --- /dev/null +++ b/samples/aaronkoolen/AStar/astar_node.bmx @@ -0,0 +1,117 @@ +' 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 diff --git a/samples/aaronkoolen/AStar/map0 b/samples/aaronkoolen/AStar/map0 new file mode 100644 index 0000000..542ffa9 Binary files /dev/null and b/samples/aaronkoolen/AStar/map0 differ diff --git a/samples/aaronkoolen/AStar/map1 b/samples/aaronkoolen/AStar/map1 new file mode 100644 index 0000000..285a679 Binary files /dev/null and b/samples/aaronkoolen/AStar/map1 differ diff --git a/samples/aaronkoolen/AStar/map2 b/samples/aaronkoolen/AStar/map2 new file mode 100644 index 0000000..baa5e45 Binary files /dev/null and b/samples/aaronkoolen/AStar/map2 differ diff --git a/samples/aaronkoolen/AStar/map3 b/samples/aaronkoolen/AStar/map3 new file mode 100644 index 0000000..3d1ea51 Binary files /dev/null and b/samples/aaronkoolen/AStar/map3 differ diff --git a/samples/aaronkoolen/AStar/map4 b/samples/aaronkoolen/AStar/map4 new file mode 100644 index 0000000..30063c4 Binary files /dev/null and b/samples/aaronkoolen/AStar/map4 differ diff --git a/samples/aaronkoolen/AStar/map5 b/samples/aaronkoolen/AStar/map5 new file mode 100644 index 0000000..db1e218 Binary files /dev/null and b/samples/aaronkoolen/AStar/map5 differ diff --git a/samples/aaronkoolen/AStar/map6 b/samples/aaronkoolen/AStar/map6 new file mode 100644 index 0000000..0a06346 Binary files /dev/null and b/samples/aaronkoolen/AStar/map6 differ diff --git a/samples/aaronkoolen/AStar/map7 b/samples/aaronkoolen/AStar/map7 new file mode 100644 index 0000000..d3b6fee Binary files /dev/null and b/samples/aaronkoolen/AStar/map7 differ diff --git a/samples/aaronkoolen/AStar/mountain.png b/samples/aaronkoolen/AStar/mountain.png new file mode 100644 index 0000000..5ca2107 Binary files /dev/null and b/samples/aaronkoolen/AStar/mountain.png differ diff --git a/samples/aaronkoolen/AStar/nums.png b/samples/aaronkoolen/AStar/nums.png new file mode 100644 index 0000000..5813a74 Binary files /dev/null and b/samples/aaronkoolen/AStar/nums.png differ diff --git a/samples/aaronkoolen/AStar/priority_queue.bmx b/samples/aaronkoolen/AStar/priority_queue.bmx new file mode 100644 index 0000000..845ea69 --- /dev/null +++ b/samples/aaronkoolen/AStar/priority_queue.bmx @@ -0,0 +1,161 @@ +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 + diff --git a/samples/aaronkoolen/AStar/road.png b/samples/aaronkoolen/AStar/road.png new file mode 100644 index 0000000..aa2b97c Binary files /dev/null and b/samples/aaronkoolen/AStar/road.png differ diff --git a/samples/aaronkoolen/AStar/tree.png b/samples/aaronkoolen/AStar/tree.png new file mode 100644 index 0000000..bacff75 Binary files /dev/null and b/samples/aaronkoolen/AStar/tree.png differ diff --git a/samples/aaronkoolen/AStar/water.png b/samples/aaronkoolen/AStar/water.png new file mode 100644 index 0000000..537a8ef Binary files /dev/null and b/samples/aaronkoolen/AStar/water.png differ diff --git a/samples/birdie/games/tempest/tempest.bmx b/samples/birdie/games/tempest/tempest.bmx new file mode 100644 index 0000000..c89456f --- /dev/null +++ b/samples/birdie/games/tempest/tempest.bmx @@ -0,0 +1,443 @@ +'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=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 diff --git a/samples/birdie/games/zombieblast/game.bmx b/samples/birdie/games/zombieblast/game.bmx new file mode 100644 index 0000000..fa77c8f --- /dev/null +++ b/samples/birdie/games/zombieblast/game.bmx @@ -0,0 +1,771 @@ +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_cntfrm_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 + + diff --git a/samples/birdie/games/zombieblast/media/HitSpace.png b/samples/birdie/games/zombieblast/media/HitSpace.png new file mode 100644 index 0000000..9040371 Binary files /dev/null and b/samples/birdie/games/zombieblast/media/HitSpace.png differ diff --git a/samples/birdie/games/zombieblast/media/Title.png b/samples/birdie/games/zombieblast/media/Title.png new file mode 100644 index 0000000..9d9e92a Binary files /dev/null and b/samples/birdie/games/zombieblast/media/Title.png differ diff --git a/samples/birdie/games/zombieblast/media/cloud.png b/samples/birdie/games/zombieblast/media/cloud.png new file mode 100644 index 0000000..b720fb6 Binary files /dev/null and b/samples/birdie/games/zombieblast/media/cloud.png differ diff --git a/samples/birdie/games/zombieblast/media/mud.png b/samples/birdie/games/zombieblast/media/mud.png new file mode 100644 index 0000000..7f99f91 Binary files /dev/null and b/samples/birdie/games/zombieblast/media/mud.png differ diff --git a/samples/birdie/games/zombieblast/media/sand.png b/samples/birdie/games/zombieblast/media/sand.png new file mode 100644 index 0000000..8b293cf Binary files /dev/null and b/samples/birdie/games/zombieblast/media/sand.png differ diff --git a/samples/birdie/games/zombieblast/media/scan.png b/samples/birdie/games/zombieblast/media/scan.png new file mode 100644 index 0000000..ccbc483 Binary files /dev/null and b/samples/birdie/games/zombieblast/media/scan.png differ diff --git a/samples/birdie/games/zombieblast/media/ship1.png b/samples/birdie/games/zombieblast/media/ship1.png new file mode 100644 index 0000000..088788a Binary files /dev/null and b/samples/birdie/games/zombieblast/media/ship1.png differ diff --git a/samples/birdie/games/zombieblast/media/shot.png b/samples/birdie/games/zombieblast/media/shot.png new file mode 100644 index 0000000..099ee8d Binary files /dev/null and b/samples/birdie/games/zombieblast/media/shot.png differ diff --git a/samples/birdie/games/zombieblast/media/shot2.PNG b/samples/birdie/games/zombieblast/media/shot2.PNG new file mode 100644 index 0000000..e795a3b Binary files /dev/null and b/samples/birdie/games/zombieblast/media/shot2.PNG differ diff --git a/samples/birdie/games/zombieblast/media/smoke.png b/samples/birdie/games/zombieblast/media/smoke.png new file mode 100644 index 0000000..feee4ed Binary files /dev/null and b/samples/birdie/games/zombieblast/media/smoke.png differ diff --git a/samples/birdie/games/zombieblast/media/zombie_0.png b/samples/birdie/games/zombieblast/media/zombie_0.png new file mode 100644 index 0000000..a12a458 Binary files /dev/null and b/samples/birdie/games/zombieblast/media/zombie_0.png differ diff --git a/samples/birdie/games/zombieblast/media/zombie_1.png b/samples/birdie/games/zombieblast/media/zombie_1.png new file mode 100644 index 0000000..1dcfee6 Binary files /dev/null and b/samples/birdie/games/zombieblast/media/zombie_1.png differ diff --git a/samples/birdie/games/zombieblast/media/zombie_2.PNG b/samples/birdie/games/zombieblast/media/zombie_2.PNG new file mode 100644 index 0000000..3a9c2e1 Binary files /dev/null and b/samples/birdie/games/zombieblast/media/zombie_2.PNG differ diff --git a/samples/birdie/games/zombieblast/media/zombie_3.PNG b/samples/birdie/games/zombieblast/media/zombie_3.PNG new file mode 100644 index 0000000..ff158a0 Binary files /dev/null and b/samples/birdie/games/zombieblast/media/zombie_3.PNG differ diff --git a/samples/birdie/games/zombieblast/media/zombie_4.PNG b/samples/birdie/games/zombieblast/media/zombie_4.PNG new file mode 100644 index 0000000..63458de Binary files /dev/null and b/samples/birdie/games/zombieblast/media/zombie_4.PNG differ diff --git a/samples/birdie/games/zombieblast/media/zombie_5.PNG b/samples/birdie/games/zombieblast/media/zombie_5.PNG new file mode 100644 index 0000000..e16fa17 Binary files /dev/null and b/samples/birdie/games/zombieblast/media/zombie_5.PNG differ diff --git a/samples/birdie/games/zombieblast/media/zombie_6.png b/samples/birdie/games/zombieblast/media/zombie_6.png new file mode 100644 index 0000000..7877b24 Binary files /dev/null and b/samples/birdie/games/zombieblast/media/zombie_6.png differ diff --git a/samples/birdie/games/zombieblast/media/zombie_7.PNG b/samples/birdie/games/zombieblast/media/zombie_7.PNG new file mode 100644 index 0000000..944dddc Binary files /dev/null and b/samples/birdie/games/zombieblast/media/zombie_7.PNG differ diff --git a/samples/birdie/misc/filmclip/main.bmx b/samples/birdie/misc/filmclip/main.bmx new file mode 100644 index 0000000..6dc2e1d --- /dev/null +++ b/samples/birdie/misc/filmclip/main.bmx @@ -0,0 +1,56 @@ + + +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 yb 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 x1 + 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.xy-4 And b.yHEIGHT + 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 diff --git a/samples/breakout/media/B-Max.png b/samples/breakout/media/B-Max.png new file mode 100644 index 0000000..701db3d Binary files /dev/null and b/samples/breakout/media/B-Max.png differ diff --git a/samples/breakout/media/back1.png b/samples/breakout/media/back1.png new file mode 100644 index 0000000..19084ba Binary files /dev/null and b/samples/breakout/media/back1.png differ diff --git a/samples/breakout/media/back2.png b/samples/breakout/media/back2.png new file mode 100644 index 0000000..ef38308 Binary files /dev/null and b/samples/breakout/media/back2.png differ diff --git a/samples/breakout/media/ball.png b/samples/breakout/media/ball.png new file mode 100644 index 0000000..0862646 Binary files /dev/null and b/samples/breakout/media/ball.png differ diff --git a/samples/breakout/media/paddle.png b/samples/breakout/media/paddle.png new file mode 100644 index 0000000..9ffde5d Binary files /dev/null and b/samples/breakout/media/paddle.png differ diff --git a/samples/breakout/media/pipes.png b/samples/breakout/media/pipes.png new file mode 100644 index 0000000..dafe7f7 Binary files /dev/null and b/samples/breakout/media/pipes.png differ diff --git a/samples/breakout/media/tiles.png b/samples/breakout/media/tiles.png new file mode 100644 index 0000000..6db868b Binary files /dev/null and b/samples/breakout/media/tiles.png differ diff --git a/samples/digesteroids/Digesteroids.ppf b/samples/digesteroids/Digesteroids.ppf new file mode 100644 index 0000000..539784b --- /dev/null +++ b/samples/digesteroids/Digesteroids.ppf @@ -0,0 +1,90 @@ + + + + + + + + + + + + True,True,True + False + True + Blitz Basic + True,True,True + True + False + VersionShort + True,True,True + True,True,True + VersionLong + True,True,True + 0 + True,True,True + True + + + + D:\BlitzMax\robhutchinson\digesteroids + Release + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/digesteroids/MathUtil.bmx b/samples/digesteroids/MathUtil.bmx new file mode 100644 index 0000000..aa5a075 --- /dev/null +++ b/samples/digesteroids/MathUtil.bmx @@ -0,0 +1,60 @@ +' ******************************************************************* +' 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 diff --git a/samples/digesteroids/digesteroids.bmx b/samples/digesteroids/digesteroids.bmx new file mode 100644 index 0000000..e73c278 --- /dev/null +++ b/samples/digesteroids/digesteroids.bmx @@ -0,0 +1,1496 @@ +' ******************************************************************* +' 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 + + + + + + diff --git a/samples/digesteroids/dynamicgame.bmx b/samples/digesteroids/dynamicgame.bmx new file mode 100644 index 0000000..652826c --- /dev/null +++ b/samples/digesteroids/dynamicgame.bmx @@ -0,0 +1,203 @@ +' ******************************************************************* +' 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! :) + 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 + + + + + + + + diff --git a/samples/digesteroids/graphics/bullet1.png b/samples/digesteroids/graphics/bullet1.png new file mode 100644 index 0000000..46e2e2a Binary files /dev/null and b/samples/digesteroids/graphics/bullet1.png differ diff --git a/samples/digesteroids/graphics/bullet2.png b/samples/digesteroids/graphics/bullet2.png new file mode 100644 index 0000000..e09794e Binary files /dev/null and b/samples/digesteroids/graphics/bullet2.png differ diff --git a/samples/digesteroids/graphics/digestive.png b/samples/digesteroids/graphics/digestive.png new file mode 100644 index 0000000..eb5ee48 Binary files /dev/null and b/samples/digesteroids/graphics/digestive.png differ diff --git a/samples/digesteroids/graphics/lower.png b/samples/digesteroids/graphics/lower.png new file mode 100644 index 0000000..602e96f Binary files /dev/null and b/samples/digesteroids/graphics/lower.png differ diff --git a/samples/digesteroids/graphics/options.png b/samples/digesteroids/graphics/options.png new file mode 100644 index 0000000..436c76c Binary files /dev/null and b/samples/digesteroids/graphics/options.png differ diff --git a/samples/digesteroids/graphics/piece1.png b/samples/digesteroids/graphics/piece1.png new file mode 100644 index 0000000..f790943 Binary files /dev/null and b/samples/digesteroids/graphics/piece1.png differ diff --git a/samples/digesteroids/graphics/piece2.png b/samples/digesteroids/graphics/piece2.png new file mode 100644 index 0000000..51e72e0 Binary files /dev/null and b/samples/digesteroids/graphics/piece2.png differ diff --git a/samples/digesteroids/graphics/piece3.png b/samples/digesteroids/graphics/piece3.png new file mode 100644 index 0000000..dae3207 Binary files /dev/null and b/samples/digesteroids/graphics/piece3.png differ diff --git a/samples/digesteroids/graphics/pop.png b/samples/digesteroids/graphics/pop.png new file mode 100644 index 0000000..0d768a7 Binary files /dev/null and b/samples/digesteroids/graphics/pop.png differ diff --git a/samples/digesteroids/graphics/ship.png b/samples/digesteroids/graphics/ship.png new file mode 100644 index 0000000..53cc063 Binary files /dev/null and b/samples/digesteroids/graphics/ship.png differ diff --git a/samples/digesteroids/graphics/sparkle.png b/samples/digesteroids/graphics/sparkle.png new file mode 100644 index 0000000..7dd3851 Binary files /dev/null and b/samples/digesteroids/graphics/sparkle.png differ diff --git a/samples/digesteroids/graphics/stars.png b/samples/digesteroids/graphics/stars.png new file mode 100644 index 0000000..c4ededa Binary files /dev/null and b/samples/digesteroids/graphics/stars.png differ diff --git a/samples/digesteroids/graphics/title.png b/samples/digesteroids/graphics/title.png new file mode 100644 index 0000000..ec26ff9 Binary files /dev/null and b/samples/digesteroids/graphics/title.png differ diff --git a/samples/digesteroids/minitimer.bmx b/samples/digesteroids/minitimer.bmx new file mode 100644 index 0000000..e2f6746 --- /dev/null +++ b/samples/digesteroids/minitimer.bmx @@ -0,0 +1,72 @@ +' ******************************************************************* +' 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 + '''----------------------------------------------------------------------------- + ''' + ''' Resets the timer. + ''' + '''----------------------------------------------------------------------------- + Method Reset() + Self.TimeStarted = MilliSecs() + End Method +'#End Region +'#Region Method: MiillisecondsElapsed + '''----------------------------------------------------------------------------- + ''' + ''' Gets the number of milliseconds that have passed since a call to Reset. + ''' + '''----------------------------------------------------------------------------- + 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 + '''----------------------------------------------------------------------------- + ''' + ''' Returns true if the given interval has been reached. + ''' + '''----------------------------------------------------------------------------- + Method IntervalReached:Int() + If Self.Enabled Then Return (Self.MiillisecondsElapsed() > Self.Interval) + End Method +'#End Region + +End Type + + + + + + + diff --git a/samples/digesteroids/simplephysics.bmx b/samples/digesteroids/simplephysics.bmx new file mode 100644 index 0000000..65c0196 --- /dev/null +++ b/samples/digesteroids/simplephysics.bmx @@ -0,0 +1,593 @@ +' 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 + + + + + diff --git a/samples/digesteroids/sounds/crash.wav b/samples/digesteroids/sounds/crash.wav new file mode 100644 index 0000000..cc8fe20 Binary files /dev/null and b/samples/digesteroids/sounds/crash.wav differ diff --git a/samples/digesteroids/sounds/ending.ogg b/samples/digesteroids/sounds/ending.ogg new file mode 100644 index 0000000..7120d63 Binary files /dev/null and b/samples/digesteroids/sounds/ending.ogg differ diff --git a/samples/digesteroids/sounds/fire.wav b/samples/digesteroids/sounds/fire.wav new file mode 100644 index 0000000..9e08490 Binary files /dev/null and b/samples/digesteroids/sounds/fire.wav differ diff --git a/samples/digesteroids/sounds/impactlarge.wav b/samples/digesteroids/sounds/impactlarge.wav new file mode 100644 index 0000000..33af1a7 Binary files /dev/null and b/samples/digesteroids/sounds/impactlarge.wav differ diff --git a/samples/digesteroids/sounds/menu.ogg b/samples/digesteroids/sounds/menu.ogg new file mode 100644 index 0000000..95bb202 Binary files /dev/null and b/samples/digesteroids/sounds/menu.ogg differ diff --git a/samples/digesteroids/sounds/teleport.wav b/samples/digesteroids/sounds/teleport.wav new file mode 100644 index 0000000..fe8109d Binary files /dev/null and b/samples/digesteroids/sounds/teleport.wav differ diff --git a/samples/digesteroids/sounds/thrust.wav b/samples/digesteroids/sounds/thrust.wav new file mode 100644 index 0000000..214f8fc Binary files /dev/null and b/samples/digesteroids/sounds/thrust.wav differ diff --git a/samples/firepaint/bullet.png b/samples/firepaint/bullet.png new file mode 100644 index 0000000..05d28ca Binary files /dev/null and b/samples/firepaint/bullet.png differ diff --git a/samples/firepaint/color.bmx b/samples/firepaint/color.bmx new file mode 100644 index 0000000..542651b --- /dev/null +++ b/samples/firepaint/color.bmx @@ -0,0 +1,276 @@ + +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 _grnhmax 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 diff --git a/samples/firepaint/firepaint.bmx b/samples/firepaint/firepaint.bmx new file mode 100644 index 0000000..b2f0d3e --- /dev/null +++ b/samples/firepaint/firepaint.bmx @@ -0,0 +1,191 @@ +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 diff --git a/samples/firepaint/player.png b/samples/firepaint/player.png new file mode 100644 index 0000000..cf8f76b Binary files /dev/null and b/samples/firepaint/player.png differ diff --git a/samples/firepaint/shoot.wav b/samples/firepaint/shoot.wav new file mode 100644 index 0000000..0bea437 Binary files /dev/null and b/samples/firepaint/shoot.wav differ diff --git a/samples/firepaint/stars.png b/samples/firepaint/stars.png new file mode 100644 index 0000000..16e7539 Binary files /dev/null and b/samples/firepaint/stars.png differ diff --git a/samples/flameduck/circlemania/anim0.png b/samples/flameduck/circlemania/anim0.png new file mode 100644 index 0000000..0858c19 Binary files /dev/null and b/samples/flameduck/circlemania/anim0.png differ diff --git a/samples/flameduck/circlemania/anim1.png b/samples/flameduck/circlemania/anim1.png new file mode 100644 index 0000000..0858c19 Binary files /dev/null and b/samples/flameduck/circlemania/anim1.png differ diff --git a/samples/flameduck/circlemania/anim10.png b/samples/flameduck/circlemania/anim10.png new file mode 100644 index 0000000..2551453 Binary files /dev/null and b/samples/flameduck/circlemania/anim10.png differ diff --git a/samples/flameduck/circlemania/anim11.png b/samples/flameduck/circlemania/anim11.png new file mode 100644 index 0000000..c8e9c59 Binary files /dev/null and b/samples/flameduck/circlemania/anim11.png differ diff --git a/samples/flameduck/circlemania/anim12.png b/samples/flameduck/circlemania/anim12.png new file mode 100644 index 0000000..9e286d3 Binary files /dev/null and b/samples/flameduck/circlemania/anim12.png differ diff --git a/samples/flameduck/circlemania/anim13.png b/samples/flameduck/circlemania/anim13.png new file mode 100644 index 0000000..e80fffc Binary files /dev/null and b/samples/flameduck/circlemania/anim13.png differ diff --git a/samples/flameduck/circlemania/anim14.png b/samples/flameduck/circlemania/anim14.png new file mode 100644 index 0000000..833ba05 Binary files /dev/null and b/samples/flameduck/circlemania/anim14.png differ diff --git a/samples/flameduck/circlemania/anim15.png b/samples/flameduck/circlemania/anim15.png new file mode 100644 index 0000000..577f791 Binary files /dev/null and b/samples/flameduck/circlemania/anim15.png differ diff --git a/samples/flameduck/circlemania/anim2.png b/samples/flameduck/circlemania/anim2.png new file mode 100644 index 0000000..0895006 Binary files /dev/null and b/samples/flameduck/circlemania/anim2.png differ diff --git a/samples/flameduck/circlemania/anim3.png b/samples/flameduck/circlemania/anim3.png new file mode 100644 index 0000000..62637e7 Binary files /dev/null and b/samples/flameduck/circlemania/anim3.png differ diff --git a/samples/flameduck/circlemania/anim4.png b/samples/flameduck/circlemania/anim4.png new file mode 100644 index 0000000..a7e4af6 Binary files /dev/null and b/samples/flameduck/circlemania/anim4.png differ diff --git a/samples/flameduck/circlemania/anim5.png b/samples/flameduck/circlemania/anim5.png new file mode 100644 index 0000000..244a7a9 Binary files /dev/null and b/samples/flameduck/circlemania/anim5.png differ diff --git a/samples/flameduck/circlemania/anim6.png b/samples/flameduck/circlemania/anim6.png new file mode 100644 index 0000000..96657cf Binary files /dev/null and b/samples/flameduck/circlemania/anim6.png differ diff --git a/samples/flameduck/circlemania/anim7.png b/samples/flameduck/circlemania/anim7.png new file mode 100644 index 0000000..e333488 Binary files /dev/null and b/samples/flameduck/circlemania/anim7.png differ diff --git a/samples/flameduck/circlemania/anim8.png b/samples/flameduck/circlemania/anim8.png new file mode 100644 index 0000000..7be7a9d Binary files /dev/null and b/samples/flameduck/circlemania/anim8.png differ diff --git a/samples/flameduck/circlemania/anim9.png b/samples/flameduck/circlemania/anim9.png new file mode 100644 index 0000000..7b12a07 Binary files /dev/null and b/samples/flameduck/circlemania/anim9.png differ diff --git a/samples/flameduck/circlemania/circlebob.png b/samples/flameduck/circlemania/circlebob.png new file mode 100644 index 0000000..2be24b0 Binary files /dev/null and b/samples/flameduck/circlemania/circlebob.png differ diff --git a/samples/flameduck/circlemania/cmania.bmx b/samples/flameduck/circlemania/cmania.bmx new file mode 100644 index 0000000..397a0b6 --- /dev/null +++ b/samples/flameduck/circlemania/cmania.bmx @@ -0,0 +1,235 @@ +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 + diff --git a/samples/flameduck/circlemania/cmanialogo.png b/samples/flameduck/circlemania/cmanialogo.png new file mode 100644 index 0000000..31ba9c0 Binary files /dev/null and b/samples/flameduck/circlemania/cmanialogo.png differ diff --git a/samples/flameduck/circlemania/rasta.png b/samples/flameduck/circlemania/rasta.png new file mode 100644 index 0000000..beaab97 Binary files /dev/null and b/samples/flameduck/circlemania/rasta.png differ diff --git a/samples/flameduck/oldskool2/binarytherapy.png b/samples/flameduck/oldskool2/binarytherapy.png new file mode 100644 index 0000000..e96cb89 Binary files /dev/null and b/samples/flameduck/oldskool2/binarytherapy.png differ diff --git a/samples/flameduck/oldskool2/bouncy.ogg b/samples/flameduck/oldskool2/bouncy.ogg new file mode 100644 index 0000000..c507b47 Binary files /dev/null and b/samples/flameduck/oldskool2/bouncy.ogg differ diff --git a/samples/flameduck/oldskool2/circlefont.png b/samples/flameduck/oldskool2/circlefont.png new file mode 100644 index 0000000..6ef6ea0 Binary files /dev/null and b/samples/flameduck/oldskool2/circlefont.png differ diff --git a/samples/flameduck/oldskool2/oldskool.png b/samples/flameduck/oldskool2/oldskool.png new file mode 100644 index 0000000..6200f9f Binary files /dev/null and b/samples/flameduck/oldskool2/oldskool.png differ diff --git a/samples/flameduck/oldskool2/oldskool2.bmx b/samples/flameduck/oldskool2/oldskool2.bmx new file mode 100644 index 0000000..9180632 --- /dev/null +++ b/samples/flameduck/oldskool2/oldskool2.bmx @@ -0,0 +1,241 @@ +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 diff --git a/samples/flameduck/oldskool2/readme.txt b/samples/flameduck/oldskool2/readme.txt new file mode 100644 index 0000000..edd62ba --- /dev/null +++ b/samples/flameduck/oldskool2/readme.txt @@ -0,0 +1,32 @@ +**** 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 diff --git a/samples/hitoro/arraysort.bmx b/samples/hitoro/arraysort.bmx new file mode 100644 index 0000000..35e65e5 --- /dev/null +++ b/samples/hitoro/arraysort.bmx @@ -0,0 +1,44 @@ + +' 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 + diff --git a/samples/hitoro/bench.bmx b/samples/hitoro/bench.bmx new file mode 100644 index 0000000..ff684d0 --- /dev/null +++ b/samples/hitoro/bench.bmx @@ -0,0 +1,39 @@ + +' 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 + diff --git a/samples/hitoro/debugprintq.bmx b/samples/hitoro/debugprintq.bmx new file mode 100644 index 0000000..79a452f --- /dev/null +++ b/samples/hitoro/debugprintq.bmx @@ -0,0 +1,78 @@ + +' 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 + + diff --git a/samples/hitoro/extendednewtest.bmx b/samples/hitoro/extendednewtest.bmx new file mode 100644 index 0000000..e67a953 --- /dev/null +++ b/samples/hitoro/extendednewtest.bmx @@ -0,0 +1,41 @@ + +' 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 + + diff --git a/samples/hitoro/fireworks.bmx b/samples/hitoro/fireworks.bmx new file mode 100644 index 0000000..a5651c9 --- /dev/null +++ b/samples/hitoro/fireworks.bmx @@ -0,0 +1,219 @@ + +' 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 + diff --git a/samples/hitoro/gfx/bg.png b/samples/hitoro/gfx/bg.png new file mode 100644 index 0000000..b6f5fdf Binary files /dev/null and b/samples/hitoro/gfx/bg.png differ diff --git a/samples/hitoro/gfx/block.png b/samples/hitoro/gfx/block.png new file mode 100644 index 0000000..13283d1 Binary files /dev/null and b/samples/hitoro/gfx/block.png differ diff --git a/samples/hitoro/gfx/boing.png b/samples/hitoro/gfx/boing.png new file mode 100644 index 0000000..4ff8a6b Binary files /dev/null and b/samples/hitoro/gfx/boing.png differ diff --git a/samples/hitoro/gfx/flame.png b/samples/hitoro/gfx/flame.png new file mode 100644 index 0000000..f284a93 Binary files /dev/null and b/samples/hitoro/gfx/flame.png differ diff --git a/samples/hitoro/gfx/grass.png b/samples/hitoro/gfx/grass.png new file mode 100644 index 0000000..cf34f2c Binary files /dev/null and b/samples/hitoro/gfx/grass.png differ diff --git a/samples/hitoro/gfx/land.png b/samples/hitoro/gfx/land.png new file mode 100644 index 0000000..6ad1976 Binary files /dev/null and b/samples/hitoro/gfx/land.png differ diff --git a/samples/hitoro/gfx/rock.png b/samples/hitoro/gfx/rock.png new file mode 100644 index 0000000..4f91435 Binary files /dev/null and b/samples/hitoro/gfx/rock.png differ diff --git a/samples/hitoro/gfx/shot.png b/samples/hitoro/gfx/shot.png new file mode 100644 index 0000000..f2bf8f9 Binary files /dev/null and b/samples/hitoro/gfx/shot.png differ diff --git a/samples/hitoro/gfx/sky.png b/samples/hitoro/gfx/sky.png new file mode 100644 index 0000000..16e4673 Binary files /dev/null and b/samples/hitoro/gfx/sky.png differ diff --git a/samples/hitoro/gfx/water.png b/samples/hitoro/gfx/water.png new file mode 100644 index 0000000..08190de Binary files /dev/null and b/samples/hitoro/gfx/water.png differ diff --git a/samples/hitoro/info.txt b/samples/hitoro/info.txt new file mode 100644 index 0000000..aa01f45 --- /dev/null +++ b/samples/hitoro/info.txt @@ -0,0 +1,13 @@ + +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. diff --git a/samples/hitoro/listextendedtypes.bmx b/samples/hitoro/listextendedtypes.bmx new file mode 100644 index 0000000..9b481e7 --- /dev/null +++ b/samples/hitoro/listextendedtypes.bmx @@ -0,0 +1,85 @@ + +' ----------------------------------------------------------------------------- +' 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 + + diff --git a/samples/hitoro/newmethod.bmx b/samples/hitoro/newmethod.bmx new file mode 100644 index 0000000..c548544 --- /dev/null +++ b/samples/hitoro/newmethod.bmx @@ -0,0 +1,24 @@ + +' 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 + diff --git a/samples/hitoro/passvarptr.bmx b/samples/hitoro/passvarptr.bmx new file mode 100644 index 0000000..d788763 --- /dev/null +++ b/samples/hitoro/passvarptr.bmx @@ -0,0 +1,21 @@ + +' 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 + + diff --git a/samples/hitoro/rockout.bmx b/samples/hitoro/rockout.bmx new file mode 100644 index 0000000..dd9b2df --- /dev/null +++ b/samples/hitoro/rockout.bmx @@ -0,0 +1,1161 @@ +' ----------------------------------------------------------------------------- +' 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 diff --git a/samples/hitoro/rockout_gui.bmx b/samples/hitoro/rockout_gui.bmx new file mode 100644 index 0000000..8f2cfc5 --- /dev/null +++ b/samples/hitoro/rockout_gui.bmx @@ -0,0 +1,1229 @@ + +' 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 diff --git a/samples/hitoro/shadowimage.bmx b/samples/hitoro/shadowimage.bmx new file mode 100644 index 0000000..ff180e8 --- /dev/null +++ b/samples/hitoro/shadowimage.bmx @@ -0,0 +1,113 @@ + +' 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 + diff --git a/samples/hitoro/singlelist.bmx b/samples/hitoro/singlelist.bmx new file mode 100644 index 0000000..ac8897f --- /dev/null +++ b/samples/hitoro/singlelist.bmx @@ -0,0 +1,78 @@ + +' 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 + + diff --git a/samples/hitoro/sounds/beep.ogg b/samples/hitoro/sounds/beep.ogg new file mode 100644 index 0000000..d96ac86 Binary files /dev/null and b/samples/hitoro/sounds/beep.ogg differ diff --git a/samples/hitoro/sounds/fall.ogg b/samples/hitoro/sounds/fall.ogg new file mode 100644 index 0000000..12cf317 Binary files /dev/null and b/samples/hitoro/sounds/fall.ogg differ diff --git a/samples/hitoro/sounds/gameover.ogg b/samples/hitoro/sounds/gameover.ogg new file mode 100644 index 0000000..b6a563e Binary files /dev/null and b/samples/hitoro/sounds/gameover.ogg differ diff --git a/samples/hitoro/sounds/hit.ogg b/samples/hitoro/sounds/hit.ogg new file mode 100644 index 0000000..6ef2153 Binary files /dev/null and b/samples/hitoro/sounds/hit.ogg differ diff --git a/samples/hitoro/sounds/shot.ogg b/samples/hitoro/sounds/shot.ogg new file mode 100644 index 0000000..1a279d4 Binary files /dev/null and b/samples/hitoro/sounds/shot.ogg differ diff --git a/samples/hitoro/throwrockets.bmx b/samples/hitoro/throwrockets.bmx new file mode 100644 index 0000000..fb2def5 --- /dev/null +++ b/samples/hitoro/throwrockets.bmx @@ -0,0 +1,133 @@ +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 diff --git a/samples/hitoro/tilerocket.bmx b/samples/hitoro/tilerocket.bmx new file mode 100644 index 0000000..2436b5f --- /dev/null +++ b/samples/hitoro/tilerocket.bmx @@ -0,0 +1,205 @@ + +' 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 + diff --git a/samples/hitoro/viewport.bmx b/samples/hitoro/viewport.bmx new file mode 100644 index 0000000..c821a09 --- /dev/null +++ b/samples/hitoro/viewport.bmx @@ -0,0 +1,65 @@ + +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 + + + + + + diff --git a/samples/mak/bullet1.png b/samples/mak/bullet1.png new file mode 100644 index 0000000..46e2e2a Binary files /dev/null and b/samples/mak/bullet1.png differ diff --git a/samples/mak/gnetchat.bmx b/samples/mak/gnetchat.bmx new file mode 100644 index 0000000..93c309a --- /dev/null +++ b/samples/mak/gnetchat.bmx @@ -0,0 +1,126 @@ + +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 diff --git a/samples/mak/gnetdemo.bmx b/samples/mak/gnetdemo.bmx new file mode 100644 index 0000000..138221e --- /dev/null +++ b/samples/mak/gnetdemo.bmx @@ -0,0 +1,287 @@ + +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 diff --git a/samples/mak/requestgraphicsmode.bmx b/samples/mak/requestgraphicsmode.bmx new file mode 100644 index 0000000..080b9b9 --- /dev/null +++ b/samples/mak/requestgraphicsmode.bmx @@ -0,0 +1,102 @@ + +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 + diff --git a/samples/mak/ship.png b/samples/mak/ship.png new file mode 100644 index 0000000..5712f1a Binary files /dev/null and b/samples/mak/ship.png differ diff --git a/samples/mak/sparkle.png b/samples/mak/sparkle.png new file mode 100644 index 0000000..7dd3851 Binary files /dev/null and b/samples/mak/sparkle.png differ diff --git a/samples/maxgui/bigsearch.bmx b/samples/maxgui/bigsearch.bmx new file mode 100644 index 0000000..5b6849d --- /dev/null +++ b/samples/maxgui/bigsearch.bmx @@ -0,0 +1,142 @@ +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 + diff --git a/samples/maxgui/glcube.bmx b/samples/maxgui/glcube.bmx new file mode 100644 index 0000000..f0d357d --- /dev/null +++ b/samples/maxgui/glcube.bmx @@ -0,0 +1,134 @@ + +'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 + diff --git a/samples/maxgui/guilauncher/PNGHeader.bmx b/samples/maxgui/guilauncher/PNGHeader.bmx new file mode 100644 index 0000000..79d35e1 --- /dev/null +++ b/samples/maxgui/guilauncher/PNGHeader.bmx @@ -0,0 +1,60 @@ +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 diff --git a/samples/maxgui/guilauncher/TLauncher.bmx b/samples/maxgui/guilauncher/TLauncher.bmx new file mode 100644 index 0000000..3b1d862 --- /dev/null +++ b/samples/maxgui/guilauncher/TLauncher.bmx @@ -0,0 +1,145 @@ +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 diff --git a/samples/maxgui/guilauncher/bmxlogo.png b/samples/maxgui/guilauncher/bmxlogo.png new file mode 100644 index 0000000..fa1e3bf Binary files /dev/null and b/samples/maxgui/guilauncher/bmxlogo.png differ diff --git a/samples/maxgui/guilauncher/test.bmx b/samples/maxgui/guilauncher/test.bmx new file mode 100644 index 0000000..ebbe453 --- /dev/null +++ b/samples/maxgui/guilauncher/test.bmx @@ -0,0 +1,24 @@ +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 + diff --git a/samples/maxgui/imagedrop.bmx b/samples/maxgui/imagedrop.bmx new file mode 100644 index 0000000..1ac2e97 --- /dev/null +++ b/samples/maxgui/imagedrop.bmx @@ -0,0 +1,135 @@ +' 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 diff --git a/samples/maxgui/sliderthing/sliderthing.bmx b/samples/maxgui/sliderthing/sliderthing.bmx new file mode 100644 index 0000000..87a16a6 --- /dev/null +++ b/samples/maxgui/sliderthing/sliderthing.bmx @@ -0,0 +1,211 @@ +' +' 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 diff --git a/samples/maxgui/sliderthing/sourcepic.jpg b/samples/maxgui/sliderthing/sourcepic.jpg new file mode 100644 index 0000000..eba4ee6 Binary files /dev/null and b/samples/maxgui/sliderthing/sourcepic.jpg differ diff --git a/samples/shooter/_shooter_main.bmx b/samples/shooter/_shooter_main.bmx new file mode 100644 index 0000000..4f1659d --- /dev/null +++ b/samples/shooter/_shooter_main.bmx @@ -0,0 +1,148 @@ +'=============================================================================== +' 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 +' ----------------------------------------------------------------------------- + diff --git a/samples/shooter/background.bmx b/samples/shooter/background.bmx new file mode 100644 index 0000000..e97754d --- /dev/null +++ b/samples/shooter/background.bmx @@ -0,0 +1,192 @@ +'=============================================================================== +' 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 +'=============================================================================== diff --git a/samples/shooter/enemies.bmx b/samples/shooter/enemies.bmx new file mode 100644 index 0000000..41e69fa --- /dev/null +++ b/samples/shooter/enemies.bmx @@ -0,0 +1,78 @@ +'=============================================================================== +' 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 diff --git a/samples/shooter/gfont.bmx b/samples/shooter/gfont.bmx new file mode 100644 index 0000000..82db21d --- /dev/null +++ b/samples/shooter/gfont.bmx @@ -0,0 +1,83 @@ +'=============================================================================== +' 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 diff --git a/samples/shooter/gfx/Hill_1.png b/samples/shooter/gfx/Hill_1.png new file mode 100644 index 0000000..6f723ed Binary files /dev/null and b/samples/shooter/gfx/Hill_1.png differ diff --git a/samples/shooter/gfx/SMOKE.png b/samples/shooter/gfx/SMOKE.png new file mode 100644 index 0000000..7e9ab78 Binary files /dev/null and b/samples/shooter/gfx/SMOKE.png differ diff --git a/samples/shooter/gfx/TitlePage.png b/samples/shooter/gfx/TitlePage.png new file mode 100644 index 0000000..7e25ebc Binary files /dev/null and b/samples/shooter/gfx/TitlePage.png differ diff --git a/samples/shooter/gfx/abduction.png b/samples/shooter/gfx/abduction.png new file mode 100644 index 0000000..57d11bb Binary files /dev/null and b/samples/shooter/gfx/abduction.png differ diff --git a/samples/shooter/gfx/alien_shot.png b/samples/shooter/gfx/alien_shot.png new file mode 100644 index 0000000..b1f55d6 Binary files /dev/null and b/samples/shooter/gfx/alien_shot.png differ diff --git a/samples/shooter/gfx/bigcloud.png b/samples/shooter/gfx/bigcloud.png new file mode 100644 index 0000000..01a4339 Binary files /dev/null and b/samples/shooter/gfx/bigcloud.png differ diff --git a/samples/shooter/gfx/bonusdecals.png b/samples/shooter/gfx/bonusdecals.png new file mode 100644 index 0000000..003a791 Binary files /dev/null and b/samples/shooter/gfx/bonusdecals.png differ diff --git a/samples/shooter/gfx/expl.png b/samples/shooter/gfx/expl.png new file mode 100644 index 0000000..df5ebb5 Binary files /dev/null and b/samples/shooter/gfx/expl.png differ diff --git a/samples/shooter/gfx/multdecals.png b/samples/shooter/gfx/multdecals.png new file mode 100644 index 0000000..374c638 Binary files /dev/null and b/samples/shooter/gfx/multdecals.png differ diff --git a/samples/shooter/gfx/player.png b/samples/shooter/gfx/player.png new file mode 100644 index 0000000..076c6d2 Binary files /dev/null and b/samples/shooter/gfx/player.png differ diff --git a/samples/shooter/gfx/player_shot.png b/samples/shooter/gfx/player_shot.png new file mode 100644 index 0000000..fd51613 Binary files /dev/null and b/samples/shooter/gfx/player_shot.png differ diff --git a/samples/shooter/gfx/playera.png b/samples/shooter/gfx/playera.png new file mode 100644 index 0000000..252eac5 Binary files /dev/null and b/samples/shooter/gfx/playera.png differ diff --git a/samples/shooter/gfx/sky32.png b/samples/shooter/gfx/sky32.png new file mode 100644 index 0000000..8b6fa13 Binary files /dev/null and b/samples/shooter/gfx/sky32.png differ diff --git a/samples/shooter/gfx/spark1.png b/samples/shooter/gfx/spark1.png new file mode 100644 index 0000000..bf1b81e Binary files /dev/null and b/samples/shooter/gfx/spark1.png differ diff --git a/samples/shooter/gfx/spark2.png b/samples/shooter/gfx/spark2.png new file mode 100644 index 0000000..8f57744 Binary files /dev/null and b/samples/shooter/gfx/spark2.png differ diff --git a/samples/shooter/gfx/spark3.png b/samples/shooter/gfx/spark3.png new file mode 100644 index 0000000..6cc5a82 Binary files /dev/null and b/samples/shooter/gfx/spark3.png differ diff --git a/samples/shooter/gfx/spikeyball.png b/samples/shooter/gfx/spikeyball.png new file mode 100644 index 0000000..2a3289a Binary files /dev/null and b/samples/shooter/gfx/spikeyball.png differ diff --git a/samples/shooter/gfx/tex_1.png b/samples/shooter/gfx/tex_1.png new file mode 100644 index 0000000..6cc5a82 Binary files /dev/null and b/samples/shooter/gfx/tex_1.png differ diff --git a/samples/shooter/gfx/tex_2.png b/samples/shooter/gfx/tex_2.png new file mode 100644 index 0000000..b791e27 Binary files /dev/null and b/samples/shooter/gfx/tex_2.png differ diff --git a/samples/shooter/globals.bmx b/samples/shooter/globals.bmx new file mode 100644 index 0000000..8a89b11 --- /dev/null +++ b/samples/shooter/globals.bmx @@ -0,0 +1,46 @@ +'=============================================================================== +' 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 diff --git a/samples/shooter/init.bmx b/samples/shooter/init.bmx new file mode 100644 index 0000000..b1dc9c8 --- /dev/null +++ b/samples/shooter/init.bmx @@ -0,0 +1,21 @@ +'=============================================================================== +' 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 diff --git a/samples/shooter/particles.bmx b/samples/shooter/particles.bmx new file mode 100644 index 0000000..42a4045 --- /dev/null +++ b/samples/shooter/particles.bmx @@ -0,0 +1,168 @@ +'=============================================================================== +' 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 diff --git a/samples/shooter/player.bmx b/samples/shooter/player.bmx new file mode 100644 index 0000000..b981743 --- /dev/null +++ b/samples/shooter/player.bmx @@ -0,0 +1,168 @@ +'=============================================================================== +' 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 diff --git a/samples/shooter/playershots.bmx b/samples/shooter/playershots.bmx new file mode 100644 index 0000000..bcb277b --- /dev/null +++ b/samples/shooter/playershots.bmx @@ -0,0 +1,56 @@ +'=============================================================================== +' 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 diff --git a/samples/shooter/sound.bmx b/samples/shooter/sound.bmx new file mode 100644 index 0000000..0587115 --- /dev/null +++ b/samples/shooter/sound.bmx @@ -0,0 +1,26 @@ +'=============================================================================== +' 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 diff --git a/samples/shooter/sound/EXPLOSION1.WAV b/samples/shooter/sound/EXPLOSION1.WAV new file mode 100644 index 0000000..dd0a1b6 Binary files /dev/null and b/samples/shooter/sound/EXPLOSION1.WAV differ diff --git a/samples/shooter/sound/bombfall.wav b/samples/shooter/sound/bombfall.wav new file mode 100644 index 0000000..bf302da Binary files /dev/null and b/samples/shooter/sound/bombfall.wav differ diff --git a/samples/shooter/sound/explode.wav b/samples/shooter/sound/explode.wav new file mode 100644 index 0000000..0e16c9f Binary files /dev/null and b/samples/shooter/sound/explode.wav differ diff --git a/samples/shooter/sound/shot-1.wav b/samples/shooter/sound/shot-1.wav new file mode 100644 index 0000000..0bea437 Binary files /dev/null and b/samples/shooter/sound/shot-1.wav differ diff --git a/samples/shooter/titles.bmx b/samples/shooter/titles.bmx new file mode 100644 index 0000000..00041a1 --- /dev/null +++ b/samples/shooter/titles.bmx @@ -0,0 +1,52 @@ +'=============================================================================== +' 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 diff --git a/samples/simonh/fireworks/fireworks.bmx b/samples/simonh/fireworks/fireworks.bmx new file mode 100644 index 0000000..01532f8 --- /dev/null +++ b/samples/simonh/fireworks/fireworks.bmx @@ -0,0 +1,115 @@ +' 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 diff --git a/samples/simonh/fireworks/spark.png b/samples/simonh/fireworks/spark.png new file mode 100644 index 0000000..2493de3 Binary files /dev/null and b/samples/simonh/fireworks/spark.png differ diff --git a/samples/simonh/snow/flake.png b/samples/simonh/snow/flake.png new file mode 100644 index 0000000..2493de3 Binary files /dev/null and b/samples/simonh/snow/flake.png differ diff --git a/samples/simonh/snow/snowfall.bmx b/samples/simonh/snow/snowfall.bmx new file mode 100644 index 0000000..905e945 --- /dev/null +++ b/samples/simonh/snow/snowfall.bmx @@ -0,0 +1,83 @@ +' 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.yWIDTH + 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 bxpy1-40 And bypx2-10 And bxpy2-40 And byWIDTH-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 py2by + If py2>40 + py2=py2-3 + EndIf + EndIf + DrawText sc1,width/2-40,0 + DrawText sc2,width/2+40,0 + + Flip +Wend diff --git a/samples/teapot/teapot.bmx b/samples/teapot/teapot.bmx new file mode 100644 index 0000000..48c7b0a --- /dev/null +++ b/samples/teapot/teapot.bmx @@ -0,0 +1,243 @@ +' The UTAH Teapot. See sjbaker.org/teapot/ 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 diff --git a/samples/teapot/teapot_test.bmx b/samples/teapot/teapot_test.bmx new file mode 100644 index 0000000..a82cb04 --- /dev/null +++ b/samples/teapot/teapot_test.bmx @@ -0,0 +1,142 @@ + +' 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 diff --git a/samples/tempest/Readme.txt b/samples/tempest/Readme.txt new file mode 100644 index 0000000..3c3035d --- /dev/null +++ b/samples/tempest/Readme.txt @@ -0,0 +1,52 @@ +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. + diff --git a/samples/tempest/boarddata.bmx b/samples/tempest/boarddata.bmx new file mode 100644 index 0000000..1732ad1 --- /dev/null +++ b/samples/tempest/boarddata.bmx @@ -0,0 +1,97 @@ +' 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 diff --git a/samples/tempest/boardgen.bmx b/samples/tempest/boardgen.bmx new file mode 100644 index 0000000..738a067 --- /dev/null +++ b/samples/tempest/boardgen.bmx @@ -0,0 +1,495 @@ +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$ + Case 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) + diff --git a/samples/tempest/sfx.bmx b/samples/tempest/sfx.bmx new file mode 100644 index 0000000..39f43df --- /dev/null +++ b/samples/tempest/sfx.bmx @@ -0,0 +1,71 @@ +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 diff --git a/samples/tempest/sfx/bonus.wav b/samples/tempest/sfx/bonus.wav new file mode 100644 index 0000000..4ec52c2 Binary files /dev/null and b/samples/tempest/sfx/bonus.wav differ diff --git a/samples/tempest/sfx/bullet.wav b/samples/tempest/sfx/bullet.wav new file mode 100644 index 0000000..4054a34 Binary files /dev/null and b/samples/tempest/sfx/bullet.wav differ diff --git a/samples/tempest/sfx/flippershot.wav b/samples/tempest/sfx/flippershot.wav new file mode 100644 index 0000000..73c415c Binary files /dev/null and b/samples/tempest/sfx/flippershot.wav differ diff --git a/samples/tempest/sfx/killedbybullet.wav b/samples/tempest/sfx/killedbybullet.wav new file mode 100644 index 0000000..8ee5d65 Binary files /dev/null and b/samples/tempest/sfx/killedbybullet.wav differ diff --git a/samples/tempest/sfx/killedbyflipper.wav b/samples/tempest/sfx/killedbyflipper.wav new file mode 100644 index 0000000..bd2c538 Binary files /dev/null and b/samples/tempest/sfx/killedbyflipper.wav differ diff --git a/samples/tempest/sfx/pulse.wav b/samples/tempest/sfx/pulse.wav new file mode 100644 index 0000000..ff5a7ae Binary files /dev/null and b/samples/tempest/sfx/pulse.wav differ diff --git a/samples/tempest/sfx/shot.wav b/samples/tempest/sfx/shot.wav new file mode 100644 index 0000000..8853c9d Binary files /dev/null and b/samples/tempest/sfx/shot.wav differ diff --git a/samples/tempest/sfx/spikeshot.wav b/samples/tempest/sfx/spikeshot.wav new file mode 100644 index 0000000..7eb4503 Binary files /dev/null and b/samples/tempest/sfx/spikeshot.wav differ diff --git a/samples/tempest/sfx/tick.wav b/samples/tempest/sfx/tick.wav new file mode 100644 index 0000000..f82f118 Binary files /dev/null and b/samples/tempest/sfx/tick.wav differ diff --git a/samples/tempest/sfx/zap.wav b/samples/tempest/sfx/zap.wav new file mode 100644 index 0000000..12caf08 Binary files /dev/null and b/samples/tempest/sfx/zap.wav differ diff --git a/samples/tempest/sfx/zoomin.wav b/samples/tempest/sfx/zoomin.wav new file mode 100644 index 0000000..4212fc9 Binary files /dev/null and b/samples/tempest/sfx/zoomin.wav differ diff --git a/samples/tempest/sfx/zoomout.wav b/samples/tempest/sfx/zoomout.wav new file mode 100644 index 0000000..542fd21 Binary files /dev/null and b/samples/tempest/sfx/zoomout.wav differ diff --git a/samples/tempest/tempest.bmx b/samples/tempest/tempest.bmx new file mode 100644 index 0000000..a024d37 --- /dev/null +++ b/samples/tempest/tempest.bmx @@ -0,0 +1,3469 @@ +'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" diff --git a/samples/tempest/transformfunctions.bmx b/samples/tempest/transformfunctions.bmx new file mode 100644 index 0000000..b172722 --- /dev/null +++ b/samples/tempest/transformfunctions.bmx @@ -0,0 +1,159 @@ +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 + diff --git a/samples/tempest/vectorfont.bmx b/samples/tempest/vectorfont.bmx new file mode 100644 index 0000000..4c11018 --- /dev/null +++ b/samples/tempest/vectorfont.bmx @@ -0,0 +1,306 @@ +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 + diff --git a/samples/threads/background_loading.bmx b/samples/threads/background_loading.bmx new file mode 100644 index 0000000..10ce606 --- /dev/null +++ b/samples/threads/background_loading.bmx @@ -0,0 +1,220 @@ + + + + + +' ----------------------------------------------------------------------------- +' 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 + diff --git a/samples/threads/bluboing.png b/samples/threads/bluboing.png new file mode 100644 index 0000000..386786d Binary files /dev/null and b/samples/threads/bluboing.png differ diff --git a/samples/threads/bluegem.png b/samples/threads/bluegem.png new file mode 100644 index 0000000..7785967 Binary files /dev/null and b/samples/threads/bluegem.png differ diff --git a/samples/threads/boing.png b/samples/threads/boing.png new file mode 100644 index 0000000..4b365ed Binary files /dev/null and b/samples/threads/boing.png differ diff --git a/samples/threads/dead.png b/samples/threads/dead.png new file mode 100644 index 0000000..18df9cc Binary files /dev/null and b/samples/threads/dead.png differ diff --git a/samples/threads/greengem.png b/samples/threads/greengem.png new file mode 100644 index 0000000..6d190ef Binary files /dev/null and b/samples/threads/greengem.png differ diff --git a/samples/threads/redgem.png b/samples/threads/redgem.png new file mode 100644 index 0000000..0239bf6 Binary files /dev/null and b/samples/threads/redgem.png differ diff --git a/samples/threads/threaded image downloader.bmx b/samples/threads/threaded image downloader.bmx new file mode 100644 index 0000000..1068696 --- /dev/null +++ b/samples/threads/threaded image downloader.bmx @@ -0,0 +1,317 @@ + + + + + +' ----------------------------------------------------------------------------- +' 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 diff --git a/samples/threads/threads.bmx b/samples/threads/threads.bmx new file mode 100644 index 0000000..b4c1123 --- /dev/null +++ b/samples/threads/threads.bmx @@ -0,0 +1,96 @@ + + + + +' ----------------------------------------------------------------------------- +' 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 +