Karthik Karkala (kck23)
Vijay Paruchuru (vkp4)
ECE 476
Scorched Earth: The
Mother of All Games
Rationale,
Sources, and Hardware/Software Tradeoffs
Patents, Copyrights, and
Intellectual Property
Safety, Interference, and Usability
Conformity
to Applicable Standards
Intellectual property
considerations
For our design project, we decided to replicate the video
game known as “Scorched Earth: The Mother of All Games” where two tanks fire
missiles at one another by adjusting angles and power while adjusting for
variable wind.
The
objective of Scorched Earth is to destroy the enemy tank before your tank is
destroyed. Two tanks are randomly positioned on their respective sides on
a randomly chosen terrain. Through a trial and error process of adjusting
angles and the strength of the tank’s turret, each user can manipulate the
trajectory of the bullets to eventually target the enemy tank. With three
different weapons of different blast radii and three unique difficulty levels
that incorporate variable wind speeds, the users can test their abilities as the
most accurately firing tank. We wanted
to recreate this game to allow players to relive childhood memories.
The foremost reason we chose to simulate Scorched Earth
was because no one had recreated this game as a final project during past
years. Furthermore, we wanted to create
a video game because we would not have any limitations within our budget
regarding additional hardware. Since we
do not have many sources for additional parts, an advantage that other groups
possessed, a project that consisted entirely of code would allow us to make a
project that was still fun and still extremely complex and challenging. The necessary hardware to run our project was
simply the digital to analog converter circuit that displayed our program onto
the television. After completion of the
project, we also added sound (a pitch that gradually decreased as a bullet was
in motion), which required very simple circuitry that also connected to the
television.
We originally had other video game ideas in mind, but
with the help of fellow ECE and housemate, Brandon Richter, we rediscovered
this particularly entertaining game that immediately found its way to our
hearts. During the brainstorming
processes of recreating this game, we basically decided to implement everything
that we successfully completed. We
wanted to randomly draw terrains at the start of each game; however, this would
have been too complicated since our code was dependent on the arrays of
previously created terrains.
Upon starting the game, a startup screen displays a
sample terrain and the two tanks as normally seen during an actual game. “Scorched Earth” and a difficulty of “easy”
as its default setting are also shown on the screen. By pressing the debounced button C.6, the
difficulty levels rotate from “easy” to “med” (medium), “med” to “hard”, and
then “hard” back to “easy”. An easy
difficulty level has zero wind effect on the bullets during the game, while a
medium difficulty level has a constant wind throughout the entire game, and a
hard difficulty level has a variable wind after both players have completed a
round of firing a bullet.
To begin the game, the user must press C.7 to clear the
screen and display a randomly chosen but previously created terrain. Player 1’s tank will always be located on the
left half the screen while player 2’s tank will always be located on the right
half of the screen. Along the top of the
screen is an information bar that displays which player’s turn it is to fire,
the power of that player’s shot, the angle at which that tank’s turret is
pointing, the weapon chosen, the number of remaining shots of that weapon, and
the speed and direction (east or west) of wind.
By using buttons C.4 and C.5 to increase and decrease the angle of the tank’s
turret and by using buttons C.2 and C.3 to increase and decrease the power of
the tank’s shot, the user can adjust the trajectory of their shot. At the default setting, weapon 1 is displayed
on the screen with infinite number of shots.
Using C.1, the user can cycle through to weapon 2 or weapon 3, of which
each tank only has 3 and 1 bullets available during each game. As the user uses either weapon 2 or weapon 3,
availability of the bullets, denoted with single pixels, will decrease in
number. The user must then use button
C.6 to fire the bullet. The settings
used for the first player to go are saved and the second player can now perform
the same procedure with the original default settings that will also be saved
upon firing a shot as well.
Once a tank has been hit, a message that reads who is the
winner will show up. To play another
game or to simply see the number of accumulated wins each player has since
reset, the user must press C.7. This
clears the screen and displays the number of wins that each player has. “Play Again” is also displayed on the screen,
and to do so, the user must press button C.6 to clear the screen again and
redisplay another randomly chosen map and random starting positions for each of
the tanks.
The
standards that we used are the same as those in Lab 4 (
The foremost important copyright that must be addressed
is the person(s) who originally created the game known as Scorched Earth. Unfortunately, since we are not able to find the
names of those who deserve our appreciation, we can only publish the official
website for the game: http://www.classicgaming.com/scorch/. However, must also thank
Since our final project is a video game, our sole
hardware necessary was the microcontroller unit, located on the STK500,
pushbuttons to operate the game, also located on the STK500, and the video
digital to analog converter shown in the design schematic below. We used PORT C for the pushbuttons and PORT D
for the circuit to implement the television.
The circuit was connected was wired to the yellow connector on the TV
using clip leads and a RCA phone jack. The TV was set to video input.
Design Schematic
Since we were able to use Professor Bruce Land’s video generation code, all we had to do was write the code for the game and were able to use the video generation code to output all our date. In general the code was pretty straightforward to write and we didn’t have any major problems with it. Our biggest problem was timing restrictions which forced us to break up our output into multiple frames/states so that we would not get artifacts.
Start Screen
The start screen has the game name, “Scorched Earth”, and a map with two tanks as the background. At this screen you are allowed to choose the difficulty of the game. There are 3 difficulties: Easy which has no wind, Medium where wind is randomly generated but doesn’t change for the rest of the map, and Hard where wind changes after each player has shot.
Clear Screen/Draw Map
One of the trickier parts of the code were erasing the screen and drawing a new map. It took us a few tries to figure out how many lines we could draw or erase in each frame without causing artifacts to occur. We finally decided on only drawing or erasing 3 lines per frame to ensure that no artifacts would occur. We have 5 pre-generated maps that have been stored into memory of our cpu. We used a random number generator to randomize which map would be chosen. We also used a random number generator to randomize the starting location of P1 and P2.
Player Turn
We used a random number generator after drawing the maps to
randomize which player gets to go first.
At the start of the players turn, the player is allowed to change his
power, angle and weapon, and fire when he feels he is ready. The values for power went from 0 to 99, the
values for angle go from 0 to 180 and the weapon is cycled from 1 to 3. We debounced all the buttons used to
increment/decrement the power, angle and weapon. We used a timed increment so that power and
angle would be changed by one every quarter second. First we had it such that each button press
would result in a change of one, but we found it tiresome to press a button 15
times, and it was much easier just to hold the button down. And for the weapon since it was only cycling
through 3 possible values, we just decided to leave it as a simple debounced button
where each press increments the value by 1.
Also weapons 2 and 3, which are considered super weapons, have a limit
on the number of shots available. Weapon
2 only has 3 shots while Weapon 1 only has 1 shot. Once the bullet is fired the
trajectory of the bullet is calculated, how this is done is explained in the
next section. Also you are not allowed
to fire if you have no more shots left of the selected weapon.
Bullet Trajectory
First thing we did to determine the trajectory of our bullet was to divide the power of our shot into x and y components (xpow, ypow) using the sine and cosine functions. We had a start position (xpos, ypos) which was at the top of the turret of the tank and we changed these values by a factor of the power (xpos = xpos+xpow/25, ypos = ypos-ypow/25). Once we changed the position variables we changed the power variables, the xpow would be changed by a factor of wind/25 and ypow was reduced by one every frame to account for gravity. This caused us to skip a few points because at our highest power of 100 it was possible to skip 3 pixels ever frame and it was possible that our shots would skip over the top of the tank or not hit the top of the terrain but instead hit somewhere lower. In order to correct this we scaled our changes by ¼ so that we wouldn’t skip any points, for example xpos now equaled xpos+xpow/100 and xpow = xpow + wind/100 and ypow = ypow - ¼. This ensured that at full power we would only be moving one pixel, and though this increased the time it took for the bullet to travel from one point to other, it made the point of impact much more accurate. Also the xpos and ypos variables were stored into other variables (prevx,prevy) so that we know what the last position of the bullet was and delete it.
Walls
We decided to make the left and right walls absolute boundaries. If any of the shots hit these boundaries that players turn would end, we assumed that if the shot has passed the edge of the screen that it was shot too far and thus will not hit. The top of the screen however was not an absolute boundary, any shot went higher had a chance of coming back. We just stopped our program from drawing new points or erasing old points if the bullet went higher than the top of our screen, and we resumed drawing and erasing once the bullet re-entered the screen. This allows the player to take high shots to get over hills that they might be next to without worrying about hitting an absolute boundary.
End of Round
The round ends when one of the tanks is struck by the bullet. Regardless of which player shot the bullet, the player whose tank is hit loses and the other player wins. At this point we print out a message letting the players know which player has won the round. Then by pressing fire you go to another screen, where you have the option of changing the difficulty, this screen also shows the number of games each player has won. By pressing start at this screen you can start a new game. At this point all your weapons are refilled and once again which player starts is randomized.
Music
The final part of our code that needs to be talked about is the sound effects. During the shot when the trajectory is being calculated we have a note which increases in pitch every quarter second. The timebase of quarter second was achieved by using a variable to count up to 15, and since each time it increments corresponds to one frame and each frame is 1/60th of a second, the counter hits 15 at approximately quarter second. We set OCR0 to 60 and ran timer0 without interrupts enabled on a prescaler of 100, to generate our initial note and every quarter second we incremented OCR0 by 1, to increase the pitch and thus make our sound a little more realistic. Also when a player tank is hit we have a much higher pitched sound with an OCR0 starting and 195 and going to 193 in order to create a sound for three-quarters of a second to simulate the explosion of a tank getting hit. Though since I, Karthik, did the sound in my free time and am not musically talented the sound is most likely quite a bit lacking in their effect.
Challenges
As I mentioned earlier in this section we had no major
problems with this code. The only part
of the code that was tricky to figure out was trying to keep the artifacts from
occurring when our bullet crossed our boundaries. When the bullet left the top it would erase
part of the line or the last bullet location drawn would not be erased. This was a little tricky for us because we
tried to play with which point we called our boundary, this was usually the pixel
before the actual line itself, and we still ended up with the stray unerased
bullet. We finally fixed this problem
when we noticed that when we ended the turn due to the boundaries we would not
erase the previous point because we would be skipping a state when compared to
if the bullet had hit terrain. Once we
figured this out it was easy to make the change and remove the stray unerased
bullet point. Other parts of our code
such as timing constraints for drawing the map, though they weren’t tricky took
some experimenting to make sure that they were efficient.
Most of our code executes with no delay. The only part of our code where you might see a bit of delay is in our drawing of the map or clearing the screen, since each of this program is divided over approximately 42 frames which equal three-quarters of a second you will see a scrolling effect when the screen is being cleared or a map is being drawn. This is the only part of the program where the desired output isn’t output to the screen all at once but instead is broken up into smaller pieces over a lot of frames, and therefore the only part where any delay recognizable by the human eye can be seen.
The only issue of accuracy in our program was in the trajectory calculation. Since with our original idea it was possible to skip a few points, it was not very accurate. But we changed it so that it wouldn’t skip any points and thus the point at which the bullet hit is now close to 100% accurate. However this slowed down the speed at which the bullet traveled by a factor of 4.
We enforced safety in our design by following the
applicable standards for NTSC rate and RS170.
By also carefully wiring our circuits and using low voltages, no one can
be harmed by our project. Players of the
game will use the pushbuttons, located on the STK500, that cause no threat to
safety. By adhering to the no food and
drink policy in the lab, we also prevented careless accidents from
occurring. Since our program was mostly
software based and we did not create any RF interference or CPU noise, our project
did not interfere with any one else. Our
project is usable by anyone who has the ability to watch a television screen
and push buttons. However, the ability
to logically determine how to win a game by firing a missile at the enemy tank
by adjusting the tank’s power and angle would also greatly help in using this
project.
We were completely satisfied with our project on all
levels. Everything that we planned to
implement within the video game was achieved: a startup screen; the option to
choose difficulties of no wind, one wind, or variable wind throughout the game;
randomly chosen terrains, tank positions, and starting player; the ability to
increase and decrease the power and angle of the tanks; the ability to choose
one of three weapons that created different blast radii in the terrain and two
of which that were limited in number; the ability to save each player’s most
recently used angle, power, and weapon settings; a line that indicated the
strength and direction of the wind; and a “Play Again?” screen that would allow
the players to change the difficulty level again. As far as we know, the only thing about our
project that we would have changed was the implementation of Sega Contollers to
play the game. This is extremely simple
and given the access to controllers and a little more time, we would have
completed this task as well.
As stated earlier in this report, we conformed to
applicable standards as well as possible.
The standards that we used are the same as those in Lab 4 (
Also stated
earlier in this report, the foremost important
copyright that must be addressed is the person(s) who originally created the
game known as Scorched Earth.
Unfortunately, since we are not able to find the names of those who
deserve our appreciation, we can only publish the official website for the
game: http://www.classicgaming.com/scorch/. However, must also thank
1.
to accept responsibility in making engineering decisions consistent with
the safety, health and welfare of the public, and to disclose promptly factors
that might endanger the public or the environment;
Our video game has not impact on society that will produce any negative effects on the safety, health and welfare of the public. Given that we are using a television to display our game, we are following all protocols compliant with NTSC and RS170 standards.
2.
to avoid real or perceived conflicts of interest whenever possible, and
to disclose them to affected parties when they do exist;
If there was ever a conflict of interest between the group members, such as a difference in opinion as to what additional features should be added to the game (should missiles reflect off of the side walls), we rationally discussed our problem and reached a solution. We did not have any conflicts of interest outside of the group.
3.
to be honest and realistic in stating claims or estimates based on
available data;
During the entire duration of this project, we projected realistic goals and successfully completed those goals within our knowledge and budget constraints. Furthermore, the project as well as the report was created in the utmost honest manner
4. to seek, accept, and offer honest criticism
of technical work, to acknowledge and correct errors, and to credit properly
the contributions of others;
During the creation of our project, we sought advice from
our teaching assistant, Vladimir Kozitsky,
5.
to assist colleagues and co-workers in their professional development
and to support them in following this code of ethics.
We offered our support to other teams by offering constructive criticism and advice that would help other groups succeed. We did not break any of the rules discussed in the IEEE Code of Ethics.
Our project will have a tremendous societal impact since playing this game will bring create hours upon hours of video gaming pleasures. Reliving many childhood memories is priceless. There are very few safety considerations. If we decide to encase the breadboard and the exposed wires, there will be no risk to health in any way. Furthermore, since we are using the RS170 standards with very few modifications, there is no need for problems such as epilepsy. Two humans will be able to play the game; each player will alternate firing the tank until one of the tanks has been destroyed. The user will solely need the ability to press buttons on a keypad in addition to being able to turn on the microcontroller unit and the television. The ability to make appropriately adjust the strength and angle of each shot will also help in winning at the game. The copyrights for this game have yet to be found but will be properly acknowledged soon.
The drawing below shows the state diagram used for our video game. Nested within this state diagram was the state diagram for button debouncing, shown later in this section.
State Diagram for Scorched Earch: The Mother of All Video Games
The following diagram illustrates the basic schema used to debounce buttons used during the execution of the game. Two of the eight buttons used are shown below, but the same basic schema was used for the remaining six buttons. We used this schema for use of the buttons while playing the game, as well as C.0 during both the startup and game over screens to change the difficulty settings.
State Machine for
Button Debouncing
Project Setup
Title Screen
Game Screen
Win Screen
Play Again Screen
Given our budget constraint of $40.00, we were well within our limitations, as seen:
STK500 Development Board $0.00
Television $0.00
Breadboard $0.00
Wires/Cables $0.00
Total $0.00
During the entire design of our project, we split up most our tasks equally. Once the project was complete, Karthik implemented the sound of the video game during his spare time. Since he also has more experience in programming and thus has a stronger background, Karthik was therefore slightly more responsible for creating some of the program code. However, to compensate for this, Vijay was responsible for writing a slight majority of this written report.
Atmel Mega32 Data Sheet:
http://instruct1.cit.cornell.edu/courses/ee476/AtmelStuff/full32.pdf
http://instruct1.cit.cornell.edu/courses/ee476/video/index.html
http://instruct1.cit.cornell.edu/courses/ee476/video/Video32v2.c
//video gen and
sound
//D.5 is
sync:1000 ohm + diode to 75 ohm resistor
//D.6 is
video:330 ohm + diode to 75 ohm resistor
//B.3 is
sound and should have a 10k resistor to
gnd
#pragma regalloc- //I allocate the
registers myself
#pragma optsize- //optimize for
speed
#include <Mega32.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <delay.h>
//cycles = 63.625
* 16 Note NTSC is 63.55
//but this line
duration makes each frame exactly 1/60 sec
//which is nice
for keeping a realtime clock
#define lineTime 1018
#define begin {
#define end }
#define ScreenTop 30
#define ScreenBot 230
//main state
machine vars
#define Startup 0
#define Clear 1
#define Drawmap 2
#define Drawtank 3
#define Top 4
#define P1T 5
#define Fire1 6
#define P2T 7
#define Fire2 8
#define Erase1 9
#define Erase2 10
#define HIT2 11
#define HIT1 12
#define Startagain 13
#define Clearmap 14
#define Next 15
// debouncing
state machine vars
#define Nopush 0
#define MaybeC5 1
#define C5 2
#define NoC5 3
#define MaybeC4 4
#define C4 5
#define NoC4 6
#define MaybeC3 7
#define C3 8
#define NoC3 9
#define MaybeC2 10
#define C2 11
#define NoC2 12
#define MaybeC1 13
#define C1 14
#define NoC1 15
#define MaybeC6 16
#define C6 17
#define NoC6 18
//NOTE that v1 to
v8 and i must be in registers!
register char
v1 @4;
register char
v2 @5;
register char
v3 @6;
register char
v4 @7;
register char
v5 @8;
register char
v6 @9;
register char
v7 @10;
register char
v8 @11;
register int
i @12;
#pragma regalloc+
// statevariables
char statevar;
char dstate; //debouncing
char screen[1600];
char syncON, syncOFF;
int LineCount;
char wind; // wind power
char difficulty; // dificulty level
char play1w,play2w;
char test;
int count; // random number gen
char p1x,p2x; // horizontal
position
char p1pow,p2pow; //power of
shot
unsigned char
p1ang,p2ang; // angle of shot
char p1weap, p2weap; // weapon choice
char p1xy[2],p2xy[2]; // end turret start bullet
char temp[20]; // sprintf var
int time; // for variable
loop var
int c1,c2; // erase and map vars
char numshots;
char randmap;
char p1w2avail; // number of
shots available for each weapon
char p1w3avail;
char p2w2avail;
char p2w3avail;
int timer; // for inc/dec ang and pow
float xpow,ypow; // x and y components
of power
float xpos,ypos; // current pos of
bullet
float prevx,prevy; // previous point
of bullet
char note, musicT; // music vars
//Musical note
values
//C below middle
C to C above middle C
//zeros are rests
flash char notes[] = {239,213,189,179,159,142,126,
120,106,94,90,80,71,63,60,0,0,0,0};
//Point plot
lookup table
//One bit masks
flash char pos[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
flash char map[5][128] = {
//map1
65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65,
66, 66, 66, 66, 67, 67, 67,
67, 68, 68, 68, 68, 69, 69, 69, 69,
70, 70, 70, 70, 70, 70, 70,
70, 68, 66, 64, 62, 60, 58, 56, 54,
52, 52, 52, 51, 51, 50, 51,
51, 52, 52, 52, 54, 56, 58, 60, 62,
64, 64, 66, 68, 70, 70, 70,
70, 70, 70, 70, 70, 71, 71, 71, 71,
72, 72, 72, 72, 73, 73, 73,
73, 74, 74, 74, 74, 75, 75, 75, 75,
73, 71, 69, 68, 67, 66, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65,
//map2
50, 50, 50, 50, 50, 51, 51,
51, 52, 52, 52, 53, 53, 53, 55, 57,
59, 59, 59, 59, 59, 58, 58,
58, 57, 57, 58, 58, 58, 60, 62, 64,
65, 66, 66, 66, 66, 66, 66,
66, 67, 67, 67, 65, 63, 61, 59, 57,
55, 55, 55, 55, 55, 56, 57,
58, 59, 60, 61, 62, 63, 64, 65, 66,
67, 68, 69, 70, 70, 70, 70,
70, 70, 70, 70, 70, 72, 74, 76, 77,
78, 79, 80, 81, 83, 85, 87,
87, 87, 88, 88, 88, 88, 87, 85, 83,
81, 79, 78, 77, 76, 75, 74,
73, 71, 69, 67, 65, 63, 61, 61, 61,
62, 63, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64,
//map3
50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, 50,
//map4
45, 45, 45, 45, 45, 45, 45,
45, 46, 46, 46, 47, 47, 47, 48, 48,
49, 50, 51, 52, 53, 54, 54,
55, 55, 56, 57, 58, 58, 58, 58, 58,
59, 60, 60, 60, 60, 60, 60,
60, 60, 60, 60, 60, 60, 60, 60, 60,
61, 62, 62, 62, 62, 62, 63,
63, 63, 63, 63, 63, 63, 64, 65, 66,
67, 68, 69, 70, 71, 71, 71,
71, 71, 71, 71, 72, 73, 74, 74, 75,
75, 76, 77, 77, 77, 77, 77,
77, 78, 78, 78, 78, 79, 80, 81, 82,
82, 82, 82, 82, 82, 82, 82,
82, 82, 82, 82, 82, 82, 82, 83, 84,
85, 86, 87, 88, 88, 88, 88,
89, 89, 90, 90, 90, 90, 90, 90, 90,
//map5
90, 90, 90, 90, 90, 90, 90, 89,
89, 88, 88, 88, 88, 87, 86, 85,
84, 83, 82, 82, 82, 82, 82,
82, 82, 82, 82, 82, 82, 82, 82, 82,
82, 81, 80, 79, 78, 78, 78,
78, 77, 77, 77, 77, 77, 77, 76, 75,
75, 74, 74, 73, 72, 71, 71,
71, 71, 71, 71, 71, 70, 69, 68, 67,
66, 65, 64, 63, 63, 63, 63, 63,
63, 63, 62, 62, 62, 62, 62, 61,
60, 60, 60, 60, 60, 60, 60,
60, 60, 60, 60, 60, 60, 60, 60, 59,
58, 58, 58, 58, 58, 57, 56,
55, 55, 54, 54, 53, 52, 51, 50, 49,
48, 48, 47, 47, 46, 46, 46,
45, 45, 45, 45, 45, 45, 45, 45, 45
};
//define some
character bitmaps
//5x7 characters
flash char bitmap[38][7]={
//0
0b01110000,
0b10001000,
0b10011000,
0b10101000,
0b11001000,
0b10001000,
0b01110000,
//1
0b00100000,
0b01100000,
0b00100000,
0b00100000,
0b00100000,
0b00100000,
0b01110000,
//2
0b01110000,
0b10001000,
0b00001000,
0b00010000,
0b00100000,
0b01000000,
0b11111000,
//3
0b11111000,
0b00010000,
0b00100000,
0b00010000,
0b00001000,
0b10001000,
0b01110000,
//4
0b00010000,
0b00110000,
0b01010000,
0b10010000,
0b11111000,
0b00010000,
0b00010000,
//5
0b11111000,
0b10000000,
0b11110000,
0b00001000,
0b00001000,
0b10001000,
0b01110000,
//6
0b01000000,
0b10000000,
0b10000000,
0b11110000,
0b10001000,
0b10001000,
0b01110000,
//7
0b11111000,
0b00001000,
0b00010000,
0b00100000,
0b01000000,
0b10000000,
0b10000000,
//8
0b01110000,
0b10001000,
0b10001000,
0b01110000,
0b10001000,
0b10001000,
0b01110000,
//9
0b01110000,
0b10001000,
0b10001000,
0b01111000,
0b00001000,
0b00001000,
0b00010000,
//A
0b01110000,
0b10001000,
0b10001000,
0b10001000,
0b11111000,
0b10001000,
0b10001000,
//B
0b11110000,
0b10001000,
0b10001000,
0b11110000,
0b10001000,
0b10001000,
0b11110000,
//C
0b01110000,
0b10001000,
0b10000000,
0b10000000,
0b10000000,
0b10001000,
0b01110000,
//D
0b11110000,
0b10001000,
0b10001000,
0b10001000,
0b10001000,
0b10001000,
0b11110000,
//E
0b11111000,
0b10000000,
0b10000000,
0b11111000,
0b10000000,
0b10000000,
0b11111000,
//F
0b11111000,
0b10000000,
0b10000000,
0b11111000,
0b10000000,
0b10000000,
0b10000000,
//G
0b01110000,
0b10001000,
0b10000000,
0b10011000,
0b10001000,
0b10001000,
0b01110000,
//H
0b10001000,
0b10001000,
0b10001000,
0b11111000,
0b10001000,
0b10001000,
0b10001000,
//I
0b01110000,
0b00100000,
0b00100000,
0b00100000,
0b00100000,
0b00100000,
0b01110000,
//J
0b00111000,
0b00010000,
0b00010000,
0b00010000,
0b00010000,
0b10010000,
0b01100000,
//K
0b10001000,
0b10010000,
0b10100000,
0b11000000,
0b10100000,
0b10010000,
0b10001000,
//L
0b10000000,
0b10000000,
0b10000000,
0b10000000,
0b10000000,
0b10000000,
0b11111000,
//M
0b10001000,
0b11011000,
0b10101000,
0b10101000,
0b10001000,
0b10001000,
0b10001000,
//N
0b10001000,
0b10001000,
0b11001000,
0b10101000,
0b10011000,
0b10001000,
0b10001000,
//O
0b01110000,
0b10001000,
0b10001000,
0b10001000,
0b10001000,
0b10001000,
0b01110000,
//P
0b11110000,
0b10001000,
0b10001000,
0b11110000,
0b10000000,
0b10000000,
0b10000000,
//Q
0b01110000,
0b10001000,
0b10001000,
0b10001000,
0b10101000,
0b10010000,
0b01101000,
//R
0b11110000,
0b10001000,
0b10001000,
0b11110000,
0b10100000,
0b10010000,
0b10001000,
//S
0b01111000,
0b10000000,
0b10000000,
0b01110000,
0b00001000,
0b00001000,
0b11110000,
//T
0b11111000,
0b00100000,
0b00100000,
0b00100000,
0b00100000,
0b00100000,
0b00100000,
//U
0b10001000,
0b10001000,
0b10001000,
0b10001000,
0b10001000,
0b10001000,
0b01110000,
//V
0b10001000,
0b10001000,
0b10001000,
0b10001000,
0b10001000,
0b01010000,
0b00100000,
//W
0b10001000,
0b10001000,
0b10001000,
0b10101000,
0b10101000,
0b10101000,
0b01010000,
//X
0b10001000,
0b10001000,
0b01010000,
0b00100000,
0b01010000,
0b10001000,
0b10001000,
//Y
0b10001000,
0b10001000,
0b10001000,
0b01010000,
0b00100000,
0b00100000,
0b00100000,
//Z
0b11111000,
0b00001000,
0b00010000,
0b00100000,
0b01000000,
0b10000000,
0b11111000,
//figure1 (blank)
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
/* //figure1
0b01110000,
0b00100000,
0b01110000,
0b10101000,
0b00100000,
0b01010000,
0b10001000, */
//figure2
0b01110000,
0b10101000,
0b01110000,
0b00100000,
0b00100000,
0b01010000,
0b10001000};
//================================
//3x5 font
numbers, then letters
//packed two per
definition for fast
//copy to the
screen at x-position divisible by 4
flash char smallbitmap[39][5]={
//0
0b11101110,
0b10101010,
0b10101010,
0b10101010,
0b11101110,
//1
0b01000100,
0b11001100,
0b01000100,
0b01000100,
0b11101110,
//2
0b11101110,
0b00100010,
0b11101110,
0b10001000,
0b11101110,
//3
0b11101110,
0b00100010,
0b11101110,
0b00100010,
0b11101110,
//4
0b10101010,
0b10101010,
0b11101110,
0b00100010,
0b00100010,
//5
0b11101110,
0b10001000,
0b11101110,
0b00100010,
0b11101110,
//6
0b11001100,
0b10001000,
0b11101110,
0b10101010,
0b11101110,
//7
0b11101110,
0b00100010,
0b01000100,
0b10001000,
0b10001000,
//8
0b11101110,
0b10101010,
0b11101110,
0b10101010,
0b11101110,
//9
0b11101110,
0b10101010,
0b11101110,
0b00100010,
0b01100110,
//:
0b00000000,
0b01000100,
0b00000000,
0b01000100,
0b00000000,
//=
0b00000000,
0b11101110,
0b00000000,
0b11101110,
0b00000000,
//blank
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
//A
0b11101110,
0b10101010,
0b11101110,
0b10101010,
0b10101010,
//B
0b11001100,
0b10101010,
0b11101110,
0b10101010,
0b11001100,
//C
0b11101110,
0b10001000,
0b10001000,
0b10001000,
0b11101110,
//D
0b11001100,
0b10101010,
0b10101010,
0b10101010,
0b11001100,
//E
0b11101110,
0b10001000,
0b11101110,
0b10001000,
0b11101110,
//F
0b11101110,
0b10001000,
0b11101110,
0b10001000,
0b10001000,
//G
0b11101110,
0b10001000,
0b10001000,
0b10101010,
0b11101110,
//H
0b10101010,
0b10101010,
0b11101110,
0b10101010,
0b10101010,
//I
0b11101110,
0b01000100,
0b01000100,
0b01000100,
0b11101110,
//J
0b00100010,
0b00100010,
0b00100010,
0b10101010,
0b11101110,
//K
0b10001000,
0b10101010,
0b11001100,
0b11001100,
0b10101010,
//L
0b10001000,
0b10001000,
0b10001000,
0b10001000,
0b11101110,
//M
0b10101010,
0b11101110,
0b11101110,
0b10101010,
0b10101010,
//N
0b00000000,
0b11001100,
0b10101010,
0b10101010,
0b10101010,
//O
0b01000100,
0b10101010,
0b10101010,
0b10101010,
0b01000100,
//P
0b11101110,
0b10101010,
0b11101110,
0b10001000,
0b10001000,
//Q
0b01000100,
0b10101010,
0b10101010,
0b11101110,
0b01100110,
//R
0b11101110,
0b10101010,
0b11001100,
0b11101110,
0b10101010,
//S
0b11101110,
0b10001000,
0b11101110,
0b00100010,
0b11101110,
//T
0b11101110,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
//U
0b10101010,
0b10101010,
0b10101010,
0b10101010,
0b11101110,
//V
0b10101010,
0b10101010,
0b10101010,
0b10101010,
0b01000100,
//W
0b10101010,
0b10101010,
0b11101110,
0b11101110,
0b10101010,
//X
0b00000000,
0b10101010,
0b01000100,
0b01000100,
0b10101010,
//Y
0b10101010,
0b10101010,
0b01000100,
0b01000100,
0b01000100,
//Z
0b11101110,
0b00100010,
0b01000100,
0b10001000,
0b11101110
};
//==================================
//This is the
sync generator and raster generator. It MUST be entered from
//sleep mode to
get accurate timing of the sync pulses
#pragma warn-
interrupt [TIM1_COMPA] void t1_cmpA(void)
{
//start the
Horizontal sync pulse
PORTD = syncON;
//update the curent
scanline number
LineCount ++ ;
//begin inverted
(Vertical) synch after line 247
if
(LineCount==248)
{
syncON = 0b00100000;
syncOFF = 0;
}
//back to regular
sync after line 250
if
(LineCount==251)
{
syncON = 0;
syncOFF = 0b00100000;
}
//start new frame
after line 262
if
(LineCount==263)
{
LineCount = 1;
}
delay_us(2); //adjust
to make 5 us pulses
//end sync pulse
PORTD = syncOFF;
if
(LineCount<ScreenBot && LineCount>=ScreenTop)
{
//compute byte
index for beginning of the next line
//left-shift 4
would be individual lines
// <<3
means line-double the pixels
//The 0xfff8
truncates the odd line bit
//i=(LineCount-ScreenTop)<<3
& 0xfff8; //
#asm
push r16
lds
r12, _LineCount
lds
r13, _Linecount+1
ldi
r16, 30
sub
r12, r16
ldi
r16,0
sbc
r13, r16
lsl
r12
rol
r13
lsl
r12
rol
r13
lsl
r12
rol
r13
mov
r16,r12
andi r16,0xf0
mov
r12,r16
pop r16
#endasm
//load 16
registers with screen info
#asm
push r14
push r15
push r16
push r17
push r18
push r19
push r26
push r27
ldi
r26,low(_screen) ;base address
of screen
ldi
r27,high(_screen)
add
r26,r12 ;offset into
screen (add i)
adc
r27,r13
ld
r4,x+ ;load 16 registers and
inc pointer
ld
r5,x+
ld
r6,x+
ld
r7,x+
ld
r8,x+
ld
r9,x+
ld
r10,x+
ld
r11,x+
ld
r12,x+
ld
r13,x+
ld
r14,x+
ld
r15,x+
ld
r16,x+
ld
r17,x+
ld
r18,x+
ld
r19,x
pop
r27
pop
r26
#endasm
delay_us(4); //adjust to center
image on screen
//blast 16
bytes to the screen
#asm
;but first a macro to make the code
shorter
;the macro takes a register number as a parameter
;and dumps its bits serially to portD.6
;the nop can be eliminated to make the
display narrower
.macro videobits ;regnum
BST
@0,7
IN R30,0x12
BLD R30,6
nop
OUT 0x12,R30
BST @0,6
IN R30,0x12
BLD R30,6
nop
OUT 0x12,R30
BST @0,5
IN R30,0x12
BLD R30,6
nop
OUT 0x12,R30
BST @0,4
IN R30,0x12
BLD R30,6
nop
OUT 0x12,R30
BST @0,3
IN R30,0x12
BLD R30,6
nop
OUT 0x12,R30
BST @0,2
IN R30,0x12
BLD R30,6
nop
OUT 0x12,R30
BST @0,1
IN R30,0x12
BLD R30,6
nop
OUT 0x12,R30
BST @0,0
IN R30,0x12
BLD R30,6
nop
OUT 0x12,R30
.endm
videobits r4 ;video line -- byte 1
videobits r5 ;byte 2
videobits r6 ;byte 3
videobits r7 ;byte 4
videobits r8 ;byte 5
videobits r9 ;byte 6
videobits r10 ;byte 7
videobits r11 ;byte 8
videobits r12 ;byte 9
videobits r13 ;byte 10
videobits r14 ;byte 11
videobits r15 ;byte 12
videobits r16 ;byte 13
videobits r17 ;byte 14
videobits r18 ;byte 15
videobits r19 ;byte 16
clt ;clear video after
the last pixel on the line
IN R30,0x12
BLD R30,6
OUT 0x12,R30
pop r19
pop r18
pop r17
pop r16
pop r15
pop r14
#endasm
}
if (timer
> 0) timer--;
}
#pragma warn+
//==================================
//plot one point
//at x,y with
color 1=white 0=black 2=invert
#pragma warn-
void video_pt(char x, char y, char c)
{
#asm
; i=(x>>3) + ((int)y<<4) ;
the byte with the pixel in it
push r16
ldd r30,y+2 ;get
x
lsr r30
lsr r30
lsr r30 ;divide x by 8
ldd r12,y+1 ;get
y
lsl
r12 ;mult
y by 16
clr
r13
lsl r12
rol r13
lsl r12
rol r13
lsl r12
rol r13
add r12, r30 ;add in x/8
;v2 = screen[i]; r5
;v3 = pos[x & 7]; r6
;v4 = c r7
ldi r30,low(_screen)
ldi r31,high(_screen)
add r30, r12
adc r31, r13
ld r5,Z ;get screen byte
ldd r26,y+2 ;get
x
ldi r27,0
andi r26,0x07
;form x & 7
ldi r30,low(_pos*2)
ldi r31,high(_pos*2)
add r30,r26
adc r31,r27
lpm r6,Z
ld r16,y ;get
c
;if
(v4==1) screen[i] = v2 | v3 ;
;if
(v4==0) screen[i] = v2 & ~v3;
;if
(v4==2) screen[i] = v2 ^ v3 ;
cpi r16,1
brne tst0
or
r5,r6
tst0:
cpi r16,0
brne tst2
com r6
and r5,r6
tst2:
cpi r16,2
brne writescrn
eor r5,r6
writescrn:
ldi
r30,low(_screen)
ldi r31,high(_screen)
add r30, r12
adc r31, r13
st Z, r5 ;write the byte back to the screen
pop r16
#endasm
}
#pragma warn+
//==================================
// put a big
character on the screen
// c is index
into bitmap
void video_putchar(char x, char y, char c)
{
v7 = x;
for
(v6=0;v6<7;v6++)
{
v1 = bitmap[c][v6];
v8 = y+v6;
video_pt(v7, v8, (v1 & 0x80)==0x80);
video_pt(v7+1, v8, (v1 &
0x40)==0x40);
video_pt(v7+2, v8, (v1 &
0x20)==0x20);
video_pt(v7+3, v8, (v1 &
0x10)==0x10);
video_pt(v7+4, v8, (v1 &
0x08)==0x08);
}
}
//==================================
// put a string
of big characters on the screen
void video_puts(char x, char y, char *str)
{
char i ;
for (i=0; str[i]!=0; i++)
{
if (str[i]>=0x30
&& str[i]<=0x3a)
video_putchar(x,y,str[i]-0x30);
else if (str[i] == ' ')
video_putchar(x,y,36);
else
video_putchar(x,y,str[i]-0x40+9);
x = x+6;
}
}
//==================================
// put a small
character on the screen
// x-cood must be
on divisible by 4
// c is index
into bitmap
void video_smallchar(char x, char y, char c)
{
char mask;
i=((int)x>>3) + ((int)y<<4) ;
if (x == (x & 0xf8)) mask
= 0x0f; //f8
else mask = 0xf0;
screen[i] =
(screen[i] & mask) | (smallbitmap[c][0] & ~mask);
screen[i+16]
= (screen[i+16] & mask) | (smallbitmap[c][1] & ~mask);
screen[i+32] = (screen[i+32] &
mask) | (smallbitmap[c][2] & ~mask);
screen[i+48] = (screen[i+48] &
mask) | (smallbitmap[c][3] & ~mask);
screen[i+64]
= (screen[i+64] & mask) | (smallbitmap[c][4] & ~mask);
}
//==================================
// put a string
of small characters on the screen
// x-cood must be
on divisible by 4
void video_putsmalls(char x, char y, char *str)
{
char i ;
for (i=0; str[i]!=0; i++)
{
if (str[i]>=0x30
&& str[i]<=0x3a)
video_smallchar(x,y,str[i]-0x30);
else if (str[i] == ' ')
video_smallchar(x,y,12);
else
video_smallchar(x,y,str[i]-0x40+12);
x = x+4;
}
}
//==================================
//plot a line
//at x1,y1 to
x2,y2 with color 1=white 0=black 2=invert
//NOTE: this function
requires signed chars
//Code is from
David Rodgers,
//"Procedural
Elements of Computer Graphics",1985
void video_line(char x1, char y1, char x2, char y2, char c)
{
int e;
signed char dx,dy,j, temp;
signed char s1,s2, xchange;
signed char x,y;
x = x1;
y = y1;
dx = cabs(x2-x1);
dy = cabs(y2-y1);
s1 = csign(x2-x1);
s2 = csign(y2-y1);
xchange = 0;
if (dy>dx)
{
temp = dx;
dx = dy;
dy = temp;
xchange = 1;
}
e = ((int)dy<<1) - dx;
for (j=0; j<=dx; j++)
{
video_pt(x,y,c) ;
if (e>=0)
{
if (xchange==1)
x = x + s1;
else y = y + s2;
e = e - ((int)dx<<1);
}
if (xchange==1) y = y +
s2;
else x = x + s1;
e = e + ((int)dy<<1);
}
}
//==================================
//return the
value of one point
//at x,y with
color 1=white 0=black 2=invert
char video_set(char x, char y)
{
//The following construction
//detects exactly one bit at the x,y location
i=((int)x>>3) + ((int)y<<4) ;
return (
screen[i] & 1<<(7-(x & 0x7)));
}
void tankdraw(char x, char y) // function to
draw tank
{
// draw tank centered around x,y
video_line(x-4,y+1,x+4,y+1,0);
video_line(x-3,y,x+3,y,1);
video_pt(x-4,y,0);
video_pt(x+4,y,0);
y--;
video_line(x-2,y,x+2,y,1);
video_pt(x-3,y,0);
video_pt(x+3,y,0);
y--;
video_line(x-1,y,x+1,y,1);
video_pt(x-2,y,0);
video_pt(x+2,y,0);
video_pt(x,y-1,1);
}
void turretdraw(char pos, unsigned char ang, char
y) // function
to draw/erase turret
{
if (pos == 1)
{
if (ang <= 7){ p1xy[0] =
p1x+3; p1xy[1] = map[randmap][p1x]-3; }
else if
(ang <= 22) { p1xy[0] = p1x+3;
p1xy[1] = map[randmap][p1x]-4; }
else if
(ang <= 37) { p1xy[0] = p1x+3;
p1xy[1] = map[randmap][p1x]-5; }
else if
(ang <= 52) { p1xy[0] = p1x+3;
p1xy[1] = map[randmap][p1x]-6; }
else if
(ang <= 67) { p1xy[0] = p1x+2;
p1xy[1] = map[randmap][p1x]-6; }
else if
(ang <= 82) { p1xy[0] = p1x+1;
p1xy[1] = map[randmap][p1x]-6; }
else if
(ang <= 97) { p1xy[0] = p1x; p1xy[1] = map[randmap][p1x]-6; }
else if
(ang <= 112) { p1xy[0] = p1x-1; p1xy[1] = map[randmap][p1x] - 6;}
else if
(ang <= 127) { p1xy[0] = p1x-2; p1xy[1] = map[randmap][p1x] - 6;}
else if
(ang <= 142) { p1xy[0] = p1x-3; p1xy[1] = map[randmap][p1x] - 6;}
else if
(ang <= 157){ p1xy[0] = p1x-3; p1xy[1] = map[randmap][p1x] - 5;}
else if
(ang <= 172){ p1xy[0] = p1x-3; p1xy[1] = map[randmap][p1x] - 4;}
else { p1xy[0] =
p1x-3; p1xy[1] = map[randmap][p1x] - 3; }
video_line(p1x, map[randmap][p1x] -
3, p1xy[0],p1xy[1],y);
}
else if
(pos == 2)
{
if (ang <= 7) { p2xy[0]
= p2x-3; p2xy[1] = map[randmap][p2x] -
3; }
else if
(ang <= 22) { p2xy[0] = p2x-3;
p2xy[1] = map[randmap][p2x] - 4;}
else if
(ang <= 37) { p2xy[0] = p2x-3;
p2xy[1] = map[randmap][p2x] - 5;}
else if
(ang <= 52) { p2xy[0] = p2x-3;
p2xy[1] = map[randmap][p2x] - 6;}
else if
(ang <= 67) { p2xy[0] = p2x-2;
p2xy[1] = map[randmap][p2x] - 6;}
else if
(ang <= 82) { p2xy[0] = p2x-1;
p2xy[1] = map[randmap][p2x] - 6;}
else if
(ang <= 97) { p2xy[0] = p2x; p2xy[1]
= map[randmap][p2x] - 6;}
else if
(ang <= 112) { p2xy[0] = p2x+1; p2xy[1] = map[randmap][p2x]-6; }
else if
(ang <= 127) { p2xy[0] = p2x+2; p2xy[1] = map[randmap][p2x]-6; }
else if
(ang <= 142) { p2xy[0] = p2x+3; p2xy[1] = map[randmap][p2x]-6; }
else if
(ang <= 157) { p2xy[0] = p2x+3; p2xy[1] = map[randmap][p2x]-5; }
else if
(ang <= 172) { p2xy[0] = p2x+3; p2xy[1] = map[randmap][p2x]-4; }
else { p2xy[0] = p2x+3; p2xy[1] =
map[randmap][p2x]-3; }
video_line (p2x,map[randmap][p2x] - 3, p2xy[0],p2xy[1],y);
}
}
//==================================
// set up the
ports and timers
void main(void)
{
//init timer 1 to
generate sync
OCR1A = lineTime; //One NTSC line
TCCR1B = 9; //full speed; clear-on-match
TCCR1A = 0x00; //turn off pwm and oc lines
TIMSK = 0x10; //enable interrupt T1 cmp
TCCR0 = 0b00000011;
//init hyperterm
UCSRB = 0x18;
UBRRL = 103;
//init pushbuttons
DDRC = 0x00;
PORTC = 0xff;
//init ports
DDRD = 0xf0; //video out and switches
//d.5 is sync:1000
ohm + diode to 75 ohm resistor
//d.6 is video:330
ohm + diode to 75 ohm resistor
//initialize synch
constants
LineCount = 1;
syncON = 0b00000000;
syncOFF = 0b00100000;
#define width
126
play1w = 0;
play2w = 0;
video_line(0,0,0,99,1);
video_line(width,0,width,99,1);
video_line(0,0,width,0,1);
video_line(0,99,width,99,1);
//init musical
scale
note = 0;
musicT = 0;
//use OC0 (pin B.3)
for music
DDRB.3 = 1 ;
//Start up screen
sprintf (temp,"SCORCHED EARTH");
video_puts(20,20,temp);
sprintf(temp, "DIFFICULTY:");
video_putsmalls(28,40,temp);
for ( time =
1; time < 126; time++)
{
video_line
(time,map[1][time], time,99,1);
}
randmap = 1;
p1x = 40;
p2x = 120;
tankdraw(40, map[1][40]);
turretdraw(1, 45, 1);
tankdraw(120, map[1][120]);
turretdraw(2, 45, 1);
// init vars
statevar = Startup;
dstate = Nopush;
c1=1;
c2=1;
p1pow = 50;
p2pow = 50;
p1ang = 45;
p2ang = 45;
p1weap = 0;
p2weap = 0;
wind = 0;
difficulty = 0;
numshots = 0;
p1w2avail = 3;
p1w3avail = 1;
p2w2avail = 3;
p2w3avail = 1;
timer = 0;
//enable sleep mode
MCUCR = 0b10000000;
#asm ("sei");
//The following
loop executes once/video line during lines
//1-230, then does
all of the frame-end processing
while(1)
{
//stall here until next line starts
//sleep enable;
mode=idle
//use sleep to
make entry into sync ISR uniform time
#asm ("sleep");
ADCSR.6 = 1;
//The following
code executes during the vertical blanking
//Code here can
be as long as
//a total of 60
lines x 63.5 uSec/line x 8 cycles/uSec
if
(LineCount==231)
{
switch
(statevar)
{
case
Startup: //
startup screen, allows user to change difficulty
switch (dstate) // state machine to debounce select button
{
case Nopush:
if
(~PINC == 0x01) dstate = MaybeC6;
else
dstate = Nopush;
break;
case MaybeC6:
if
(~PINC == 0x01)
{
dstate = C6;
difficulty++;
if
(difficulty > 2) difficulty = 0;
//difficulty
= difficulty%3;
}
else
dstate = Nopush;
break;
case C6:
if
(~PINC == 0x01) dstate = C6;
else
dstate = NoC6;
break;
case NoC6:
if
(~PINC == 0x01) dstate = C6;
else
dstate = Nopush;
break;
}
if
(difficulty == 0) // display the current difficulty level
{
sprintf(temp, "EASY");
video_putsmalls(76,40,temp);
}
else if (difficulty == 1)
{
sprintf(temp, "MED ");
video_putsmalls(76,40,temp);
}
else if (difficulty == 2)
{
sprintf(temp, "HARD");
video_putsmalls(76,40,temp);
}
if (
~PINC == 0x80) // on fire go to state clear
{
statevar
= Clear;
c2
= 1;
}
break;
case
Drawmap: //
draw map
if
(c1 < 126)
{
for ( time = c1; time < 3+c1; time++)
{
video_line
(time,map[randmap][time], time,99,1);
}
c1
= time;
}
else
{
statevar=
Drawtank; //
draw tanks once you finish drawing the map
}
break;
case
Clear: //
clear the screen
if (c2 < 126)
{
for (time = c2; time < 3 + c2; time++)
{
video_line
(time,11,time,98,0);
}
c2
= time;
}
else
{
video_line (126,1,126,98,1);
statevar = Drawmap;
// random number
generation
srand(TCNT0);
//random map
count = (rand()/(32767/999));
randmap = count/200;
c1=1;
}
break;
case Drawtank: // draw the tanks
p1x = 7 +(rand()/(32767/50));
p2x = 71 + (rand()/(32767/50));
tankdraw(p1x,map[randmap][p1x]) ;
tankdraw(p2x,map[randmap][p2x]);
statevar = Top;
break;
case Top: // draw the top
of the screen, nonvariable text
video_line(0,10,width,10,1);
sprintf(temp,
"ANG:",);
video_putsmalls(44,4,temp);
sprintf(temp,"POW:");
video_putsmalls(16,4,temp);
sprintf(temp,"WEA:");
video_putsmalls(76,4,temp);
turretdraw(1,p1ang,1);
turretdraw(2,p2ang,1);
if (rand()/(32767/100) > 50) // randomly choose which player goes first
statevar
= P1T;
else
statevar
= P2T;
if (difficulty == 1)
{
wind
= rand()/(32767/18);
wind
-= 9;
}
video_line(13,0,13,10,1);
video_line(41,0,41,10,1);
video_line(73,0,73,10,1);
video_line(105,0,105,10,1);
video_line(115,0,115,2,1);
video_line(115,4,115,6,1);
video_line(115,8,115,10,1);
break;
case P1T: // player 1 turn
sprintf
(temp,"P1"); // display the values of p1's variables
video_putsmalls(4,4,temp);
sprintf
(temp, "%02d", p1pow);
video_putsmalls(32,4,temp);
sprintf(temp,"%03d",p1ang);
video_putsmalls(60,4,temp);
sprintf(temp,"%01d",p1weap+1);
video_putsmalls(92,4,temp);
// weapons cache
drawing
if (p1weap+1 == 1)
{
video_pt(97,5,1);
video_line(98,4,99,4,1);
video_line(98,6,99,6,1);
video_pt(100,5,1);
video_line(101,4,102,4,1);
video_line(101,6,102,6,1);
video_pt(103,5,1);
}
else if (p1weap+1 ==
2 && p1w2avail == 3)
{
video_pt(97,5,1);
video_pt(100,5,1);
video_pt(103,5,1);
video_line(98,4,102,4,0);
video_line(98,6,102,6,0);
}
else if (p1weap+1 ==
2 && p1w2avail == 2)
{
video_pt(97,5,0);
video_pt(100,5,1);
video_pt(103,5,1);
video_line(98,4,102,4,0);
video_line(98,6,102,6,0);
}
else if (p1weap+1 ==
2 && p1w2avail == 1)
{
video_pt(97,5,0);
video_pt(100,5,0);
video_pt(103,5,1);
video_line(98,4,102,4,0);
video_line(98,6,102,6,0);
}
else if (p1weap+1 == 2 && p1w2avail == 0)
{
video_pt(97,5,0);
video_pt(100,5,0);
video_pt(103,5,0);
video_line(98,4,102,4,0);
video_line(98,6,102,6,0);
}
else if (p1weap+1 ==
3 && p1w3avail == 1)
{
video_pt(97,5,0);
video_pt(100,5,0);
video_pt(103,5,1);
video_line(98,4,102,4,0);
video_line(98,6,102,6,0);
}
else if (p1weap+1 ==
3 && p1w3avail == 0)
{
video_pt(97,5,0);
video_pt(100,5,0);
video_pt(103,5,0);
video_line(98,4,102,4,0);
video_line(98,6,102,6,0);
}
video_line(106,5,125,5,0);
video_line(115,5,115+wind,5,1); // draw the
wind force and direction
// statemachine to
debounce buttons for inc/dec of pow, angle and weapon choice
switch (dstate)
{
case Nopush:
if
(~PINC == 0x20) dstate = MaybeC5;
else
if (~PINC == 0x10) dstate = MaybeC4;
else
if (~PINC == 0x08) dstate = MaybeC3;
else
if (~PINC == 0x04) dstate = MaybeC2;
else
if (~PINC == 0x02) dstate = MaybeC1;
else dstate = Nopush;
break;
case MaybeC5:
if
(~PINC == 0x20)
{
timer = 3929;
dstate = C5;
}
else
dstate = Nopush;
break;
case C5:
if
(~PINC == 0x20)
{
if
(timer == 0)
{
timer = 3929;
turretdraw(1,p1ang,0);
if (p1ang > 0) p1ang--;
turretdraw(1,p1ang,1);
}
dstate = C5;
}
else
dstate = NoC5;
break;
case NoC5:
if
(~PINC == 0x20) dstate = C5;
else
dstate = Nopush;
break;
case MaybeC4:
if
(~PINC == 0x10)
{
timer = 3929;
dstate = C4;
}
else
dstate = Nopush;
break;
case C4:
if
(~PINC == 0x10)
{
if
(timer == 0)
{
timer = 3929;
turretdraw(1,p1ang,0);
if (p1ang <
180) p1ang++;
turretdraw(1,p1ang,1);
}
dstate = C4;
}
else
dstate = NoC4;
break;
case NoC4:
if
(~PINC == 0x10) dstate = C4;
else
dstate = Nopush;
break;
case MaybeC3:
if
(~PINC == 0x08)
{
dstate = C3;
timer = 3929;
}
else
dstate = Nopush;
break;
case C3:
if
(~PINC == 0x08)
{
if
(timer == 0)
{
timer = 3929;
if (p1pow > 0) p1pow--;
}
dstate = C3;
}
else
dstate = NoC3;
break;
case NoC3:
if
(~PINC == 0x08) dstate = C3;
else
dstate = Nopush;
break;
case MaybeC2:
if
(~PINC == 0x04)
{
dstate = C2;
timer = 3929;
}
else
dstate = Nopush;
break;
case C2:
if
(~PINC == 0x04)
{
if
(timer == 0)
{
if (p1pow
< 99) p1pow++;
timer = 3929;
}
dstate = C2;
}
else
dstate = NoC2;
break;
case NoC2:
if
(~PINC == 0x04) dstate = C2;
else
dstate = Nopush;
break;
case MaybeC1:
if
(~PINC == 0x02)
{
dstate = C1;
p1weap++;
p1weap = p1weap%3;
}
else
dstate = Nopush;
break;
case C1:
if
(~PINC == 0x02) dstate = C1;
else
dstate = NoC1;
break;
case NoC1:
if
(~PINC == 0x02) dstate = C1;
else
dstate = Nopush;
break;
}
if (~PINC == 0x40)
// if fire button is pressed goto fire 1 state
{
if(
(p1weap == 0) || (p1weap == 1 && p1w2avail > 0) || ( p1weap == 2
&& p1w3avail > 0))
{
statevar = Fire1;
//
start position and power for the bullet
xpow = cos(p1ang*PI/180)*p1pow;
ypow = sin(p1ang*PI/180)*p1pow;
xpos = p1xy[0]+1;
ypos = p1xy[1]-1;
prevx = xpos;
prevy = ypos;
OCR0 = 60;
}
}
if (difficulty == 2)
// change wind every other shot if difficulty
is in hard mode
{
if (numshots ==
2)
{
numshots = 0;
wind = rand()/(32767/18);
wind
-= 9;
}
}
break;
case Fire1: //
trajectory calculation for player 1
if (xpos
>= 126 || xpos <= 1) // if bullet hits boundary end turn and goto P2's turn
{
statevar = P2T;
if
(numshots < 2) numshots++;
if (p1weap
== 1) p1w2avail--;
if (p1weap
== 2) p1w3avail--;
video_pt((char)prevx,(char)prevy,0);
TCCR0 = 0b00000100;
}
else if ((test > 0) && !(prevx == xpos
&& prevy == ypos)) // if the point is black, then you hit something so goto
erase state
{
statevar = Erase1;
TCCR0 = 0b00000100;
}
else
{
// change
the location and power of the bullet, play a note.
prevx
= xpos;
prevy
= ypos;
ypos
-= (float)ypow/100.0;
xpos += (float)xpow/100.0;
ypow = ypow - .25;
xpow += (float)wind/100.0;
if((musicT++)
> 15) //each
note 1/4 second
{
musicT
= 0;
//TCCR0 = 0;
TCCR0
= 0b00011100 ; //not
a rest
OCR0++
;
//test for
end of scale
}
}
if
(ypos <= 12 && prevy >12) video_pt((char)prevx,(char)prevy,0);
if
(ypos >= 12 && prevy >= 12)
{
test = video_set((char)xpos,(char)ypos); //check to see if
the next point is set or not(did u hit terrain)
video_pt((char)xpos,(char)ypos,1);
if((char)prevx == (char)xpos && (char)prevy
== (char)ypos)
{
test = 0;
}
else
{
video_pt((char)prevx,(char)prevy,0);
//test =
video_set((char)prevx,(char)prevy);
}
}
else test = 0;
break;
case Erase1: // erase terrain
depending on which weapon was chosen
if (numshots < 2) numshots++;
if (p1weap == 0)
{
video_line((char)xpos-1,(char)ypos,(char)xpos+1,(char)ypos,0);
video_pt((char)xpos,(char)ypos+1,0);
video_pt((char)xpos,(char)ypos-1,0);
}
else if (p1weap == 1)
{
p1w2avail--;
video_line((char)xpos-2,(char)ypos,(char)xpos+2,(char)ypos,0);
video_line((char)xpos-1,(char)ypos+1,(char)xpos+1,(char)ypos+1,0);
video_line((char)xpos-1,(char)ypos-1,(char)xpos+1,(char)ypos-1,0);
video_pt((char)xpos,(char)ypos+2,0);
video_pt((char)xpos,(char)ypos-2,0);
}
else if (p1weap == 2)
{
p1w3avail--;
video_line((char)xpos-3,(char)ypos,(char)xpos+3,(char)ypos,0);
video_line((char)xpos-2,(char)ypos-1,(char)xpos+2,(char)ypos-1,0);
video_line((char)xpos-2,(char)ypos+1,(char)xpos+2,(char)ypos+1,0);
video_line((char)xpos-1,(char)ypos+2,(char)xpos+1,(char)ypos+2,0);
video_line((char)xpos-1,(char)ypos-2,(char)xpos+1,(char)ypos-2,0);
video_pt((char)xpos,(char)ypos+3,0);
video_pt((char)xpos,(char)ypos-3,0);
}
// check to see if any
of the tanks were hit and if they were go to the corresponding state
if ((char)xpos >= (p2x - 3 - p1weap) && (char)xpos <= (p2x + 3 + p1weap) && (char)ypos <= (map[randmap][p2x]+1+p2weap))
{
statevar = HIT2;
OCR0=195;
}
else if ((char)xpos >= (p1x - 3 - p2weap) && (char)xpos <= (p1x + 3 + p2weap) && (char)ypos <= (map[randmap][p1x]+1+p1weap))
{
statevar = HIT1;
OCR0 = 195;
}
else
{
statevar = P2T;
}
break;
case P2T: // player 2's
turn
// display p2's
variables at the top
sprintf (temp,"P2");
video_putsmalls(4,4,temp);
sprintf
(temp, "%02d", p2pow);
video_putsmalls(32,4,temp);
sprintf(temp,"%03d",p2ang);
video_putsmalls(60,4,temp);
sprintf(temp,"%01d",p2weap+1);
video_putsmalls(92,4,temp);
// draw weapon cache
if (p2weap+1 == 1)
{
video_pt(97,5,1);
video_line(98,4,99,4,1);
video_line(98,6,99,6,1);
video_pt(100,5,1);
video_line(101,4,102,4,1);
video_line(101,6,102,6,1);
video_pt(103,5,1);
}
else if (p2weap+1 ==
2 && p2w2avail == 3)
{
video_pt(97,5,1);
video_pt(100,5,1);
video_pt(103,5,1);
video_line(98,4,102,4,0);
video_line(98,6,102,6,0);
}
else if (p2weap+1 ==
2 && p2w2avail == 2)
{
video_pt(97,5,0);
video_pt(100,5,1);
video_pt(103,5,1);
video_line(98,4,102,4,0);
video_line(98,6,102,6,0);
}
else if (p2weap+1 ==
2 && p2w2avail == 1)
{
video_pt(97,5,0);
video_pt(100,5,0);
video_pt(103,5,1);
video_line(98,4,102,4,0);
video_line(98,6,102,6,0);
}
else if (p2weap+1 == 2 && p2w2avail == 0)
{
video_pt(97,5,0);
video_pt(100,5,0);
video_pt(103,5,0);
video_line(98,4,102,4,0);
video_line(98,6,102,6,0);
}
else if (p2weap+1 ==
3 && p2w3avail == 1)
{
video_pt(97,5,0);
video_pt(100,5,0);
video_pt(103,5,1);
video_line(98,4,102,4,0);
video_line(98,6,102,6,0);
}
else if (p2weap+1 ==
3 && p2w3avail == 0)
{
video_pt(97,5,0);
video_pt(100,5,0);
video_pt(103,5,0);
video_line(98,4,102,4,0);
video_line(98,6,102,6,0);
}
video_line(106,5,125,5,0);
video_line(115,5,115+wind,5,1); // draw wind power and direction
// state machine to
debounce buttons for inc/dec pow,angle and weapon choice
switch (dstate)
{
case Nopush:
if
(~PINC == 0x20) dstate = MaybeC5;
else
if (~PINC == 0x10) dstate = MaybeC4;
else
if (~PINC == 0x08) dstate = MaybeC3;
else
if (~PINC == 0x04) dstate = MaybeC2;
else
if (~PINC == 0x02) dstate = MaybeC1;
else
dstate = Nopush;
break;
case MaybeC5:
if
(~PINC == 0x20)
{
timer = 3929;
dstate = C5;
}
else dstate = Nopush;
break;
case C5:
if
(~PINC == 0x20)
{
if
(timer == 0)
{
turretdraw(2,p2ang,0);
timer = 3929;
if (p2ang > 0) p2ang--;
turretdraw(2,p2ang,1);
}
dstate = C5;
}
else
dstate = NoC5;
break;
case NoC5:
if
(~PINC == 0x20) dstate = C5;
else
dstate = Nopush;
break;
case MaybeC4:
if
(~PINC == 0x10)
{
timer = 3929;
dstate = C4;
}
else
dstate = Nopush;
break;
case C4:
if
(~PINC == 0x10)
{
if
(timer == 0)
{
turretdraw(2,p2ang,0);
timer
= 3929;
if (p2ang < 180) p2ang++;
turretdraw(2,p2ang,1);
}
dstate = C4;
}
else
dstate = NoC4;
break;
case NoC4:
if
(~PINC == 0x10) dstate = C4;
else
dstate = Nopush;
break;
case MaybeC3:
if
(~PINC == 0x08)
{
dstate = C3;
timer = 3929;
}
else
dstate = Nopush;
break;
case C3:
if
(~PINC == 0x08)
{
dstate = C3;
if (timer ==
0)
{
timer
= 3929;
if (p2pow > 0) p2pow--;
}
}
else
dstate = NoC3;
break;
case NoC3:
if
(~PINC == 0x08) dstate = C3;
else
dstate = Nopush;
break;
case MaybeC2:
if
(~PINC == 0x04)
{
dstate = C2;
timer = 3929;
}
else
dstate = Nopush;
break;
case C2:
if
(~PINC == 0x04)
{
if
(timer == 0)
{
timer = 3929;
if
(p2pow < 99) p2pow++;
}
dstate = C2;
}
else
dstate = NoC2;
break;
case NoC2:
if
(~PINC == 0x04) dstate = C2;
else
dstate = Nopush;
break;
case MaybeC1:
if
(~PINC == 0x02)
{
dstate = C1;
p2weap++;
p2weap = p2weap%3;
}
else
dstate = Nopush;
break;
case C1:
if
(~PINC == 0x02) dstate = C1;
else
dstate = NoC1;
break;
case NoC1:
if
(~PINC == 0x02) dstate = C1;
else
dstate = Nopush;
break;
}
if (~PINC == 0x40)
// goto fire 2 state if fire button is pressed
{
if(
(p2weap == 0) || (p2weap == 1 && p2w2avail > 0) || ( p2weap == 2
&& p2w3avail > 0))
{
statevar = Fire2;
//
start power and position for the bullet
xpow = cos(p2ang*PI/180)*p2pow;
ypow = sin(p2ang*PI/180)*p2pow;
xpos = p2xy[0]-1;
ypos = p2xy[1]-1;
prevx = xpos;
prevy = ypos;
OCR0 = 60;
}
}
if (difficulty == 2) // change wind every other shot if difficulty is set to
hard
{
if (numshots ==
2)
{
numshots = 0;
wind = rand()/(32767/18);
wind
-= 9;
}
}
break;
case Fire2: // calculate trajectory
of player 2's shot
if ((xpos >= 126) ||
(xpos <= 1)) //
end player 2's turn if boundary is hit
{
statevar = P1T;
if
(numshots < 2) numshots++;
if (p2weap
== 1) p2w2avail--;
if (p2weap == 2)
p2w3avail--;
video_pt((char)prevx,(char)prevy,0);
TCCR0= 0b00000100;
}
else if ((test > 0) && !(prevx == xpos
&& prevy == ypos)) // if terrain is hit goto erase2 to erase the terrain
{
statevar = Erase2;
TCCR0= 0b00000100;
}
else
{
prevx
= xpos;
prevy
= ypos;
// make changes
to power and location of bullet.
ypos
-= (float)ypow/100.0;
xpos -= (float)xpow/100.0;
ypow = ypow - .25;
xpow -= (float)wind/100.0;
if((musicT++)
> 15) //each
note 1/4 second
{
musicT
= 0;
//TCCR0 = 0;
TCCR0
= 0b00011100 ; //not
a rest
OCR0++
;
//test for
end of scale
}
}
if (ypos <= 12 && prevy >12) video_pt((char)prevx,(char)prevy,0);
if
(ypos >= 12 && prevy >= 12)
{
test = video_set((char)xpos,(char)ypos); // check to see if
the point is terrain or not
video_pt((char)xpos,(char)ypos,1);
if((char)prevx == (char)xpos && (char)prevy
== (char)ypos)
{
test = 0;
}
else
{
video_pt((char)prevx,(char)prevy,0);
}
}
break;
case Erase2: // erase terrain
depending on choice of weapon
if (numshots
< 2) numshots++;
if (p2weap == 0)
{
video_line((char)xpos-1,(char)ypos,(char)xpos+1,(char)ypos,0);
video_pt((char)xpos,(char)ypos+1,0);
video_pt((char)xpos,(char)ypos-1,0);
}
else if (p2weap == 1)
{
p2w2avail--;
video_line((char)xpos-2,(char)ypos,(char)xpos+2,(char)ypos,0);
video_line((char)xpos-1,(char)ypos+1,(char)xpos+1,(char)ypos+1,0);
video_line((char)xpos-1,(char)ypos-1,(char)xpos+1,(char)ypos-1,0);
video_pt((char)xpos,(char)ypos+2,0);
video_pt((char)xpos,(char)ypos-2,0);
}
else if (p2weap == 2)
{
p2w3avail--;
video_line((char)xpos-3,(char)ypos,(char)xpos+3,(char)ypos,0);
video_line((char)xpos-2,(char)ypos-1,(char)xpos+2,(char)ypos-1,0);
video_line((char)xpos-2,(char)ypos+1,(char)xpos+2,(char)ypos+1,0);
video_line((char)xpos-1,(char)ypos+2,(char)xpos+1,(char)ypos+2,0);
video_line((char)xpos-1,(char)ypos-2,(char)xpos+1,(char)ypos-2,0);
video_pt((char)xpos,(char)ypos+3,0);
video_pt((char)xpos,(char)ypos-3,0);
}
// check to see if any
of the tanks were hit and if so goto respective state
if ((char)xpos >= (p1x - 3 - p2weap) && (char)xpos <= (p1x + 3 + p2weap) && (char)ypos <= (map[randmap][p1x]+1+p2weap))
{
statevar = HIT1;
OCR0=195;
}
else if ((char)xpos >=
(p2x - 3 - p2weap) && (char)xpos <=
(p2x + 3 + p2weap) && (char)ypos <=
(map[randmap][p2x]+1+p2weap))
{
statevar = HIT2;
OCR0=195;
}
else
statevar = P1T;
break;
case
HIT2: // player 2 is hit, display p1 wins and wait
for fire button
sprintf(temp,
"PLAYER 1 WIN");
video_putsmalls(40,20,temp);
sprintf(temp,
"HIT FIRE TO CONTINUE");
video_putsmalls(28,32,temp);
if
(~PINC == 0x40) // advance to clear map state when
fire is pressed
{
play1w = play1w + 1; // increment player 1's win count
statevar = Clearmap;
c2 = 1;
}
else
{
statevar = HIT2;
// play a small
note to imitate the tank blowing up
if((musicT++)
> 15) //each
note 1/4 second
{
musicT
= 0;
TCCR0
= 0b00000100;
if (OCR0 > 193)
{
TCCR0
= 0b00011100 ; //not
a rest
OCR0--
;
}
}
}
break;
case
HIT1: //player
1 is hit, display p2 wins and wait for fire button
sprintf(temp,
"PLAYER 2 WIN");
video_putsmalls(40,20,temp);
sprintf(temp,
"HIT FIRE TO CONTINUE");
video_putsmalls(28,32,temp);
if (~PINC == 0x40) //
advance to clear map state when fire is pressed
{
play2w = play2w + 1; // increment player
2's win count
statevar = Clearmap;
c2 = 1;
}
else
{
statevar = HIT1;
// play a small note to imitate the tank blowing up
if((musicT++)
> 15) //each
note 1/4 second
{
musicT
= 0;
TCCR0
= 0b00000100;
if (OCR0 > 193)
{
TCCR0 = 0b00011100 ; //not a rest
OCR0-- ;
}
}
}
break;
case
Clearmap: //
clear the screen and then advance to next state (Start again)
if (c2 < 126)
{
for
(time = c2; time < 3 + c2; time++)
{
video_line
(time,1,time,98,0);
}
c2
= time;
}
else
{
video_line
(126,1,126,98,1);
statevar
= Startagain;
}
break;
case Startagain:
// print out "Play again" and
display p1 and p2 win counts and goto Next state
sprintf(temp,"PLAY AGAIN");
video_puts(32,20,temp);
sprintf(temp,"PLAYER 1");
video_putsmalls(24,60,temp);
sprintf(temp,"PLAYER 2");
video_putsmalls(68,60,temp);
sprintf( temp, "%d", play1w);
video_putsmalls(36,68,temp);
sprintf(temp, "%d", play2w);
video_putsmalls(80,68,temp);
statevar = Next;
dstate =
Nopush;
break;
case Next: // allow the user to change the difficulty and start a new
game
sprintf(temp,"DIFFICULTY:"); //
display current difficulty setting
video_putsmalls(28,40,temp);
if
(difficulty == 0)
{
sprintf(temp, "EASY");
video_putsmalls(76,40,temp);
}
else if (difficulty == 1)
{
sprintf(temp, "MED ");
video_putsmalls(76,40,temp);
}
else if (difficulty == 2)
{
sprintf(temp, "HARD");
video_putsmalls(76,40,temp);
}
else
{
sprintf(temp,"%d",difficulty);
video_putsmalls(76,40,temp);
}
// state machine to debounce the select button
switch (dstate)
{
case Nopush:
if
(~PINC == 0x01) dstate = MaybeC6;
else
dstate = Nopush;
break;
case MaybeC6:
if
(~PINC == 0x01)
{
dstate = C6;
difficulty++;
if
(difficulty > 2) difficulty = 0;
//difficulty
= difficulty % 3;
}
else
dstate = Nopush;
break;
case C6:
if
(~PINC == 0x01) dstate = C6;
else
dstate = NoC6;
break;
case NoC6:
if
(~PINC == 0x01) dstate = C6;
else
dstate = Nopush;
break;
}
if (~PINC
== 0x80) //
when start is press reinitialize all the variables and start a new game
{
c1=1;
c2=1;
p1pow
= 50;
p2pow
= 50;
p1ang
= 45;
p2ang
= 45;
p1weap
= 0;
p2weap
= 0;
numshots
= 0;
if (difficulty == 0) wind = 0;
statevar = Clear;
numshots
= 0;
p1w2avail
= 3;
p1w3avail
= 1;
p2w2avail
= 3;
p2w3avail
= 1;
timer
= 0;
}
break;
}
} //line 231
} //while
} //main