Chapter XII

Picking Up and Attaching Items

 

  This is the chapter where the program will actually start looking like a game. This is also the hardest chapter of them all. We will be covering a lot of cool techniques, but it won't be easy. At this point of the tutorial, we ought to be well motivated though, because the end is near. So let's get started, shall we?

 

Attaching the Weapons to the Players

  You might remember from our game documentation that the players will be assigned their weapons at game start. So, since we don't have to worry about picking up the weapons, lets start with them. Let's go ahead and get our weapons declared in Resources in our declarations first.

 

//Models

string str_cbabe = <cbabe.mdl>; // CBabe Model

string str_warlock = <blue_warlock.mdl>; // Blue Warlock Model

string str_guard = <red_guard.mdl>; // Red Guard Model

string str_rifle = <rifle.mdl>; // compression rifle model

string str_nuclear_rod = <nuclearrod.mdl>; // nuclear rod mdl

string str_emulator = <lightgun.mdl>; // photosynthesis emulator model

 

  The rifle(compression rifle) and lightgun(photosynthesis emulator) models were free models I found by searching the forms. Their creators can be found in Appendix II - Contibutions. The nuclear rod was one of my 15 minute jobs.

  Anyhow, now that we have our weapon models declared, how in the world are we going to attach them to the players? What we want to do is to attach the weapons to the player's right hand. I am sure there are some good attach scripts out there. I just threw this one together and kept it a simple as possible, like most scripts in the tutorial (the camera for example). I decide to use vec_for_vertex() command to achieve this. The attach code will be simple, so the weapon will not tilt or roll.

  We have to start somewhere, so let's start with the biological scientest. Let's find a vertex on his right hand to attach his photosynthesis emulator to. This may take some trial and error to finally get the weapon so it looks decent in the hand. You will just have to play around trying different vertexes until you find one that looks good.

  Ok, we are now going to use the Model Editor (MED) for the first time. Open MED and the go File-Open, then find your tutorial work folder and open it, then open Entities folder and select red_guard.mdl. Hit OK. Now we should have our biological scientist model in MED.

  Now, find and click on the Vertex Mode icon.

  This will allow us to select single vertexes. Now click on the Select Icon.

  Go ahead and click on some vertexes on the model's right hand. The vertex you select will turn red and the vertex number will show up at the bottom left-hand of window.

  The vertex I finally came up with vertex #2 (note: I had to run the attach part of code a few times changing vertex number until it looked pretty good) as shown below.

  Now that we have a vertex to attach the weapon model to, let's go ahead and use vec_for_vertex() and to get intial position of the photosynthesis emulator and then create it with ent_create(). So add the following code in function move_player.

 

my.weapon = WEAPON_PHOTOSYNTHESIS_EMULATOR; // set weapon

you = my; // save me as owner for weapon being created

// Attach correct weapon to player

ent_create(str_emulator, my.X, weapon_local); // created emulator at

// player position locally

proc_local(my,local_activities_bio); // local animation and other activities

 

  First we save the player's entity (MY) to (YOU) so it can be used in the weapons function, you = my; // save me as owner for weapon being created.

  Then we get the position to create the weapon using the vertex number we got from MED, vec_for_vertex(temp_vec,my,2); // get position of weapon.

  After that, we create the weapon, ent_create(str_emulator, temp_vec, weapon_local); // created emulator.

  I suppose I should explain why we have named are weapon function weapon_local() when we are using ent_create(). The reason is, we will soon be changing ent_create() to ent_createlocal(), but I wanted to show you why we are going to do this before we change it. That way you will understand. "Seeing is believing," so they say. To tell you the truth, I have rued the day that I would have to write this chapter.

    So let's go ahead and add our weapon function now beneath player_events(). This function will totally change in a moment, after we see what is happening.

 

send_var(number_of_players); // let everyone know new number of players

ent_remove(me); // remove ent of player that quit

}

}

 

//--------------------------------------------------------------------

// WEAPON_FUNCTION - moves weapon with player or NPC

//--------------------------------------------------------------------

function weapon_local()

{

my.passable = ON; // make passable so can ignore during player move

 

while(you)

{

 

if (you.weapon == WEAPON_PHOTOSYNTHESIS_EMULATOR)

{

vec_for_vertex(my.X,you,2); // get position of weapon

vec_set(my.pan,you.pan); // set pan to owner's pan

my.pan -= 10; // adjust weapon so player can see it better

}

wait(1);

}

remove(my); // remove weapon if owner does not exist any more

}

 

  Here's what this function is doing. It first set's the weapon to passable so it does not interfer with player movement or camera traces. Then as long as the owner, which is the player (You, which we set before function was called) exist, we move the weapon with a vertex on the hand of the owner. We also keep the pan set to the owner's, although I have asdjusted it a bit for this weapon so the player could see his weapon better. You could also adjust the position too if you liked.

  Now let's see what the problem is now. Go ahead and Publish or Resource and copy over to secondary computer. Then run the program on both computers making sure you select the Biological Scientist profession for each player since that is the only weapon we have set up so far. Now run the Host and then the Client.

  You will notice everything looks fine on the Host, but go over and play around on the Client for a while and you will see something like this.

  Notice how the weapon isn't always staying attached to the proper position in the entity's hand on the Client? There is a reason for this. It makes sense if you think about it.

  Here is a description of what is happening. In Chapter X we changed our animation so it is now being done locally to lessen network traffic. Now the weapons function code we just added is attaching itself to the server's entity hand. The problem we have is this. Even though the animations being ran on the Host and Client are the same, the current animation frame on the Client may be a bit off of the Host because of the time for the animation_state to be transmitted over the network. That is why, sometimes the weapon looks like it is attached about right on the Client and other times it looks off. The weapon is just moving with the vertex on the server's animation frame.

  So the solution to this is to create the weapons locally and attach them to that computer's (Client's) animation instead of the Host's animation. Do you remember back in Chapter X when we put the animation() call into the local activities of the player? We are now going to do something similar here, but instead of making a call, we are actually going to create the weapon locally from within the local activities function with ent_createlocal(). It is important to remember that local_activities_bio() is being ran locally on the Client's computer, not on the server, thus when we run a ent_createlocal() command from within the function it is creating that entity locally and running that entity's function locally too on the Client's computer.

  Let's go ahead and add the ent_createlocal() into function local_activities_bio() now.

 

//--------------------------------------------------------------------

// LOCAL_ACTIVITIES_BIO - performs local ativities for Bio Scientist

//--------------------------------------------------------------------

function local_activities_bio()

{

 

my.weapon = WEAPON_PHOTOSYNTHESIS_EMULATOR; // set weapon type

 

you = my; // save MY into YOU because weapon_local() needs to know who owner is

// Attach correct weapon to player

ent_createlocal(str_emulator, my.x, weapon_local); // created emulator at

// player position locally

 

animate(); // do my animation locally

}

 

  First we saved My into You because our weapon_local() function will be attaching the weapon to a vertex on You, which is the player. My will become a pointer to the weapon in the weapon_local().

  Next we create the weapon locally with ent_createlocal(), which calls weapon_local() as it's function.

  Now, we need to change the weapon_local() function so it will work locally. Actually, all we have to add is some code for what I believe is a Server Mode bug, this code might be able to be removed sometime after A6.31, but I am not certain at this time. The problem this code is fixing is when in Dedicated Server Mode (connection == 1), when a Client quits game and tries to re-join, a local procedure is still running, even though the entity running this procedure was removed and the client will immediately crash when level is loading.

 

function weapon_local()

{

my.dynamic = ON; // let server know this entity is not static - do not remove

my.passable = ON; // make passable so can ignore during player move

 

// temporary code for what I think is a Bug in Server Mode A6.31

// you can remove this in later versions of 3DGS I believe

if(!my.x && !my.y && !my.z)

{

return;

}

 

while(you)

First we set the my.dynamic = on, which is supposed to let the server know that this is not a static entity (static entities cannot be removed from level).

Note: At the time of the release of this tutorial there is what seems to be a bug when a client exits a game and tries to re-join in Server Mode his old local procedures are still running even though we set his entity's dynamic flag on. This results in the program immediately crashing when the client tries to rejoin. The next lines of script is a temporary solution to this problem and will work. Giorgi3 figured out this work around to the problem. The only thing is you cannot create a player at the position (0,0,0) or the animation will not run. Anyhow, I think this issue will be resolved after A6.31 and this code can be removed. You don't really need to concern yourself with this code at this time, but you may want to remember it is there and comment it out and see if the program runs ok after the next A6 release. Leave the my.dynamic = on in the code, that is what is supposed to make this crash not happen.

// temporary code for what I think is a Bug in Server Mode A6.31

// you can remove this in later versions of 3DGS I believe

if(!my.x && !my.y && !my.z)

{

return;

}

  Since we are fixing this problem, we also need to fix it the same way in animate() or it will crash there too under the same circumstances.

 

function animate()

{

my.dynamic = ON; // let server know this entity is not static - do not remove

proc_late(); // make sure forces set before we run animation

 

// temporary code for what I think is a Bug in Server Mode A6.31

// you can remove this in later versions of 3DGS I believe

if(!my.x && !my.y && !my.z)

{

return;

}

 

 

while(my)

 

  Now all there is left to do is to create the entity that the Server creates locally on itself too so it doesn't make clones of the weapons and send them to all the Clients since the Clients are creating their weapons locally.

  So, in function move_player Change this.

 

my.weapon = WEAPON_PHOTOSYNTHESIS_EMULATOR; // set wepaon

you = my; // save me as owner for weapon being created

// Attach correct weapon to player

ent_create(str_emulator, my.X, weapon_local); // created emulator at

 

To this.

 

my.weapon = WEAPON_PHOTOSYNTHESIS_EMULATOR; // set wepaon

you = my; // save me as owner for weapon being created

// Attach correct weapon to player

ent_createlocal(str_emulator, my.X, weapon_local); // created emulator at

 

  Now if you run the program selecting the biological scientist as the profession for Host and Client, you will see that the weapons are now attached correctly to the local animation.

  Let's go ahead and get all of the professions set up now.

  In action main under the if (my.profession == PROF_NUCLEAR_SCIENTIST) statement add this code.

 

send_skill(my.health, SEND_VEC+SEND_ALL);

 

my.weapon = WEAPON_NUCLEAR_ROD; // set weapon type

 

you = my; // save me as owner for weapon being created

// Attach correct weapon to player

ent_createlocal(str_nuclear_rod, my.X, weapon_local); // created rod at

// player position locally

 

proc_local(my,local_activities_nuclear); // local animation and other activities

 

In action main under the if (my.profession == PROF_OPERATION_OFFICER) statement add this code.

 

send_skill(my.health, SEND_VEC+SEND_ALL);

 

my.weapon = WEAPON_COMPRESSION_RIFLE; // set weapon type

 

you = my; // save me as owner for weapon being created

// Attach correct weapon to player

ent_createlocal(str_rifle, my.X, weapon_local); // created rifle at

// player position locally

 

proc_local(my,local_activities_officer); // local animation and other activities

 

  Now in function local_activities_nuclear() add this code.

 

//--------------------------------------------------------------------

// LOCAL_ACTIVITIES_NUCLEAR - performs local ativities for Nuke Scientist

//--------------------------------------------------------------------

function local_activities_nuclear()

{

my.weapon = WEAPON_NUCLEAR_ROD; // set weapon type

 

you = my; // save MY into YOU because weapon_local() needs to know who owner is

// Attach correct weapon to player

ent_createlocal(str_nuclear_rod, my.x, weapon_local); // created emulator at

// player position locally

 

animate(); // do my animation locally

}

 

  In local_activities_officer() add this code.

 

function local_activities_officer()

{

my.weapon = WEAPON_COMPRESSION_RIFLE; // set weapon type

 

you = my; // save MY into YOU because weapon_local() needs to know who owner is

// Attach correct weapon to player

ent_createlocal(str_rifle, my.x, weapon_local); // created emulator at

// player position locally

 

animate(); // do my animation locally

}

 

  Save the multiplayer.wdl from SED. Run MED and open up the blue_warlock.mdl(nuclera scientest), we need find a vertex to attach the nuclear rod too like we did earlier with the red_guard.mdl (biological scientist) earlier in chapter.

  Using the same techniques as earlier I cam up with vertex# 9 as shown below.

 

  Since we have MED open, let's get a vertex for Cbabe.mdl (operations officer) to attach the rifle to. I chose vertex # 354 as shown below.

  Now that we know our vertex numbers, let add the code to attach the weapons to these vertexes in function weapon_local().

 

if (you.weapon == WEAPON_PHOTOSYNTHESIS_EMULATOR)

{

vec_for_vertex(my.X,you,2); // get position of weapon

vec_set(my.pan,you.pan); // set pan to owner's pan

my.pan -= 10; // adjust weapon so player can see it better

}

if (you.weapon == WEAPON_NUCLEAR_ROD)

{

vec_for_vertex(my.X,you,9); // get position of weapon

vec_set(my.pan,you.pan); // set pan to owner's pan

}

if (you.weapon == WEAPON_COMPRESSION_RIFLE)

{

vec_for_vertex(my.X,you,354); // get position of weapon

vec_set(my.pan,you.pan); // set pan to owner's pan

}

 

  We are moving right along here, aren't we? Ok go ahead and save and do a test run now as Host and Client. We can now select any profession and the correct weapon will be attached locally like so.

 

  Life is good! Life is grand! Let's get on to picking up power-ups now.

 

Picking Up Items from Level

  As stated in our game documentation in Chapter II, we will have 4 power-ups that can be pick up from level.

 

Lunar_Candy:

 Lunar Candy heals a player. Adds Health when picked up to Max_Health.

Ammunition_Packs:

  Used_Nuclear_Fuel: Ammunition for Nuclear Rod.

  Photosynthesis_Jelly: Ammunition for Photosythesis Emulator.

  SR-45b Ammunition Cartridge: Ammunition for Compression Rifle.

 

  First let's get the models declared in declarations.

 

string str_rifle = <rifle.mdl>; // compression rifle model

string str_nuclear_rod = <nuclearrod.mdl>; // nuclear rod mdl

string str_emulator = <lightgun.mdl>; // photosynthesis emulator model

string str_ammo_cartridge = <ammocartridge.mdl>; //ammo cartridge model

string str_nuclear_fuel = <nuclearfuel.mdl>; // nuclear fuel model

string str_photo_jelly = <photojelly.mdl>; // photosynthesis jelly model

string str_lunar_candy = <lunarcandy.mdl>; // lunar candy model

 

  Next, let's define our power-up types in declarations.

 

// Animation Speeds

define PLAYER_ANIM_WALK_SPEED, 6; // animation speed for walk

define PLAYER_ANIM_RUN_SPEED, 8; // animation speed for run

define PLAYER_ANIM_BLEND_SPEED, 10; // speed for blend

 

// Powerup Types

define POWER_UP_USED_NUCLEAR_FUEL ,1; // Ammunition for Nuclear Rod.

define POWER_UP_PHOTOSYNTHESIS_JELLY, 2; // Ammunition for Photosythesis Emulator.

define POWER_UP_AMMUNITION_CARTRIDGE, 3; // Ammunition for Compression Rifle.

define POWER_UP_LUNAR_CANDY, 4; // lunar candy power-up

 

  Right beneath that, lets add a define for the maximum number of any power-up model that can be present in the level at one time. So there can be a maximum of 10 ammunition cartridges models, 10 used nuclear fuel models, 10 photosynthesis jelly models, and 10 lunar candy models present in the level at one tim.

 

 define POWER_UP_AMMUNITION_CARTRIDGE, 3; // Ammunition for Compression Rifle.

define POWER_UP_LUNAR_CANDY, 4; // lunar candy power-up

 

// Power-up defines

define MAX_POWERUP_TYPE_IN_LEVEL, 10; // Maximum number of any power-up type

 

  Now, let's create a function that will create power-ups randomly in level over time up to the maximum number of possible power-ups. This function will be called at game start from server or if single player and will contually check for power-up spawns. Place this function below chat_entry().

 

str_cpy(strSendChat,""); // clear the strings for client and

str_cpy(strChatEntry,""); // server called functions

}

}

 

//--------------------------------------------------------------------

// Function Spawn_Power_Ups: randomly create power-ups

//--------------------------------------------------------------------

function spawn_power_ups()

{

var random_number;

 

while(1)

{

randomize();

random_number = int(random(1000)) + 1;

// the more players in game, the more likely a power-up is spawned

if (random_number > (999 - number_of_players))

{

create_power_up(); // place a power up into level

}

wait(1);

}

}

 

  Now, right above that function let's add the function that figures out what to spawn.

 

//--------------------------------------------------------------------

// Function Create_Power_Up: create a random power-up

//--------------------------------------------------------------------

function create_power_up()

{

var random_number;

var position_found = FALSE; // have we found the floor?

 

// while floor not found try new random vector for creation

while (position_found == FALSE)

{

// get random start place in middle of level

vecFrom.x =-550 + random(1100);

vecFrom.y =-550 + random(1100);

vecFrom.z = 200;

 

// get where to trace to

vec_set(vecTo,VecFrom);

vecTo.z = -200;

 

// trace checking for texture hit

trace_mode = IGNORE_SPRITES + IGNORE_PASSENTS + IGNORE_PASSABLE + IGNORE_MODELS + SCAN_TEXTURE + USE_BOX;

TRACE(vecFrom,vecTo);

 

// if we hit floor's texture than place power-up there

if(str_stri(tex_name,"earthtile") != FALSE)

{

position_found = TRUE; // we have now found the floor so set power-up

//type if that type not maxed

 

// set origin position

vec_set(temp_loc,vecTo);

temp_loc.z = 25;

// get random power-up type

random_number = int(random(10)) + 1;

 

// 7 - 10 = Lunar_Candy - if level not full of them, create 1

if ((random_number >= 7) && (lunar_candy_count <= 10))

{

ptrTempEnt = ent_create(str_lunar_candy,temp_loc,candy_function);

ptrTempEnt.power_up_type = POWER_UP_LUNAR_CANDY;

lunar_candy_count += 1; // increment lunar candies in level

goto(created_power_up);

}

 

// 5 - 6 = Photosynthesis Jelly - if level not full of them, create 1

if ((random_number >= 5) && (photo_jelly_count <= 10))

{

ptrTempEnt = ent_create(str_photo_jelly,temp_loc,ammo_function);

ptrTempEnt.power_up_type = POWER_UP_PHOTOSYNTHESIS_JELLY;

photo_jelly_count += 1; // increment photo jellies in level

goto(created_power_up);

}

 

// 3 - 4 = Nuclear Rod - if level not full of them, create 1

if ((random_number >= 3) && (nuclear_fuel_count <= 10))

{

ptrTempEnt = ent_create(str_nuclear_fuel,temp_loc,ammo_function);

ptrTempEnt.power_up_type = POWER_UP_USED_NUCLEAR_FUEL;

nuclear_fuel_count += 1; // increment nuclear fuels in level

goto(created_power_up);

}

 

// 1 - 2 = Ammo Catridge - if level not full of them, create 1

if ((random_number >= 1) && (ammo_cartridge_count <= 10))

{

ptrTempEnt = ent_create(str_ammo_cartridge,temp_loc,ammo_function);

ptrTempEnt.power_up_type = POWER_UP_AMMUNITION_CARTRIDGE;

ammo_cartridge_count += 1; // increment ammo cartridges in level

}

 

created_power_up:

 

}

}

}

 

//--------------------------------------------------------------------

// Function Spawn_Power_Ups: randomly create power-ups

//--------------------------------------------------------------------

 

  Ok, there will be no long drawn out explaination here. This function is called randomly from spawn_power_ups() functunction when a power up need to be created. If first finds a position on the floor. Once it finds a position it then choses once of our 4 possible power-ups randomly. If the number of that power-up is not maxed-out already it will create that power-up incrementing the count of that power-up type currently in level, otherwise it won't create a power-up at all.

  We added some new variables, let's declare them now.

 

var profession_not_set = TRUE; // don't start game until profession set

var force[3]; // temp forces

var nuclear_fuel_count; // number of nuclear fuel power ups in level

var ammo_cartridge_count; // number ammo cartridges in level

var photo_jelly_count; // number of photo jellies in level

var lunar_candy_count; // number of lunar candies in level

 

 We put in a new skill to hold the power-up type of this entity. Let's add that now in Skill Definitions.

 

define prev_anim_state, skill14; // previous animation state for blending

define weapon, skill15; // weapon ID this ent wields

define power_up_type, skill16; // power up type of this ent if a power up

 

  We also added a temporary entity pointer that we need to declare under Strings.

 

string strChat7[100];

string strChat8[100];

string strChatEntry[80]; // current chat entry

string strSendChat[100]; // chat to send

 

//--------------------------------------------------------------------

// Entity Pointers

//--------------------------------------------------------------------

entity* ptrTempEnt; // temp pointer to a entity

 

  You will notice we also added 2 new functions that will be called on the power-up creation. One for the ammunitions and one for lunar candy.

Let's go ahead and add these 2 functions above the create_power_up() function.

 

//--------------------------------------------------------------------

// Function Ammo_Function: set-up ammunition power-ups

//--------------------------------------------------------------------

function ammo_function()

{

//*** A6.3 feature my.floor_dist to scan the floor and to set entity onto the floor

if(MY.FLOOR_DIST > (MY.MAX_Z - MY.MIN_Z)/2)

{

MY.Z -= MY.FLOOR_DIST - (MY.MAX_Z - MY.MIN_Z)/2;

}

 

/* A5 scan floor;

// scan for floor if we are moving

if (my.force_x || my.force_y || my.force_z)

{

//Scan floor pre A6.30

trace_mode = IGNORE_SPRITES + IGNORE_PASSENTS + IGNORE_PASSABLE + IGNORE_MODELS + USE_BOX;

vec_set(vecFrom,my.X);

vec_set(vecTo,my.X);

vecTo.z -= 400;

RESULT = TRACE(vecFrom,vecTo);

my.Z = TARGET.Z + ((my.MAX_Z - my.MIN_Z)/2);//Set to the floor

}

*/

 

 

my.z -= 30; // model box adjustment for ammo

}

 

//--------------------------------------------------------------------

// Function Candy_Function: set-up lunar candy power-ups

//--------------------------------------------------------------------

function candy_function()

{

//*** A6.3 feature my.floor_dist to scan the floor and to set entity onto the floor

if(MY.FLOOR_DIST > (MY.MAX_Z - MY.MIN_Z)/2)

{

MY.Z -= MY.FLOOR_DIST - (MY.MAX_Z - MY.MIN_Z)/2;

}

/* A5 scan floor;

// scan for floor if we are moving

if (my.force_x || my.force_y || my.force_z)

{

//Scan floor pre A6.30

trace_mode = IGNORE_SPRITES + IGNORE_PASSENTS + IGNORE_PASSABLE + IGNORE_MODELS + USE_BOX;

vec_set(vecFrom,my.X);

vec_set(vecTo,my.X);

vecTo.z -= 400;

RESULT = TRACE(vecFrom,vecTo);

my.Z = TARGET.Z + ((my.MAX_Z - my.MIN_Z)/2);//Set to the floor

}

*/

 

my.z -= 25;

}

 

//--------------------------------------------------------------------

// Function Create_Power_Up: create a random power-up

//--------------------------------------------------------------------

 

  Next. let's add the call to the function spawn_power_ups() from our main() function.

 

level_load(world_str); // load level

sleep(.5); // make sure level is loaded

 

// had to place check for power ups here incase server doesn't choose professiion

// until after clients choose theirs

// if not client, create random power-ups (if server, host, or single-player)

if(connection != 2)

{

spawn_power_ups(); // continually create power-ups

}

 

  OK, we can do a test run as Host now and watch the the power-ups be randomly spawned over time. We can't pick them up yet because we haven't added that code yet, but we can test to make sure they are spawning.

  Now, let's get the power-ups so we can pick them up add do the proper action when we do so. Let's go ahead and do the hardest power-up's first, which are the ammunition power-ups. In declarations under skill definitions add this line of code so player knows how much ammunition he has left.

 

define prev_anim_state, skill14; // previous animation state for blending

define weapon, skill15; // weapon ID this ent wields

define power_up_type, skill16; // power up type of this ent if a power up

define ammunition_remaining, skill17; // ammunition entity has left

 

  Let's also add a define for the number of rounds of ammunition each player starts game with and the max ammunition that can be carried..

 

 

// Power-up defines

define MAX_POWERUP_TYPE_IN_LEVEL, 10; // Maximum number of any power-up type

 

// Ammunition Misc

define AMMO_GAME_START, 10; // The number of rounds each player starts game with

define AMMO_MAX, 50; // maximum ammunition a entity can have // Ammunition at Game Start

 

  Now in function move_player let's set the ammunition_remaining skill to are game start ammunition.

 

my.nosend_frame = ON; // don't send animation

 

my.pan = random(360); // face random direction

 

my.ammunition_remaining = AMMO_GAME_START; // rounds of ammunition at creation

 

 

  Next, in the function ammo_function(), let's enable collison and set our event function for when a ammunition power-up is collided with.

 

function ammo_function()

{

 

my.ENABLE_IMPACT = ON; // event for hit by another moving entity

my.EVENT = ammo_event; // event function

 

  Now let's add the ammo_event() function above the ammo_function(), which should give us a migrane as we go through the code.

 

//--------------------------------------------------------------------

// Function Ammo_Event: ammunition power-up touched, see if it needs

// picked up

//--------------------------------------------------------------------

function ammo_event()

{

// if player holding rifle and ammo is ammunition cartridge

// player picks it up

if ((you.weapon == WEAPON_COMPRESSION_RIFLE) && (my.power_up_type == POWER_UP_AMMUNITION_CARTRIDGE))

{

// only can pick-up if ammo not full

if (you.ammunition_remaining < AMMO_MAX)

{

// add up to 15 rounds of ammo

you.ammunition_remaining += int(random(15)) + 1;

// if over max ammo, set to max ammo

if (you.ammunition_remaining > AMMO_MAX)

{

you.ammunition_remaining = AMMO_MAX;

}

 

// play power-up sound at ent

ent_playsound (you, snd_power_up, 60);

// send ammo remaining to clients

send_skill(you.ammunition_remaining, 0);

ammo_cartridge_count -= 1; // 1 less cartridge in level

ent_remove(my); // remove ammo

}

}

else

{

// if player holding nuclear rod and ammo is nuclear fuel

/// player picks it up

if ((you.weapon == WEAPON_NUCLEAR_ROD) && (my.power_up_type == POWER_UP_USED_NUCLEAR_FUEL))

{

// only can pick-up if ammo not full

if (you.ammunition_remaining < AMMO_MAX)

{

// add up to 15 rounds of ammo

you.ammunition_remaining += int(random(15)) + 1;

// if over max ammo, set to max ammo

if (you.ammunition_remaining > AMMO_MAX)

{

you.ammunition_remaining = AMMO_MAX;

}

// play power-up sound at ent

ent_playsound (you, snd_power_up, 60);

// send ammo remaining to clients

send_skill(you.ammunition_remaining, 0);

nuclear_fuel_count -= 1; // 1 less nuke fuel in level

ent_remove(my); // remove ammo

}

}

else

{

if ((you.weapon == WEAPON_PHOTOSYNTHESIS_EMULATOR) && (my.power_up_type == POWER_UP_PHOTOSYNTHESIS_JELLY))

{

// only can pick-up if ammo not full

if (you.ammunition_remaining < AMMO_MAX)

{

you.ammunition_remaining += int(random(15)) + 1;

// if over max ammo, set to max ammo

if (you.ammunition_remaining > AMMO_MAX)

{

you.ammunition_remaining = AMMO_MAX;

}

// play power-up sound at ent

ent_playsound (you, snd_power_up, 60);

// send ammo remaining to clients

send_skill(you.ammunition_remaining, 0);

photo_jelly_count -= 1; // 1 less jelly in level

ent_remove(my); // remove ammo

}

}

}

}

}

 

//--------------------------------------------------------------------

// Function Ammo_Function: set-up ammunition power-ups

//--------------------------------------------------------------------

 

  I am just going to explain what is happenning in the first if stament since all the rest of them are the same except they are setting up different ammunition types. We must remember that the you variable has been set to the entity that collided with the ammunition pack. Ok now we can go through the code.

  First we check if the ammunition_type of this ammo pack that was collided with is a AMMO_AMMUNITION_CARTRIDGE and if the colliding entity's weapon is a WEAPON_COMPRESSION_RIFLE. Since a compression rifle uses a ammunition cartridge, if this if statement is true we can go ahead and add ammunition to the colliding entity's ammunition_remaining.

// if player holding rifle and ammo is ammunition cartridge

// player picks it up

if ((you.weapon == WEAPON_COMPRESSION_RIFLE) && (my.ammo_type == AMMO_AMMUNITION_CARTRIDGE))

{

  Ok, so if a the entity that collided with the ammo pack is carrying a compression rifle and the ammunition is a ammunition cartridge we can add the ammunition catridge rounds into the player's ammunition remaining.

  Next we make sure the player's ammunition_remaining is not maxed out. If it is we do not allow him to pick up the ammo pack.

// only can pick-up if ammo not full

if (you.ammunition_remaining < AMMO_MAX)

{

  If his ammunition is not maxed, then we add some random ammunition and if we go over the max ammunition allowed then we set the ammunition_remaining to AMMO_MAX.

// add up to 15 rounds of ammo

you.ammunition_remaining += int(random(15)) + 1;

// if over max ammo, set to max ammo

if (you.ammunition_remaining > AMMO_MAX)

{

you.ammunition_remaining = AMMO_MAX;

}

  Then we use ent_playsound() to play the power-up pick up sound at the player's position.

// play power-up sound at ent

ent_playsound (you, snd_power_up, 60);

  After that, we send the player's ammunition_remaining skill to the client that we added the ammunition to, so he can display his ammo by using send_skill(entity.skill, 0). Then we decrement the number of ammunition cartridge power-ups in the level. and then remove the ammunition cartridge model from the level.

// send ammo remaining to clients

send_skill(you.ammunition_remaining, 0);

ammo_cartridge_count -= 1; // 1 less cartridge in level

ent_remove(my); // remove ammo

  Then, if the first if statement wasn't true we check for the other weapons and ammunition types and then do the same kind of action for them if they match.

  Ok, let's declare our power-up sound since we have it in the code now. Actually, let's go ahead and define all of our sounds since we be using them all soon. In declarations under Resources add these sound declarations.

 

// Fonts

font fnt_century12 = <Centur12.pcx>,16,20; // Century12 font

 

// Sounds

sound snd_radiation_beam = <radiationbeam.wav>;

sound snd_emulator = <emulator.wav>;

sound snd_rifle = <rifle.wav>;

sound snd_damage = <damage.wav>;

sound snd_no_damage = <nodamage.wav>;

sound snd_power_up = <powerup.wav>;

 

  We are not quite done yet. Lets add a string and some text to display the player's ammuntion_remaining and his AMMO_MAX so he knows what he has left. In declarations under Display Strings add this.

 

string str_number_of_players; // string to hold number of players

string str_player_name; // player name for text display

string str_ammo_remaining; // ammunition count of this player

 

  Then in declarations under Text add this.

 

// text to display this player's name

text txt_player_name

{

pos_x = 5;

pos_y = 5;

layer = 15;

font fnt_century12;

string str_player_name;

}

 

// text to display this player's ammunition remaining

text txt_ammo_remaining

{

pos_x = 5;

pos_y = 95;

layer = 15;

font fnt_century12;

string str_ammo_remaining;

}

 

  And then in the display_info() function add this code in the if(player != NULL) statement.

 

if (player != NULL) // player exist

{

str_cpy(str_player_num, "Player Number: ");

str_for_num(str_temp, player.player_number);

str_cat(str_player_num, str_temp);

 

str_cpy(str_ammo_remaining, "Ammunition Remaining: ");

str_for_num(str_temp, player.ammunition_remaining);

str_cat(str_ammo_remaining, str_temp);

str_cat(str_ammo_remaining, "/");

str_for_num(str_temp, AMMO_MAX);

str_cat(str_ammo_remaining, str_temp);

}

 

  Now we need to add the turn on and turn off script for the text in function main().

 

mouse_mode = 2; // mouse with no force changes

mouse_on(); // turn mouse on

 

// turn off all text not needed for profession selection

txt_ammo_remaining.visible = OFF;

 

And...

 

// wait for this player's entity to be created

while(!player)

{

wait(1);

}

 

// display player specific text

txt_ammo_remaining.visible = ON;

 

And last but not least, let's make some changes that will make un-pickable power-ups passable depending on the weapon carried by the player. I didn't original have this code in the tutorial, but after some test some people suggested that non-usable power-ups should be passable so I added it. But this one you will have to do your homework on if you want to figure it out. In function move_player remove these lines of code.

 

// use time & speed_forwards_factor

force.x = my.force_x * my.speed * time;

force.y = 0;

force.z = 0;

 

// A5 move

move_mode = glide + ignore_passable;

ent_move(force.x,nullvector);

 

//A6.3 move the player, c_move bugged at time of release so using ent_move instead

//c_move(my, force.x, nullvector, glide + ignore_passable);

 

  And replace it with this wonderous script. Beware what you wish for.

 

// use time & speed_forwards_factor

force.x = my.force_x * my.speed * time;

force.y = 0;

force.z = 0;

 

trace_vecTo.x = my.x + (force.x*2) * cos(my.pan);

trace_vecTo.y = my.y + (force.x*2) * sin(my.pan);

trace_vecTo.z = my.z;

 

//A5 trace

trace_mode = ignore_passable + ignore_passents + ignore_me + use_box;

trace(my.X, trace_vecTo);

 

//A6.3 trace

//c_trace(my.X, trace_vecTo, ignore_passable + ignore_passents + ignore_me + use_box);

 

// if hit something check if it should be passable or not

if(you)

{

if((my.ammunition_remaining == AMMO_MAX)&&(my.health == my.max_health))

{

// A5 move

move_mode = glide + ignore_passable + ignore_passents + ignore_you;

ent_move(force.x,nullvector);

 

//A6.3 move the player, c_move bugged at time of release so using ent_move instead

//c_move(my, force.x, nullvector, glide + ignore_passable + ignore_passents + ignore_you);

}

else

{

if(you.power_up_type == POWER_UP_LUNAR_CANDY)

{

if(my.health < my.max_health)

{

// A5 move

move_mode = glide + ignore_passable + ignore_passents;

ent_move(force.x,nullvector);

 

//A6.3 move the player, c_move bugged at time of release so using ent_move instead

//c_move(my, force.x, nullvector, glide + ignore_passable + ignore_passents);

}

else

{

// A5 move

move_mode = glide + ignore_passable + ignore_passents + ignore_you;

ent_move(force.x,nullvector);

 

// A6.3 move the player, c_move bugged at time of release so using ent_move instead

//c_move(my, force.x, nullvector, glide + ignore_passable + ignore_passents + ignore_you);

}

}

else // power up is ammo

{

if ((((my.weapon == WEAPON_COMPRESSION_RIFLE) && (you.power_up_type == POWER_UP_AMMUNITION_CARTRIDGE))

|| ((my.weapon == WEAPON_NUCLEAR_ROD) && (you.power_up_type == POWER_UP_USED_NUCLEAR_FUEL))

|| ((my.weapon == WEAPON_PHOTOSYNTHESIS_EMULATOR) && (you.power_up_type == POWER_UP_PHOTOSYNTHESIS_JELLY)))

&& (my.ammunition_remaining < AMMO_MAX))

{

// A5 move

move_mode = glide + ignore_passable + ignore_passents;

ent_move(force.x,nullvector);

 

//A6.3 move the player, c_move bugged at time of release so using ent_move instead

//c_move(my, force.x, nullvector, glide + ignore_passable + ignore_passents);

}

else

{

// A5 move

move_mode = glide + ignore_passable + ignore_passents + ignore_you;

ent_move(force.x,nullvector);

 

// A6.3 move the player, c_move bugged at time of release so using ent_move instead

//c_move(my, force.x, nullvector, glide + ignore_passable + ignore_passents + ignore_you);

}

}

}

}

else

{

// A5 move

move_mode = glide + ignore_passable + ignore_passents;

ent_move(force.x,nullvector);

 

//A6.3 move the player, c_move bugged at time of release so using ent_move instead

//c_move(my, force.x, nullvector, glide + ignore_passable + ignore_passents);

}

 

  Yeah baby! That was a fun one. That ought to get the old brain juices flowing.

  We added a couple of variables in there, so let's add them now to function move_player.

 

function move_player()

{

var trace_vecFrom[3];

var trace_vecTo[3];

 

  You may be asking yourself right about now, "Did I really want to make a multiplayer game?"

  Well, of course you did. You just might not have realized the complexity of it all. Anyhow, let's give it a test run on with a Host and Client and make sure all is working well. You should be able to pick up the right ammunition packs for your player's profession now up to the maximum allowed ammo. You should now see something like this.

  Darn! We are not done with this chapter just yet. We still need to add the lunar candy power-up's code for health. I am not going to explain everything we are adding here, because it's basically the same as what we just did for the ammunition packs. It is actually much easier since we don't have to compare the weapon type to the ammunition type.

  First, in the candy_function() add the event code.

 

function candy_function()

{

 

my.ENABLE_IMPACT = ON; // event for hit by another moving entity

my.EVENT = candy_event; // event function

 

  Then above the candy_function() add the candy_event().

 

//--------------------------------------------------------------------

// Function Candy_Event: lunar_candy(health) power-up touched,add some health

//--------------------------------------------------------------------

function candy_event()

{

// if touched lunar candy and not at max health

if (you.health < you.max_health)

{

// add health

you.health += int(random(10) + you.max_health/10);

 

// if over max health, set to max health

if(you.health > you.max_health)

{

you.health = you.max_health;

}

// play power-up sound ant ent

ent_playsound (you, snd_power_up, 60);

send_skill(you.health,SEND_ALL); // send new health to clients

lunar_candy_count -= 1; // 1 less lunar candy in level

ent_remove(my); // remove lunar candy

}

}

 

//--------------------------------------------------------------------

// Function Candy_Function: set-up lunar candy power-ups

//--------------------------------------------------------------------

 

  Next, let's add the string declarations.

 

string str_player_name; // player name for text display

string str_ammo_remaining; // ammunition count of this player

string str_health; // player's health string

 

  Then the text declaration.

 

// text to display this player's ammunition remaining

text txt_ammo_remaining

{

pos_x = 5;

pos_y = 95;

layer = 15;

font fnt_century12;

string str_ammo_remaining;

}

 

// text to display this player's health

text txt_health

{

pos_x = 5;

pos_y = 65;

layer = 15;

font fnt_century12;

string str_health;

}

 

  Next in display_info() add this code.

 

if (player != NULL) // player exist

{

str_cpy(str_player_num, "Player Number: ");

str_for_num(str_temp, player.player_number);

str_cat(str_player_num, str_temp);

 

str_cpy(str_health, "Health: ");

str_for_num(str_temp, int(player.health));

str_cat(str_health, str_temp);

str_cat(str_health, "/");

str_for_num(str_temp, player.max_health);

str_cat(str_health, str_temp);

 

  In function main() we need to add

 

// turn off all text not needed for profession selection

txt_health.visible = OFF;

txt_ammo_remaining.visible = OFF;

 

and

 

// display player specific text

txt_health.visible = ON;

txt_ammo_remaining.visible = ON;

 

  After that, we need to take a break because we are finally done with this chapter. Whew! I thought it would never end.

  If you run the program now each player's health and max health should be displayed, but you cannot pick up the lunar candy yet because all of the the player's health skills are at max. The player's will need to do some damage to each other before they can pick up the candy, which will be in the next chapter. Hooray!

 

Previous Page Contents Next Page