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
}