Thursday, November 20, 2008

Tanks tanks tanks

The player tank is coming along. We have a small level with one enemy (who can shoot but is disabled for testing). And the script for the player tank is almost complete:


//FUNCTION pTurret()
//controls the turret of the tank and the firing mechanism
//stays oriented with the tank's roll and tilt
//points in the direction of the mouse
function pTurret()
{
wait(1); //wait a frame for c_setminmax
c_setminmax(me); //set the bounding box
set(my,FLAG2 | SHADOW | CAST | METAL); //set flags
my.pan = player.pan;
cannonFire(); //call cannonFire() to allow shooting
while(player.health > 0) //while the player is alive
{
my.pan += -20 * mouse_force.x * time_step;
vec_set(my.x,player.x); //keep the turret at the tank's position
my.tilt = player.tilt; //set the tilt to player tilt.
proc_mode = PROC_LATE; //move to the end of the scheduler to smooth out its movement with the body
wait(1); //avoid missing loops.
}
}

action pTank()
{
ANGLE slopeAng;
VECTOR smkSpot;
var engSnd;
var counter;
wait(1); //wait for the first frame for c_setminmax
c_setminmax(me); //set the bounding box
set(my,FLAG2 | SHADOW | CAST | METAL); //set some flags
player = my; //set the point
my.health = 100; //set health to the default 100
my.maxHealth = 100;
my.velocity = 30;
my.damage = 25;
turret = ent_create("tankturret.mdl",my.x,pTurret); //create the turret and give it a pointer
vec_for_min(my.bottom,me); //find the bottom of the tank
engSnd = ent_playloop(my,engine,1000);
kill(); //REMOVE BEFORE PUBLISH
articBlast();
while(my.health > 0) //while the tank is alive
{
my.pan += 10 * (key_a - key_d) * time_step; //turn the tank via [A] and [D]
speed.x = my.velocity * (key_w - key_s) * time_step; //move the tank via [W] and [S]
speed.y = 0; //no side movement
c_trace(my.x,vector(my.x,my.y,-2000),IGNORE_ME | IGNORE_PASSABLE | USE_BOX); //trace to find the ground
my.distDown = my.z + my.bottom - target.z; //find distance to the ground
if(my.distDown > 0) //if in the air
my.g = -30 * time_step; //fall down
else { //else
my.g = 0; //don't fall down (keeps the tank from sliding down slopes)
//vec_to_angle(slopeAng.tilt,normal);
//my.tilt = ang(90 - slopeAng.tilt);
}
var distCovered = c_move(my,speed,vector(0,0,my.g),IGNORE_FLAG2 | IGNORE_PASSABLE | GLIDE); //move, ignoring FLAG2, PASSABLE. glide when hitting blocks
if((distCovered > 0) && (my.distDown < 0)) //if moving and not airborne
{
if(counter <= 0)
{
you = ent_create("tracks.tga",my.x,track);
you.pan = my.pan + 90;
counter = 1.5;
}
else
{
counter -= 1 * time_step;
}
}
else
{
counter = 0;
}
if(((my.health / my.maxHealth) <= 0.25) && (pEffects))
{
vec_for_vertex(smkSpot,my,173);
ent_create("smoke.tga",smkSpot,smoke);
}
wait(1); //avoid endless loops
}
snd_stop(engSnd);
ent_playsound(my,crash,1000);
}


This tank can move up and down hills, turn to shoot at the enemy and lay down a track as if its churning up dirt as it moves. It will also spew smoke if it becomes wounded

What is left: Fix gravity so it doesn't stop falling in mid-air. Replace the model so I can separate the cannon from the turret, allowing me to shoot up and down as well as side to side. Make it sit on a slope at the proper angle.

Photobucket

This is the most recent screen shot of the project. Here you can see the enemy tank is smoking. Tanks smoke if their health is less than 25% of the max. The smoke, in order to keep the game small, does not occur if the tank is not in view and is limited to 150 sprites.

function smoke()
{
VECTOR test;
set(my,PASSABLE | TRANSLUCENT);
my.alpha = 25 + random(25);
reset(my,DECAL);
while(my.alpha > 0)
{
vec_set(test,my.x);
if(vec_to_screen(test,camera) == NULL) break;
my.speedx = random(20) - 10;
my.speedy = random(10) - 5;
my.speedz = 8 * time_step;
c_move(me,vector(my.speedx,my.speedy,0),vector(0,0,my.speedz),IGNORE_PASSABLE);
my.alpha -= 1 * time_step;
wait(1);
}
smokeNum--;
ent_remove(me);
}


The above code is the function of each smoke sprite in the effect. Each one rises up by its z-value but moves at random x and y values. Setting DECAL makes the smoke effect face me at all times. The smoke exists as long as it is in the camera view and as long as its alpha is greater than zero (alpha is a measure of opacity, 0 being completely invisible).

while(thisCodeDoesntWork)
{
smashComputer(); //take out some pent out anger
throwDartsAtBoss(); //take out more anger
sleep(6*60*60); //sleep six hours
}

Tuesday, November 18, 2008

Debugging

So while the thief game under goes some planning, I decided to finish up an old script I had from back in the A5 days, a silly game called "TANKS!"

Well, the game's programming is really simple and a good amount of it has been scripted, it just isn't in Lite-C yet.

And there I was trying to get a health bar to show up on the screen. The way a health bar works is that it's size_x (horizontal size) is affected by a percentage of the players health. So to get this percent, I defined a skill and called it "maxHealth." MaxHealth defaults (allegedly) at 100. The code looked like this:



#define distDown skill6
#define bottom skill7
#define maxHealth skill8

//..more variables and other things that do not affect maxHealth

action pTank()
{
var engSnd;
wait(1); //wait for the first frame for c_setminmax
c_setminmax(me); //set the bounding box
set(my,FLAG2 | SHADOW | CAST | METAL); //set some flags
player = my; //set the point
my.health = 100; //set health to the default 100
my.maxHealth = 100;
turret = ent_create("tankturret.mdl",my.x,pTurret);
//... code continues, does not use my.maxHealth after this


function initScreen()
{
while(!player) { wait(1); } //wait for the player to be loaded
set(healthBar,VISIBLE | LIGHT); //turn on the health bar and allow it to be set by RGB values
vec_set(healthBar.blue,vector(0,200,0)); //health bar is green
while(player) //while the player exists
{
healthBar.size_x = 190 * (player.health / player.maxHealth);
wait(1);
}
}


function initScreen() sets up the GUI and places a health bar where you can see the part divided by total formula being used to make the healthBar.size_x proportional to the player's health.

After putting this together, I got an empty pointer crash. After I fixed that with a wait(1) instruction, everything was great and dandy, except the healthBar was not showing up on my screen.

So I thought maybe the while condition is for some reason not true. I stuck "beep()" in initScreen in several places and did several test runs. What beep() does is it plays a sound so it is an easy way to test code.

They beeped.

I thought maybe something somewhere is setting the VISIBLE flag on the healthBar off. I wrote a quick function:


if(is(healthbar,VISIBLE))
{
beep();
}


It beeped. I had a health bar that was visible. Its flags were correct, in fact, the flags were the same exact flags as I have set on a health bar in a different project, a health bar that works. The while conditions were good. There was no reason why the bar wasn't showing up. None.

And then I thought, maybe its the equation itself, the "healthBar.size_x = 190 * (player.health / player.maxHealth);"

I used diag_var() in here to get the engine to write down what the size was.

And what I got astonished me.

-317.

The scale was a negative number. Then I looked at player.health.

The diagnostic spit 100 at me.

Then I looked at player.maxHealth

The diagnostic spit a negative number that made no sense.

Looking at the code I posted at the beginning of this post, I could not see what it was. player.maxHealth was set to 100 and that was the only place where I set it equal to anything. There was no other point in the script where it was used.

So I decided to try a global variable rather than a skill. I called it "pMaxHealth." I set that to 100 and put it in place of "player.maxHealth" in function initScreen().

And the bar worked.

And then it hit me.

I looked at skill7, above player.maxHealth, the one called "bottom."

Bottom is used here:

vec_for_min(my.bottom,me);

That one line of code finds the bottom of an object and returns a vector. my.bottom was now a vector. In 3D graphics, vectors have 3 parts, x,y,z. If you define a skill and use it as a vector, the following two consecutive skills become the y and z components. skill7, bottom, was a vector, and thus skill8 and skill9 were parts of that vector. And so maxHealth, which was skill8, was in fact being modified by the vec_for_min instruction, which came after its assignment of "player.maxHealth = 100;"

So the whole issue was fixed by changing maxHealth to skill 11.

I guess breakpoint was a good name for this blog. There are so many things that can go wrong.

while(thisCodeDoesntWork)
{
smashComputer(); //take out some pent out anger
throwDartsAtBoss(); //take out more anger
sleep(6*60*60); //sleep six hours
}

Monday, November 10, 2008

A crate and sprinting

So while working, I quickly built a crate since there are quite a few areas already in which I need crates, cellars, storage rooms.

Here is the crate, a basic six sided cube with a skin made in Photoshop:

As simple as it is, I'm proud of the cube because of the skin. Its a simple skin that a pro could do in photoshop in two minutes, but me? I felt like I leveled up, like I gained a skill, when I saw how it came out.



I was roughly following a tutorial, but had to make most of it up because the tutorial was using a program I don't have.

What I did was I opened a file, 512x512 pixels. I filled it with a brown color. Then I grabbed the Paintbrush tool, picked a large brush, Chalk, size 44, reduced the opacity to around 25%, chose a darker brown and made several diagnal and vertical strokes to rough in the big parts of a wood textured.

What I got looked like a large 'M' with a few extra lines. But that's okay, I added the grain with a filter called "Graphic Pen," and set the stroke direction to horizontal. It made a whole bunch of brown and white jagged strokes. Since I didn't want the white, I went to the magic wand tool, turned off "contiguous" and "anti-aliasing" and deleted the white areas, and finally, I got a wooden texture.

Then I went to the rectangle marquee selection tool. I picked up the paint brush and selected a very dark brown with a low opacity and colored in around the edges, making the illusion of an indented square.

I grabbed the airbrush and made several vertical lines to give the illusion of planks. Then I used the airbrush to add knots. Last, I used the airbrush, by clicking in one place only to make the nails, which I think came out well.

And then I was finished. After fighting with MED to get the faces in the skin editor arranged properly, my cube was skinned and is now a crate.



Above is a stack of crates in game.

Since the game is based on a lot of movement, I actually have to get a movement code working before I go too far with the level design. The player needs to be able to crawl, climb, sprint and other things to get from place to place.

Sprint should be finished.

function sprint()  
{
if(key_shift)
{
if(tired == 0)
{
if(sprinting < 15)
{
sprinting += time_step * 0.0625;
player.speed = 60;
}
else
{
tired = 1;
player.speed = 10;
}
}
else
{
sprinting -= (time_step * 0.0625) * (sprinting >= 0);
tired = (sprinting >= 0);
}
}
else
{
sprinting -= (time_step * 0.0625) * (sprinting >= 0);
tired = (sprinting >= 0);
player.speed = 10;
}
}


What does this code do? Basically, it allows the player to run at an increased speed for a maximum of fifteen seconds. When she stops running, she can't sprint again for the time that she sprinted.

Line by line: The first line, "if(key_shift)" checks if the shift key is pressed. If so, it goes to the next if() statement, "if(tired == 0)." If not, it goes to the else section and sets things back to walking speed.

The next line, "if(tired == 0)" checks if she's run recently. If not, it goes to the next if-branch, "if(sprinting < 15) checks that the sprinting variable is under 15. The next line adds to "sprinting," time_step * 0.0625, which allows for the counter to hit 15 in 15 seconds. Time_step is the time one frame of the game takes, and is about 1/16th of a second, hence "time_step * 0.0625." 0.0625 = 1/16.

The line under it sets the player speed to 60, a high variable to help me see the results. Since this function is called in a while() loop, it will continue to increase sprinting until sprinting == 15.

(side note, = is an assignment. x = 2 means that x has been assigned the value '2'. == is comparison. x == 2 means that x is being compared with the value 2, but x can be anything)

When sprinting is 15, the lines under the Else part of the "if(sprinting > 15) branch are read. They set tired to one and set speed back to the default, which is 10.

Since tired is now 1, the if(tired == 0) branch goes to its else branch. You see this line: "sprinting -= (time_step * 0.0625) * (sprinting >= 0);" All this does is reduce sprinting by 1 every second until sprinting is less than or equal to 0. The way it works is, if sprinting is greater than zero, the second part, "(sprinting >= 0), is equal to 1 because it is true, that part of the expression is a binary conditional. 1 * 1 = 1 and so it decreases by 1 every second. But when sprinting is zero, (sprinting >= 0); is false, thus it equals 0, and 1 * 0 = 0 which means it no longer decreases.

Then tired also uses a conditional, which works the same way. If sprinting is less than or equal to zero, the conditional becomes false, tired becomes zero and Suzetta can run again.

The last chunk of code is the else branch attached to the if(key_shift). This does the same thing as I described in the above two paragraphs. It is also down here in case the player releases shift before sprinting == 15. And then finally, the speed is set back to its default of 10.



while(thisCodeDoesntWork)
{
smashComputer(); //take out some pent out anger
throwDartsAtBoss(); //take out more anger
sleep(6*60*60); //sleep six hours
}

Saturday, November 08, 2008

The Church of the One God

I had a compiler crash the other day, nothing I can't work around and hopefully a new computer will fix it. But to try to discern the cause of the problem, I made another part of Darincedon in another file.

And I do think there is a problem. The compiler took 45 minutes to do this scene and while compiling, it took 90% or more of the CPU usage. I somehow don't think that is normal. Something this size should, if my memory from messing around with A5 serves me right, should only take a minute or two.

Well, I can still work with long compile times. I'll compile while I'm in class or something.

Anyways, here is the church as of today, unfinished and not quite ready to be placed into the city:



This is a shot from the editor, you can see the whole thing.



This is the front of the church. It is a two steeple church, you will be able to climb up the steeples. There is also a small graveyard to the side.



The graveyard and holes for what will be stained glass windows.



Here you can see an error in the texture I need to fix.



The interior. The pews I think I'll replace with models, which will allow for a bit more detail and will lessen the compile by a bit. The lighting is done by candles which I have yet to create. I might make the lightrange bigger.



The graveyard. I'll be adding two new headstones later, probably tomorrow.



The entrance. I'm going to do something different with the doors.

while(thisCodeDoesntWork)
{
smashComputer(); //take out some pent out anger
throwDartsAtBoss(); //take out more anger
sleep(6*60*60); //sleep six hours
}

Wednesday, November 05, 2008

The City of Darincedon, Week 1.

The city of Darincedon is beginning to come alive, one building at a time. Right now, it is two streets, one in, and 3-4 foundations where houses and other buildings will be added later.

Some of the textures are final, some are not. The models that are in place, are probably final, but the skins are definately not ready. Here are some screenies of what exists so far:

Click to enlarge:



The view from 3d Gamestudio (A7 Engine). The white lines are level geometry built in Gamestudio. The blue lines are models built in trueSpace 7.6. The purple are sprites made in Photoshop. The yellow orbs are lights.

From the top left, clockwise: Birds-eye view, 3d View, Back, Side

The level is further divided into groups so if I want to edit one building, I select it, scope down and do what I need to do. What this does is it makes everything else in the level invisible so I don't work in a cluttered workspace. This also enables me to do macroscopic edits to a building without having to select everything by hand.



Here is another angle



This is another shot in the editor, with the 3d screen maximized. Here you can see the street between the inn and the bakery (the inn is to the left, the bakery is the unfinished building to the right.

The cobblestone street will be getting a new texture, as will the texture. The bakery will be recieving walls as soon as the inn is finished. I want to get the half-timber design in the inn and finish the level geometry within. Then light map the inn, then move on to the bakery.

I might add windows to the bottom floor of the inn, but I'm not sure how I want to do that, so I haven't done it yet.

The black squares are sprites. They're suppose to simulate the halos that are around a streetlamp at night. The editor's 3d view does not do overlay (removal of black areas) or transparencies so you don't get a good view of the effect from this screen. When I run the game, a script sets those sprites to transparent and overlay.



From above, looking down on the unfinished in. The large stone blocks you see (one by the stable and the other is on the bottom to the right) are going to be closed houses (houses the player can't enter)



Another birds eye view in the editor. Circled is Tomas's secret hide-out, which is accessed through the bakery. This area is underground.



Here is a shot in the map preview after the scene was compiled. I compile often since it allows me to preview the map and check the geometry, make sure everything looks right.

Since this is preview mode, the script isn't running, therefore the streetlamp halo is opaque. But the halo is a targa file with an alpha channel, which allows for certain parts to not be drawn.

There is a static light sitting over both streetlamps. RGB(200,150,0), range 400 units.



This shot looks down the street from the stable. It is still dark in that area because there are no lights there yet. Right now, the game's resolution is 640x480 and I will increase that later.



This dark preview shot shows the interior of the ground floor of the inn. Later, it will be detailed with several tables, ale barrels, chairs, a fireplace, ale bottles, wine bottles, steins, and other items. Upstairs is a hallway with six closed rooms. The walls will also take on a half timber design. The opening is a back door that opens to another street. Eventually, the room will be populated with patrons, a barmaid, an innkeeper, and cooks.

There is also a cellar that will hold barrels and crates.

It will also be brightly lit, but since I have nothing inside, I would not know where to put the lighting yet.



This was shot while running the level with the scripts active. Right now, I have a model that moves around via the WSAD keys just so I can check collision detection and make sure that doorways are wide and high enough and to make sure that the character can climb stairs.

This room will be a meeting hall. The bookshelves will be reskinned and the table replaced. The fireplace will be retextured and a fire will be put in it, as well as a dynamic light that simulates a fire flicker.



And last, here's a shot after the character comes out of the hide out, out of the bakery and onto the street. Here I hoped to get a screenshot of the lamp halo but it didn't show up in the screenshot.

The lampposts will be reskinned. But A7 doesn't draw unskinned models so I threw something on to get the model to show up.

while(thisCodeDoesntWork)
{
smashComputer(); //take out some pent out anger
throwDartsAtBoss(); //take out more anger
sleep(6*60*60); //sleep six hours
}

Saturday, February 23, 2008

Tanks!

Well after my last PC crashed, half my RTS game went out the window.

I've been making a little tank game, nothing too complex. The script is in the link below.

Tank script


Note that while I'm calling it Tanks! right now, thats an unofficial name, just something I'm using so I have something to go by. It going to be a very simple game, in essence, its capture the flag. In each level, there is a flag and a boss tank guarding the flag. It is your job to pick up that flag.

At this stage, the game is far from complete, but I have the player controls nearly where I want it. The tank moves forward and backwards with W and S, and it turns with A and D, leaving the player free to use the mouse with ease, as is convention with many games. The mouse is used to aim the turret and to fire the turret. The turret and the tank are separate models with separate actions.

The player's tank:


action playerTank
{
player = my;
my.fat = on;
my.narrow = off;
my.health = 100;
while(my.health > 0)
{
my.speedx = velocity * (key_w - key_s);
my.pan += 1 * (key_a - key_d);
move_mode = ignore_passable + glide;
ent_move(my.speedx,nullvector);
camera.x = my.x;
camera.y = my.y - 750;
wait(1);
}
ent_remove(me);
}
What you have here is the script that is attached to the body of the tank. It is a very simple script right now, and is likely to remain simple throughout the development of the game as it already does everything I think it needs to do.

First line within the action sets the pointer. This is rather important as other entities will be dealing with the player and they need to know what entity they are suppose to be interacting with. Next three lines give it a wide bounding box and set the player's health to a starting point of 100.

Now everything the tank does is done while the tank is alive. Thus we need to put the remainder of the instructions in a while loop and set the following instructions to continue processing as long as the tank is alive.

First thing to do is to get the tank to move. The tank needs to respond appropriately to the WSAD keys. And I need to use the command ent_move(relative distance,absolute distance) to get collision detection and to make the tank turn properly. The two parameters in ent_move() are vectors. My.speedx is the vector in this instance and velocity is the forward speed, set initially to 10 and modified elsewhere in the script (as of right now, turbo().) Velocity needs to be modified with keys on the keyboard.

Once we have the speed defined in the vector, we need the keybindings. Every key on the keyboard is assigned a variable that can be called. The W key is assigned to key_w and it is equal to 1 when pressed and 0 if not. Thus, we can simply use these variables in the equation.

my.speedx = velocity * (key_w - key_s);
What does this do? Its simple really. Velocity is 10 (we will ignore turbo() for now.) key_w and key_s are both 1 or 0 depending on if they are pressed or not. You have 4 possibilities:

If W is pressed, my.speedx = 10 * (1 - 0) = 10 * 1 = 10
If S is pressed, my.speedx = 10 * (0 - 1) = 10 * -1 = -10
If neither is pressed, my.speedx = 10 * (0 - 0) = 10 * 0 = 0
If both are pressed, my.speedx = 10 * (1 - 1) = 10 * 0 = 0

The next line:
my.pan += 1 * (key_a - key_d);
works the same way only instead of affecting forward/backward movement, it causes the tank to rotate around its z-axis (up and down), in a pan movement. The next line ensures that the tank ignores anything marked "passable" and that it glides when it hits something, rather than getting stuck. Then ent_move is called and the tank will move according to whichever buttons are being pressed. My.speedx is placed in the first parameter of ent_move() because that parameter uses the model's origin rather than the world's origin. This is important because if I pan the tank, the tank's origin rotates but the world's origin does not. Turning the tank makes its x-axis rotate, but not the world's x-axis. If I used the world's origin, then if I turned the tank, the tank would still go forward, even though the body is facing in another direction. So I use the first parameter in ent_move(). The second parameter, unlike the first, uses the world's origin. I don't need that, so I set it to 0 (nullvector is (0,0,0) .)
ent_move(my.speedx,nullvector); 
The next two lines put the camera above the tank and back a bit. This position is updated every frame (and there are about 30 frames per second, depending on the computers performance and a number of factors) Now it may seem odd that I used the y axis to move the camera back. It should be the x-axis, but I believe that this is due with the position and the orientation of the camera as it is placed in Gamestudio. The camera is behind the tank, looking down at about 70ยบ from the horizontal.

The last line in the while() loop is a wait. This ensures that all the instructions execute themselves in that frame before the loop goes back to the beginning. Otherwords, the script will attempt to call these instructions on top of each other and the loop will be endless and the game will crash.

The last line is ent_remove(me) and that was put in simply for testing. Later it will be removed and the game will, at player death, bring up a menu that allows for the player to try again, go to a saved game, or quit the game.

Well, we have a tank that moves. But it has no turret and it doesn't shoot anything! The turret is a separate model and I will talk about that in the next post.

Sunday, August 20, 2006

Scripting! Scripting!

Last few days, I've scripted my ass off. Got the tank function partway up and running. He can move to where the player right clicks in straight lines. He can be selected in many different ways. I have completed single unit selection with left click a long time ago, and multi unit selection with control click. I just added several new ways to select a unit. Press Q to select all military units, Press A to select all units the same type of the unit currently selected (will also be done via double click, which I haven't done yet), and click and drag to lasso units in a box. (This one was really fun to make :P )

What the tank needs now is to pathfind its way around obstacles, detect a nearby enemy and shoot him, shoot an enemy at the player's command, switch from main gun to machine gun when attacking soft targets. And I need to create the statistics panel for this tank. And make him upgrade from from tank to tank. And so on and so forth.

And this is just one tank.

So I spent all day doing that lasso code. What happens is that the player drags the mouse on the screen. A box is formed. Everything inside that box is selected. It took so long because I realized that the program was dependant on which direction the player dragged the mouse to create the box. And there were four different ways that he could. Check out the diagram:



The black dot is the starting vector of the mouse (mouseCoord1[3]) and the red dot is the final position of the mouse, when the mouse is released (mouseCoord2[3]) and the blue dot is where an entity needs to be if he is to be selected (my.x,my.y). First, I had to check, with the inequalities next to the box name, which box it was by discerning where mouseCoord1 was in relation to mouseCoord2. Then I had to check through the next four inequalities to make sure the unit was in the box. Then I had to check that the unit was the player's and not the computers, and that he was not already selected, and that he wasn't a building (I hated that in Age of Empires, you could lasso buildings and accidently group them along with the army . . . nah, none of that.) And now it works, sort of. I don't have the visible aspects of the lasso, no box on the screen, but my units are selected, but when I lasso selected units, they don't yet deselect. Here it is, the entire freaking script so far:


function deselect()
{
var recorded;
if(!key_ctrl) //if ctrl is not pressed
{
unselect = 1; //set unselect flag to unselect everything
wait(1); //wait a frame
unselect = 0; //re-enable selection
}
while(mouse_left) //while mouseLeft is pressed
{
if(recorded != 1) //if first position if mouse is not recorded
{
mouse_trace();
vec_set(mouseCoord1,target);
recorded = 1;
}
mouse_trace();
vec_set(mouseCoord2,target);
wait(1);
}
mouse_trace();
vec_set(mouseCoord2,target);
mouseCoord3.x = mouseCoord1.x; //find the next two corners
mouseCoord3.y = mouseCoord2.y;
mouseCoord3.z = 0;
mouseCoord4.x = mouseCoord2.x;
mouseCoord4.x = mouseCoord1.y;
mouseCoord4.z = 0;
lasso = 1;
wait(1);
lasso = 0;
recorded = 0;
return; //exit function
}

function lassoMe() //groups tanks via a lasso created by click and drag
{
if((mouseCoord1.x < mouseCoord2.x) && (mouseCoord1.y > mouseCoord2.y))
//box#1,if lasso diagnal went from up to down left to right, then:
{
debug = 1;
if(((my.x <= mouseCoord2.x) && (my.x >= mouseCoord1.x))
&& ((my.y >= mouseCoord2.y) && (my.y <= mouseCoord1.y)))
//if inside the box
{
//select the unit if he is not selected, or is not the humans. If not, deselect.
my.selected = ((my.selected != 1) && (my.person == 1));
}
}
if((mouseCoord1.x < mouseCoord2.x) && (mouseCoord1.y < mouseCoord2.y))
//box#2, if lasso diagnal went from down to up left to right, then:
{
debug = 2;
if(((my.x <= mouseCoord2.x) && (my.x >= mouseCoord1.x))
&& ((my.y >= mouseCoord1.y) && (my.y <= mouseCoord2.y)))
//if inside the box
{
//select the unit if he is not selected, or is not the humans. If not, deselect.
my.selected = ((my.selected != 1) && (my.person == 1));
}
}
if((mouseCoord1.x > mouseCoord2.x) && (mouseCoord1.y < mouseCoord2.y))
//box#3, if lasso diagnal went from down to up right to left, then:
{
debug = 3;
if(((my.x <= mouseCoord1.x) && (my.x >= mouseCoord2.x))
&& ((my.y >= mouseCoord1.y) && (my.y <= mouseCoord2.y)))
//if inside the box
{
//select the unit if he is not selected, or is not the humans. If not, deselect.
my.selected = ((my.selected != 1) && (my.person == 1));
}
}
if((mouseCoord1.x > mouseCoord2.x) && (mouseCoord1.y > mouseCoord2.y))
//box#4, if lasso diagnal went from up to down right to left, then:
{
debug = 4;
if(((my.x <= mouseCoord1.x) && (my.x >= mouseCoord2.x))
&& ((my.y >= mouseCoord2.y) && (my.y <= mouseCoord1.y)))
//if inside the box
{
//select the unit if he is not selected, or is not the humans. If not, deselect.
my.selected = ((my.selected != 1) && (my.person == 1));
}
}
}

action Tank //controls tanks
{
my.metal = on;
my.narrow = on;
my.fat = off;
my.health = 250 + TankBonus; //set health to 250. Since this is the USA, apply a bonus
my.LoS = 500; //set view distance
my.enable_click = on; //enable click
my.event = handleTankEvents; //handle events, such as stuck, collision, click
my.team = 1; //team is set to 1, these are the player's units and the player defaults to team 1.
my.person = 1; //used by player one, the human player
my.stance = aggressive; //default stance is agressive
my.type = 2; //is a vehicle
my.mode = 0; //moving or no?
my.civ = America; //civ is set by the tank factory that creates it. (AMERICAN FOR TESTING)
my.pan = ang(my.pan); //set rotation to -180 . . . +180 range
my.selected = off; //default to unselected
createTurret(my.civ); //create the turret according to civilization and era.
while(my.health > 0)
{
if(degroup == 1) { my.group = 0; }
if(lasso == 1) { lassoMe(); }
if(my.selected == on) //if selected
{
if(my.selecter != 1) //if doesn't have selection ring
{
ent_create(selectBMP,my.x,selectSprite); //create it
my.selecter = 1; //set selecter to one
}
my.selected = (unselect == 0); //deselect if something else is clicked on
if((mouse_right) && (!key_x)) //if rightclicked while selected
{
mouse_trace(); //find coordinate
ent_create(targBMP,target,beacon);
if(you != null)
{
wait(1);
}
vec_set(my.targX,target); //set to unit's targets
my.mode = 1; //set him to move
}
if(key_ctrl) //assign hotkeys on ctrl+#
{
if(key_1) { ungroup(); my.group = 1; ent_create(grpNum1,my.x,grpNum); }
if(key_2) { ungroup(); my.group = 2; ent_create(grpNum1,my.x,grpNum); }
if(key_3) { ungroup(); my.group = 3; ent_create(grpNum1,my.x,grpNum); }
if(key_4) { ungroup(); my.group = 4; ent_create(grpNum1,my.x,grpNum); }
if(key_5) { ungroup(); my.group = 5; ent_create(grpNum1,my.x,grpNum); }
if(key_6) { ungroup(); my.group = 6; ent_create(grpNum1,my.x,grpNum); }
if(key_7) { ungroup(); my.group = 7; ent_create(grpNum1,my.x,grpNum); }
if(key_8) { ungroup(); my.group = 8; ent_create(grpNum1,my.x,grpNum); }
if(key_9) { ungroup(); my.group = 9; ent_create(grpNum1,my.x,grpNum); }
if(key_0) { my.group = 0; } //Ungroup via ctrl+0.
}
if(key_del) { my.health = -50; } //kill on delete command
if(key_s) { my.mode = 0; } //stop the unit on key s
if(key_a) { selectAllOfType(); } //select all of the same unit on key a
if(key_e) { /*evacuate();*/ } //on e evacuate all units in the tank.
if((key_x) && (mouse_right)) //attack ground on X + rightclick
{
mouse_trace();
vec_set(my.targX,target);
my.mode = 2;
}
}
if(my.mode == 0) //if standing still:
{
if(my.stance != noAttack)
{
//scan for enemies at LOS range, if one is found, move in and attack according to stance.
temp.pan = 360;
temp.tilt = 0;
temp.z = my.LoS;
scan_entity(my.x,temp);
if(you != null)
{
if(you.team != my.team)
{
my.mode = 2;
}
}
}
}
if(my.mode == 1) //if moving
{
moveVehicle(10);
if(vec_dist(my.x,my.targX) <= 20) { my.mode = 0; } //when there, return to still mode.
}
if(my.mode == 2) //if attacking
{
if(my.stance == aggressive)
{
if(vec_dist(my.x,my.targX) > p1TankRange)
{
moveVehicle(10); //move in range
}
else
{
my.inRange = on; //rotate turret. Turret commences the attack.
}
}
if(my.stance == standGround)
{
if(vec_dist(my.x,my.targX) < p1TankRange)
{
my.inRange = on;
}
else
{
my.mode = 0;
}
}

}
wait(1);
}
//run death animation, make passable and remove
ent_remove(me);
}


It when the player presses the mouse. A trace is fired from the mouse and records its position. Then a trace is fired from the mouse again and records the mouses new position until the mouse is no longer being pressed (this is happening in function deselect(); which is called by the mouse) then the other corners of the box are defined (but unused, I'll need them for the visual). The lasso variable set to one and during the wait command, the tanks call lassoMe(); and then the variable is set back to zero so no more tanks try to add themselves to the selected group.

Then in lassoMe(), each possible stroke that the player can make with the mouse is defined. There are four of them, determined by where mouseCoord1 is in respect to mouseCoord2 (see diagram). Then I check to see if the entity is in the box (see diagram) and then I have him selected.

And you'll notice that the selection command has a conditional in it. Yep, something I found out about recently. Since a true condition returns 1 and a false one returns zero, I can set flags this way and save space on the editor.

Speaking of space, this is enough typing.
while(thisCodeDoesntWork)
{
smashComputer(); //take out some pent out anger
throwDartsAtBoss(); //take out more anger
sleep(6*60*60); //sleep six hours
}