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, };
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 accountSign in
Already have an account? Sign in here.
Sign In Now