Jump to content

Jedi Academy turned 20 this year! We celebrated in a ton of different ways: mod contest, server event, podcast, etc. Thank you to all who have been a part of this game for the last two decades. Check out the anniversary content!

Read more

Welcome to JKHub

This community is dedicated to the games Star Wars: Jedi Outcast (2002) and Jedi Academy (2003). We host over 3,000 mods created by passionate fans around the world, and thousands of threads of people showcasing their works in progress and asking for assistance. From mods to art to troubleshooting help, we probably have it. If we don't, request or contribute!

Get started

This game turned 20 years old this year, and it is still one of the greatest Star Wars games of all time. If you're new or returning from a long hiatus, here are the basics of getting started with Star Wars Jedi Knight Jedi Academy in 2023.

Read more

Create a new Force Power [MP]


eezstreet

Welcome to the second entry in the FAQ (Frequently Asked Questions) series. These will teach you how to do things which are frequently asked about on the forums.

 

Important Note: You must compile the engine as well as the DLLs in order to do this.

 

Important Note 2: You are limited to 32 force powers, no more.

 

Important Note 3: This will render your game non-backwards compatible with savegames and connectivity.

 

You will need a new icon, but the rest is up to you.

Sorry in advance about the broken indentation, JKHub doesn't like it when I copy/paste indents. :(

 

Today you're going to learn about the Jedi's ways of the Force, young padawan. Specifically, you're going to learn how to create a new force power. The first thing we're going to do, just like in the weapons tutorial, is define our force power. I'm going to be making a force power called Magic Missiles to use as an example. It fires multiple homing missiles and costs 70 force power to use.

 

So to specify the power, we're going to alter the forcePowers_t enum in q_shared.h. It looks like this:

 

typedef enum

{

FP_FIRST = 0,//marker

FP_HEAL = 0,//instant

FP_LEVITATION,//hold/duration

FP_SPEED,//duration

FP_PUSH,//hold/duration

FP_PULL,//hold/duration

FP_TELEPATHY,//instant

FP_GRIP,//hold/duration

FP_LIGHTNING,//hold/duration

FP_RAGE,//duration

FP_PROTECT,

FP_ABSORB,

FP_TEAM_HEAL,

FP_TEAM_FORCE,

FP_DRAIN,

FP_SEE,

FP_SABER_OFFENSE,

FP_SABER_DEFENSE,

FP_SABERTHROW,

NUM_FORCE_POWERS

} forcePowers_t;

So we'll want to add ourselves a new power here. I'm going to add my power right between Sense and Saber Offense, since that seems most appropriate.

 

typedef enum

{

FP_FIRST = 0,//marker

FP_HEAL = 0,//instant

FP_LEVITATION,//hold/duration

FP_SPEED,//duration

FP_PUSH,//hold/duration

FP_PULL,//hold/duration

FP_TELEPATHY,//instant

FP_GRIP,//hold/duration

FP_LIGHTNING,//hold/duration

FP_RAGE,//duration

FP_PROTECT,

FP_ABSORB,

FP_TEAM_HEAL,

FP_TEAM_FORCE,

FP_DRAIN,

FP_SEE,

FP_MAGIC_MISSILES, // eezstreet add

FP_SABER_OFFENSE,

FP_SABER_DEFENSE,

FP_SABERTHROW,

NUM_FORCE_POWERS

} forcePowers_t;

 

I'm going to skip around here a little bit, and work on a few key areas that need addressed first.

In bg_misc.c, we have a number of important structures that need filled out. First off, we have bgForcePowerCost. This defines how many force points are needed for each rank to unlock the power. Magic Missiles is going to be a quite hefty costing power, because it's pretty powerful. So, this is what mine looks like:

 

int bgForcePowerCost[NUM_FORCE_POWERS][NUM_FORCE_POWER_LEVELS] = //0 == neutral

{

{ 0, 2, 4, 6 }, // Heal // FP_HEAL

{ 0, 0, 2, 6 }, // Jump //FP_LEVITATION,//hold/duration

{ 0, 2, 4, 6 }, // Speed //FP_SPEED,//duration

{ 0, 1, 3, 6 }, // Push //FP_PUSH,//hold/duration

{ 0, 1, 3, 6 }, // Pull //FP_PULL,//hold/duration

{ 0, 4, 6, 8 }, // Mind Trick //FP_TELEPATHY,//instant

{ 0, 1, 3, 6 }, // Grip //FP_GRIP,//hold/duration

{ 0, 2, 5, 8 }, // Lightning //FP_LIGHTNING,//hold/duration

{ 0, 4, 6, 8 }, // Dark Rage //FP_RAGE,//duration

{ 0, 2, 5, 8 }, // Protection //FP_PROTECT,//duration

{ 0, 1, 3, 6 }, // Absorb //FP_ABSORB,//duration

{ 0, 1, 3, 6 }, // Team Heal //FP_TEAM_HEAL,//instant

{ 0, 1, 3, 6 }, // Team Force //FP_TEAM_FORCE,//instant

{ 0, 2, 4, 6 }, // Drain //FP_DRAIN,//hold/duration

{ 0, 2, 5, 8 }, // Sight //FP_SEE,//duration

{ 0, 4, 6, 8 }, // Magic Missiles //FP_MAGIC_MISSILES // eezstreet add

{ 0, 1, 5, 8 }, // Saber Attack //FP_SABER_OFFENSE,

{ 0, 1, 5, 8 }, // Saber Defend //FP_SABER_DEFENSE,

{ 0, 4, 6, 8 } // Saber Throw //FP_SABERTHROW,

//NUM_FORCE_POWERS

};

 

Directly below that, we have forcePowerSorted, which is the position of the force powers in the wheel (when scrolling through the powers using q/e on the keyboard). I'm going to put mine along with all the other light side powers.

 

 

int forcePowerSorted[NUM_FORCE_POWERS] =

{ //rww - always use this order when drawing force powers for any reason

FP_TELEPATHY,

FP_HEAL,

FP_ABSORB,

FP_MAGIC_MISSILES, // eezstreet add

FP_PROTECT,

FP_TEAM_HEAL,

FP_LEVITATION,

FP_SPEED,

FP_PUSH,

FP_PULL,

FP_SEE,

FP_LIGHTNING,

FP_DRAIN,

FP_RAGE,

FP_GRIP,

FP_TEAM_FORCE,

FP_SABER_OFFENSE,

FP_SABER_DEFENSE,

FP_SABERTHROW

};

 

Next we have forcePowerDarkLight, which describes the affiliation of a power (whether or not it is dark-sided or light-sided). Mine is going to be a light-sided power, so we're going to make sure we specify that.

 

 

int forcePowerDarkLight[NUM_FORCE_POWERS] = //0 == neutral

{ //nothing should be usable at rank 0..

FORCE_LIGHTSIDE,//FP_HEAL,//instant

0,//FP_LEVITATION,//hold/duration

0,//FP_SPEED,//duration

0,//FP_PUSH,//hold/duration

0,//FP_PULL,//hold/duration

FORCE_LIGHTSIDE,//FP_TELEPATHY,//instant

FORCE_DARKSIDE,//FP_GRIP,//hold/duration

FORCE_DARKSIDE,//FP_LIGHTNING,//hold/duration

FORCE_DARKSIDE,//FP_RAGE,//duration

FORCE_LIGHTSIDE,//FP_PROTECT,//duration

FORCE_LIGHTSIDE,//FP_ABSORB,//duration

FORCE_LIGHTSIDE,//FP_TEAM_HEAL,//instant

FORCE_DARKSIDE,//FP_TEAM_FORCE,//instant

FORCE_DARKSIDE,//FP_DRAIN,//hold/duration

0,//FP_SEE,//duration

FORCE_LIGHTSIDE, // FP_MAGIC_MISSILES // eezstreet add

0,//FP_SABER_OFFENSE,

0,//FP_SABER_DEFENSE,

0//FP_SABERTHROW,

//NUM_FORCE_POWERS

};

 

That's the last of the edits in bg_misc.c. Next up is bg_saga.c. This is only a minor edit, we're going to add one entry to FPTable so that people can use Magic Missiles in a siege class.

 

stringID_table_t FPTable[] =

{

ENUM2STRING(FP_HEAL),

ENUM2STRING(FP_LEVITATION),

ENUM2STRING(FP_SPEED),

ENUM2STRING(FP_PUSH),

ENUM2STRING(FP_PULL),

ENUM2STRING(FP_TELEPATHY),

ENUM2STRING(FP_GRIP),

ENUM2STRING(FP_LIGHTNING),

ENUM2STRING(FP_RAGE),

ENUM2STRING(FP_PROTECT),

ENUM2STRING(FP_ABSORB),

ENUM2STRING(FP_TEAM_HEAL),

ENUM2STRING(FP_TEAM_FORCE),

ENUM2STRING(FP_DRAIN),

ENUM2STRING(FP_SEE),
ENUM2STRING(FP_MAGIC_MISSILES), // eezstreet add

ENUM2STRING(FP_SABER_OFFENSE),

ENUM2STRING(FP_SABER_DEFENSE),

ENUM2STRING(FP_SABERTHROW),

{"", -1}

};

 

Now we need to edit showPowersName in cg_draw.c. This defines an entry in .str files that is shown on screen when we're cycling through the powers, e.g. "Force Sight". The string can be replaced in sp_ingame.str for your language.

 

char *showPowersName[] =

{

"HEAL2",//FP_HEAL

"JUMP2",//FP_LEVITATION

"SPEED2",//FP_SPEED

"PUSH2",//FP_PUSH

"PULL2",//FP_PULL

"MINDTRICK2",//FP_TELEPTAHY

"GRIP2",//FP_GRIP

"LIGHTNING2",//FP_LIGHTNING

"DARK_RAGE2",//FP_RAGE

"PROTECT2",//FP_PROTECT

"ABSORB2",//FP_ABSORB

"TEAM_HEAL2",//FP_TEAM_HEAL

"TEAM_REPLENISH2",//FP_TEAM_FORCE

"DRAIN2",//FP_DRAIN

"SEEING2",//FP_SEE

"MAGICMISSILES2", // FP_MAGIC_MISSILES // eezstreet add

"SABER_OFFENSE2",//FP_SABER_OFFENSE

"SABER_DEFENSE2",//FP_SABER_DEFENSE

"SABER_THROW2",//FP_SABERTHROW

NULL

};

 

Optional: Holocron model

If you plan on reimplementing Holocron FFA, we need to add an entry to forceHolocronModels in cg_ents.c, so that the game knows what model to use for Holocrons.

 

 
char *forceHolocronModels[] = {

"models/map_objects/mp/lt_heal.md3", //FP_HEAL,

"models/map_objects/mp/force_jump.md3", //FP_LEVITATION,

"models/map_objects/mp/force_speed.md3", //FP_SPEED,

"models/map_objects/mp/force_push.md3", //FP_PUSH,

"models/map_objects/mp/force_pull.md3", //FP_PULL,

"models/map_objects/mp/lt_telepathy.md3", //FP_TELEPATHY,

"models/map_objects/mp/dk_grip.md3", //FP_GRIP,

"models/map_objects/mp/dk_lightning.md3", //FP_LIGHTNING,

"models/map_objects/mp/dk_rage.md3", //FP_RAGE,

"models/map_objects/mp/lt_protect.md3", //FP_PROTECT,

"models/map_objects/mp/lt_absorb.md3", //FP_ABSORB,

"models/map_objects/mp/lt_healother.md3", //FP_TEAM_HEAL,

"models/map_objects/mp/dk_powerother.md3", //FP_TEAM_FORCE,

"models/map_objects/mp/dk_drain.md3", //FP_DRAIN,

"models/map_objects/mp/force_sight.md3", //FP_SEE,
"INSERT_PATH_TO_MODEL_HERE", // FP_MAGIC_MISSILES // eezstreet add

"models/map_objects/mp/saber_attack.md3", //FP_SABER_OFFENSE,

"models/map_objects/mp/saber_defend.md3", //FP_SABER_DEFENSE,

"models/map_objects/mp/saber_throw.md3" //FP_SABERTHROW

};

 

Next up, we need to define the force power icon. These are (very strangely) kept in holocronicons.h, which is unique in that it only contains this structure.

 

const char *HolocronIcons[NUM_FORCE_POWERS] = {

"gfx/mp/f_icon_lt_heal", //FP_HEAL,

"gfx/mp/f_icon_levitation", //FP_LEVITATION,

"gfx/mp/f_icon_speed", //FP_SPEED,

"gfx/mp/f_icon_push", //FP_PUSH,

"gfx/mp/f_icon_pull", //FP_PULL,

"gfx/mp/f_icon_lt_telepathy", //FP_TELEPATHY,

"gfx/mp/f_icon_dk_grip", //FP_GRIP,

"gfx/mp/f_icon_dk_l1", //FP_LIGHTNING,

"gfx/mp/f_icon_dk_rage", //FP_RAGE,

"gfx/mp/f_icon_lt_protect", //FP_PROTECT,

"gfx/mp/f_icon_lt_absorb", //FP_ABSORB,

"gfx/mp/f_icon_lt_healother", //FP_TEAM_HEAL,

"gfx/mp/f_icon_dk_forceother", //FP_TEAM_FORCE,

"gfx/mp/f_icon_dk_drain", //FP_DRAIN,

"gfx/mp/f_icon_sight", //FP_SEE,
"gfx/mp/f_icon_pull", // FP_MAGIC_MISSILES // eezstreet add, I'm just using the Pull icon for now as a test.

"gfx/mp/f_icon_saber_attack", //FP_SABER_OFFENSE,

"gfx/mp/f_icon_saber_defend", //FP_SABER_DEFENSE,

"gfx/mp/f_icon_saber_throw" //FP_SABERTHROW

};

Alright, cool! Now we're going to dive into the engine source code. Brace yourself, because this is going to be a doozy.

We're first going to open up q_shared.h again, and this time we're going to be focusing on a different enum: genCmds_t. This is used for quick commands by the client, and if we don't add a special case for our new power, the game will throw a fit (but still likely work as expected).

 

typedef enum

{

GENCMD_SABERSWITCH = 1,

GENCMD_ENGAGE_DUEL,

GENCMD_FORCE_HEAL,

GENCMD_FORCE_SPEED,

GENCMD_FORCE_THROW,

GENCMD_FORCE_PULL,

GENCMD_FORCE_DISTRACT,

GENCMD_FORCE_RAGE,

GENCMD_FORCE_PROTECT,

GENCMD_FORCE_ABSORB,

GENCMD_FORCE_HEALOTHER,

GENCMD_FORCE_FORCEPOWEROTHER,

GENCMD_FORCE_SEEING,

GENCMD_FORCE_MAGIC_MISSILES, // eezstreet add

GENCMD_USE_SEEKER,

GENCMD_USE_FIELD,

GENCMD_USE_BACTA,

GENCMD_USE_ELECTROBINOCULARS,

GENCMD_ZOOM,

GENCMD_USE_SENTRY,

GENCMD_USE_JETPACK,

GENCMD_USE_BACTABIG,

GENCMD_USE_HEALTHDISP,

GENCMD_USE_AMMODISP,

GENCMD_USE_EWEB,

GENCMD_USE_CLOAK,

GENCMD_SABERATTACKCYCLE,

GENCMD_TAUNT,

GENCMD_BOW,

GENCMD_MEDITATE,

GENCMD_FLOURISH,

GENCMD_GLOAT

} genCmds_t;

Right. Now in cl_input.cpp (it's in client/), we're going to add a new entry here in the switch within IN_UseGivenForce. Fairly straightforward.

 

switch(forceNum) {

case FP_DRAIN:

IN_Button11Down();

IN_Button11Up();

break;

case FP_PUSH:

genCmdNum = GENCMD_FORCE_THROW;

break;

case FP_SPEED:

genCmdNum = GENCMD_FORCE_SPEED;

break;

case FP_PULL:

genCmdNum = GENCMD_FORCE_PULL;

break;

case FP_TELEPATHY:

genCmdNum = GENCMD_FORCE_DISTRACT;

break;

case FP_GRIP:

IN_Button6Down();

IN_Button6Up();

break;

case FP_LIGHTNING:

IN_Button10Down();

IN_Button10Up();

break;

case FP_RAGE:

genCmdNum = GENCMD_FORCE_RAGE;

break;

case FP_PROTECT:

genCmdNum = GENCMD_FORCE_PROTECT;

break;

case FP_ABSORB:

genCmdNum = GENCMD_FORCE_ABSORB;

break;

case FP_SEE:

genCmdNum = GENCMD_FORCE_SEEING;

break;

case FP_HEAL:

genCmdNum = GENCMD_FORCE_HEAL;

break;

case FP_TEAM_HEAL:

genCmdNum = GENCMD_FORCE_HEALOTHER;

break;

case FP_TEAM_FORCE:

genCmdNum = GENCMD_FORCE_FORCEPOWEROTHER;

break;

case FP_MAGIC_MISSILES: // eezstreet add

genCmdNum = GENCMD_FORCE_MAGIC_MISSILES;

break;

default:

assert(0);

break;

}

Last, but certainly not least, we have w_force.c. This is the main meat of the force power, and it defines what the force power actually does when used. I'm not going to go too far into detail here, because most of what the power does is up to you. However, I will be setting up the framework for your power.

The first change here involves WP_ForcePowerStart. The changes here vary between whether you're making a duration power (such as Sense), a held power (such as Lightning) or a one-press power (such as Heal). In my example, I'm using a one-press power, which is by far the easiest thing to deal with in this case. If you want to make a duration power, I suggest you pay close attention as to how Sense does it, because it's a very good example of how you should be dealing with it.

But in my example, there really isn't much that needs to be done here.

 

//hearable and hearDist are merely for the benefit of bots, and not related to if a sound is actually played.

//If duration is set, the force power will assume to be timer-based.

switch( (int)forcePower )

{

case FP_HEAL:

hearable = qtrue;

hearDist = 256;

self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );

break;

case FP_LEVITATION:

hearable = qtrue;

hearDist = 256;

self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );

break;

case FP_SPEED:

hearable = qtrue;

hearDist = 256;

if (self->client->ps.fd.forcePowerLevel[FP_SPEED] == FORCE_LEVEL_1)

{

duration = 10000;

}

else if (self->client->ps.fd.forcePowerLevel[FP_SPEED] == FORCE_LEVEL_2)

{

duration = 15000;

}

else if (self->client->ps.fd.forcePowerLevel[FP_SPEED] == FORCE_LEVEL_3)

{

duration = 20000;

}

else //shouldn't get here

{

break;

}



if (overrideAmt)

{

duration = overrideAmt;

}



self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );

break;

case FP_PUSH:

hearable = qtrue;

hearDist = 256;

break;

case FP_PULL:

hearable = qtrue;

hearDist = 256;

break;

case FP_TELEPATHY:

hearable = qtrue;

hearDist = 256;

if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_1)

{

duration = 20000;

}

else if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_2)

{

duration = 25000;

}

else if (self->client->ps.fd.forcePowerLevel[FP_TELEPATHY] == FORCE_LEVEL_3)

{

duration = 30000;

}

else //shouldn't get here

{

break;

}



self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );

break;

case FP_GRIP:

hearable = qtrue;

hearDist = 256;

self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );

self->client->ps.powerups[PW_DISINT_4] = level.time + 60000;

break;

case FP_LIGHTNING:

hearable = qtrue;

hearDist = 512;

duration = overrideAmt;

overrideAmt = 0;

self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );

self->client->ps.activeForcePass = self->client->ps.fd.forcePowerLevel[FP_LIGHTNING];

break;

case FP_RAGE:

hearable = qtrue;

hearDist = 256;

if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_1)

{

duration = 8000;

}

else if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_2)

{

duration = 14000;

}

else if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_3)

{

duration = 20000;

}

else //shouldn't get here

{

break;

}



self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );

break;

case FP_PROTECT:

hearable = qtrue;

hearDist = 256;

duration = 20000;

self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );

break;

case FP_ABSORB:

hearable = qtrue;

hearDist = 256;

duration = 20000;

self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );

break;

case FP_TEAM_HEAL:

hearable = qtrue;

hearDist = 256;

self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );

break;

case FP_TEAM_FORCE:

hearable = qtrue;

hearDist = 256;

self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );

break;

case FP_DRAIN:

hearable = qtrue;

hearDist = 256;

duration = overrideAmt;

overrideAmt = 0;

self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );

//self->client->ps.activeForcePass = self->client->ps.fd.forcePowerLevel[FP_DRAIN];

break;

case FP_SEE:

hearable = qtrue;

hearDist = 256;

if (self->client->ps.fd.forcePowerLevel[FP_SEE] == FORCE_LEVEL_1)

{

duration = 10000;

}

else if (self->client->ps.fd.forcePowerLevel[FP_SEE] == FORCE_LEVEL_2)

{

duration = 20000;

}

else if (self->client->ps.fd.forcePowerLevel[FP_SEE] == FORCE_LEVEL_3)

{

duration = 30000;

}

else //shouldn't get here

{

break;

}



self->client->ps.fd.forcePowersActive |= ( 1 << forcePower );

break;

case FP_SABER_OFFENSE:

break;

case FP_SABER_DEFENSE:

break;

case FP_SABERTHROW:

break;

case FP_MAGIC_MISSILES:

// eezstreet add

hearDist = 256;

hearable = qtrue;

break;

default:

break;

}

 

OK! Now we're going to create a function that is where we define the behavior of the force power when used (yay!). Very simply put, we're going to first declare the function in g_local.h (towards the bottom).

 

void ForceMagicMissiles( gentity_t *self );

And then in w_force.c, we're going to actually define the function and get in depth with how we're going to shoot missiles and the like. So right around where the other functions are being defined (such as ForceTeamReplenish), we're going to define our own:

 

void ForceMagicMissiles( gentity_t *self )

{

// Write the code here yourself, as this is the behavior of your force power --eez

}

 

Okay. We have one last edit that needs to be done here in w_force.c, and we're good to go.

After this:

 

case FP_TEAM_FORCE:

powerSucceeded = 0; //always 0 for nonhold powers

if (self->client->ps.fd.forceButtonNeedRelease)

{ //need to release before we can use nonhold powers again

break;

}

ForceTeamForceReplenish(self);

self->client->ps.fd.forceButtonNeedRelease = 1;

break;

Add our little block of code, which looks like this:

 

case FP_MAGIC_MISSILES:

ForceMagicMissiles( self );

break;

 

Okay, super. Now in bg_pmove.c, we need to make an edit here so that the force power costs don't get thrown out of whack...

 

int forcePowerNeeded[NUM_FORCE_POWER_LEVELS][NUM_FORCE_POWERS] =

{

{ //nothing should be usable at rank 0..

999,//FP_HEAL,//instant

999,//FP_LEVITATION,//hold/duration

999,//FP_SPEED,//duration

999,//FP_PUSH,//hold/duration

999,//FP_PULL,//hold/duration

999,//FP_TELEPATHY,//instant

999,//FP_GRIP,//hold/duration

999,//FP_LIGHTNING,//hold/duration

999,//FP_RAGE,//duration

999,//FP_PROTECT,//duration

999,//FP_ABSORB,//duration

999,//FP_TEAM_HEAL,//instant

999,//FP_TEAM_FORCE,//instant

999,//FP_DRAIN,//hold/duration

999,//FP_SEE,//duration

999,//FP_MAGIC_MISSILES // eezstreet add

999,//FP_SABER_OFFENSE,

999,//FP_SABER_DEFENSE,

999//FP_SABERTHROW,

//NUM_FORCE_POWERS

},

{

65,//FP_HEAL,//instant //was 25, but that was way too little

10,//FP_LEVITATION,//hold/duration

50,//FP_SPEED,//duration

20,//FP_PUSH,//hold/duration

20,//FP_PULL,//hold/duration

20,//FP_TELEPATHY,//instant

30,//FP_GRIP,//hold/duration

1,//FP_LIGHTNING,//hold/duration

50,//FP_RAGE,//duration

50,//FP_PROTECT,//duration

50,//FP_ABSORB,//duration

50,//FP_TEAM_HEAL,//instant

50,//FP_TEAM_FORCE,//instant

20,//FP_DRAIN,//hold/duration

20,//FP_SEE,//duration

70,//FP_MAGIC_MISSILES // eezstreet add

0,//FP_SABER_OFFENSE,

2,//FP_SABER_DEFENSE,

20//FP_SABERTHROW,

//NUM_FORCE_POWERS

},

{

60,//FP_HEAL,//instant

10,//FP_LEVITATION,//hold/duration

50,//FP_SPEED,//duration

20,//FP_PUSH,//hold/duration

20,//FP_PULL,//hold/duration

20,//FP_TELEPATHY,//instant

30,//FP_GRIP,//hold/duration

1,//FP_LIGHTNING,//hold/duration

50,//FP_RAGE,//duration

25,//FP_PROTECT,//duration

25,//FP_ABSORB,//duration

33,//FP_TEAM_HEAL,//instant

33,//FP_TEAM_FORCE,//instant

20,//FP_DRAIN,//hold/duration

20,//FP_SEE,//duration

60,//FP_MAGIC_MISSILES // eezstreet add

0,//FP_SABER_OFFENSE,

1,//FP_SABER_DEFENSE,

20//FP_SABERTHROW,

//NUM_FORCE_POWERS

},

{

50,//FP_HEAL,//instant //You get 5 points of health.. for 50 force points!

10,//FP_LEVITATION,//hold/duration

50,//FP_SPEED,//duration

20,//FP_PUSH,//hold/duration

20,//FP_PULL,//hold/duration

20,//FP_TELEPATHY,//instant

60,//FP_GRIP,//hold/duration

1,//FP_LIGHTNING,//hold/duration

50,//FP_RAGE,//duration

10,//FP_PROTECT,//duration

10,//FP_ABSORB,//duration

25,//FP_TEAM_HEAL,//instant

25,//FP_TEAM_FORCE,//instant

20,//FP_DRAIN,//hold/duration

20,//FP_SEE,//duration

50,//FP_MAGIC_MISSILES // eezstreet add

0,//FP_SABER_OFFENSE,

0,//FP_SABER_DEFENSE,

20//FP_SABERTHROW,

//NUM_FORCE_POWERS

}

};

 

One last edit in g_active.c is needed, and then we get to focus on the lovely UI.

Right after:

 

case GENCMD_FORCE_SEEING:

ForceSeeing(ent);

break;

Add:

 

case GENCMD_FORCE_MAGIC_MISSILES:

ForceMagicMissiles(ent);

break;

Okay, now it's time for the UI. I'm not going to get into detail as to the changes required to the force menu (the .menu file), because that's pretty easy to figure out and also because I am slightly lazy. But we do need to make some actual changes in the code in order for that stuff to work properly.

Most of our changes are going to take place in ui_force.c (of course!) so let's open that up. Towards the top of the file, we already have some things that need additions to them.

 

 

qboolean uiForcePowersDisabled[NUM_FORCE_POWERS] = {

qfalse,//FP_HEAL,//instant

qfalse,//FP_LEVITATION,//hold/duration

qfalse,//FP_SPEED,//duration

qfalse,//FP_PUSH,//hold/duration

qfalse,//FP_PULL,//hold/duration

qfalse,//FP_TELEPATHY,//instant

qfalse,//FP_GRIP,//hold/duration

qfalse,//FP_LIGHTNING,//hold/duration

qfalse,//FP_RAGE,//duration

qfalse,//FP_PROTECT,

qfalse,//FP_ABSORB,

qfalse,//FP_TEAM_HEAL,

qfalse,//FP_TEAM_FORCE,

qfalse,//FP_DRAIN,

qfalse,//FP_SEE,
qfalse,//FP_MAGIC_MISSILES // eezstreet add

qfalse,//FP_SABER_OFFENSE,

qfalse,//FP_SABER_DEFENSE,

qfalse//FP_SABERTHROW,

};



int uiForcePowersRank[NUM_FORCE_POWERS] = {

0,//FP_HEAL = 0,//instant

1,//FP_LEVITATION,//hold/duration, this one defaults to 1 (gives a free point)

0,//FP_SPEED,//duration

0,//FP_PUSH,//hold/duration

0,//FP_PULL,//hold/duration

0,//FP_TELEPATHY,//instant

0,//FP_GRIP,//hold/duration

0,//FP_LIGHTNING,//hold/duration

0,//FP_RAGE,//duration

0,//FP_PROTECT,

0,//FP_ABSORB,

0,//FP_TEAM_HEAL,

0,//FP_TEAM_FORCE,

0,//FP_DRAIN,

0,//FP_SEE,
0,//FP_MAGIC_MISSILES // eezstreet add

1,//FP_SABER_OFFENSE, //default to 1 point in attack

1,//FP_SABER_DEFENSE, //defualt to 1 point in defense

0//FP_SABERTHROW,

};



int uiForcePowerDarkLight[NUM_FORCE_POWERS] = //0 == neutral

{ //nothing should be usable at rank 0..

FORCE_LIGHTSIDE,//FP_HEAL,//instant

0,//FP_LEVITATION,//hold/duration

0,//FP_SPEED,//duration

0,//FP_PUSH,//hold/duration

0,//FP_PULL,//hold/duration

FORCE_LIGHTSIDE,//FP_TELEPATHY,//instant

FORCE_DARKSIDE,//FP_GRIP,//hold/duration

FORCE_DARKSIDE,//FP_LIGHTNING,//hold/duration

FORCE_DARKSIDE,//FP_RAGE,//duration

FORCE_LIGHTSIDE,//FP_PROTECT,//duration

FORCE_LIGHTSIDE,//FP_ABSORB,//duration

FORCE_LIGHTSIDE,//FP_TEAM_HEAL,//instant

FORCE_DARKSIDE,//FP_TEAM_FORCE,//instant

FORCE_DARKSIDE,//FP_DRAIN,//hold/duration

0,//FP_SEE,//duration
FORCE_LIGHTSIDE,//FP_MAGIC_MISSILES, // eezstreet add

0,//FP_SABER_OFFENSE,

0,//FP_SABER_DEFENSE,

0//FP_SABERTHROW,

//NUM_FORCE_POWERS

};

 

One more thing in ui_force.c that needs changing. We need to change this:

 

int gCustPowersRank[NUM_FORCE_POWERS] = {

0,//FP_HEAL = 0,//instant

1,//FP_LEVITATION,//hold/duration, this one defaults to 1 (gives a free point)

0,//FP_SPEED,//duration

0,//FP_PUSH,//hold/duration

0,//FP_PULL,//hold/duration

0,//FP_TELEPATHY,//instant

0,//FP_GRIP,//hold/duration

0,//FP_LIGHTNING,//hold/duration

0,//FP_RAGE,//duration

0,//FP_PROTECT,

0,//FP_ABSORB,

0,//FP_TEAM_HEAL,

0,//FP_TEAM_FORCE,

0,//FP_DRAIN,

0,//FP_SEE,
0,//FP_MAGIC_MISSILES, // eezstreet add

0,//FP_SABER_OFFENSE,

0,//FP_SABER_DEFENSE,

0//FP_SABERTHROW,

};
Xaimm likes this

User Feedback

Recommended Comments

There are no comments to display.



Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...