JKHub staff note: TODO, this is broken
HOW TO MAKE A NEW WEAPON – SINGLE PLAYER
Before i start i wanna told that i am not an expert coder. i learn many of this studying the eezstreet tutorial about make a new weapon into the multiplayer, and searching into SP code all results about the WP_TUSKEN_RIFLE. so i locate all part i need to edit for add new weapon. this weapon i share with your is simply another blaster pistol with charge fire alt function, nothing more, nothing less. but an expert code can make every kind of wonderful weapon with any kind of shoot, alt shoot, damage and balistic of projectile. i am sure you can make best of me, all you need is to know as much C++ as you can. i really advice you to read all eezstreet tutorial for learn the basic about this.
add a new weapon into SP game is difficult and you need really a big amount of coding work.
are you ready? let we start!
Welcome to alls! In this tutorial i wanna tell you how you can add a new weapon to the weapons of the Single player game.
I wanna to share with you the code of my bloodgun weapon. It’s act like the blaster pistol, but do a little more damage.
What you need:
1 Microsoft visual studio 2010 \ 2012
2 OpenJk
3 a model of a textured and shaded shooting weapons.
4 efx of muzzle flash, alt muzzle flash, projectiles, wall impact, alt impacts and an optional flesh \ droid impacts.
5 a weapon icon.
You got all that?
Good! We start!
First off... add your weapon.
Open code/qcommon/q_shared.h
Find this line:
#define MAX_WEAPONS 28
edit to
#define MAX_WEAPONS 32
Attention: unfortunaley, without a large editing of the coding arrays system, you cannot add more of 32 weapons. According with ensiform and ent, more of 32 weapon enumerated will cause crashing of the savegames. The engine need to allocate memory for every new thing you add to code. If you are not expert programmers or coders, not add more of 32 weapons, you need really to edit a large amount of code for expand the storing cache memory. The 32 bit memory system of JKA cannot manage more of that value, sorry. I tryed to add more of 32 weapons, but the 33th weapon inexorabile still crashing.
Okay, now you need to add weapons to enumeration:
Go to.
Code/game/weapons.H
c[] typedef enum //# weapon_e { WP_NONE, // Player weapons WP_SABER, // player and NPC weapon WP_BLASTER_PISTOL, // player and NPC weapon WP_BLASTER, // player and NPC weapon WP_DISRUPTOR, // player and NPC weapon WP_BOWCASTER, // NPC weapon - player can pick this up, but never starts with them WP_REPEATER, // NPC weapon - player can pick this up, but never starts with them WP_DEMP2, // NPC weapon - player can pick this up, but never starts with them WP_FLECHETTE, // NPC weapon - player can pick this up, but never starts with them WP_ROCKET_LAUNCHER, // NPC weapon - player can pick this up, but never starts with them WP_THERMAL, // player and NPC weapon WP_TRIP_MINE, // NPC weapon - player can pick this up, but never starts with them WP_DET_PACK, // NPC weapon - player can pick this up, but never starts with them WP_CONCUSSION, // NPC weapon - player can pick this up, but never starts with them //extras WP_MELEE, // player and NPC weapon - Any ol' melee attack //when in atst WP_ATST_MAIN, WP_ATST_SIDE, // These can never be gotten directly by the player WP_STUN_BATON, // stupid weapon, should remove //NPC weapons WP_BRYAR_PISTOL, // NPC weapon - player can pick this up, but never starts with them WP_EMPLACED_GUN, WP_BOT_LASER, // Probe droid - Laser blast WP_TURRET, // turret guns WP_TIE_FIGHTER, WP_RAPID_FIRE_CONC, WP_JAWA, WP_TUSKEN_RIFLE, WP_TUSKEN_STAFF, WP_SCEPTER, WP_NOGHRI_STICK, WP_BLOODGUN, WP_CANNON, WP_POISON, //# #eol WP_NUM_WEAPONS } weapon_t; #define FIRST_WEAPON WP_SABER // this is the first weapon for next and prev weapon switching #define MAX_PLAYER_WEAPONS WP_POISON // WP_POISON // this is the max you can switch to and get with the give all. - FIXME: it's actually this one *minus* one... why? // WP_STUN_BATON
as you can see, i tryed to add more of 32 weapons, i put on green the weapons engine cannot supports. On SP game you can add only 3 new weapon. Put the new name at the end of the enumeration, between WP_NOGHRI_STICK and WP_NUM_WEAPONS. Why at the end of the enumeration? Because engine expect that some weapons get a specific value of the enum list. So is better to add at the end so, this not change the enumeration of the default weapons game. That’s avoid potentially conflict.
As you can see on the MAX_PLAYERS_WEAPONS i edited with the last weapon of my array. This unlock all npc weapons and you can give it to the player with the “give weapon_*NAME* cheat. Of the consolle. Jawa weapon is unfinished, atst side not work on player hands, and noghri stick crash when you fire. You are warning about this.
Okay now we pass to the second step.
Go down to weapons.h and you can see a large array of parameter relative of NPC damage and speed of every weapon.
You need to think about this. How will act your weapon? You wanna make a blaster pistol kind weapons? Or a flechette kind weapons? Or a concussion kind weapon? It’s time to think about this.
I assume you wanna make a new blaster pistol weapon. So you need to add these values:
// Blood Gun //-------- #define BLOODGUN_VEL 2500 #define BLOODGUN_DAMAGE 20 #define BLOODGUN_CHARGE_UNIT 200.0 f // bryar charging gives us one more unit every 200ms--if you change this, you'll have to do the same in bg_pmove
you can define every kind amount of damage, npc damage, velocity of projectiles, and charging floating parameter if you wanna make a charging weapon. You need to copy the definition of the weapon you wanna use as example for you custom weapons and change they to the custom definition name. Remember this names. They are importart. Because they will called by all function that will make your weapons working.
We end on weapons.h
Open now code/game/g_items.h
Find this enumeration
// Items enums enum { ITM_NONE, ITM_SABER_PICKUP, ITM_BLASTER_PISTOL_PICKUP, ITM_BLASTER_PICKUP, ITM_DISRUPTOR_PICKUP, ITM_BOWCASTER_PICKUP, ITM_REPEATER_PICKUP, ITM_DEMP2_PICKUP,
At the end add this after itm_sectury_key:
ITM_SECURITY_KEY_PICKUP, ITM_BLOODGUN_PICKUP,
This is necessary if you want your weapon is recognized by weapons.dat external data. Now open g_itemload.cpp
Find this
else if (!Q_stricmp(tokenStr,"WP_NOGHRI_STICK")) tag = WP_NOGHRI_STICK;
below add
else if (!Q_stricmp(tokenStr,"WP_BLOODGUN")) tag = WP_BLOODGUN;
this is necessary also for MP client. Your weapons will be now recognized as item available and pickable on the map. If you don’t it, npcs and player cannot use it. Also you cannot use “give weapon_” fuction.
Now open the q3_interface.cpp
Search this string array
stringID_table_t WPTable[] = { { "NULL", WP_NONE }, ENUM2STRING(WP_NONE), // Player weapons ENUM2STRING(WP_SABER), // NOTE: lots of code assumes this is the first weapon (... which is crap) so be careful -Ste. ENUM2STRING(WP_BLASTER_PISTOL), // apparently some enemy only version of the blaster ENUM2STRING(WP_BLASTER), ENUM2STRING(WP_DISRUPTOR), ENUM2STRING(WP_BOWCASTER), ENUM2STRING(WP_REPEATER), ENUM2STRING(WP_DEMP2), ENUM2STRING(WP_FLECHETTE), ENUM2STRING(WP_ROCKET_LAUNCHER), ENUM2STRING(WP_THERMAL), ENUM2STRING(WP_TRIP_MINE), ENUM2STRING(WP_DET_PACK), ENUM2STRING(WP_CONCUSSION), ENUM2STRING(WP_MELEE), // Any ol' melee attack //NOTE: player can only have up to 16 weapons), anything after that is enemy only ENUM2STRING(WP_STUN_BATON), // NPC enemy weapons ENUM2STRING(WP_BRYAR_PISTOL), ENUM2STRING(WP_EMPLACED_GUN), ENUM2STRING(WP_BOT_LASER), // Probe droid - Laser blast ENUM2STRING(WP_TURRET), // turret guns ENUM2STRING(WP_ATST_MAIN), ENUM2STRING(WP_ATST_SIDE), ENUM2STRING(WP_TIE_FIGHTER), ENUM2STRING(WP_RAPID_FIRE_CONC), ENUM2STRING(WP_JAWA), ENUM2STRING(WP_TUSKEN_RIFLE), ENUM2STRING(WP_TUSKEN_STAFF), ENUM2STRING(WP_SCEPTER), ENUM2STRING(WP_NOGHRI_STICK), ENUM2STRING(WP_BLOODGUN), ENUM2STRING(WP_CANNON), ENUM2STRING(WP_POISON), { "", 0 } };
after noghri_stick you can add you new weapons.
What make this? This allow to the weapons to be recognized from external data files of JKA. On weapons.dat now you can define all paramter of your weapons relative to muzzleeffect, altmuzzleefx, kind of ammo, icon, etc etc. Also, with this you can get your new weapons to the Npcs! They can use the new weapon against you! When they die, the weapons will be dropped and you can pickup and gain ammo. This is the finality or this edit.
Now the hardest part. Open weapons.dat into ext_data folder of your mod.
After the noghri stick branks parameter you need to add the paramer of your new weapons. In that case...
// WP_BLOODGUN – PUT YOUR DESCRIPTION { weapontype WP_BLOODGUN weaponclass weapon_bloodgun weaponmodel models / weapons2 / bloodgun / bloodgun.md3 weaponIcon gfx / hud / w_icon_bloodgun missileFuncName bloodgun_func altmissileFuncName bloodgun_alt_func ammotype 2 ammolowcount 10 energypershot 10 firetime 600 range 8192 altenergypershot 20 altfiretime 500 altrange 8192 muzzleEffect bloodgun / muzzle_flash altmuzzleEffect bloodgun / altmuzzle_flash altchargesound sound / weapons / bloodgun / altcharge.wav selectSound sound / weapons / bloodgun / select.wav selectforce fffx / weapons / bryar / select damage 40 altdamage 50 }
Okay, now opens item.dat and add this on the item list:
{ itemname ITM_BLOODGUN_PICKUP classname weapon_bloodgun worldmodel models / weapons2 / bloodgun / bloodgun.md3 // Amount of ammo given with weapon count 500 type IT_WEAPON tag WP_BLOODGUN }
and now you understand why you need to edit the items.h and itemload.cpp file: because that’s allow items.dat to recognize your weapon as item usable and pickable.
Now the hardest part! Programming your weapon!
Open g_weaponload.cpp
At the start of the file you can see a large amount of void definition. They define the function of the visual efx of the weapons. Add this for your new pistol:
// Blood Gun shot void FX_BloodGunProjectileThink(centity_t * cent, const struct weaponInfo_s * weapon); void FX_BloodGunAltProjectileThink(centity_t * cent, const struct weaponInfo_s * weapon);
below you can find this:
// Table used to attach an extern missile function string to the actual cgame function func_t funcs[] = { { "bryar_func", FX_BryarProjectileThink }, { "bryar_alt_func", FX_BryarAltProjectileThink },
add into the table:
{"bloodgun_func", FX_BloodGunProjectileThink}, {"bloodgun_alt_func", FX_BloodGunAltProjectileThink},
this is used by weapon.dat for call the function of the efx of the projectiles of your weapons, for main and altfire.
Below, you can find this:
// This is used as a fallback for each new field, in case they're using base files --eez const int defaultDamage[] = { 0, // WP_NONE 0, // WP_SABER // handled elsewhere BRYAR_PISTOL_DAMAGE, // WP_BLASTER_PISTOL BLASTER_DAMAGE, // WP_BLASTER DISRUPTOR_MAIN_DAMAGE, // WP_DISRUPTOR BOWCASTER_DAMAGE, // WP_BOWCASTER REPEATER_DAMAGE, // WP_REPEATER DEMP2_DAMAGE, // WP_DEMP2 FLECHETTE_DAMAGE, // WP_FLECHETTE ROCKET_DAMAGE, // WP_ROCKET_LAUNCHER TD_DAMAGE, // WP_THERMAL LT_DAMAGE, // WP_TRIP_MINE FLECHETTE_MINE_DAMAGE, // WP_DET_PACK // HACK, this is what the code sez. CONC_DAMAGE, // WP_CONCUSSION 0, // WP_MELEE // handled by the melee attack function ATST_MAIN_DAMAGE, // WP_ATST_MAIN ATST_SIDE_MAIN_DAMAGE, // WP_ATST_SIDE STUN_BATON_DAMAGE, // WP_STUN_BATON BRYAR_PISTOL_DAMAGE, // WP_BRYAR_PISTOL EMPLACED_DAMAGE, // WP_EMPLACED_GUN BRYAR_PISTOL_DAMAGE, // WP_BOT_LASER 0, // WP_TURRET // handled elsewhere EMPLACED_DAMAGE, // WP_TIE_FIGHTER EMPLACED_DAMAGE, // WP_RAPID_FIRE_CONC, BRYAR_PISTOL_DAMAGE, // WP_JAWA 0, // WP_TUSKEN_RIFLE 0, // WP_TUSKEN_STAFF 0, // WP_SCEPTER 0, // WP_NOGHRI_STICK BLOODGUN_DAMAGE, // WP_BLOODGUN CANNON_DAMAGE, // WP_CANNON POISON_DAMAGE, // WP_POISON };
the value follow the enumeration of weapons.h, at the position of the number of your new weapon you need to add a voice like i did. The kind of voice depend by the kind of your weapons. As you can see, the weapons there not use a limitated number of ammos get the 0 value.
You need to make the same thinks also for defaultaltdamage, splashdamage,splashradius, altsplashdamage, altsplashradius.
const int defaultAltDamage[] = { 0, // WP_NONE 0, // WP_SABER // handled elsewhere BRYAR_PISTOL_DAMAGE, // WP_BLASTER_PISTOL BLASTER_DAMAGE, // WP_BLASTER DISRUPTOR_ALT_DAMAGE, // WP_DISRUPTOR BOWCASTER_DAMAGE, // WP_BOWCASTER REPEATER_ALT_DAMAGE, // WP_REPEATER DEMP2_ALT_DAMAGE, // WP_DEMP2 FLECHETTE_ALT_DAMAGE, // WP_FLECHETTE ROCKET_DAMAGE, // WP_ROCKET_LAUNCHER TD_ALT_DAMAGE, // WP_THERMAL LT_DAMAGE, // WP_TRIP_MINE FLECHETTE_MINE_DAMAGE, // WP_DET_PACK // HACK, this is what the code sez. CONC_ALT_DAMAGE, // WP_CONCUSION 0, // WP_MELEE // handled by the melee attack function ATST_MAIN_DAMAGE, // WP_ATST_MAIN ATST_SIDE_ALT_DAMAGE, // WP_ATST_SIDE STUN_BATON_ALT_DAMAGE, // WP_STUN_BATON BRYAR_PISTOL_DAMAGE, // WP_BRYAR_PISTOL EMPLACED_DAMAGE, // WP_EMPLACED_GUN BRYAR_PISTOL_DAMAGE, // WP_BOT_LASER 0, // WP_TURRET // handled elsewhere EMPLACED_DAMAGE, // WP_TIE_FIGHTER 0, // WP_RAPID_FIRE_CONC // repeater alt damage is used instead BRYAR_PISTOL_DAMAGE, // WP_JAWA 0, // WP_TUSKEN_RIFLE 0, // WP_TUSKEN_STAFF 0, // WP_SCEPTER 0, // WP_NOGHRI_STICK BLOODGUN_DAMAGE, // WP_BLOODGUN CANNON_ALT_DAMAGE, // WP_CANNON POISON_ALT_DAMAGE, // WP_POISON }; const int defaultSplashDamage[] = { 0, // WP_NONE 0, // WP_SABER 0, // WP_BLASTER_PISTOL 0, // WP_BLASTER 0, // WP_DISRUPTOR BOWCASTER_SPLASH_DAMAGE, // WP_BOWCASTER 0, // WP_REPEATER 0, // WP_DEMP2 0, // WP_FLECHETTE ROCKET_SPLASH_DAMAGE, // WP_ROCKET_LAUNCHER TD_SPLASH_DAM, // WP_THERMAL LT_SPLASH_DAM, // WP_TRIP_MINE FLECHETTE_MINE_SPLASH_DAMAGE, // WP_DET_PACK // HACK, this is what the code sez. CONC_SPLASH_DAMAGE, // WP_CONCUSSION 0, // WP_MELEE 0, // WP_ATST_MAIN ATST_SIDE_MAIN_SPLASH_DAMAGE, // WP_ATST_SIDE 0, // WP_STUN_BATON 0, // WP_BRYAR_PISTOL 0, // WP_EMPLACED_GUN 0, // WP_BOT_LASER 0, // WP_TURRET 0, // WP_TIE_FIGHTER 0, // WP_RAPID_FIRE_CONC 0, // WP_JAWA 0, // WP_TUSKEN_RIFLE 0, // WP_TUSKEN_STAFF 0, // WP_SCEPTER 0, // WP_NOGHRI_STICK 0, // WP_BLOODGUN 0, // WP_CANNON 0, // WP_POISON }; const float defaultSplashRadius[] = { 0.0 f, // WP_NONE 0.0 f, // WP_SABER 0.0 f, // WP_BLASTER_PISTOL 0.0 f, // WP_BLASTER 0.0 f, // WP_DISRUPTOR BOWCASTER_SPLASH_RADIUS, // WP_BOWCASTER 0.0 f, // WP_REPEATER 0.0 f, // WP_DEMP2 0.0 f, // WP_FLECHETTE ROCKET_SPLASH_RADIUS, // WP_ROCKET_LAUNCHER TD_SPLASH_RAD, // WP_THERMAL LT_SPLASH_RAD, // WP_TRIP_MINE FLECHETTE_MINE_SPLASH_RADIUS, // WP_DET_PACK // HACK, this is what the code sez. CONC_SPLASH_RADIUS, // WP_CONCUSSION 0.0 f, // WP_MELEE 0.0 f, // WP_ATST_MAIN ATST_SIDE_MAIN_SPLASH_RADIUS, // WP_ATST_SIDE 0.0 f, // WP_STUN_BATON 0.0 f, // WP_BRYAR_PISTOL 0.0 f, // WP_EMPLACED_GUN 0.0 f, // WP_BOT_LASER 0.0 f, // WP_TURRET 0.0 f, // WP_TIE_FIGHTER 0.0 f, // WP_RAPID_FIRE_CONC 0.0 f, // WP_JAWA 0.0 f, // WP_TUSKEN_RIFLE 0.0 f, // WP_TUSKEN_STAFF 0.0 f, // WP_SCEPTER 0.0 f, // WP_NOGHRI_STICK }; const int defaultAltSplashDamage[] = { 0, // WP_NONE 0, // WP_SABER // handled elsewhere 0, // WP_BLASTER_PISTOL 0, // WP_BLASTER 0, // WP_DISRUPTOR BOWCASTER_SPLASH_DAMAGE, // WP_BOWCASTER REPEATER_ALT_SPLASH_DAMAGE, // WP_REPEATER DEMP2_ALT_DAMAGE, // WP_DEMP2 FLECHETTE_ALT_SPLASH_DAM, // WP_FLECHETTE ROCKET_SPLASH_DAMAGE, // WP_ROCKET_LAUNCHER TD_ALT_SPLASH_DAM, // WP_THERMAL TD_ALT_SPLASH_DAM, // WP_TRIP_MINE FLECHETTE_MINE_SPLASH_DAMAGE, // WP_DET_PACK // HACK, this is what the code sez. 0, // WP_CONCUSSION 0, // WP_MELEE // handled by the melee attack function 0, // WP_ATST_MAIN ATST_SIDE_ALT_SPLASH_DAMAGE, // WP_ATST_SIDE 0, // WP_STUN_BATON 0, // WP_BRYAR_PISTOL 0, // WP_EMPLACED_GUN 0, // WP_BOT_LASER 0, // WP_TURRET // handled elsewhere 0, // WP_TIE_FIGHTER 0, // WP_RAPID_FIRE_CONC 0, // WP_JAWA 0, // WP_TUSKEN_RIFLE 0, // WP_TUSKEN_STAFF 0, // WP_SCEPTER 0, // WP_NOGHRI_STICK }; const float defaultAltSplashRadius[] = { 0.0 f, // WP_NONE 0.0 f, // WP_SABER // handled elsewhere 0.0 f, // WP_BLASTER_PISTOL 0.0 f, // WP_BLASTER 0.0 f, // WP_DISRUPTOR BOWCASTER_SPLASH_RADIUS, // WP_BOWCASTER REPEATER_ALT_SPLASH_RADIUS, // WP_REPEATER DEMP2_ALT_SPLASHRADIUS, // WP_DEMP2 FLECHETTE_ALT_SPLASH_RAD, // WP_FLECHETTE ROCKET_SPLASH_RADIUS, // WP_ROCKET_LAUNCHER TD_ALT_SPLASH_RAD, // WP_THERMAL LT_SPLASH_RAD, // WP_TRIP_MINE FLECHETTE_ALT_SPLASH_RAD, // WP_DET_PACK // HACK, this is what the code sez. 0.0 f, // WP_CONCUSSION 0.0 f, // WP_MELEE // handled by the melee attack function 0.0 f, // WP_ATST_MAIN ATST_SIDE_ALT_SPLASH_RADIUS, // WP_ATST_SIDE 0.0 f, // WP_STUN_BATON 0.0 f, // WP_BRYAR_PISTOL 0.0 f, // WP_EMPLACED_GUN 0.0 f, // WP_BOT_LASER 0.0 f, // WP_TURRET // handled elsewhere 0.0 f, // WP_TIE_FIGHTER 0.0 f, // WP_RAPID_FIRE_CONC 0.0 f, // WP_JAWA 0.0 f, // WP_TUSKEN_RIFLE 0.0 f, // WP_TUSKEN_STAFF 0.0 f, // WP_SCEPTER 0.0 f, // WP_NOGHRI_STICK 0.0 f, // WP_BLOODGUN CANNON_ALT_SPLASH_RAD, // WP_CANNON POISON_ALT_SPLASHRADIUS, // WP_POISON };
i think this will allow you to customize these values on weapons.dat.
now go down and add entries for your weapons at the end of this array:
// FIXME : put this in an array (maybe a weaponDataInternal array???) if (!Q_stricmp(tokenStr, "WP_NONE")) weaponNum = WP_NONE; else if (!Q_stricmp(tokenStr, "WP_SABER")) weaponNum = WP_SABER; else if (!Q_stricmp(tokenStr, "WP_BLASTER_PISTOL")) weaponNum = WP_BLASTER_PISTOL; else if (!Q_stricmp(tokenStr, "WP_BRYAR_PISTOL")) weaponNum = WP_BRYAR_PISTOL; else if (!Q_stricmp(tokenStr, "WP_BLASTER")) weaponNum = WP_BLASTER; else if (!Q_stricmp(tokenStr, "WP_DISRUPTOR")) weaponNum = WP_DISRUPTOR; else if (!Q_stricmp(tokenStr, "WP_BOWCASTER")) weaponNum = WP_BOWCASTER; else if (!Q_stricmp(tokenStr, "WP_REPEATER")) weaponNum = WP_REPEATER; else if (!Q_stricmp(tokenStr, "WP_DEMP2")) weaponNum = WP_DEMP2; else if (!Q_stricmp(tokenStr, "WP_FLECHETTE")) weaponNum = WP_FLECHETTE; else if (!Q_stricmp(tokenStr, "WP_ROCKET_LAUNCHER")) weaponNum = WP_ROCKET_LAUNCHER; else if (!Q_stricmp(tokenStr, "WP_CONCUSSION")) weaponNum = WP_CONCUSSION; else if (!Q_stricmp(tokenStr, "WP_THERMAL")) weaponNum = WP_THERMAL; else if (!Q_stricmp(tokenStr, "WP_TRIP_MINE")) weaponNum = WP_TRIP_MINE; else if (!Q_stricmp(tokenStr, "WP_DET_PACK")) weaponNum = WP_DET_PACK; else if (!Q_stricmp(tokenStr, "WP_STUN_BATON")) weaponNum = WP_STUN_BATON; else if (!Q_stricmp(tokenStr, "WP_BOT_LASER")) weaponNum = WP_BOT_LASER; else if (!Q_stricmp(tokenStr, "WP_EMPLACED_GUN")) weaponNum = WP_EMPLACED_GUN; else if (!Q_stricmp(tokenStr, "WP_MELEE")) weaponNum = WP_MELEE; else if (!Q_stricmp(tokenStr, "WP_TURRET")) weaponNum = WP_TURRET; else if (!Q_stricmp(tokenStr, "WP_ATST_MAIN")) weaponNum = WP_ATST_MAIN; else if (!Q_stricmp(tokenStr, "WP_ATST_SIDE")) weaponNum = WP_ATST_SIDE; else if (!Q_stricmp(tokenStr, "WP_TIE_FIGHTER")) weaponNum = WP_TIE_FIGHTER; else if (!Q_stricmp(tokenStr, "WP_RAPID_FIRE_CONC")) weaponNum = WP_RAPID_FIRE_CONC; else if (!Q_stricmp(tokenStr, "WP_JAWA")) weaponNum = WP_JAWA; else if (!Q_stricmp(tokenStr, "WP_TUSKEN_RIFLE")) weaponNum = WP_TUSKEN_RIFLE; else if (!Q_stricmp(tokenStr, "WP_TUSKEN_STAFF")) weaponNum = WP_TUSKEN_STAFF; else if (!Q_stricmp(tokenStr, "WP_SCEPTER")) weaponNum = WP_SCEPTER; else if (!Q_stricmp(tokenStr, "WP_NOGHRI_STICK")) weaponNum = WP_NOGHRI_STICK; else if (!Q_stricmp(tokenStr, "WP_BLOODGUN")) weaponNum = WP_BLOODGUN; else if (!Q_stricmp(tokenStr, "WP_CANNON")) weaponNum = WP_CANNON; else if (!Q_stricmp(tokenStr, "WP_POISON")) weaponNum = WP_POISON;
as you can see, i added mines.
Now you end with g_weaponload.cpp. it was hard, phew.
Now you need really to program your weapon. Efx and functions of shooting. You can have two choice. You can create a new file of your solution of you can use the WP_ and FX_ file of the openjk solution. I prefear to use the default files.
So for our pistol now open that:
Wp_blaster_pistol.cpp
Add these functions:
// BLOODGUN //--------------------------------------------------------- void WP_FireBloodGun(gentity_t * ent, qboolean alt_fire) //--------------------------------------------------------- { vec3_t start; int damage = !alt_fire ? weaponData[WP_BLOODGUN].damage : weaponData[WP_BLOODGUN].altDamage; VectorCopy(muzzle, start); WP_TraceSetStart(ent, start, vec3_origin, vec3_origin); //make sure our start point isn't on the other side of a wall if (!(ent - > client - > ps.forcePowersActive & (1 << FP_SEE)) || ent - > client - > ps.forcePowerLevel[FP_SEE] < FORCE_LEVEL_2) { //force sight 2+ gives perfect aim //FIXME: maybe force sight level 3 autoaims some? if (ent - > NPC && ent - > NPC - > currentAim < 5) { vec3_t angs; vectoangles(forwardVec, angs); if (ent - > client - > NPC_class == CLASS_IMPWORKER) { //*sigh*, hack to make impworkers less accurate without affecteing imperial officer accuracy angs[PITCH] += (crandom() * (BLASTER_NPC_SPREAD + (6 - ent - > NPC - > currentAim) * 0.25 f)); //was 0.5f angs[YAW] += (crandom() * (BLASTER_NPC_SPREAD + (6 - ent - > NPC - > currentAim) * 0.25 f)); //was 0.5f } else { angs[PITCH] += (crandom() * ((5 - ent - > NPC - > currentAim) * 0.25 f)); angs[YAW] += (crandom() * ((5 - ent - > NPC - > currentAim) * 0.25 f)); } AngleVectors(angs, forwardVec, NULL, NULL); } } WP_MissileTargetHint(ent, start, forwardVec); gentity_t * missile = CreateMissile(start, forwardVec, BLOODGUN_VEL, 10000, ent, alt_fire); missile - > classname = "bloodgun_proj"; if (ent - > s.weapon == WP_BLOODGUN) { //*SIGH*... I hate our weapon system... missile - > s.weapon = ent - > s.weapon; } else { missile - > s.weapon = WP_BLASTER_PISTOL; } if (alt_fire) { int count = (level.time - ent - > client - > ps.weaponChargeTime) / BLOODGUN_CHARGE_UNIT; if (count < 1) { count = 1; } else if (count > 5) { count = 5; } damage *= count; missile - > count = count; // this will get used in the projectile rendering code to make a beefier effect } // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) // { // // in overcharge mode, so doing double damage // missile->flags |= FL_OVERCHARGED; // damage *= 2; // } missile - > damage = damage; missile - > dflags = DAMAGE_DEATH_KNOCKBACK; if (alt_fire) { missile - > methodOfDeath = MOD_BLOODGUN_ALT; } else { missile - > methodOfDeath = MOD_BLOODGUN; } missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; // we don't want it to bounce forever missile - > bounceCount = 8; if (ent - > weaponModel[1] > 0) { //dual pistols, toggle the muzzle point back and forth between the two pistols each time he fires ent - > count = (ent - > count) ? 0 : 1; } }
its exaclty like the weapon bryar and weapon blaster pistol behavour. Obvious you can set every kind of shooting function you desire.
Learn the coding and combine with your mind and fantasy! Your weapon can do every kind of thing you desire! You can use as example the function of the weapons of the game for studyng and understand it, but is really better if you will make something of your genius!
Now the same things for the FX.
open the FX_bryarpistol.cpp
add this and you can customize fx of your weapon.
void FX_BloodGunProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon ) { vec3_t forward; if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f ) { if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) { forward[2] = 1.0f; } } // hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly int dif = cg.time - cent->gent->s.pos.trTime; if ( dif < 75 ) { if ( dif < 0 ) { dif = 0; } float scale = ( dif / 75.0f ) * 0.95f + 0.05f; VectorScale( forward, scale, forward ); } if ( cent->gent && cent->gent->owner && cent->gent->owner->s.number > 0 ) { theFxScheduler.PlayEffect( "bloodgun/NPCshot", cent->lerpOrigin, forward ); } else { theFxScheduler.PlayEffect( cgs.effects.bloodgunShotEffect, cent->lerpOrigin, forward ); } }
/*
-------------------------
FX_BryarHitWall
-------------------------
*/
void FX_BryarHitWall( vec3_t origin, vec3_t normal ) { theFxScheduler.PlayEffect( cgs.effects.bryarWallImpactEffect, origin, normal ); } void FX_BloodGunHitWall( vec3_t origin, vec3_t normal ) { theFxScheduler.PlayEffect( cgs.effects.bloodgunWallImpactEffect, origin, normal ); }
= 1.0 f; } } // hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly int dif = cg.time - cent - > gent - > s.pos.trTime; if (dif < 75) { if (dif < 0) { dif = 0; } float scale = (dif / 75.0 f) * 0.95 f + 0.05 f; VectorScale(forward, scale, forward); } // see if we have some sort of extra charge going on for (int t = 1; t < cent - > gent - > count; t++) { // just add ourselves over, and over, and over when we are charged theFxScheduler.PlayEffect(cgs.effects.bryarPowerupShotEffect, cent - > lerpOrigin, forward); } theFxScheduler.PlayEffect(cgs.effects.bryarShotEffect, cent - > lerpOrigin, forward); } void FX_BloodGunAltProjectileThink(centity_t * cent, const struct weaponInfo_s * weapon) { vec3_t forward; if (VectorNormalize2(cent - > gent - > s.pos.trDelta, forward) == 0.0 f) { if (VectorNormalize2(cent - > currentState.pos.trDelta, forward) == 0.0 f) { forward[2] = 1.0 f; } } // hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly int dif = cg.time - cent - > gent - > s.pos.trTime; if (dif < 75) { if (dif < 0) { dif = 0; } float scale = (dif / 75.0 f) * 0.95 f + 0.05 f; VectorScale(forward, scale, forward); } // see if we have some sort of extra charge going on for (int t = 1; t < cent - > gent - > count; t++) { // just add ourselves over, and over, and over when we are charged theFxScheduler.PlayEffect(cgs.effects.bloodgunPowerupShotEffect, cent - > lerpOrigin, forward); } theFxScheduler.PlayEffect(cgs.effects.bloodgunShotEffect, cent - > lerpOrigin, forward); } /* ------------------------- FX_BryarAltHitWall ------------------------- */ void FX_BryarAltHitWall(vec3_t origin, vec3_t normal, int power) { switch (power) { case 4: case 5: theFxScheduler.PlayEffect(cgs.effects.bryarWallImpactEffect3, origin, normal); break; case 2: case 3: theFxScheduler.PlayEffect(cgs.effects.bryarWallImpactEffect2, origin, normal); break; default: theFxScheduler.PlayEffect(cgs.effects.bryarWallImpactEffect, origin, normal); break; } } void FX_BloodGunAltHitWall(vec3_t origin, vec3_t normal, int power) { switch (power) { case 4: case 5: theFxScheduler.PlayEffect(cgs.effects.bloodgunWallImpactEffect3, origin, normal); break; case 2: case 3: theFxScheduler.PlayEffect(cgs.effects.bloodgunWallImpactEffect2, origin, normal); break; default: theFxScheduler.PlayEffect(cgs.effects.bloodgunWallImpactEffect, origin, normal); break; } } /* ------------------------- FX_BryarAltHitPlayer ------------------------- */ void FX_BryarAltHitPlayer(vec3_t origin, vec3_t normal, qboolean humanoid) { theFxScheduler.PlayEffect(cgs.effects.bryarFleshImpactEffect, origin, normal); } void FX_BloodGunAltHitPlayer(vec3_t origin, vec3_t normal, qboolean humanoid) { theFxScheduler.PlayEffect(cgs.effects.bloodgunFleshImpactEffect, origin, normal); } Now you end to programm your weapons. You told it how shoot, and what efx play when they shoot. But you need to define again a lot of things. For example, you need to declare the definition of your effect! Go on cg_media.h and add this on the fxHandle_t field [code=auto:0]// BLOODGUN fxHandle_t bloodgunShotEffect; fxHandle_t bloodgunPowerupShotEffect; fxHandle_t bloodgunWallImpactEffect; fxHandle_t bloodgunWallImpactEffect2; fxHandle_t bloodgunWallImpactEffect3; fxHandle_t bloodgunFleshImpactEffect;[/code] that’s are the function called on the fx_bryarpistol.cpp effect. So you have the definition and you have also used and called they. But... miss something right? Where is in the code that you tell exactly what kind of efx is played by bloodgunshoteffect? Here! Find this on cg_weapons.cpp this manage the client game parameter of the weapons. That’s regard primary player and Npcs. -------------------------------------------------------------------------------- [code=auto:0]case WP_BRYAR_PISTOL: //Edit the names in the the (" Bow shoot ") cgs.effects.bowShotEffect = theFxScheduler.RegisterEffect("bow/shot"); theFxScheduler.RegisterEffect("bow/NPCshot"); cgs.effects.bowPowerupShotEffect = theFxScheduler.RegisterEffect("bow/crackleShot"); cgs.effects.bowWallImpactEffect = theFxScheduler.RegisterEffect("bow/wall_impact"); cgs.effects.bowWallImpactEffect2 = theFxScheduler.RegisterEffect("bow/wall_impact2"); cgs.effects.bowWallImpactEffect3 = theFxScheduler.RegisterEffect("bow/wall_impact3"); cgs.effects.bowFleshImpactEffect = theFxScheduler.RegisterEffect("bow/flesh_impact"); break;[/code] ------------------------------------------------------------------------------------------------------------ below add this new case: -------------------------------------------------------------------------------- [code=auto:0]case WP_BLOODGUN:/Edit the names in the the (" Prophet blood pistol ") cgs.effects.bloodgunShotEffect= theFxScheduler.RegisterEffect( "bloodgun/shot" ); theFxScheduler.RegisterEffect( "bloodgun/NPCshot" ); cgs.effects.bloodgunPowerupShotEffect = theFxScheduler.RegisterEffect( "bloodgun/crackleShot" ); cgs.effects.bloodgunWallImpactEffect = theFxScheduler.RegisterEffect( "bloodgun/wall_impact" ); cgs.effects.bloodgunWallImpactEffect2 = theFxScheduler.RegisterEffect( "bloodgun/wall_impact2" ); cgs.effects.bloodgunWallImpactEffect3 = theFxScheduler.RegisterEffect( "bloodgun/wall_impact3" ); cgs.effects.bloodgunFleshImpactEffect = theFxScheduler.RegisterEffect( "bloodgun/flesh_impact" );[/code] ------------------------------------------------------------------------------------------------------------------------------ now you have ended with efx array. Your weapon have new definition, new effects and will shot that effects. Now you need to make a last painful big amount of work. We are near to the end! This concerned g_weapons.cpp and cg_weapons.cpp Here are definied some important global function necessary for the correct working of your weapons. G weapons are global setting, cg weapons client game setting. In this section you can find a large amount of data to edit for making working your new weapon. This data edit change at second of kind of weapon you want to add, so not expect this istruction is valid for all your project. You need to manual scroll the cg_weapons.cpp and also the g_weapons.cpp file and edit specific part of the code. i not told you exactly what edit you need to edit, but i can tell you the location of the place to edit and the finality of these edits. Let we starts. Scrolling down cg_weapons.cpp until you see this: [code=auto:0]// Do special charge bits //----------------------- if ((ps - > weaponstate == WEAPON_CHARGING_ALT && ps - > weapon == WP_BRYAR_PISTOL) add in this entry your weapon.if((ps - > weaponstate == WEAPON_CHARGING_ALT && ps - > weapon == WP_BRYAR_PISTOL) || (ps - > weaponstate == WEAPON_CHARGING_ALT && ps - > weapon == WP_BLASTER_PISTOL) || (ps - > weapon == WP_BOWCASTER && ps - > weaponstate == WEAPON_CHARGING) || (ps - > weapon == WP_DEMP2 && ps - > weaponstate == WEAPON_CHARGING_ALT) || (ps - > weapon == WP_BLOODGUN && ps - > weaponstate == WEAPON_CHARGING_ALT) || (ps - > weapon == WP_POISON && ps - > weaponstate == WEAPON_CHARGING_ALT)[/code] this is a if condition. It mean “if your weapon are in alt fire mode and are charging a shoot they... will use some custom shader in front of their barrel [code=auto:0]{ int shader = 0; float val = 0.0 f, scale = 1.0 f; vec3_t WHITE = { 1.0 f, 1.0 f, 1.0 f }; if (ps - > weapon == WP_BRYAR_PISTOL) { // Hardcoded max charge time of 1 second val = (cg.time - ps - > weaponChargeTime) * 0.001 f; shader = cgi_R_RegisterShader("gfx/effects/bryarFrontFlash"); } if (ps - > weapon == WP_BLASTER_PISTOL) { // Hardcoded max charge time of 1 second val = (cg.time - ps - > weaponChargeTime) * 0.001 f; shader = cgi_R_RegisterShader("gfx/damage/burnmark3"); } else if (ps - > weapon == WP_BLOODGUN) { // Hardcoded max charge time of 1 second val = (cg.time - ps - > weaponChargeTime) * 0.001 f; shader = cgi_R_RegisterShader("gfx/damage/burnmark3"); } else if (ps - > weapon == WP_BOWCASTER) { // Hardcoded max charge time of 1 second val = (cg.time - ps - > weaponChargeTime) * 0.001 f; shader = cgi_R_RegisterShader("gfx/effects/bryarFrontFlash"); } else if (ps - > weapon == WP_DEMP2) { // Hardcoded max charge time of 1 second val = (cg.time - ps - > weaponChargeTime) * 0.001 f; shader = cgi_R_RegisterShader("gfx/misc/lightningFlash"); scale = 1.75 f; } else if (ps - > weapon == WP_POISON) { // Hardcoded max charge time of 1 second val = (cg.time - ps - > weaponChargeTime) * 0.001 f; shader = cgi_R_RegisterShader("gfx/effects/greenFrontFlash"); scale = 1.5 f; }[/code] if you make some weapons that need a charging efx like bowcaster, bryar, blaster, demp2, and you are searching for the code part that set the shader used for charge flash of the weapons, you can find here... and you can change, customize and define it here. So now your weapon can have a custom flash infront of his barrel when is charging is letal shoot. Cool, isn’t it? We now continue to dive the cg_weapons.cpp code. The next step we will make is this! Hud! You need to set make your weapons usable by player! You need that your weapons can be selected and scrolled on HUD weapons list after the player pick up or obtain they. So search for this: CG_NextWeapon_f You will found a raven hacked part of the code, when they have insert the WP_CONCUSSION into the hud slide. You can use this for add a new else if entry for put your weapon into the weapons slide, i make this also with the NPC weapons i wanna play with my player. But in your case you need simply to add your weapons in this array. [code=auto:0]for (i = 0; i <= MAX_PLAYER_WEAPONS; i++) { //*SIGH*... Hack to put concussion rifle before rocketlauncher if (cg.weaponSelect == WP_FLECHETTE) { cg.weaponSelect = WP_CONCUSSION; } else if (cg.weaponSelect == WP_CONCUSSION) { cg.weaponSelect = WP_ROCKET_LAUNCHER; } else if (cg.weaponSelect == WP_DET_PACK) { cg.weaponSelect = WP_MELEE; } else if (cg.weaponSelect == WP_MELEE) { cg.weaponSelect = WP_ATST_MAIN; } else if (cg.weaponSelect == WP_ATST_MAIN) { cg.weaponSelect = WP_STUN_BATON; } else if (cg.weaponSelect == WP_STUN_BATON) { cg.weaponSelect = WP_BRYAR_PISTOL; } else if (cg.weaponSelect == WP_BRYAR_PISTOL) { cg.weaponSelect = WP_JAWA; } else if (cg.weaponSelect == WP_JAWA) { cg.weaponSelect = WP_TUSKEN_RIFLE; } else if (cg.weaponSelect == WP_TUSKEN_RIFLE) { cg.weaponSelect = WP_BLOODGUN; } else if (cg.weaponSelect == WP_BLOODGUN) { cg.weaponSelect = WP_CANNON; } else if (cg.weaponSelect == WP_CANNON) { cg.weaponSelect = WP_POISON; } else if (cg.weaponSelect == WP_POISON) { cg.weaponSelect = firstWeapon; } else { cg.weaponSelect++; }[/code] you need to put a question when you make this: what weapon you want is selected on the HUD BEFORE and AFTER the yours? Well. Now you know what you need to put into this array! As you can see, after my last weapon (WP_POISON) the slide will select the firstWeapon. FirstWeapon is the saber. So this close the Hud cycle. You need to make the same thing also with previous weapon function and for each the two datapad function, so your weapon will appear also in datapad. as you will can see, if player get the last weapon of the array, using the previous weapon key can equip the weapon swithing by the first weapoin, the saber, to the last enumerated weapons. Understood? [code=auto:0]/* =============== CG_PrevWeapon_f =============== */ void CG_PrevWeapon_f(void) { int i; int original; if (!cg.snap) { return; } /* if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { return; } */ if (g_entities[0].flags & FL_LOCK_PLAYER_WEAPONS) { CG_PlayerLockedWeaponSpeech(qfalse); return; } if (g_entities[0].client && g_entities[0].client - > NPC_class == CLASS_ATST) { CG_ToggleATSTWeapon(); return; } if (cg.snap - > ps.eFlags & EF_LOCKED_TO_WEAPON) { // can't do any sort of weapon switching when in the emplaced gun return; } if (cg.snap - > ps.viewEntity) { // yeah, probably need a better check here if (g_entities[cg.snap - > ps.viewEntity].client && (g_entities[cg.snap - > ps.viewEntity].client - > NPC_class == CLASS_R5D2 || g_entities[cg.snap - > ps.viewEntity].client - > NPC_class == CLASS_R2D2 || g_entities[cg.snap - > ps.viewEntity].client - > NPC_class == CLASS_MOUSE)) { return; } } original = cg.weaponSelect; int firstWeapon = FIRST_WEAPON; if (G_IsRidingVehicle( & g_entities[cg.snap - > ps.viewEntity])) { firstWeapon = 0; // include WP_NONE here } for (i = 0; i <= MAX_PLAYER_WEAPONS; i++) { //*SIGH*... Hack to put concussion rifle before rocketlauncher if (cg.weaponSelect == WP_ROCKET_LAUNCHER) { cg.weaponSelect = WP_CONCUSSION; } else if (cg.weaponSelect == WP_CONCUSSION) { cg.weaponSelect = WP_FLECHETTE; } else if (cg.weaponSelect == WP_MELEE) { cg.weaponSelect = WP_DET_PACK; } else if (cg.weaponSelect == WP_ATST_MAIN) { cg.weaponSelect = WP_MELEE; } else if (cg.weaponSelect == WP_STUN_BATON) { cg.weaponSelect = WP_ATST_MAIN; } else if (cg.weaponSelect == WP_BRYAR_PISTOL) { cg.weaponSelect = WP_STUN_BATON; } else if (cg.weaponSelect == WP_JAWA) { cg.weaponSelect = WP_BRYAR_PISTOL; } else if (cg.weaponSelect == WP_TUSKEN_RIFLE) { cg.weaponSelect = WP_JAWA; } else if (cg.weaponSelect == WP_BLOODGUN) { cg.weaponSelect = WP_TUSKEN_RIFLE; } else if (cg.weaponSelect == WP_CANNON) { cg.weaponSelect = WP_BLOODGUN; } else if (cg.weaponSelect == WP_POISON) { cg.weaponSelect = WP_CANNON; } else { cg.weaponSelect--; } if (cg.weaponSelect < firstWeapon || cg.weaponSelect > MAX_PLAYER_WEAPONS) { cg.weaponSelect = MAX_PLAYER_WEAPONS; } if (CG_WeaponSelectable(cg.weaponSelect, original, qfalse)) { SetWeaponSelectTime(); // cg.weaponSelectTime = cg.time; return; } } cg.weaponSelect = original; }[/code] Now you need to specific a thing. When the projectile hit a wall or a player, the projectile need to start the FX function that play the effect of the impact. This can be setted here: [code=auto:0]void CG_MissileHitWall[/code] add this into the switch case of weapons [code=auto:0]case WP_BLOODGUN: if (altFire) { parm = 0; if (cent - > gent) { parm += cent - > gent - > count; } FX_BloodGunAltHitWall(origin, dir, parm); } else { FX_BloodGunHitWall(origin, dir); } break;[/code] now do the same with the hitting of players. [code=auto:0]void CG_MissileHitPlayer( centity_t *cent, int weapon, vec3_t origin, vec3_t dir, qboolean altFire )[/code] [code=auto:0]case WP_BLOODGUN: if (altFire) { FX_BloodGunAltHitPlayer(origin, dir, humanoid); } else { FX_BloodGunHitPlayer(origin, dir, humanoid); } break;[/code] Okay men! You did it with cg_weapons. Now is the turn of g_weapons.cpp Locate the weapon helper velocity array and add your weapons at the end of the list. Important: you need to respect the enumeration order of weapons in weapons.h [code=auto:0]// Weapon Helper Functions float weaponSpeed[WP_NUM_WEAPONS][2] = { { 0, 0 }, //WP_NONE, { 0, 0 }, //WP_SABER, // NOTE: lots of code assumes this is the first weapon (... which is crap) so be careful -Ste. { BRYAR_PISTOL_VEL, BRYAR_PISTOL_VEL }, //WP_BLASTER_PISTOL, { BLASTER_VELOCITY, BLASTER_VELOCITY }, //WP_BLASTER, { Q3_INFINITE, Q3_INFINITE }, //WP_DISRUPTOR, { BOWCASTER_VELOCITY, BOWCASTER_VELOCITY }, //WP_BOWCASTER, { REPEATER_VELOCITY, REPEATER_ALT_VELOCITY }, //WP_REPEATER, { DEMP2_VELOCITY, DEMP2_ALT_RANGE }, //WP_DEMP2, { FLECHETTE_VEL, FLECHETTE_MINE_VEL }, //WP_FLECHETTE, { ROCKET_VELOCITY, ROCKET_ALT_VELOCITY }, //WP_ROCKET_LAUNCHER, { TD_VELOCITY, TD_ALT_VELOCITY }, //WP_THERMAL, { 0, 0 }, //WP_TRIP_MINE, { 0, 0 }, //WP_DET_PACK, { CONC_VELOCITY, Q3_INFINITE }, //WP_CONCUSSION, { 0, 0 }, //WP_MELEE, // Any ol' melee attack { 0, 0 }, //WP_STUN_BATON, { BRYAR_PISTOL_VEL, BRYAR_PISTOL_VEL }, //WP_BRYAR_PISTOL, { EMPLACED_VEL, EMPLACED_VEL }, //WP_EMPLACED_GUN, { BRYAR_PISTOL_VEL, BRYAR_PISTOL_VEL }, //WP_BOT_LASER, // Probe droid - Laser blast { 0, 0 }, //WP_TURRET, // turret guns { ATST_MAIN_VEL, ATST_MAIN_VEL }, //WP_ATST_MAIN, { ATST_SIDE_MAIN_VELOCITY, ATST_SIDE_ALT_NPC_VELOCITY }, //WP_ATST_SIDE, { EMPLACED_VEL, EMPLACED_VEL }, //WP_TIE_FIGHTER, { EMPLACED_VEL, REPEATER_ALT_VELOCITY }, //WP_RAPID_FIRE_CONC, { 0, 0 }, //WP_JAWA, { TUSKEN_RIFLE_VEL, TUSKEN_RIFLE_VEL }, //WP_TUSKEN_RIFLE, { 0, 0 }, //WP_TUSKEN_STAFF, { 0, 0 }, //WP_SCEPTER, { 0, 0 }, //WP_NOGHRI_STICK, { BLOODGUN_VEL, BLOODGUN_VEL }, //WP_BLOODGUN, { CANNON_VEL, CANNON_MINE_VEL }, //WP_CANNON, { POISON_VELOCITY, POISON_VELOCITY }, //WP_POISON, };[/code] Now locate this: [code=auto:0]qboolean W_AccuracyLoggableWeapon(int weapon, qboolean alt_fire, int mod) { if (mod != MOD_UNKNOWN) { switch (mod) { //standard weapons case MOD_BRYAR: case MOD_BRYAR_ALT: case MOD_CANNON: case MOD_CANNON_ALT: case MOD_SONIC: case MOD_SONIC_ALT: case MOD_BLOODGUN: case MOD_BLOODGUN_ALT: case MOD_BLASTER: case MOD_BLASTER_ALT: case MOD_DISRUPTOR: case MOD_SNIPER: case MOD_BOWCASTER: case MOD_BOWCASTER_ALT: case MOD_POISON: case MOD_POISON_ALT: case MOD_ROCKET: case MOD_ROCKET_ALT: case MOD_CONC: case MOD_CONC_ALT: return qtrue; break; //non-alt standard case MOD_REPEATER: case MOD_DEMP2: case MOD_FLECHETTE: case MOD_ALCHEMIC: return qtrue; break; //emplaced gun case MOD_EMPLACED: return qtrue; break; //atst case MOD_ENERGY: case MOD_EXPLOSIVE: if (weapon == WP_ATST_MAIN || weapon == WP_ATST_SIDE) { return qtrue; } break; } } else if (weapon != WP_NONE) { switch (weapon) { case WP_BRYAR_PISTOL: case WP_BLOODGUN: case WP_BLASTER_PISTOL: case WP_BLASTER: case WP_DISRUPTOR: case WP_BOWCASTER: case WP_ROCKET_LAUNCHER: case WP_CONCUSSION: return qtrue; break; //non-alt standard case WP_REPEATER: case WP_DEMP2: case WP_FLECHETTE: case WP_CANNON: case WP_POISON: if (!alt_fire) { return qtrue; } break; //emplaced gun case WP_EMPLACED_GUN: return qtrue; break; //atst case WP_ATST_MAIN: case WP_ATST_SIDE: return qtrue; break; } } return qfalse; }[/code] now scroll down until it: [code=auto:0]void CalcMuzzlePoint( gentity_t *const ent, vec3_t forwardVec, vec3_t right, vec3_t up, vec3_t muzzlePoint, float lead_in ) /---------------------------------------------------------[/code] this place the muzzle flash position coordinates of your weapons. Because my weapon is a gun, i added it to the bryar \ blaster pistol case. [code=auto:0]switch (ent - > s.weapon) { case WP_BRYAR_PISTOL: case WP_BLASTER_PISTOL: case WP_BLOODGUN: ViewHeightFix(ent); muzzlePoint[2] += ent - > client - > ps.viewheight; //By eyes muzzlePoint[2] -= 16; VectorMA(muzzlePoint, 28, forwardVec, muzzlePoint); VectorMA(muzzlePoint, 6, vrightVec, muzzlePoint); break;[/code] there’s another setting, an array, a matrix that setting coordinates of muzzle flash point on xyz axis. [code=auto:0]// Muzzle point table... vec3_t WP_MuzzlePoint[WP_NUM_WEAPONS] = { // Fwd, right, up. { 0, 0, 0 }, // WP_NONE, { 8, 16, 0 }, // WP_SABER, { 12, 6, -6 }, // WP_BLASTER_PISTOL, { 12, 6, -6 }, // WP_BLASTER, { 12, 6, -6 }, // WP_DISRUPTOR, { 12, 2, -6 }, // WP_BOWCASTER, { 12, 4.5, -6 }, // WP_REPEATER, { 12, 6, -6 }, // WP_DEMP2, { 12, 6, -6 }, // WP_FLECHETTE, { 12, 8, -4 }, // WP_ROCKET_LAUNCHER, { 12, 0, -4 }, // WP_THERMAL, { 12, 0, -10 }, // WP_TRIP_MINE, { 12, 0, -4 }, // WP_DET_PACK, { 12, 8, -4 }, // WP_CONCUSSION, { 0, 8, 0 }, // WP_MELEE, { 0, 0, 0 }, // WP_ATST_MAIN, { 0, 0, 0 }, // WP_ATST_SIDE, { 0, 8, 0 }, // WP_STUN_BATON, { 12, 6, -6 }, // WP_BRYAR_PISTOL, { 12, 6, -6 }, // WP_BLOODGUN, { 12, 6, -6 }, // WP_CANNON, { 12, 6, -6 }, // WP_POISON, };[/code] also here, you need to respect the the enumeration position of your weapon in weapon.h. so be careful. Now go down... now... you wanna your weapons shoots, right? You not want is a simple toy XD. So here you can set the function of shooting of your weapons, you need just to add your weapoin into the switch case sequence. [code=auto:0]// fire the specific weapon switch (ent - > s.weapon) case WP_BLOODGUN: WP_FireBloodGun(ent, alt_fire); break; case WP_BLASTER: WP_FireBlaster(ent, alt_fire); break; case WP_TUSKEN_RIFLE: if (alt_fire) { WP_FireTuskenRifle(ent); } else { WP_Melee(ent); } break; case WP_DISRUPTOR: alert = 50; // if you want it to alert enemies, remove this WP_FireDisruptor(ent, alt_fire); break;[/code] that’s will call the function you putted on WP_BLASTER_PISTOL.CPP and will execute it. So weapon will shoots! We end with g_weapons setup! A little thing... open w_local.h There are the list of the void that declair the definition of the fire weapons function. Add the custom fire void definition of your weapon here! [code=auto:0]void WP_FireBloodGun( gentity_t *ent, qboolean alt_fire );[/code] Now, if you remember, on the blaster_pistol.cpp you used new custom MOD for your weapons. MOD mean MeansOfDeath. Every kind of thing can kills clints... for clients i mean player, npcs etc XD you need to add the new MOD for the your new weapons. Is not necessary, because you can use the same mod of default weapons, but in this case i teach you how do it. if you remember, we added also the mod of your weapon on cg_weapons into the accuracy switch. Open bg_public.h And add your new MOD at the end of enum MeansofDeathlist [code=auto:0]// means of death typedef enum { MOD_UNKNOWN, // weapons MOD_SABER, MOD_BRYAR, MOD_BRYAR_ALT, MOD_BLASTER, MOD_BLASTER_ALT, MOD_DISRUPTOR, MOD_SNIPER, MOD_BOWCASTER, MOD_BOWCASTER_ALT, MOD_REPEATER, MOD_REPEATER_ALT, MOD_DEMP2, MOD_DEMP2_ALT, MOD_FLECHETTE, MOD_FLECHETTE_ALT, MOD_ROCKET, MOD_ROCKET_ALT, //NEW for JKA weapons: MOD_CONC, MOD_CONC_ALT, //END JKA weapons. MOD_THERMAL, MOD_THERMAL_ALT, MOD_DETPACK, MOD_LASERTRIP, MOD_LASERTRIP_ALT, MOD_MELEE, MOD_SEEKER, MOD_FORCE_GRIP, MOD_FORCE_LIGHTNING, MOD_FORCE_DRAIN, MOD_EMPLACED, // world / generic MOD_ELECTROCUTE, MOD_EXPLOSIVE, MOD_EXPLOSIVE_SPLASH, MOD_KNOCKOUT, MOD_ENERGY, MOD_ENERGY_SPLASH, MOD_WATER, MOD_SLIME, MOD_LAVA, MOD_CRUSH, MOD_IMPACT, MOD_FALLING, MOD_SUICIDE, MOD_TRIGGER_HURT, MOD_GAS, MOD_SONIC, MOD_SONIC_ALT, MOD_CANNON, MOD_CANNON_ALT, MOD_BLOODGUN, MOD_BLOODGUN_ALT,[/code] Looong, isnt’it? Now is the turn of CG_weapons.cpp You need to set what MOD are used by your player when equip a weapon you can add the new mod or set the new mod to your weapon in this switch case: [code=auto:0]if (weapon == WP_NONE) { switch (mod) { case MOD_SABER: weapon = WP_SABER; break; case MOD_BRYAR: case MOD_BRYAR_ALT: weapon = WP_BLASTER_PISTOL; break; case MOD_BLASTER: case MOD_BLASTER_ALT: weapon = WP_BLASTER; break; case MOD_DISRUPTOR: case MOD_SNIPER: weapon = WP_DISRUPTOR; break; case MOD_BOWCASTER: case MOD_BOWCASTER_ALT: weapon = WP_BOWCASTER; break; case MOD_REPEATER: case MOD_REPEATER_ALT: weapon = WP_REPEATER; break; case MOD_DEMP2: case MOD_DEMP2_ALT: weapon = WP_DEMP2; break; case MOD_FLECHETTE: case MOD_FLECHETTE_ALT: weapon = WP_FLECHETTE; break; case MOD_ROCKET: case MOD_ROCKET_ALT: weapon = WP_ROCKET_LAUNCHER; break; case MOD_CONC: case MOD_CONC_ALT: weapon = WP_CONCUSSION; break; case MOD_THERMAL: case MOD_THERMAL_ALT: weapon = WP_THERMAL; break; case MOD_DETPACK: weapon = WP_DET_PACK; break; case MOD_LASERTRIP: case MOD_LASERTRIP_ALT: weapon = WP_TRIP_MINE; break; case MOD_MELEE: if (self - > s.weapon == WP_STUN_BATON) { weapon = WP_STUN_BATON; } else if (self - > s.weapon == WP_MELEE) { weapon = WP_MELEE; } break; case MOD_SONIC: case MOD_SONIC_ALT: weapon = WP_JAWA; break; case MOD_CANNON: case MOD_CANNON_ALT: weapon = WP_BRYAR_PISTOL; break; case MOD_BLOODGUN: case MOD_BLOODGUN_ALT: weapon = WP_BLOODGUN; break;[/code] Now... maybe you want your weapon make more damage to some kind of enemy instead of another... you want maybe to make a light side or dark side weapon that is letal for sith of jedi? Or maybe a weapon letal for organic people and letal for droid? Go to g_combat.cpp and search about the MOD_DEMP2 array You need to make something like this: [code=auto:0]if (mod == MOD_BLOODGUN || mod == MOD_BLOODGUN_ALT) { if (client) { if ( client - > NPC_class == CLASS_TAVION || client - > NPC_class == CLASS_DESANN || client - > NPC_class == CLASS_SAND_CREATURE || client - > NPC_class == CLASS_ATST ) { // DEMP2 does more damage to these guys. damage *= 0; } else if (client - > NPC_class == CLASS_MOUSE || client - > NPC_class == CLASS_JAWA || client - > NPC_class == CLASS_RANCOR || client - > NPC_class == CLASS_R2D2 || client - > NPC_class == CLASS_R5D2 || client - > NPC_class == CLASS_COMMANDO || client - > NPC_class == CLASS_MURJJ) { // DEMP2 does way more damage to these guys. damage *= 1.5; } else if (client - > NPC_class == CLASS_BOBAFETT || client - > NPC_class == CLASS_MORGANKATARN || client - > NPC_class == CLASS_WAMPA || client - > NPC_class == CLASS_MINEMONSTER || client - > NPC_class == CLASS_REELO ) { // DEMP2 does way more damage to these guys. damage *= 0.5; } else if (client - > NPC_class == CLASS_INTERROGATOR || client - > NPC_class == CLASS_PRISONER || client - > NPC_class == CLASS_TRANDOSHAN) { // DEMP2 does way more damage to these guys. damage *= 3; } else if (client - > NPC_class == CLASS_BESPIN_COP || client - > NPC_class == CLASS_REBEL || client - > NPC_class == CLASS_RODIAN || client - > NPC_class == CLASS_GALAK || client - > NPC_class == CLASS_STORMTROOPER || client - > NPC_class == CLASS_IMPWORKER || client - > NPC_class == CLASS_LANDO ) { // DEMP2 does way more damage to these guys. damage *= 1.5; } else if (client - > NPC_class == CLASS_IMPERIAL || client - > NPC_class == CLASS_JAN) { // DEMP2 does way more damage to these guys. damage *= 1.25; } else if (client - > NPC_class == CLASS_TUSKEN || client - > NPC_class == CLASS_GLIDER || client - > NPC_class == CLASS_NOGHRI || client - > NPC_class == CLASS_FLIER2 || client - > NPC_class == CLASS_WEEQUAY || client - > NPC_class == CLASS_LIZARD || client - > NPC_class == CLASS_SWAMPTROOPER || client - > NPC_class == CLASS_FISH || client - > NPC_class == CLASS_FLIER2 || client - > NPC_class == CLASS_GRAN || client - > NPC_class == CLASS_CLAW || client - > NPC_class == CLASS_JEDI || client - > NPC_class == CLASS_REBORN || client - > NPC_class == CLASS_ALORA || client - > NPC_class == CLASS_MONMOTHA ) { // DEMP2 does way more damage to these guys. damage *= 2; } else if (client - > NPC_class == CLASS_KYLE || client - > NPC_class == CLASS_LUKE) { // DEMP2 does way more damage to these guys. damage *= 1.75; } } else if (targ - > s.weapon == WP_TURRET) { damage *= 0; // more damage to turret things } }[/code] this can add weakness or resistance of damage to your new weapon! At the end of this array you will found also the code part that making hazardtrooper resistance to the WP_SABER. [code=auto:0]if (client && client - > NPC_class == CLASS_HAZARD_TROOPER) { if (mod == MOD_SABER && damage > 0 && !(dflags & DAMAGE_NO_PROTECTION)) { damage /= 10; } }[/code] in that case every swing of saber damage is divided by 10 you can add your mod also there, and putanother value for reduce the damage by a division on certain classes. For example [code=auto:0]if (client && client - > NPC_class == CLASS_JEDI) { if (mod == MOD_BLOODGUN && damage > 0 && !(dflags & DAMAGE_NO_PROTECTION)) { damage /= 10; } }[/code] As you can see, blood gun weapon now will not much efficiently against a jedi XD. When a jedi take a wouund by the gun, his damage is reduced of ten times. So if he take 50 HP of damage point, it get just 5! [code=auto:0]if (client && client - > NPC_class == CLASS_JEDI) { if (mod == MOD_BLOODGUN && damage > 0 && !(dflags & DAMAGE_NO_PROTECTION)) { damage /= 7; } }[/code] same thing for example, but now dmg is divided for seven. Understood? You can make some kind of rpg MOD playing with the MOD weakness and resistance to the weapons. You can also make classes resistance or immune to force drain or grip or force lightning. All this is possible on g_combat.cpp i really need to notice you to examinate this part of code. You will found a lot of useful thing about customization of combat of your mod. On g_combat at the start of file there is also another thing: add your weapon in this switch if you want weapon will be dropped when ends his ammo. [code=auto:0]if (weapon == WP_THERMAL && self - > client - > ps.torsoAnim == BOTH_ATTACK10) { //we were getting ready to throw the thermal, drop it! self - > client - > ps.weaponChargeTime = level.time - FRAMETIME; //so it just kind of drops it dropped = WP_DropThermal(self); } else { // find the item type for this weapon item = FindItemForWeapon((weapon_t) weapon); } if (item && !dropped) { // spawn the item dropped = Drop_Item(self, item, 0, qtrue); //TEST: dropped items never go away dropped - > e_ThinkFunc = thinkF_NULL; dropped - > nextthink = -1; if (!self - > s.number) { //player's dropped items never go away //dropped->e_ThinkFunc = thinkF_NULL; //dropped->nextthink = -1; dropped - > count = 0; //no ammo } else { //FIXME: base this on the NPC's actual amount of ammo he's used up... switch (weapon) { case WP_BRYAR_PISTOL: case WP_BLASTER_PISTOL: dropped - > count = 20; break; case WP_BLASTER: dropped - > count = 20; break; case WP_DISRUPTOR: dropped - > count = 20; break; case WP_BOWCASTER: dropped - > count = 20; break; case WP_REPEATER: dropped - > count = 20; break; case WP_DEMP2: dropped - > count = 20; break; case WP_FLECHETTE: dropped - > count = 20; break; case WP_ROCKET_LAUNCHER: dropped - > count = 3; break; case WP_CONCUSSION: dropped - > count = 100; //12; break; case WP_THERMAL: dropped - > count = 5; break; case WP_TRIP_MINE: dropped - > count = 3; break; case WP_DET_PACK: dropped - > count = 1; break; case WP_STUN_BATON: dropped - > count = 20; break; case WP_ATST_MAIN: dropped - > count = 10; break; case WP_ATST_SIDE: dropped - > count = 15; break; case WP_EMPLACED_GUN: dropped - > count = 20; break; case WP_TUSKEN_RIFLE: dropped - > count = 10; break; case WP_BLOODGUN: dropped - > count = 15; break; case WP_CANNON: dropped - > count = 12; break; case WP_POISON: dropped - > count = 10; break; default: dropped - > count = 0; break; } }[/code] you can set also the minimal ammo value when weapon is dropped. Weapon will not shoot more when reach this number of ammo. Now two last thing. Concern the NPCs, go to NPC_combat.cpp. you need to set the delay between shots of the npcs when they attack you with new weapons, if you not do this, they will shot you with a powerful repeater XDDD [code=auto:0]case WP_BLASTER_PISTOL: ent - > NPC - > aiFlags &= ~NPCAI_BURST_WEAPON; if (ent - > weaponModel[1] > 0) { //commando ent - > NPC - > aiFlags |= NPCAI_BURST_WEAPON; ent - > NPC - > burstMin = 4; ent - > NPC - > burstMax = 12; if (g_spskill - > integer == 0) ent - > NPC - > burstSpacing = 600; //attack debounce else if (g_spskill - > integer == 1) ent - > NPC - > burstSpacing = 400; //attack debounce else ent - > NPC - > burstSpacing = 250; //attack debounce } else if (ent - > client - > NPC_class == CLASS_SABOTEUR) { if (g_spskill - > integer == 0) ent - > NPC - > burstSpacing = 900; //attack debounce else if (g_spskill - > integer == 1) ent - > NPC - > burstSpacing = 600; //attack debounce else ent - > NPC - > burstSpacing = 400; //attack debounce } else { // ent->NPC->burstSpacing = 1000;//attackdebounce if (g_spskill - > integer == 0) ent - > NPC - > burstSpacing = 1000; //attack debounce else if (g_spskill - > integer == 1) ent - > NPC - > burstSpacing = 750; //attack debounce else ent - > NPC - > burstSpacing = 500; //attack debounce } break;[/code] after blaster pistol i will add this case: for my weapon Imperials will shoot more fast and quick then other classes. [code=auto:0]case WP_BLOODGUN: ent - > NPC - > aiFlags &= ~NPCAI_BURST_WEAPON; if (ent - > client - > NPC_class == CLASS_IMPERIAL) { if (g_spskill - > integer == 0) ent - > NPC - > burstSpacing = 750; //attack debounce else if (g_spskill - > integer == 1) ent - > NPC - > burstSpacing = 500; //attack debounce else ent - > NPC - > burstSpacing = 250; //attack debounce } else { // ent->NPC->burstSpacing = 1000;//attackdebounce if (g_spskill - > integer == 0) ent - > NPC - > burstSpacing = 800; //attack debounce else if (g_spskill - > integer == 1) ent - > NPC - > burstSpacing = 600; //attack debounce else ent - > NPC - > burstSpacing = 400; //attack debounce } break;[/code] Okay, the works is big, but not surrounder now! We are ending just now! Remaing only one thing: Animations ! Which animation you want npc and player use when they shoot with the new weapons? You can set on Bg_animate.cpp Locate this at the end of files: [code=auto:0]case WP_BRYAR_PISTOL: if (pm - > ps - > weaponstate == WEAPON_CHARGING_ALT || weaponBusy) { PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY2, SETANIM_FLAG_NORMAL);[/code] there is a switch case that allow you to customize animation for the shooting and weaponready of every kind of your weapons [code=auto:0]case WP_BLOODGUN: if (pm - > ps - > weaponstate == WEAPON_CHARGING_ALT || weaponBusy) { PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY2, SETANIM_FLAG_NORMAL); } else if (PM_RunningAnim(pm - > ps - > legsAnim) || PM_WalkingAnim(pm - > ps - > legsAnim) || PM_JumpingAnim(pm - > ps - > legsAnim) || PM_SwimmingAnim(pm - > ps - > legsAnim)) { //running w/1-handed weapon uses full-body anim PM_SetAnim(pm, SETANIM_TORSO, pm - > ps - > legsAnim, SETANIM_FLAG_NORMAL); } else { PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONIDLE2, SETANIM_FLAG_NORMAL); } break;
you can set every kind of animation you desire ^^
Last thing. In case your are making a Charging weapons. You need your weapon charging the shoot when you push the alt attack button. For allow this, add the weapon in this array!
//---------------------------------------
static bool PM_DoChargedWeapons(void)
//---------------------------------------
{
qboolean charging = qfalse,
altFire = qfalse;
//FIXME: make jedi aware they're being aimed at with a charged-up weapon (strafe and be evasive?)
// If you want your weapon to be a charging weapon, just set this bit up
switch (pm - > ps - > weapon) {
//------------------
case WP_BRYAR_PISTOL:
case WP_BLASTER_PISTOL:
case WP_BLOODGUN:
case WP_POISON:
// alt-fire charges the weapon
if (pm - > cmd.buttons & BUTTON_ALT_ATTACK) {
charging = qtrue;
altFire = qtrue;
}
break;
//------------------
case WP_DISRUPTOR:
// alt-fire charges the weapon...but due to zooming being controlled by the alt-button, the main button actually charges...but only when zoomed.
// lovely, eh?
if ((pm - > ps - > clientNum < MAX_CLIENTS || PM_ControlledByPlayer())) {
if (cg.zoomMode == 2) {
if (pm - > cmd.buttons & BUTTON_ATTACK) {
charging = qtrue;
altFire = qtrue; // believe it or not, it really is an alt-fire in this case!
}
}
} else if (pm - > gent && pm - > gent - > NPC) {
if ((pm - > gent - > NPC - > scriptFlags & SCF_ALT_FIRE)) {
if (pm - > gent - > fly_sound_debounce_time > level.time) {
charging = qtrue;
altFire = qtrue;
}
}
}
break;
//------------------
case WP_BOWCASTER:
// main-fire charges the weapon
if (pm - > cmd.buttons & BUTTON_ATTACK) {
charging = qtrue;
}
break;
//------------------
case WP_DEMP2:
// alt-fire charges the weapon
if (pm - > cmd.buttons & BUTTON_ALT_ATTACK) {
charging = qtrue;
altFire = qtrue;
}
break;
//------------------
case WP_ROCKET_LAUNCHER:
// Not really a charge weapon, but we still want to delay fire until the button comes up so that we can
// implement our alt-fire locking stuff
if (pm - > cmd.buttons & BUTTON_ALT_ATTACK) {
charging = qtrue;
altFire = qtrue;
}
break;
//------------------
case WP_THERMAL:
// FIXME: Really should have a wind-up anim for player
// as he holds down the fire button to throw, then play
// the actual throw when he lets go...
if (pm - > cmd.buttons & BUTTON_ALT_ATTACK) {
altFire = qtrue; // override default of not being an alt-fire
charging = qtrue;
} else if (pm - > cmd.buttons & BUTTON_ATTACK) {
charging = qtrue;
}
break;
} // end switch
Okay, we should have end. Build the code, fix your error of syntax, and enjoy your new weapon into your mod! you need to build the release of openjk86_sp.exe and the jagamex86.dll
I hope all this can be useful for you.
Have a good time!
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