Firing Line Design Notes ---------------------------------------------------------------------- 1.0.0 Layout .1.0 Locations .2.0 Forces 2.0.0 Game Loop .1.0 Mechanics .1 Monitor Input Controls .2 Monitor Timer .2.0 Horizontal Movement .1 Validating Horizontal Movement .2 Executing Horizontal Movement .3.0 Autodrop Prep .1 Calculating the Distance .2 Update Rotation Matrix Coordinates .4.0 Rotation .1 Update Rotation Matrix .2 Autodrop Translation .3 Validating Rotations .4 Executing Rotations .5 Special Rotations .5.0 Vertical Movement .6.0 Line Completion .1 Check For (and Clear) Completed Lines .2 Shift Lines Down To Fill Cleared Lines .3 Assign Points For Completed Lines .7.0 Piece Creation .1 Randomly Determine Next Piece .2 Validate New Piece (Defeat Check) .3 Create New Piece .4 Display Next Piece 3.0.0 Process 4.0.0 Statistics 5.0.0 Terminology ---------------------------------------------------------------------- 1.0.0 Layout This section discusses the layout and setup of the map. 1.1.0 Locations The first 200 locations is used to create the tank itself, in the form of 1x1 locations spread out across a 10x20 grid. Each of these "cells" is labelled by with a 2-digit alphanumeric code to indicate which row and column it refers to. This naming convention helps to organize the locations in an orderly sequence that is easy to reference from the triggers. This grid could be spread out more, but it already does not fit onto one game screen of Starcraft. The only reasonable use for a larger grid would be to increase the size of the minimap tank display. Another 16 locations are used as 1x1 locations in a 4x4 grid called the rotation matrix. These locations are labelled with 2-digit numeric codes to indicate which row and column it refers to. They use numeric instead of alphanumeric codes to distinguish them from the 200 cells used for the tank described above. 1.2.0 Forces The only real consideration taken in creating the forces is putting one neutral player on a force alone and using that player for the dropping pieces. This allows you to write the triggers such that you could swap a different player into that force to change the color of the dropping pieces. Pieces could have been color-coded by placing seven of the players in one force and referring to them as the force, but this would require keeping track of which cubes are static by some means other than player number, possibly by making nonstatic pieces a different unit type. Insufficient locations are available to duplicate the tank or even a subsection of the tank to maintain such data. ---------------------------------------------------------------------- 2.0.0 Game Loop All of the triggers are organized into a sequence of checks and actions, like the game loop of a simple game engine. Each trigger cycle is responsible for monitoring and processing all input up to that point in the game, from taking input to cleaning up the tank. No delays are used in this trigger scheme. The following subsections are listed in the order in which they are executed during the game loop. 2.1.0 Mechanics The game loop starts off with a small amount of game mechanics for the game's timing, and for processing game input. 2.1.1 Monitor Input Controls The input controls consist of a set of marines who can be shot to trigger movement or rotation in their respective directions. The player is given a cloaked ghost and the buttons are set to enemy status so that you can right-click on the marine-buttons to press them without explicitly pressing the attack button or hotkey. (A cloaked ghost does not auto-acquire targets unless issued a hold- person command.) Once any of the input control buttons are pressed, a corresponding switch is set and the control is replaced. These switches remain set until the next trigger cycle. 2.1.2 Monitor Timer The timer used in this project is a TimeSpan (tm) Marine. This Marine is created at one location and given move orders between two locations. Every time he reaches one of these locations, a timer tick is marked on a custom score. This is converted into a switch that is set during trigger cycles wherein a tick has occurred, and cleared during trigger cycles wherein no tick has occurred. The two locations used for the marine's patrol are moved closer to each other to increase the frequency of ticks (and thus the game speed) over time. 2.2.0 Horizontal Movement Handling any type of movement is a two-step process. First, one must determine whether or not the move is valid. Second, one must move the actual components of the piece. 2.2.1 Validating Horizontal Movement To determine whether or not a move is valid, one must check to see if any part of the currently active piece is prevented from moving in the desired direction. If any part of the piece is blocked by either a static cube or by the wall of the tank, the move is invalid. 2.2.2 Executing Horizontal Movement Once a horizontal move is requested and valid, the movement itself is merely a matter of shifting all cubes of the piece over by one cell. It is important to shift them in the proper order, so as to prevent shifting the same cube twice. 2.3.0 Autodrop The up-arrow UI control is used to automatically drop the current piece as far down the tank as it can go without colliding with any static cubes. This is a very tricky operation to execute. Calculating the auto-drop scenario using the most straight-forward, iterative approach would take in excess of 4000 triggers. Although the triggers themselves would be simple, this number is too large to work with in a reasonable amount of time. The approach used in this map is slightly different. Instead of recalculating and checking for collisions at every step, the trigger set checks to see how many steps the block can drop, and drops it the full distance in one pass. While using fewer triggers than the direct approach, this is still the most demanding set of triggers in the map, using over 800 triggers. 2.3.1 Calculating the Distance To calculate the maximum distance the block can drop, the triggers search column-by-column to determine the maximum distance the piece can drop in that column before colliding with another piece or the wall of the tank. After each column distance is calculated, it is compared to an overall minimum. The overall minimum of these column distances is used as the dropping distance. 2.3.2 Update Rotation Matrix Coordinates The dropping distance is used to adjust the coordinates of the rotation matrix. After the rotation matrix is updated by these new coordinates, it is used to recreate the piece, as described below. 2.4.0 Rotation Rotation is more complicated than movement as it utilizes a focal point. To facilitate this, the triggers maintain a set of 1x1 locations in a 4x4 grid. This grid of locations is referred to as the rotation matrix. 2.4.1 Update Rotation Matrix The rotation matrix is kept centered on the currently active piece as piece as it is being manipulated. To do this, a set of custom scores are maintained to indicate which row and column the current piece is centered upon. When some cells of the rotation matrix extend beyond the tank, these cells are moved to a location containing a static cube, effectively blocking these cells off from validation tests. Given the vast number of possible matrix positions, this update is one of the more trigger-intensive sections of the game loop. 2.4.2 Autodrop Translation The actual translation portion of autodrop is done immediately after the rotation matrix is updated, so that the rotation matrix correctly reflects the location of the piece. Alternatively, the drop operation could have been handled after the rotation, but this would require updating the rotation matrix a second time to get essentially the same effect. To complete an autodrop operation, the piece must be moved directly to it's new coordinates. This is merely a matter of deleting the old piece and re-creating it at the new rotation matrix location, rotated appropriately. A custom score is used to keep track of the rotational state of the current piece. Since there is no safe place to perform the rotation and insufficient locations to create another rotation grid, the piece is simply recreated in a pre-rotated state. 2.4.3 Validating Rotations Determining whether or not a rotation operation is valid is fairly easy matter with a properly updated rotation matrix, as the problem is contained within the rotation matrix, and can be viewed entirely independantly from the type of piece being rotated. For a given rotation direction, each cell of the rotation matrix has a unique destination cell. Once this is established, the check to validate the move is identical to that for horizontal movement, using the appropriate destination cells as opposed to the adjacent cells. As with horizontal movement, if any cube of the active piece is being blocked from the rotation, the entire piece is considered blocked. 2.4.4 Executing Rotations Executing the rotation can also be considered a localized problem within the rotation matrix, and is also independant of both the type of piece being rotated and the location of the piece within the overall tank. Unlike horizontal movement, however, the destination cells for a rotation are not always empty. The use of a temporary bypasses this issue altogether though, making the rotation itself yet another simple sequence of move unit actions. 2.4.5 Special Rotations One piece, the T-bar, uses a different rotation axis than the other pieces. It uses a 3x3 rotation axis instead of the standard 4x4. To account for this, a sub-matrix of the standard 4x4 rotation matrix is used for alternate collision detections and rotation transformations whenever the currently active piece is a T-bar. 2.5.0 Vertical Movement Vertical movement is almost identical to horizontal movement. It is processed after both horizontal movement and rotation so that for a given trigger cycle, any control requests can be processed before the current piece becomes static due to vertical movement. The current piece automatically drops one step every timer tick. If the move-down control is used, the game immediately issues an extra timer tick and resets the timer. This is done as part of section 2.1.2 detailed above. As mentioned for horizontal movement, it is important to execute the actual move operations in the correct order, so as to avoid double- moves. 2.6.0 Line Completion Testing for completed lines is done on a row-by-row basis, and is immediately followed by the shifting of the remaining lines down. 2.6.1 Check For (and Clear) Completed Lines A line is completed if all 10 cells in of the line contain static cubes. Once such a line is detected, the cubes in that line are immediately removed and a custom score is incremented to indicate that a line was completed. A row-specific switch is also set, for use in the next step - shifting the lines down to fill in the gaps. 2.6.2 Shift Lines Down To Fill Cleared Lines If any lines are completed and cleared, they leave a gap in the tank which needs to be filled. The lines above any cleared lines need to be shifted down a step, but may need to be shifted down multiple steps if more than one line was cleared at once. To adequeatly cover such situations, a 19-pass approach is used, with each pass concentrating on only one possible cleared line. Each pass in this approach first checks to see if the switch for the corresponding line was set. If it is, then the cubes in every cell above that line are shifted down one step to the cells beneath them. 2.6.3 Assign Points For Completed Lines The custom score that is incremented during the line completion pass is used to determine how many lines were cleared during this trigger cycle. Due to the nature of the game itself, a maximum of 4 lines can be cleared in a given cycle. For each of the 4 possible line counts, a score is added to the user's minerals. 2.7.0 Piece Creation The PieceNew switch is polled to determine if a new piece is needed. If it is, a random piece type is chosen, tested for collisions, and created. 2.7.1 Randomly Determine Next Piece To determine which type of piece to create next, a random value is chosen via three randomly set switches. There are seven standard game pieces, so one of the eight possible combinations of these three switches must be thrown out. When this eight value is generated, the triggers simply re-roll the random values again. The switches are re-rolled up to 3 times to ensure a small chance of defaulting. If, after all of these re-rolls, the random values still manage to come up with the eight combination, the piece is defaulted to piece 000 (the "long bar"). Statistically, each piece has a 14.282% chance of being generated with the exception of the default piece (the "long bar"), which has a 14.307% chance of being generated. This deviation is immensely reduced with each additional trigger, but a 0.024% deviation is sufficiently insignificant for this project. Prior to the random rolling, the prior "next piece" seed is saved off in three separate switches, which are used to validate and create the new piece on the current trigger cycle, as described below. 2.7.2 Validate New Piece (Defeat Check) Once the type of piece is decided, the triggers know which cells the piece will occupy. Determining if the piece is being prevented from creation is a simple matter of testing each of these cells for the presence of static cubes. If any required cube is blocked, PieceNew is cleared to prevent the piece from actually being generated, and Defeated is set to activate the defeat sequence. 2.7.3 Create New Piece Creating the new piece is trivial once you know which type of piece you wish to create, since the pieces are always created at the same location. The pieces themselves are composed of various unit types to add color and flavor, and the triggers work for any type of unit that fits within a single cell, provided that it is created for the proper force. In order to use larger units, one would need to spread the tank's cells out wider, although this would greatly hinder the amount of visual tank space one can see. 2.7.4 Display Next Piece Displaying the next piece is almost identical to creating the new piece, except that it is done on a separate grid and is based off of the NextSeed switches rather than the PieceSeed switches. The extra triggers used for creating a new piece could have been eliminated by instead moving the displayed next piece directly into the game tank rather than re-creating it. With such an arrangement, collision detection becomes simpler, as it can be done generically on a cell-by-cell basis. One would still need a way to create the initial piece, though. Doing so explicitly would require the same number of triggers and thus nullify the point. Alternatively, one could use a two-cycle setup phase to allow the next piece be created and moved into the tank on one cycle, and then re-created on the second cycle, but this is a bit too much work for such a minimal optimization. ---------------------------------------------------------------------- 3.0.0 Process Most triggers were created by hand with the absolute minimum use of a mouse. A simple windows macro program was used to facilitate the creation, modification, and organization of some of the triggers. The time taken to plan and create the macros evened out the amount of time that the macros saved, but these macros allowed me to do other work while they plugged away at the more tedious busywork. Some of the more massive and error-prone sets of triggers were done via a combination of macro work and keyboard circumvention, with careful monitoring. The macro aspect of this process reduced the chances of typo-related errors, but still required monitoring to prevent small UI-specific macro flaws from distorting the results. The triggers were originally written on a 96x96 map, but were soon recreated on a 64x64 map so as to provide a larger representation of the tank in the minimap window. Triggers with blank comments are assumed to have the same comment as the last commented trigger. This was done out of necessity, given the number of triggers and the trigger system string limit. ---------------------------------------------------------------------- 4.0.0 Statistics Tileset : Space Platform Map Size : 64x64 Players : 1 Triggers : 2,000+ Locations: 250 Switches : 44 Dev Time : 26 hours (roughly) ---------------------------------------------------------------------- 5.0.0 Terminology Blocked = Prevented from changing in a desired way due to collision with the wall or a static piece. Cell = A 1x1 position in the Tank that can be occupied by a cube Cube = A 1x1 section of a piece. Piece = A game piece, comprised of 4 cubes Tank = The 10x20 grid of cells on which the game is played .