-
Posts
2,023 -
Joined
-
Last visited
Content Type
Profiles
News Articles
Tutorials
Forums
Downloads
Everything posted by Asgarath83
-
years ago I created a radius stunning attack for certain classes with kain mind reaver AOE attack. you need to work about force repulse code of JAE for your mindtrickradius force power. put knockback value and dmg to zero and set mindtrick confusiontime on push_list[x] entities hitted by the shockwave that aren't jedi \ sith classes and do not use saber as weapon
-
This is a simple tutorial. It explains how to make a saber with a shockwave attack when the saber hits the ground. okay this is my little tutorial: 1 - You Need Jedi academy patched to 1.01 version but this is obvious. 2 - Make a sword or lightsaber 3 - Make a BladeEffect, BlockEffect, and the HitPersonEffect, HitOtherEffect, with effect editor, new sounds, shaders etc etc,. Create a sab file like that. glyph1 <- your name { name "Glifo della Forza" <- your own name of the saber saberType SABER_CLAW saberModel "models/weapons2/Glyph1/glyph1.glm" <- your own glm model of the blade. // You need to change that's parts with your own sound. soundOn "sound/weapons/glyph/forcedraw.mp3" soundLoop "sound/weapons/glyph/forcecharge.wav" soundOff "sound/weapons/glyph/forcedown.mp3" g2MarksShader "gfx/damage/rivetmark" noManualDeactivate 1 saberStylelearned dual saberStyleForbidden fast saberStyleForbidden desann saberStyleForbidden tavion saberStyleForbidden medium saberStyleForbidden strong saberStyleForbidden staff saberLength 5 saberRadius 10 disarmable 0 NotInMp 1 throwable 0 blockEffect "glyph/forceimpact.efx" bladeEffect "glyph/forcehand.efx" hitPersonEffect "glyph/forceimpactbody.efx" hitOtherEffect "glyph/force.efx" <- this is the shockwave effect trailStyle 2 noClashFlare 1 noBlade 1 noDlight 1 noIdleEffect 1 oninwater 1 noWallMarks 1 BounceOnWalls 1 <- is very important parameter. maxChain 0 kataMove LS_SPINATTACK_ALORA PutAwayAnim BOTH_ALORA_SPIN_SLASH drawAnim BOTH_ALORA_SPIN_SLASH readyAnim BOTH_SABERDUAL_STANCE lungeatkmove LS_SPINATTACK_ALORA jumpatkfwdmove LS_SPINATTACK_ALORA AnimSpeedScale 0.85 spinSound "sound/weapons/glyph/forceswing1.mp3" swingSound1 "sound/weapons/glyph/forceswing.mp3" swingSound2 "sound/weapons/glyph/forceswing2.mp3" swingSound3 "sound/weapons/glyph/forceswing3.mp3" damagescale 0.1 <- my sword just make repulsing of enemy, but if yu want enemy be damage by hittid yu need simply to change value. 1 is a normal lightsaber dmg. knockbackscale 0.5 <- amount of knockback intensity splashRadius 750 <- range of knockback in map units splashKnockBack 750 <- is a very huge knockback. SplashDamage 0 <- yu can set the dmg of shockwave changing this value NOTE: dmg is variable by distance. if yu set 20: enemy close player make 20 Hit point, and enemy more distance make lower damage... forcerestrict FP_PROTECT noDismemberment 1 } Okay, the effect of this blade is: a magical effect, blade has no Glow of flash so is not visible the energy blade or the trail of a lightsaber, this makes just a particle effect like "bladeeffect". The blade model is Empty, and contains Only the tag_blade1, No other. So practically it's a simple tag with an invisible damage trail. How it works: hitting an enemy: the enemy is damaged and stunned. hitting a surface: the blade smashes a shockwave of Force Repulse, something like force unleashed, every time you hit a wall or ground, the blade makes it NOT WORK with stabbing. if you pay attention, you see there aren't set hitsound1-2-3, blocksound1-2-3 and bouncesound1-2-3 this because it has more efficient emitting sound with the effects. You can do this, simply add something like this in your efx file after making the effect . Sound { cullrange 150 <- distance of earshot sounds [ sound/weapons/glyph/fireimpactbody2.mp3 sound/weapons/glyph/fireimpactbody3.mp3 sound/weapons/glyph/fireimpactbody4.mp3 ] }<- for every effect produced, the engine produces the fireimpactbody2.mp3 OR the fireimpactbody3 OR the fire impactbody for... in random mode! This is my first tutorial and I want to share a lot of my modding knowledge with others. I hope people enjoy. And now, go create your magical sword. You can do every kind of blade: fire, water, light side, dark side... Every kind do you want to do, every damage you want to set. There is no limit to the imagination. Pity there is not a command or function for making a poison saber that does damage like the interrogator droid. If someone knows how to do that please contact me in a private message. Also, if someone knows how to produce a knockback efx, NOT in the impact on the ground, but with Katamove and custom animations... I think can be done with animevent... pity the effect in that case CAN'T produce the wave of damage and knockback but just the graphical appereance.
-
Lol enabling inventory and adapting for vehicle should be funny. i image also in inventory "cloacking device" and "repair kits" like in kotor XDD but in that case the repair kit icon shoul be an R2 astromech unit droid The repair kit should replace the "bacta canister" also a ship that can fire remotes like katarn on JO should be interessing. i imaging this little spheres that fly round a vehicles shotting and defending it...
-
pretty lovable. is like XWA gameplay. epic game of past. i wanna to require also a system for allow also a SP modality with NPC that can drive fighters exactly like they drive speeders and targetting player or other stuff with icarus. (npc not jump automatically into a fighter if they are near to him on SP .-. ) also, a switch weapon system like the player does into "jedi mode" weapons: 1 blasters 2 ion cannons (they can shoot with blaster too together into a combo fire toogling this weapon) 3 seeker \ homing missile \ proton torpedo 4 heavy rockets \ bombs weapon (bomb spawn by the lower section of a fighter and fall with gravity like repeater alt fire concussion attack) rockets can damage mutliple ships at same time but very slow, low ammo and low precision , also, they can damage with shockwave the player ship. 5 heavy blaster auto aim slot weapons (for freighters or cargo ships ) longe time of recharge, high damage, low precision, low shoot rate, they also overcharge if use too much and stop to works. should nice for turbolasers and turrets 6: a defensive system that shoot projectiles on back of figher for damage or disable the purchaser : this slot can be equipped with chaff (deflect homing and rocket missile), flare (damage enemy follower ship) , seeker mines.(follow and damage enemies that follow player, auto explode after a lot of second, like thermal detonators )
-
Sorry @@Langerd i know how to deactivate saber deflection on SP code, but i not know how to do that. : \ problem is targetting. mmm maybe the answer should be about the code that pain the crosshair with red \ green when you put before a target or in the ja enhanced deadly sight force power code. (enemy you watch with force sight get damage, like if you have cyclope eyes of X men ) but at moment i am not coding, sorry : \ i am busy with other stuffs.
-
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!
-
Welcome to alls! In this tutorial i wanna tell you how you can add new sabercolors to the lightsabers, also with NPC supports, for your mod. Are you tired about the only six saber color of default? Now you can add all colors you desire! You need just to follow these istructions. What you need: 1 Microsoft visual studio 2010 \ 2012 2 OpenJk 3 two new texture for every new color of your saber: one for the line and one for the glow. You can put in everypoint you desire, but for praticity we put into the same folder of the other saber textures: Gfx/effects/sabers/ You need to add COLORNAME_line And COLORNAME_glow2 textures. With photoshop or any other software you can easily edit or change a tube, or make your custom tube. In this case i wanna add a white saber. So, make as texture white_line and white_glow2. Second steps: Go into shaders/ folder Copy or paste the default sabers.SHADER file by the assest.pk3 of the game. At the end of shaders add these two new entry: gfx/effects/sabers/white_glow { cull disable { map gfx/effects/sabers/white_glow2.tga blendFunc GL_ONE GL_ONE rgbGen vertex } } gfx/effects/sabers/white_line { cull disable { map gfx/effects/sabers/white_line.tga blendFunc GL_ONE GL_ONE rgbGen vertex } } Okay, now the code part! Open your solution of Openjk and go into cg_weapons.cpp You will find these part: cgs.media.redSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/red_glow" ); cgs.media.redSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/red_line" ); cgs.media.orangeSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/orange_glow" ); cgs.media.orangeSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/orange_line" ); cgs.media.yellowSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/yellow_glow" ); cgs.media.yellowSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/yellow_line" ); cgs.media.greenSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/green_glow" ); cgs.media.greenSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/green_line" ); cgs.media.blueSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/blue_glow" ); cgs.media.blueSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/blue_line" ); cgs.media.purpleSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/purple_glow" ); cgs.media.purpleSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/purple_glow" ); // NEW COLORS cgs.media.whiteSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/white_line" ); cgs.media.whiteSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/white_glow" ); as you can see, at the end i register two new shader for the saber, the white color! now you need to co into cg_media.h // Saber shaders //----------------------------- qhandle_t forceCoronaShader; qhandle_t saberBlurShader; qhandle_t swordTrailShader; qhandle_t yellowDroppedSaberShader; // glow qhandle_t redSaberGlowShader; qhandle_t redSaberCoreShader; qhandle_t orangeSaberGlowShader; qhandle_t orangeSaberCoreShader; qhandle_t yellowSaberGlowShader; qhandle_t yellowSaberCoreShader; qhandle_t greenSaberGlowShader; qhandle_t greenSaberCoreShader; qhandle_t blueSaberGlowShader; qhandle_t blueSaberCoreShader; qhandle_t purpleSaberGlowShader; qhandle_t purpleSaberCoreShader; qhandle_t whiteSaberGlowShader; qhandle_t whiteSaberCoreShader; as you can see, here 2 new media entries for the shaders of the white saber. Now the hardest part. Go into qcommon\qshared.h and add a new function for your new sabercolor. // Player weapons effects typedef enum { SABER_RED, SABER_ORANGE, SABER_YELLOW, SABER_GREEN, SABER_BLUE, SABER_PURPLE, SABER_WHITE } saber_colors_t; okay, done with q_shared. Now you need to associate the function to the shader, so, when character use the SABER_WHITE, the saber will got as tube and glow the new shader. You can make this into cg_player.cpp Locate the static void called dosaber static void CG_DoSaber( vec3_t origin, vec3_t dir, float length, float lengthMax, float radius, saber_colors_t color, int rfx, qboolean doLight ) { vec3_t mid; qhandle_t blade = 0, glow = 0; refEntity_t saber; float radiusmult; if ( length < MIN_SABERBLADE_DRAW_LENGTH ) { // if the thing is so short, just forget even adding me. return; } // Find the midpoint of the saber for lighting purposes VectorMA( origin, length * 0.5f, dir, mid ); switch( color ) { case SABER_RED: glow = cgs.media.redSaberGlowShader; blade = cgs.media.redSaberCoreShader; break; case SABER_ORANGE: glow = cgs.media.orangeSaberGlowShader; blade = cgs.media.orangeSaberCoreShader; break; case SABER_YELLOW: glow = cgs.media.yellowSaberGlowShader; blade = cgs.media.yellowSaberCoreShader; break; case SABER_GREEN: glow = cgs.media.greenSaberGlowShader; blade = cgs.media.greenSaberCoreShader; break; case SABER_BLUE: glow = cgs.media.blueSaberGlowShader; blade = cgs.media.blueSaberCoreShader; break; case SABER_PURPLE: glow = cgs.media.purpleSaberGlowShader; blade = cgs.media.purpleSaberCoreShader; break; // NEW SABERS case SABER_WHITE: glow = cgs.media.whiteSaberGlowShader; blade = cgs.media.whiteSaberCoreShader; break; Okay! Now... there is something missing right? Ah, yes! The Dlight! And also the support for the new color for .SAB file and .NPC file. The dinamic light, the light color emitted by the new blade, is setted again into cg_player. Is little complex this part, so be careful. The first field to edit is the Static void cg_rgbforsabercolor static void CG_RGBForSaberColor( saber_colors_t color, vec3_t rgb ) { switch( color ) { case SABER_RED: VectorSet( rgb, 1.0f, 0.2f, 0.2f ); break; case SABER_ORANGE: VectorSet( rgb, 1.0f, 0.5f, 0.1f ); break; case SABER_YELLOW: VectorSet( rgb, 1.0f, 1.0f, 0.2f ); break; case SABER_GREEN: VectorSet( rgb, 0.2f, 1.0f, 0.2f ); break; case SABER_BLUE: VectorSet( rgb, 0.2f, 0.4f, 1.0f ); break; case SABER_PURPLE: VectorSet( rgb, 0.9f, 0.2f, 1.0f ); break; case SABER_WHITE: VectorSet( rgb, 1.0f, 1.0f, 1.0f ); break; you can see for every blade color three number. They are float, decimal values. The first is the total amount of red saturation, the second is for the green, the third is for the blue. With the union of the three color gradient you can obtain every other kind of colors for the dinamic light. In case of the white, you need just to put 1.0 as value for every 3 field, and you will get a white saber. But HOW obtain the exact value of the color that you want emit your saber for Dlight? I will answer to this now. Ever into cg_player... you need to decide the color of the trail that trace your blade with the swings! You can set this here: Locate: static void CG_AddSaberBladeGo Function! And go down, go down down until you will find... #define SABER_TRAIL_TIME 40.0f // if we happen to be timescaled or running in a high framerate situation, we don't want to flood // the system with very small trail slices...but perhaps doing it by distance would yield better results? if ( saberTrail->lastTime > cg.time ) {//after a pause, cg.time jumps ahead in time for one frame //and lastTime gets set to that and will freak out, so, since //it's never valid for saberTrail->lastTime to be > cg.time, //cap it to cg.time here saberTrail->lastTime = cg.time; } if ( cg.time > saberTrail->lastTime + 2 && saberTrail->inAction ) // 2ms { if ( saberTrail->inAction && cg.time < saberTrail->lastTime + 300 ) // if we have a stale segment, don't draw until we have a fresh one { vec3_t rgb1={255,255,255}; if ( cent->gent->client->ps.saber[saberNum].type != SABER_SITH_SWORD && ( WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) || client->ps.saber[saberNum].trailStyle != 1 ) && ( !WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) || client->ps.saber[saberNum].trailStyle2 != 1 ) ) { switch( client->ps.saber[saberNum].blade[bladeNum].color ) { case SABER_RED: VectorSet( rgb1, 255.0f, 0.0f, 0.0f ); break; case SABER_ORANGE: VectorSet( rgb1, 255.0f, 64.0f, 0.0f ); break; case SABER_YELLOW: VectorSet( rgb1, 255.0f, 255.0f, 0.0f ); break; case SABER_GREEN: VectorSet( rgb1, 0.0f, 255.0f, 0.0f ); break; case SABER_BLUE: VectorSet( rgb1, 0.0f, 64.0f, 255.0f ); break; case SABER_PURPLE: VectorSet( rgb1, 220.0f, 0.0f, 255.0f ); break; case SABER_WHITE: VectorSet( rgb1, 255.0f, 255.0f, 255.0f ); break; at the end, i added a case for SABER_WHITE. The saberwhite emite a trail with a rgb value of 255 for red, green and blue, too, so a white trail! Yes, dinamic light is in decimals, but these value is into 255 parts! But How you can calculate the correct rgb value for dinamic light and for the swing trail? Well, the answer is not into the code. Open Gtk radiant. Yes, you understood! GTK RADIANT! No, we not need to make a map, relax! you need to make just a light entity. After you make a light entities, hit the K button, and you can see the color panel, when you can scroll the triangle and set the RGB color you desire for your light. After you choice the light color value of your light... take paper and pen or pencil and wrote the 3 value of red, green and blue. For example... 234, 210, 50. Okay, when you go here in cg_player, for the trail color you need just to report the value of the light color you choose into radiant. For the dlight, instead, you need just a simple operation: 234\255 = 0.91 210\255 = 0.82 50\255 = 0.19 okay, now you get the three value to put into Dlight code part. You ended with cg_players. Now, just a last thing! The NPC supports. Go into npc_stats.cpp it contain the parameter of all stats, the code related the paramter you give to the NPC with NPC files. Locate these function: saber_colors_t TranslateSaberColor( const char *name ) and add at the end the white if case. { if ( !Q_stricmp( name, "red" ) ) { return SABER_RED; } if ( !Q_stricmp( name, "orange" ) ) { return SABER_ORANGE; } if ( !Q_stricmp( name, "yellow" ) ) { return SABER_YELLOW; } if ( !Q_stricmp( name, "green" ) ) { return SABER_GREEN; } if ( !Q_stricmp( name, "blue" ) ) { return SABER_BLUE; } if ( !Q_stricmp( name, "purple" ) ) { return SABER_PURPLE; } if ( !Q_stricmp( name, "white" ) ) { return SABER_WHITE; } Now, if you make an NPC with “white” as sabercolor definition, the NPC will have a blade with white color! Now the last thing! A consolle command for use your new saber! Go into g_svcmds.cpp Locate the sabercolor_f command and... static void Svcmd_SaberColor_f() {//FIXME: just list the colors, each additional listing sets that blade int saberNum = atoi(gi.argv(1)); const char *color[MAX_BLADES]; int bladeNum; for ( bladeNum = 0; bladeNum < MAX_BLADES; bladeNum++ ) { color[bladeNum] = gi.argv(2+bladeNum); } if ( !VALIDSTRING( color ) || saberNum < 1 || saberNum > 2 ) { gi.Printf( "Usage: saberColor <saberNum> <blade1 color> <blade2 color> ... <blade8 color> \n" ); gi.Printf( "valid saberNums: 1 or 2\n" ); gi.Printf( "valid colors: red, orange, yellow, green, blue, and purple\n" ); return; } saberNum--; gentity_t *self = G_GetSelfForPlayerCmd(); for ( bladeNum = 0; bladeNum < MAX_BLADES; bladeNum++ ) { if ( !color[bladeNum] || !color[bladeNum][0] ) { break; } else { self->client->ps.saber[saberNum].blade[bladeNum].color = TranslateSaberColor( color[bladeNum] ); } } if ( saberNum == 0 ) { gi.cvar_set( "g_saber_color", color[0] ); } else if ( saberNum == 1 ) { gi.cvar_set( "g_saber2_color", color[0] ); } } well. You not need to do nothing here... the sabercolor function will auto recognize every new colors entry. Build the code and Enjoy your new blades! i see this will works also for menu g_saber color. function That's all. i checked the ui_saber_color function also for UI menu and that's call the g_saber color values, so this should works also for menus. (ui_saber_color function is coded into code/ui/ui_main.cpp
-
HOW TO MAKE A NEW FORCE POWER – SINGLE PLAYER Welcome to alls to my third tutorial. In this tutorial i wanna tell you how you can add a new force power on JKA Single player game. I wanna to share with you the code of my force controlmind power. It’s act just like the force mindtrick level 4. it’s really a simple power to make and not take much time or work. Warning: some power require more complex works. That’s depend by you and by your code skill. I will not tell you how program a new power. I wanna tell you exactly what section of SP code you need to edit for add a new power and allow to the power to be usable. About programming, you need to think carefully about the kind of force power you want to do. If you want force shockwave power, for example, you need to dive the code and to see how work work the splashdamage of sabers and the force push power. If you want instead a freezing power, you need to study careful the d_npcfreeze cheat, the force grip power level 1 for locking enemy in a position and the force mindtrick power for the casting and visual efx of stasys enemy, or maybe, if you want an aura effect related to this, you need to study the FP_PROTECT or absorb aura power. I wanna tell you an indication about where you can find the code stuff related to these powers. FP_SENSE: wp_saber.cpp storing power working stuff. Cg_players.cpp storing the aura effect and the mask effect with the 2d scrolling. FP_SABER_DEFENSE, OFFENSE, THROW: check WP_SABER.CPP FP_SPEED: basically wp_saber.cpp FP_LIGHTNING: cgplayer for lightning visual effect, WP_SABER.cpp for lightning programming. FP_DRAIN: same as lightning. FP_GRIP, it’s all on wp_saber.cpp, but it’s in 4 different block. You can find a part of grip code into the void forcegrip, and also more stuff into forcepowerstop, forcepowerstart and forcepowerrun function. FP_MINDTRICK: it’s all on wp_saber.cpp, in only a block , void forcetelepathy. FP_RAGE: wp_saber.cpp. FP_PROTECT and FP_ABSORB: programmed on wp_saber.cpp, the aura effect is created on CG_PLAYERS.cpp FP_HEAL: on WP_saber.cpp. Sound of force powers: all these are definitated in a large case and switch functions array into wp_saber.cpp Okay, now let we start with the true tutorial. What you need: Microsoft visual studio 2010 \ 2012 OpenJK force power icon. a description of your force power into SP_INGAME. I will teach how to do. You got all that? Good! We start! First off, you need to add your power to the force power list. WARNING: you can have only total amount of 32 force power, not more. But in single player there are only 16 force power active, so you should got not much trouble about this. Go to code/qcommon\q_shared.h And find this enum string: 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_SABERTHROW, FP_SABER_DEFENSE, FP_SABER_OFFENSE, //new Jedi Academy powers FP_RAGE,//duration - speed, invincibility and extra damage for short period, drains your health and leaves you weak and slow afterwards. FP_PROTECT,//duration - protect against physical/energy (level 1 stops blaster/energy bolts, level 2 stops projectiles, level 3 protects against explosions)FP_ABSORB,/duration - protect against dark force powers (grip, lightning, drain - maybe push/pull, too?) FP_DRAIN,//hold/duration - drain force power for health FP_SEE,//duration - detect/see hidden enemies FP_STUN,//instant FP_HATE,//instant FP_CONTROLMIND,//instant FP_FREEZE,//istant FP_FEAR,//instant NUM_FORCE_POWERS } forcePowers_t; Add your new power at the end of the list, how i did with my new force staff. Now scroll down q_shared and find this: typedef enum { GENCMD_FORCE_HEAL = 1, GENCMD_FORCE_SPEED, GENCMD_FORCE_THROW, GENCMD_FORCE_PULL, GENCMD_FORCE_DISTRACT, GENCMD_FORCE_GRIP, GENCMD_FORCE_LIGHTNING, GENCMD_FORCE_RAGE, GENCMD_FORCE_PROTECT, GENCMD_FORCE_ABSORB, GENCMD_FORCE_DRAIN, GENCMD_FORCE_SEEING, GENCMD_FORCE_STUN, GENCMD_FORCE_HATE, GENCMD_FORCE_CONTROLMIND, GENCMD_FORCE_FREEZE, GENCMD_FORCE_FEAR, } genCmds_t; gencmd is necessary for assign key button to use the force powers. You need to add a definition for your new force power here related this. So for q_shared. You’are okay. Now go to g_active.cpp and add the external void definition for the function of your new powers here, upon the genericcmd switch. extern void ForceGrip(gentity_t *ent); extern void ForceLightning(gentity_t *ent); extern void ForceProtect(gentity_t *ent); extern void ForceRage(gentity_t *ent); extern void ForceSeeing(gentity_t *ent); extern void ForceTelepathy(gentity_t *ent); extern void ForceStun(gentity_t *ent); extern void ForceHate(gentity_t *ent); extern void ForceControlMind(gentity_t *ent); extern void ForceFreeze(gentity_t *ent); extern void ForceFear(gentity_t *ent); extern void ForceAbsorb(gentity_t *ent); extern void ForceHeal(gentity_t *ent); as you see i added the mine. Now scroll down and you found immediatly This switch, when you can assign to every command the function of his respective power. static void ProcessGenericCmd(gentity_t *ent, byte cmd) { switch(cmd) { case 0: break; case GENCMD_FORCE_DRAIN: ForceDrain2(ent); break; case GENCMD_FORCE_THROW: ForceThrow(ent, qfalse); break; case GENCMD_FORCE_SPEED: ForceSpeed(ent); break; case GENCMD_FORCE_PULL: ForceThrow(ent, qtrue); break; case GENCMD_FORCE_DISTRACT: ForceTelepathy(ent); break; case GENCMD_FORCE_CONTROLMIND: ForceControlMind(ent); break; case GENCMD_FORCE_FEAR: ForceFear(ent); break; case GENCMD_FORCE_STUN: ForceStun(ent); break; case GENCMD_FORCE_HATE: ForceHate(ent); break; case GENCMD_FORCE_GRIP: ForceGrip(ent); case GENCMD_FORCE_FREEZE: ForceFreeze(ent); break; case GENCMD_FORCE_LIGHTNING: ForceLightning(ent); break; case GENCMD_FORCE_RAGE: ForceRage(ent); break; case GENCMD_FORCE_PROTECT: ForceProtect(ent); break; case GENCMD_FORCE_ABSORB:ForceAbsorb(ent); break; case GENCMD_FORCE_SEEING: ForceSeeing(ent); break; case GENCMD_FORCE_HEAL: ForceHeal(ent); break; } } under every case, you need to add the void function of the power is called by the command. Okay, now we need to add force power as item available, exactly how we did with the weapons: Go to g_itemload.cpp and after the weapon qstrimp tags you will found the force power tags. Add the new power in that mode: else if (!Q_stricmp(tokenStr,"FP_HEAL")) { tag = FP_HEAL; } else if (!Q_stricmp(tokenStr,"FP_LEVITATION")) { tag = FP_LEVITATION; } else if (!Q_stricmp(tokenStr,"FP_SPEED")) { tag = FP_SPEED; } else if (!Q_stricmp(tokenStr,"FP_PUSH")) { tag = FP_PUSH; } else if (!Q_stricmp(tokenStr,"FP_PULL")) { tag = FP_PULL; } else if (!Q_stricmp(tokenStr,"FP_TELEPATHY")) { tag = FP_TELEPATHY; } else if (!Q_stricmp(tokenStr,"FP_SABERTHROW")) { tag = FP_SABERTHROW; } else if (!Q_stricmp(tokenStr,"FP_STUN")) { tag = FP_STUN; } else if (!Q_stricmp(tokenStr,"FP_HATE")) { tag = FP_HATE; } else if (!Q_stricmp(tokenStr,"FP_CONTROLMIND")) { tag = FP_CONTROLMIND; } else if (!Q_stricmp(tokenStr,"FP_FREEZE")) { tag = FP_FREEZE; } else if (!Q_stricmp(tokenStr,"FP_FEAR")) { tag = FP_FEAR; } else if (!Q_stricmp(tokenStr,"FP_LIGHTNING")) { tag = FP_LIGHTNING; } else if (!Q_stricmp(tokenStr,"FP_SABERTHROW")) { tag = FP_SABERTHROW; } else if (!Q_stricmp(tokenStr,"ITM_BATTERY_PICKUP")) { tag = ITM_BATTERY_PICKUP; } as you can see i added tags for the my new force powers. Now... if you wanna that your power can be used with icarus scripting, you need to make some editing. Go to q3_interface.h and add into the set list the new icarus command for your new force powers. On booleans: SET_NO_PVS_CULL,//## %t="BOOL_TYPES" # This entity will *always* be drawn - use only for special case cinematic NPCs that have anims that cover multiple rooms!!! SET_CLOAK,//## %t="BOOL_TYPES" # Set a Saboteur to cloak (true) or un-cloak (false). SET_FORCE_HEAL,//## %t="BOOL_TYPES" # Causes this ent to start force healing at whatever level of force heal they have SET_FORCE_SPEED,//## %t="BOOL_TYPES" # Causes this ent to start force speeding at whatever level of force speed they have (may not do anything for NPCs?) SET_FORCE_PUSH,//## %t="BOOL_TYPES" # Causes this ent to do a force push at whatever level of force push they have - will not fail SET_FORCE_PUSH_FAKE,//## %t="BOOL_TYPES" # Causes this ent to do a force push anim, sound and effect, will not push anything SET_FORCE_PULL,//## %t="BOOL_TYPES" # Causes this ent to do a force push at whatever level of force push they have - will not fail SET_FORCE_MIND_TRICK,//## %t="BOOL_TYPES" # Causes this ent to do a jedi mind trick at whatever level of mind trick they have (may not do anything for NPCs?) SET_FORCE_GRIP,//## %t="BOOL_TYPES" # Causes this ent to grip their enemy at whatever level of grip they have (will grip until scripted to stop) SET_FORCE_LIGHTNING,//## %t="BOOL_TYPES" # Causes this ent to lightning at whatever level of lightning they have (will lightning until scripted to stop) SET_FORCE_SABERTHROW,//## %t="BOOL_TYPES" # Causes this ent to throw their saber at whatever level of saber throw they have (will throw saber until scripted to stop) SET_FORCE_RAGE,//## %t="BOOL_TYPES" # Causes this ent to go into force rage at whatever level of force rage they have SET_FORCE_PROTECT,//## %t="BOOL_TYPES" # Causes this ent to start a force protect at whatever level of force protect they have SET_FORCE_ABSORB,//## %t="BOOL_TYPES" # Causes this ent to do start a force absorb at whatever level of force absorb they have SET_FORCE_DRAIN,//## %t="BOOL_TYPES" # Causes this ent to start force draining their enemy at whatever level of force drain they have (will drain until scripted to stop) SET_FORCE_STUN,//## %t="BOOL_TYPES" # Causes this ent to start force draining their enemy at whatever level of force drain they have (will drain until scripted to stop) SET_FORCE_HATE,//## %t="BOOL_TYPES" # Causes this ent to start force draining their enemy at whatever level of force drain they have (will drain until scripted to stop) SET_FORCE_CONTROLMIND,//## %t="BOOL_TYPES" # Causes this ent to start force draining their enemy at whatever level of force drain they have (will drain until scripted to stop) SET_FORCE_FREEZE,//## %t="BOOL_TYPES" # Causes this ent to start force draining their enemy at whatever level of force drain they have (will drain until scripted to stop) SET_FORCE_FEAR,//## %t="BOOL_TYPES" # This NPC/player will not have any bone angle overrides or pitch or roll (should only be used in cinematics) SET_WINTER_GEAR, /## %t="BOOL_TYPES" # Set the player to wear his/her winter gear (skins torso_g1 and lower_e1), or restore the default skins. SET_NO_ANGLES, /## %t="BOOL_TYPES" # This NPC/player will not have any bone angle overrides or pitch or roll (should only be used in cinematics) And below, on the SET_FORCE_POWER_LEVELS. SET_FORCE_HEAL_LEVEL,/## %t="FORCE_LEVELS" # Change force power level SET_FORCE_JUMP_LEVEL,/## %t="FORCE_LEVELS" # Change force power level SET_FORCE_SPEED_LEVEL,/## %t="FORCE_LEVELS" # Change force power level SET_FORCE_PUSH_LEVEL,/## %t="FORCE_LEVELS" # Change force power level SET_FORCE_PULL_LEVEL,/## %t="FORCE_LEVELS" # Change force power level SET_FORCE_MINDTRICK_LEVEL,/## %t="FORCE_LEVELS" # Change force power level SET_FORCE_GRIP_LEVEL,/## %t="FORCE_LEVELS" # Change force power level SET_FORCE_LIGHTNING_LEVEL,/## %t="FORCE_LEVELS" # Change force power level SET_SABER_THROW,/## %t="FORCE_LEVELS" # Change force power level SET_SABER_DEFENSE,/## %t="FORCE_LEVELS" # Change force power level SET_SABER_OFFENSE,/## %t="SABER_STYLES" # Change force power level SET_FORCE_RAGE_LEVEL,/## %t="FORCE_LEVELS" # Change force power level SET_FORCE_PROTECT_LEVEL,/## %t="FORCE_LEVELS" # Change force power level SET_FORCE_ABSORB_LEVEL,/## %t="FORCE_LEVELS" # Change force power level SET_FORCE_DRAIN_LEVEL,/## %t="FORCE_LEVELS" # Change force power level SET_FORCE_SIGHT_LEVEL,/## %t="FORCE_LEVELS" # Change force power level SET_SABER1_COLOR1,/## %t="SABER_COLORS" # Set color of first blade of first saber SET_SABER1_COLOR2,/## %t="SABER_COLORS" # Set color of second blade of first saber SET_SABER2_COLOR1,/## %t="SABER_COLORS" # Set color of first blade of first saber SET_SABER2_COLOR2,/## %t="SABER_COLORS" # Set color of second blade of first saber SET_DISMEMBER_LIMB,/## %t="HIT_LOCATIONS" # Cut off a part of a body and send the limb flying But adding string voice is unuseful if you will not program also WHAT do this voice when they are executed on scripts. So you need to set this on Q3_INTERFACE.CPP You need to add here: case SET_FORCE_HEAL_LEVEL: case SET_FORCE_JUMP_LEVEL: case SET_FORCE_SPEED_LEVEL: case SET_FORCE_PUSH_LEVEL: case SET_FORCE_PULL_LEVEL: case SET_FORCE_MINDTRICK_LEVEL: case SET_FORCE_STUN_LEVEL: case SET_FORCE_HATE_LEVEL: case SET_FORCE_CONTROLMIND_LEVEL: case SET_FORCE_FREEZE_LEVEL: case SET_FORCE_FEAR_LEVEL: case SET_FORCE_GRIP_LEVEL: case SET_FORCE_LIGHTNING_LEVEL: case SET_SABER_THROW:case SET_SABER_DEFENSE: case SET_SABER_OFFENSE:case SET_FORCE_RAGE_LEVEL: case SET_FORCE_PROTECT_LEVEL: case SET_FORCE_ABSORB_LEVEL: case SET_FORCE_DRAIN_LEVEL: case SET_FORCE_SIGHT_LEVEL: int_data = atoi((char *) data); Q3_SetForcePowerLevel( entID, (toSet-SET_FORCE_HEAL_LEVEL), int_data ); break; case SET_SABER1: case SET_SABER2: WP_SetSaber( &g_entities[entID], toSet-SET_SABER1, (char *)data ); break; case SET_PLAYERMODEL: G_ChangePlayerModel( &g_entities[entID], (const char *)data ); break; // NOTE: Preconditions for entering a vehicle still exist. This is not assured to work. -Areis case SET_VEHICLE: Use( entID, (char *)data ); //G_DriveVehicle( &g_entities[entID], NULL, (char *)data ); break; case SET_SECURITY_KEY: Q3_GiveSecurityKey( entID, (char *)data ); break; case SET_SABER1_COLOR1: case SET_SABER1_COLOR2: WP_SaberSetColor( &g_entities[entID], 0, toSet-SET_SABER1_COLOR1, (char *)data ); break; case SET_SABER2_COLOR1: case SET_SABER2_COLOR2: WP_SaberSetColor( &g_entities[entID], 1, toSet-SET_SABER2_COLOR1, (char *)data ); break; case SET_DISMEMBER_LIMB: Q3_DismemberLimb( entID, (char *)data ); break; case SET_NO_PVS_CULL: Q3_SetBroadcast( entID, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; // Set a Saboteur to cloak (true) or un-cloak (false). case SET_CLOAK: // Created: 01/08/03 by AReis. //extern void Jedi_Cloak( gentity_t *self ); extern void Saboteur_Cloak( gentity_t *self ); if( Q_stricmp("true", ((char *)data)) == 0 ) { Saboteur_Cloak( &g_entities[entID] ); } else { Saboteur_Decloak( &g_entities[entID] ); } break; case SET_FORCE_HEAL: Q3_SetForcePower( entID, FP_HEAL, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_SPEED: Q3_SetForcePower( entID, FP_SPEED, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_PUSH: Q3_SetForcePower( entID, FP_PUSH, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_PUSH_FAKE: ForceThrow( &g_entities[entID], qfalse, qtrue ); break; case SET_FORCE_PULL: Q3_SetForcePower( entID, FP_PULL, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_MIND_TRICK: Q3_SetForcePower( entID, FP_TELEPATHY, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_STUN:Q3_SetForcePower( entID, FP_STUN, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_HATE:Q3_SetForcePower( entID, FP_HATE, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_CONTROLMIND: Q3_SetForcePower( entID, FP_CONTROLMIND, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_FREEZE: Q3_SetForcePower( entID, FP_FREEZE, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_FEAR: Q3_SetForcePower( entID, FP_FEAR, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_GRIP: Q3_SetForcePower( entID, FP_GRIP, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_LIGHTNING: Q3_SetForcePower( entID, FP_LIGHTNING, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_SABERTHROW: Q3_SetForcePower( entID, FP_SABERTHROW, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_RAGE: Q3_SetForcePower( entID, FP_RAGE, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_PROTECT: Q3_SetForcePower( entID, FP_PROTECT, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_ABSORB: Q3_SetForcePower( entID, FP_ABSORB, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; case SET_FORCE_DRAIN: Q3_SetForcePower( entID, FP_DRAIN, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; Exactly how i did. Well done. Now your force powers are available with scripts. And engine will recognize that need to execute a force power or assign a power level to a new force power with Icarus, if you need to make a huge mod with maps and scripts, this can be useful. Now... it’s turn of Npcs. You want NPCS can use your force power so you can make enemy sith that attack you with these new ability? So you need to add as NPC command. You can make this on NPCs_stats.cpp 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_SABERTHROW), ENUM2STRING(FP_SABER_DEFENSE), ENUM2STRING(FP_SABER_OFFENSE), //new Jedi Academy powers ENUM2STRING(FP_RAGE), ENUM2STRING(FP_PROTECT), ENUM2STRING(FP_ABSORB), ENUM2STRING(FP_DRAIN), ENUM2STRING(FP_SEE), ENUM2STRING(FP_STUN), ENUM2STRING(FP_HATE), ENUM2STRING(FP_CONTROLMIND), ENUM2STRING(FP_FREEZE), ENUM2STRING(FP_FEAR), { "", -1 } }; Now... the menu interface related to force power. If you they appears in your custom menu when your SP mission are completed, you need to add the new power on g_target.cpp On: void set_mission_stats_cvars(void) { char text[1024] = { 0 }; //we'll assume that the activator is the player gclient_t * const client = & level.clients[0]; if (!client) { return; } gi.cvar_set("ui_stats_enemieskilled", va("%d", client - > sess.missionStats.enemiesKilled)); //pass this on to the menu if (cg_entities[0].gent - > client - > sess.missionStats.totalSecrets) { cgi_SP_GetStringTextString("SP_INGAME_SECRETAREAS_OF", text, sizeof(text)); gi.cvar_set("ui_stats_secretsfound", va("%d %s %d", cg_entities[0].gent - > client - > sess.missionStats.secretsFound, text, cg_entities[0].gent - > client - > sess.missionStats.totalSecrets)); } else // Setting ui_stats_secretsfound to 0 will hide the text on screen { gi.cvar_set("ui_stats_secretsfound", "0"); } // Find the favorite weapon int wpn = 0, i; int max_wpn = cg_entities[0].gent - > client - > sess.missionStats.weaponUsed[0]; for (i = 1; i < WP_NUM_WEAPONS; i++) { if (cg_entities[0].gent - > client - > sess.missionStats.weaponUsed[i] > max_wpn) { max_wpn = cg_entities[0].gent - > client - > sess.missionStats.weaponUsed[i]; wpn = i; } } if (wpn) { gitem_t * wItem = FindItemForWeapon((weapon_t) wpn); cgi_SP_GetStringTextString(va("SP_INGAME_%s", wItem - > classname), text, sizeof(text)); gi.cvar_set("ui_stats_fave", va("%s", text)); //pass this on to the menu } gi.cvar_set("ui_stats_shots", va("%d", client - > sess.missionStats.shotsFired)); //pass this on to the menu gi.cvar_set("ui_stats_hits", va("%d", client - > sess.missionStats.hits)); //pass this on to the menu const float percent = cg_entities[0].gent - > client - > sess.missionStats.shotsFired ? 100.0 f * (float) cg_entities[0].gent - > client - > sess.missionStats.hits / cg_entities[0].gent - > client - > sess.missionStats.shotsFired : 0; gi.cvar_set("ui_stats_accuracy", va("%.2f%%", percent)); /pass this on to the menugi.cvar_set("ui_stats_thrown", va("%d",client->sess.missionStats.saberThrownCnt)); // pass this on to the menu gi.cvar_set("ui_stats_blocks", va("%d", client - > sess.missionStats.saberBlocksCnt)); gi.cvar_set("ui_stats_legattacks", va("%d", client - > sess.missionStats.legAttacksCnt)); gi.cvar_set("ui_stats_armattacks", va("%d", client - > sess.missionStats.armAttacksCnt)); gi.cvar_set("ui_stats_bodyattacks", va("%d", client - > sess.missionStats.torsoAttacksCnt)); gi.cvar_set("ui_stats_absorb", va("%d", client - > sess.missionStats.forceUsed[FP_ABSORB])); gi.cvar_set("ui_stats_heal", va("%d", client - > sess.missionStats.forceUsed[FP_HEAL])); gi.cvar_set("ui_stats_mindtrick", va("%d", client - > sess.missionStats.forceUsed[FP_TELEPATHY])); gi.cvar_set("ui_stats_protect", va("%d", client - > sess.missionStats.forceUsed[FP_PROTECT])); gi.cvar_set("ui_stats_jump", va("%d", client - > sess.missionStats.forceUsed[FP_LEVITATION])); gi.cvar_set("ui_stats_pull", va("%d", client - > sess.missionStats.forceUsed[FP_PULL])); gi.cvar_set("ui_stats_push", va("%d", client - > sess.missionStats.forceUsed[FP_PUSH])); gi.cvar_set("ui_stats_sense", va("%d", client - > sess.missionStats.forceUsed[FP_SEE])); gi.cvar_set("ui_stats_speed", va("%d", client - > sess.missionStats.forceUsed[FP_SPEED])); gi.cvar_set("ui_stats_defense", va("%d", client - > sess.missionStats.forceUsed[FP_SABER_DEFENSE])); gi.cvar_set("ui_stats_offense", va("%d", client - > sess.missionStats.forceUsed[FP_SABER_OFFENSE])); gi.cvar_set("ui_stats_throw", va("%d", client - > sess.missionStats.forceUsed[FP_SABERTHROW])); gi.cvar_set("ui_stats_drain", va("%d", client - > sess.missionStats.forceUsed[FP_DRAIN])); gi.cvar_set("ui_stats_grip", va("%d", client - > sess.missionStats.forceUsed[FP_GRIP])); gi.cvar_set("ui_stats_lightning", va("%d", client - > sess.missionStats.forceUsed[FP_LIGHTNING])); gi.cvar_set("ui_stats_rage", va("%d", client - > sess.missionStats.forceUsed[FP_RAGE])); gi.cvar_set("ui_stats_stun", va("%d", client - > sess.missionStats.forceUsed[FP_STUN])); gi.cvar_set("ui_stats_hate", va("%d", client - > sess.missionStats.forceUsed[FP_HATE])); gi.cvar_set("ui_stats_controlmind", va("%d", client - > sess.missionStats.forceUsed[FP_CONTROLMIND])); gi.cvar_set("ui_stats_incapacitate", va("%d", client - > sess.missionStats.forceUsed[FP_FREEZE])); gi.cvar_set("ui_stats_fear", va("%d", client - > sess.missionStats.forceUsed[FP_FEAR])); } As you can see, these programs the stats for ingamemissionselect menu. Now... the consolle command. Into the game cheats, you can add powers with setforceall (force power value) And with command like setforceprotect 3 (add force protect level 3, remember?) Sure you will know these cheats. They are important because you need to call the power into the consolle and assign to your jedi for test it on game when you will work on that. For force stun, i wrote this special static void: The file to edit is: g_svcmds.cpp static void Svcmd_ForceStun_f(void) { if (! & g_entities[0] || !g_entities[0].client) { return; } if (!g_cheats - > integer) { gi.SendServerCommand(0, "print \"Cheats are not enabled on this server.\n\""); return; } const char * newVal = gi.argv(1); if (!VALIDSTRING(newVal)) { gi.Printf("Current stun level is %d\n", g_entities[0].client - > ps.forcePowerLevel[FP_STUN]); gi.Printf("Usage: stun <level> (1 - 3)\n"); return; } int val = atoi(newVal); if (val > FORCE_LEVEL_0) { g_entities[0].client - > ps.forcePowersKnown |= (1 << FP_STUN); } else { g_entities[0].client - > ps.forcePowersKnown &= ~(1 << FP_STUN); } g_entities[0].client - > ps.forcePowerLevel[FP_STUN] = val; if (g_entities[0].client - > ps.forcePowerLevel[FP_STUN] < FORCE_LEVEL_0) { g_entities[0].client - > ps.forcePowerLevel[FP_STUN] = FORCE_LEVEL_0; } else if (g_entities[0].client - > ps.forcePowerLevel[FP_STUN] > FORCE_LEVEL_4) { g_entities[0].client - > ps.forcePowerLevel[FP_STUN] = FORCE_LEVEL_4; } } this will program your server command. For making it available and create something like setforcestun and give you the power with the setforceall cheat, you need to add also these strings: locate this /*=================ConsoleCommand/ these are added in cg_main, CG_Init so they tab-complete=================*/ down add into the if case lists: if ( Q_stricmp( cmd, "setForceStun" ) == 0 ){Svcmd_ForceStun_f();return qtrue;} add also about the command setforceall: if (Q_stricmp(cmd, "setForceAll") == 0) { if (!g_cheats - > integer) { gi.SendServerCommand(0, "print \"Cheats are not enabled on this server.\n\""); return qfalse; } Svcmd_ForceJump_f(); Svcmd_SaberThrow_f(); Svcmd_ForceHeal_f(); Svcmd_ForcePush_f(); Svcmd_ForcePull_f(); Svcmd_ForceSpeed_f(); Svcmd_ForceGrip_f(); Svcmd_ForceLightning_f(); Svcmd_MindTrick_f(); Svcmd_ForceStun_f(); Svcmd_ForceHate_f(); Svcmd_ControlMind_f(); Svcmd_ForceFreeze_f(); Svcmd_ForceFear_f(); Svcmd_SaberDefense_f(); Svcmd_SaberOffense_f(); Svcmd_ForceSetLevel_f(FP_RAGE); Svcmd_ForceSetLevel_f(FP_DRAIN); Svcmd_ForceSetLevel_f(FP_PROTECT); Svcmd_ForceSetLevel_f(FP_ABSORB); Svcmd_ForceSetLevel_f(FP_SEE); for (int i = SS_NONE + 1; i < SS_NUM_SABER_STYLES; i++) { g_entities[0].client - > ps.saberStylesKnown |= (1 << i); } return qtrue; } well! Now you got consolle command about your new powers for debug when you will test against NPCs during programming. Now... well Now it’s time to define the new force power icon path of the your new force powers. You can do these on cg_main.cpp. go on void cg_init void CG_Init(int serverCommandSequence) { cgs.serverCommandSequence = serverCommandSequence; cgi_Cvar_Set("cg_drawHUD", "1"); // fonts... // cgs.media.charsetShader = cgi_R_RegisterShaderNoMip("gfx/2d/charsgrid_med"); cgs.media.qhFontSmall = cgi_R_RegisterFont("ocr_a"); cgs.media.qhFontMedium = cgi_R_RegisterFont("ergoec"); cgs.media.whiteShader = cgi_R_RegisterShader("white"); cgs.media.loadTick = cgi_R_RegisterShaderNoMip("gfx/hud/load_tick"); cgs.media.loadTickCap = cgi_R_RegisterShaderNoMip("gfx/hud/load_tick_cap"); const char * force_icon_files[NUM_FORCE_POWERS] = { //icons matching enums forcePowers_t "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_saber_throw", //FP_SABERTHROW "gfx/mp/f_icon_saber_defend", //FP_SABERDEFEND, "gfx/mp/f_icon_saber_attack", //FP_SABERATTACK, "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_dk_drain", //FP_DRAIN, "gfx/mp/f_icon_sight", //FP_SEE, "gfx/mp/f_icon_stun", //FP_STUN "gfx/mp/f_icon_hate", //FP_HATE "gfx/mp/f_icon_controlmind", //FP_CONTROLMIND "gfx/mp/f_icon_incapacitate", //FP_INCAPACITATE "gfx/mp/f_icon_fear", //FP_FEAR}; as you can see i added the mine. VERY important: the icon need to match exactly the enumeration of force power we did at the start of this tutorial on Q_shared list. Now go to int showpowers and add your new powers here. int showPowers[MAX_SHOWPOWERS] = { FP_ABSORB, FP_HEAL, FP_PROTECT, FP_TELEPATHY, FP_SPEED, FP_PUSH, FP_PULL, FP_SEE, FP_DRAIN, FP_LIGHTNING, FP_RAGE, FP_GRIP, FP_STUN, FP_HATE, FP_CONTROLMIND, FP_FREEZE, FP_FEAR, }; at the end of the list. This will show the new force power icons when you scroll the force icon menu on HUD. Now it’s time to set a name for your power. When you select an icon of aforce power in game, appear the name of the power in yellow. Remember? You can add the strings with the name of your powers here: const char * showPowersName[MAX_SHOWPOWERS] = { "SP_INGAME_ABSORB2", "SP_INGAME_HEAL2", "SP_INGAME_PROTECT2", "SP_INGAME_MINDTRICK2", "SP_INGAME_SPEED2", "SP_INGAME_PUSH2", "SP_INGAME_PULL2", "SP_INGAME_SEEING2", "SP_INGAME_DRAIN2", "SP_INGAME_LIGHTNING2", "SP_INGAME_DARK_RAGE2", "SP_INGAME_GRIP2", "SP_INGAME_STUN2", "SP_INGAME_HATE2", "SP_INGAME_CONTROLMIND2", "SP_INGAME_FREEZE2", "SP_INGAME_FEAR2", }; This list need to merge with the show powers list. It’s IMPORTANT. Now… when you open datapad, you can get a descritpion of each force power and also information about the power level you get into your mission. You can set this chars description here: const char * forcepowerDesc[NUM_FORCE_POWERS] = { "FORCE_ABSORB_DESC", "FORCE_HEAL_DESC", "FORCE_PROTECT_DESC", "FORCE_MIND_TRICK_DESC", "FORCE_STUN_DESC", "FORCE_JUMP_DESC", "FORCE_SPEED_DESC", "FORCE_PUSH_DESC", "FORCE_PULL_DESC", "FORCE_SABER_THROW_DESC", "FORCE_SABER_DEFENSE_DESC", "FORCE_SABER_OFFENSE_DESC", "FORCE_SENSE_DESC", "FORCE_FREEZE_DESC", "FORCE_DRAIN_DESC", "FORCE_LIGHTNING_DESC", "FORCE_RAGE_DESC", "FORCE_GRIP_DESC", "FORCE_HATE_DESC", "FORCE_CONTROLMIND_DESC", "FORCE_FEAR_DESC", }; add the voice for the chars descrition of your new powers- Now, you need to make the same thing for other 3 lists, the force power level list strings: const char * forcepowerLvl1Desc[NUM_FORCE_POWERS] = { "FORCE_ABSORB_LVL1_DESC", "FORCE_HEAL_LVL1_DESC", "FORCE_PROTECT_LVL1_DESC", "FORCE_MIND_TRICK_LVL1_DESC", "FORCE_JUMP_LVL1_DESC", "FORCE_SPEED_LVL1_DESC", "FORCE_PUSH_LVL1_DESC", "FORCE_PULL_LVL1_DESC", "FORCE_SABER_THROW_LVL1_DESC", "FORCE_SABER_DEFENSE_LVL1_DESC", "FORCE_SABER_OFFENSE_LVL1_DESC", "FORCE_SENSE_LVL1_DESC", "FORCE_DRAIN_LVL1_DESC", "FORCE_LIGHTNING_LVL1_DESC", "FORCE_RAGE_LVL1_DESC", "FORCE_GRIP_LVL1_DESC", "FORCE_STUN_LVL1_DESC", "FORCE_HATE_LVL1_DESC", "FORCE_CONTROLMIND_LVL1_DESC", "FORCE_FREEZE_LVL1_DESC", "FORCE_FEAR_LVL1_DESC", }; const char * forcepowerLvl2Desc[NUM_FORCE_POWERS] = { "FORCE_ABSORB_LVL2_DESC", "FORCE_HEAL_LVL2_DESC", "FORCE_PROTECT_LVL2_DESC", "FORCE_MIND_TRICK_LVL2_DESC", "FORCE_JUMP_LVL2_DESC", "FORCE_SPEED_LVL2_DESC", "FORCE_PUSH_LVL2_DESC", "FORCE_PULL_LVL2_DESC", "FORCE_SABER_THROW_LVL2_DESC", "FORCE_SABER_DEFENSE_LVL2_DESC", "FORCE_SABER_OFFENSE_LVL2_DESC", "FORCE_SENSE_LVL2_DESC", "FORCE_DRAIN_LVL2_DESC", "FORCE_LIGHTNING_LVL2_DESC", "FORCE_RAGE_LVL2_DESC", "FORCE_GRIP_LVL2_DESC", "FORCE_STUN_LVL2_DESC", "FORCE_HATE_LVL2_DESC", "FORCE_CONTROLMIND_LVL2_DESC", "FORCE_FREEZE_LVL2_DESC", "FORCE_FEAR_LVL2_DESC", }; const char * forcepowerLvl3Desc[NUM_FORCE_POWERS] = { "FORCE_ABSORB_LVL3_DESC", "FORCE_HEAL_LVL3_DESC", "FORCE_PROTECT_LVL3_DESC", "FORCE_MIND_TRICK_LVL3_DESC", "FORCE_JUMP_LVL3_DESC", "FORCE_SPEED_LVL3_DESC", "FORCE_PUSH_LVL3_DESC", "FORCE_PULL_LVL3_DESC", "FORCE_SABER_THROW_LVL3_DESC", "FORCE_SABER_DEFENSE_LVL3_DESC", "FORCE_SABER_OFFENSE_LVL3_DESC", "FORCE_SENSE_LVL3_DESC", "FORCE_DRAIN_LVL3_DESC", "FORCE_LIGHTNING_LVL3_DESC", "FORCE_RAGE_LVL3_DESC", "FORCE_GRIP_LVL3_DESC", "FORCE_STUN_LVL3_DESC", "FORCE_HATE_LVL3_DESC", "FORCE_CONTROLMIND_LVL3_DESC", "FORCE_FREEZE_LVL3_DESC", "FORCE_FEAR_LVL3_DESC", }; so you got description for all the 3 levels of your powers! Now it’s time to add a last thing and we end with this file: // Keep these with groups light side, core, and dark side int showDataPadPowers[MAX_DPSHOWPOWERS] = { // Light side FP_ABSORB, FP_HEAL, FP_PROTECT, FP_TELEPATHY, FP_STUN, // Core Powers FP_LEVITATION, FP_SPEED, FP_PUSH, FP_PULL, FP_SABERTHROW, FP_SABER_DEFENSE, FP_SABER_OFFENSE, FP_SEE, FP_FREEZE, //Dark Side FP_DRAIN, FP_LIGHTNING, FP_RAGE, FP_GRIP, FP_HATE, FP_CONTROLMIND, FP_FEAR, }; that’s will show the powers into the your datapad. Respect the list enumeration for show the light side, dark side and core powers. In my case, hate, fear and controlmind are power of dark side, freeze is neutral, and stun is a light side power. One last thing and we end with menus staff: Open cg_info.cpp and add your power in this char array. Remember: you need to put into the SAME ORDER of enumeration of q_shared forcepowers_t. const char * showLoadPowersName[] = { "SP_INGAME_HEAL2", "SP_INGAME_JUMP2", "SP_INGAME_SPEED2", "SP_INGAME_PUSH2", "SP_INGAME_PULL2", "SP_INGAME_MINDTRICK2", "SP_INGAME_GRIP2", "SP_INGAME_LIGHTNING2", "SP_INGAME_SABER_THROW2", "SP_INGAME_SABER_OFFENSE2", "SP_INGAME_SABER_DEFENSE2", "SP_INGAME_STUN2", "SP_INGAME_HATE2", "SP_INGAME_CONTROLMIND2", "SP_INGAME_INCAPACITATE2", "SP_INGAME_FEAR2", NULL, }; Okay, now we need to add some other stuff. Wait a moment, before dream of become a jedi. We are missing a thing. If you build now your SP dll and exe, you will notice something really bad: it’s possible that your HUD will crash when you scroll the new power icon. But why? Because there is a limitation related to the max number of power that can be showed into HUD and datapad. If you go here: int showDataPadPowers[MAX_DPSHOWPOWERS] and here: const char *showPowersName[MAX_SHOWPOWERS] = you can see there are called two definition. MAX_DPSHOWPOWERS define the maximum amount of force power that show the Datapad menu. MAX_SHOWPOWERS instead, the max amount drawed into HUD. You can change this amount until the value of 32. Go to cg_local.h And find this definition: #define MAX_SHOWPOWERS 12e xtern int showPowers[MAX_SHOWPOWERS]; extern const char * showPowersName[MAX_SHOWPOWERS]; extern int force_icons[NUM_FORCE_POWERS]; #define MAX_DPSHOWPOWERS 16 now ask to you... how many force power you added into your mod? Well, you need to add the answer to the MAX values. In my case, because i added 5 new force powers. (stun, freeze, fear, controlmind and hate) i need to change 12 to 17 and 16 to 21. And so i get: #define MAX_SHOWPOWERS 17extern int showPowers[MAX_SHOWPOWERS]; extern const char *showPowersName[MAX_SHOWPOWERS]; extern int force_icons[NUM_FORCE_POWERS]; #define MAX_DPSHOWPOWERS 21 17 is now the number of maximum force power can display my HUD. 21 is the maximun force power displayed of my datapad related to force power description. Now we have and with all interface and description and hud staff related to force powers. Let pass to second step: programming the force power. Your force power get icon and hud description, but again it’s incomplete. Maybe you can see into HUD, and select, or you can give with consolle to your jedi or assign a power level, but it will not work. Is just passive. So you need know to build the true functionality of your new force power. For first thing, we need to tell to the code this: when you activate a force power with “f” key, after you selected the power into your menu or hud, the jedi need to cast the power, right? This istruction is envolved here. Open Bg_misc.cpp and find void PM_CheckForceUseButton( gentity_t *ent, usercmd_t *ucmd ) function. You will see a switch. Into the swith there are various case. When you press F key with one of these power selected, each power run the specific function of the power. You need into the cases your new force powers. In my case, i add this: if (!(ent - > client - > ps.pm_flags & PMF_USEFORCE_HELD)) { //impulse one shot switch (showPowers[cg.forcepowerSelect]) { case FP_HEAL: ForceHeal(ent); break; case FP_SPEED: ForceSpeed(ent); break; case FP_PUSH: ForceThrow(ent, qfalse); break; case FP_PULL: ForceThrow(ent, qtrue); break; case FP_TELEPATHY: ForceTelepathy(ent); break; case FP_STUN: ForceStun(ent); break; case FP_HATE: ForceHate(ent); break; case FP_CONTROLMIND: ForceControlMind(ent); break; case FP_FREEZE: ForceFreeze(ent); break; case FP_FEAR: ForceFear(ent); break; // Added 01/20/03 by AReis. // New Jedi Academy powers. case FP_RAGE: //duration - speed, invincibility and extra damage for short period, drains your health and leaves you weak and slow afterwards. ForceRage(ent); break; case FP_PROTECT: //duration - protect against physical/energy (level 1 stops blaster/energy bolts, level 2 stops projectiles, level 3 protects against explosions) ForceProtect(ent); break; case FP_ABSORB: //duration - protect against dark force powers (grip, lightning, drain - maybe push/pull, too?) ForceAbsorb(ent); break; case FP_SEE: //duration - detect/see hidden enemies ForceSeeing(ent); break; } } And so, when i select my power, and i press “f” engine run the force power function. Now we need just a last thing... Write the functions! And THAT’S is the hardest part, men. I hope you are expert coder and you know good the JKA engine source code. If you’ll not, at max you can copy \ paste an existing power and simply edit its values. Not really a scientific approach, right? Well, we shall continue now. Open WP_SABER.cpp. It’s time to end our task. At line 172 you will found an array. This array define the alignment of your force power. They are light side, dark side or neutral core power? Add add the end of the array your new force power. Important: you need to respect the NUM_FORCE_POWER enumeration stored into q_shared.h 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 0, //FP_SABERATTACK, 0, //FP_SABERDEFEND, 0, //FP_SABERTHROW, //new Jedi Academy powers FORCE_DARKSIDE, //FP_RAGE,//duration FORCE_LIGHTSIDE, //FP_PROTECT,//duration FORCE_LIGHTSIDE, //FP_ABSORB,//duration FORCE_DARKSIDE, //FP_DRAIN,//hold/duration 0, //FP_SEE,//duration FORCE_LIGHTSIDE, //FP_STUN,//duration FORCE_DARKSIDE, //FP_HATE,//duration FORCE_DARKSIDE, //FP_CONTROLMIND,//duration 0, //FP_FREEZE,//hold/duration/NUM_FORCE_POWERS FORCE_DARKSIDE, //FP_FEAR,//duration}; if the power is a light side power, put FORCE_LIGHTSIDE, if is a Dark power, FORCE_DARKSIDE, if is a core \ neutral power, put 0. Now scroll below and you can found int forcePowerNeeded[NUM_FORCE_POWERS] = This set how many force power point need your power for be casted. So you can set here the values. REMEMBER: respect the enumeration of q_share NUM_FORCE_POWERS value! int forcePowerNeeded[NUM_FORCE_POWERS] = { 0, //FP_HEAL,//instant 10, //FP_LEVITATION,//hold/duration 50, //FP_SPEED,//duration 15, //FP_PUSH,//hold/duration 15, //FP_PULL,/hold/duration 20, //FP_TELEPATHY,//instant 1, //FP_GRIP,//hold/duration - FIXME: 30? 1, //FP_LIGHTNING,/hold/duration 20, //FP_SABERTHROW, 1, //FP_SABER_DEFENSE, 0, //FP_SABER_OFFENSE, //new Jedi Academy powers 50, //FP_RAGE,//duration - speed, invincibility and extra damage for short period, drains your health and leaves you weak and slow afterwards. 30, //FP_PROTECT,//duration - protect against physical/energy (level 1 stops blaster/energy bolts, level 2 stops projectiles, level 3 protects against explosions) 30, //FP_ABSORB,/duration - protect against dark force powers (grip, lightning, drain) 1, //FP_DRAIN,//hold/duration - drain force power for health 20, //FP_SEE,//duration - detect/see hidden enemies 20, //FP_STUN 30, //INSPIREHATE 40, //CONTROLMIND 50, //INCAPACITATE 30, //FEAR/NUM_FORCE_POWERS}; Now, go down and you can find a large amount of float and int value. Depending about WHAT your power does, you need to add a float or int value in this fields. My powers are focused on stun, anger, lock, fear and control the enemy so in my case i added int value, related about the duration of time in millisecond of the force power efx. Below mindtrick time i added: int mindTrickTime[NUM_FORCE_POWER_LEVELS] = { 0, //none 10000, //5000, 15000, //10000, 30000 //15000};int StunTime[NUM_FORCE_POWER_LEVELS] ={0,//none 10000, //5000, 20000, //10000, 30000 //15000};int HateTime[NUM_FORCE_POWER_LEVELS] ={0,//none 10000, //5000, 20000, //10000, 30000 //15000};int ControlMindTime[NUM_FORCE_POWER_LEVELS] ={0,//none 60000, //5000, 120000, //10000, 300000 //15000};int FreezeTime[NUM_FORCE_POWER_LEVELS] ={0,//none 10000, //5000, 15000, //10000, 20000 //15000};int FearTime[NUM_FORCE_POWER_LEVELS] ={0,//none 10000, //5000, 20000, //10000, 30000 //15000}; as first value you need to put 0 because is the duration of force power at level 0. no power, no duration. The other value are related to the duration of force power at level 1 , 2 and 3 of the force. As you can see: freeze at level one during ten second, at level 2 15, at level three, 20 second. Fear ten, twenty and thirty second, controlmind 1 minut, 2 minut and 5 minutes at level 3. (control an enemy for an entire map area basically XD, so the NPC can reach and activate switchs many far by player position before control expiring.) Now you will see a loooong serious of void and other parameters. There all related to lightsabers. But if you go down until line 8800 will begin the void related to the force powers. /OTHER JEDI POWERS========================================================================= /OTHER JEDI POWERS========================================================================= /OTHER JEDI POWERS========================================================================= /OTHER JEDI POWERS========================================================================= /OTHER JEDI POWERS========================================================================= And now, well, you need to make a void about YOUR power. And programming it! That’s depend only by you, i cannot help you. I suggest you for deep study each force power code function for understand how works any force power in every part of its effect. As i told at begin of tutorial, some powers function are also into cg_players.cpp (protect, absorb and seeing aura, lightning and draind visual effect) here are setted the efx, sound and functions of grip, mindtrick, push, pull, rage and heal... and, obviously, also of the lightning, drain, protect and absorb. I will show you as example my force controlmind code. It’s a gift. So you will understand exactly what you weel need to do. And also, if you want, you can begin your path into the coding force with a cool power. This power will allow you to control your enemies for a LONG time as mindtrick level 4. basically. You can esc with jump key or you can wait expiration of power. Enemy will not attack you and you cannot damage other enemies. (it’s not correct ) void ForceControlMind(gentity_t * self) { trace_t tr; vec3_t end, forward; gentity_t * traceEnt; qboolean targetLive = qfalse; if (WP_CheckBreakControlMind(self)) { return; } if (self - > health <= 0) { return; } //FIXME: if mind trick 3 and aiming at an enemy need more force power if (!WP_ForcePowerUsable(self, FP_CONTROLMIND, 0)) { return; } if (self - > client - > ps.weaponTime >= 800) { //just did one! return; } if (self - > client - > ps.saberLockTime > level.time) { //FIXME: can this be a way to break out? return; } AngleVectors(self - > client - > ps.viewangles, forward, NULL, NULL); VectorNormalize(forward); VectorMA(self - > client - > renderInfo.headPoint, 2048, forward, end); //Cause a distraction if enemy is not fighting gi.trace( & tr, self - > client - > renderInfo.torsoPoint, vec3_origin, vec3_origin, end, self - > s.number, MASK_OPAQUE | CONTENTS_BODY, (EG2_Collision) 0, 0); if (tr.entityNum == ENTITYNUM_NONE || tr.fraction == 1.0 || tr.allsolid || tr.startsolid) { return; } traceEnt = & g_entities[tr.entityNum]; if (traceEnt - > NPC && traceEnt - > NPC - > scriptFlags & SCF_NO_FORCE) { return; } if (traceEnt && traceEnt - > client) { switch (traceEnt - > client - > NPC_class) { // CLASS CANNOT BE DOMINATED case CLASS_GALAKMECH: case CLASS_ATST: case CLASS_SAND_CREATURE: case CLASS_TAVION: case CLASS_DESANN: case CLASS_JAN: case CLASS_GALAK: case CLASS_LIZARD: case CLASS_MURJJ: case CLASS_FLIER2: case CLASS_GLIDER: case CLASS_FISH: case CLASS_CLAW: case CLASS_REBORN: case CLASS_JEDI: case CLASS_ALORA: case CLASS_MORGANKATARN: case CLASS_WAMPA: case CLASS_MINEMONSTER: case CLASS_KYLE: case CLASS_LUKE: case CLASS_SHADOWTROOPER: case CLASS_LANDO: case CLASS_BARTENDER: case CLASS_UGNAUGHT: case CLASS_WEEQUAY: case CLASS_NOGHRI: case CLASS_TUSKEN: case CLASS_SWAMPTROOPER: case CLASS_GRAN: case CLASS_BESPIN_COP: case CLASS_SABOTEUR: case CLASS_HAZARD_TROOPER: case CLASS_ROCKETTROOPER: case CLASS_REBEL: case CLASS_PROBE: //no droids either case CLASS_GONK: case CLASS_R2D2: case CLASS_MARK1: case CLASS_MARK2: case CLASS_MOUSE: case CLASS_JAWA: case CLASS_HOWLER: case CLASS_SEEKER: case CLASS_SENTRY: case CLASS_REMOTE: case CLASS_PROTOCOL: case CLASS_ASSASSIN_DROID: case CLASS_SABER_DROID: case CLASS_BOBAFETT: // you wanna to control galesh and arcidemon? Just try! // tutti gli elementali e i boss non posson esser stunnati! break; case CLASS_RANCOR: if (!(traceEnt - > spawnflags & 1)) { targetLive = qtrue; } break; default: targetLive = qtrue; break; } } if (targetLive && traceEnt - > NPC && traceEnt - > health > 0) { //hit an organic non-player if (G_ActivateBehavior(traceEnt, BSET_MINDTRICK)) { //activated a script on him //FIXME: do the visual sparkles effect on their heads, still? WP_ForcePowerStart(self, FP_CONTROLMIND, 0); } else if (traceEnt - > client - > playerTeam != self - > client - > playerTeam) { //an enemy int override = 0; if ((traceEnt - > NPC - > scriptFlags & SCF_NO_MIND_TRICK)) { if (traceEnt - > client - > NPC_class == CLASS_GALAKMECH) { G_AddVoiceEvent(traceEnt, Q_irand(EV_CONFUSE1, EV_CONFUSE3), Q_irand(3000, 5000)); } } /*else if ( self->client->ps.forcePowerLevel[FP_CONTROLMIND] > FORCE_LEVEL_0 ) {//control them, even jedi G_SetViewEntity( self, traceEnt ); traceEnt->NPC->controlledTime = level.time+30000; }*/ else if ( /*traceEnt->s.weapon != WP_SABER &&*/ traceEnt - > client - > NPC_class != CLASS_REBORN) { //haha! Jedi aren't easily confused! if (self - > client - > ps.forcePowerLevel[FP_CONTROLMIND] == FORCE_LEVEL_1 && traceEnt - > s.weapon != WP_NONE && traceEnt - > s.weapon != WP_SABER // AT LEVEL 1 YOU CAN CHARM ONLY SHOOTERS //don't charm people who aren't capable of fighting... like ugnaughts and droids, just confuse them /*&& traceEnt->client->NPC_class != CLASS_TUSKEN//don't charm them, just confuse them && traceEnt->client->NPC_class != CLASS_NOGHRI//don't charm them, just confuse them*/ && !Pilot_AnyVehiclesRegistered() //also, don't charm guys when bikes are near ) { //turn them to our side //if mind trick 3 and aiming at an enemy need more force power override = 50; if (self - > client - > ps.forcePower < 50) { return; } if (traceEnt - > enemy) { G_ClearEnemy(traceEnt); } if (traceEnt - > NPC) { traceEnt - > NPC - > tempBehavior = BS_HUNT_AND_KILL; } //FIXME: maybe pick an enemy right here? //FIXME: does nothing to TEAM_FREE and TEAM_NEUTRALs!!! team_t saveTeam = traceEnt - > client - > enemyTeam; traceEnt - > client - > enemyTeam = traceEnt - > client - > playerTeam; traceEnt - > client - > playerTeam = saveTeam; G_SetViewEntity(self, traceEnt); //FIXME: need a *charmed* timer on this...? Or do TEAM_PLAYERS assume that "confusion" means they should switch to team_enemy when done? traceEnt - > NPC - > controlledTime = level.time + ControlMindTime[self - > client - > ps.forcePowerLevel[FP_CONTROLMIND]]; if (traceEnt - > ghoul2.size() && traceEnt - > headBolt != -1) { //FIXME: what if already playing effect? G_PlayEffect(G_EffectIndex("force/charm"), traceEnt - > playerModel, traceEnt - > headBolt, traceEnt - > s.number, traceEnt - > currentOrigin, ControlMindTime[self - > client - > ps.forcePowerLevel[FP_CONTROLMIND]], qtrue); G_SoundOnEnt(self, CHAN_ITEM, "sound/weapons/force/charm.mp3"); } if (WP_ForcePowerStop, FP_CONTROLMIND) { team_t saveTeam = traceEnt - > client - > playerTeam; traceEnt - > client - > playerTeam = traceEnt - > client - > enemyTeam; traceEnt - > client - > enemyTeam = saveTeam; NPC_PlayConfusionSound(traceEnt); } // AT LEVEL 1 ENEMY NOT TURN TO YOUR SIDE } else if (self - > client - > ps.forcePowerLevel[FP_CONTROLMIND] == FORCE_LEVEL_2 && traceEnt - > s.weapon != WP_NONE // AT LEVEL 2 YOU CAN CHARM SABERIST //don't charm people who aren't capable of fighting... like ugnaughts and droids, just confuse them /*&& traceEnt->client->NPC_class != CLASS_TUSKEN//don't charm them, just confuse them && traceEnt->client->NPC_class != CLASS_NOGHRI//don't charm them, just confuse them*/ && !Pilot_AnyVehiclesRegistered() //also, don't charm guys when bikes are near ) { //turn them to our side //if mind trick 3 and aiming at an enemy need more force power override = 50; if (self - > client - > ps.forcePower < 50) { return; } if (traceEnt - > enemy) { G_ClearEnemy(traceEnt); } if (traceEnt - > NPC) { traceEnt - > NPC - > tempBehavior = BS_HUNT_AND_KILL; } //FIXME: maybe pick an enemy right here? //FIXME: does nothing to TEAM_FREE and TEAM_NEUTRALs!!! team_t saveTeam = traceEnt - > client - > enemyTeam; traceEnt - > client - > enemyTeam = traceEnt - > client - > playerTeam; traceEnt - > client - > playerTeam = saveTeam; G_SetViewEntity(self, traceEnt); // traceEnt->NPC->controlledTime = level.time+20000; //FIXME: need a *charmed* timer on this...? Or do TEAM_PLAYERS assume that "confusion" means they should switch to team_enemy when done? traceEnt - > NPC - > controlledTime = level.time + ControlMindTime[self - > client - > ps.forcePowerLevel[FP_CONTROLMIND]]; if (traceEnt - > ghoul2.size() && traceEnt - > headBolt != -1) { //FIXME: what if already playing effect? G_PlayEffect(G_EffectIndex("force/charm"), traceEnt - > playerModel, traceEnt - > headBolt, traceEnt - > s.number, traceEnt - > currentOrigin, ControlMindTime[self - > client - > ps.forcePowerLevel[FP_CONTROLMIND]], qtrue); G_SoundOnEnt(self, CHAN_ITEM, "sound/weapons/force/charm.mp3"); } if (WP_ForcePowerStop, FP_CONTROLMIND) { team_t saveTeam = traceEnt - > client - > playerTeam; traceEnt - > client - > playerTeam = traceEnt - > client - > enemyTeam; traceEnt - > client - > enemyTeam = saveTeam; NPC_PlayConfusionSound(traceEnt); // AT LEVEL 2 ENEMY TURN TO ENEMY SIDE WHEN POWER END } } else if (self - > client - > ps.forcePowerLevel[FP_CONTROLMIND] == FORCE_LEVEL_3 && traceEnt - > s.weapon != WP_NONE // AT LEVEL 2 YOU CAN CHARM SABERIST //don't charm people who aren't capable of fighting... like ugnaughts and droids, just confuse them && !Pilot_AnyVehiclesRegistered() //also, don't charm guys when bikes are near ) { //turn them to our side //if mind trick 3 and aiming at an enemy need more force power override = 50; if (self - > client - > ps.forcePower < 50) { return; } if (traceEnt - > enemy) { G_ClearEnemy(traceEnt); } if (traceEnt - > NPC) { traceEnt - > NPC - > tempBehavior = BS_HUNT_AND_KILL; } //FIXME: maybe pick an enemy right here? //FIXME: does nothing to TEAM_FREE and TEAM_NEUTRALs!!! G_SetViewEntity(self, traceEnt); //FIXME: need a *charmed* timer on this...? Or do TEAM_PLAYERS assume that "confusion" means they should switch to team_enemy when done? traceEnt - > NPC - > controlledTime = level.time + ControlMindTime[self - > client - > ps.forcePowerLevel[FP_CONTROLMIND]]; if (traceEnt - > ghoul2.size() && traceEnt - > headBolt != -1) { //FIXME: what if already playing effect? G_PlayEffect(G_EffectIndex("force/charm"), traceEnt - > playerModel, traceEnt - > headBolt, traceEnt - > s.number, traceEnt - > currentOrigin, ControlMindTime[self - > client - > ps.forcePowerLevel[FP_CONTROLMIND]], qtrue); G_SoundOnEnt(self, CHAN_ITEM, "sound/weapons/force/charm.mp3"); } else if (WP_ForcePowerStop, FP_CONTROLMIND) { NPC_PlayConfusionSound(traceEnt); } /*AT LEVEL 3 ENEMY WHEN POWER END BECOME ALLIED. */ } else { //enemy will not attack at this control mind mastery //somehow confuse them? Set don't fire to true for a while? Drop their aggression? Maybe just take their enemy away and don't let them pick one up for a while unless shot? // AT LEVEL 3 ENEMY WILL NOT ATTACK YOU AND TAKE YOU FOR A TEAMMATE. if (self - > client - > ps.forcePower < 90) { return; } if (traceEnt - > enemy) { G_ClearEnemy(traceEnt); } if (traceEnt - > NPC) { traceEnt - > NPC - > tempBehavior = BS_HUNT_AND_KILL; } G_SetViewEntity(self, traceEnt); traceEnt - > NPC - > controlledTime = level.time + ControlMindTime[self - > client - > ps.forcePowerLevel[FP_CONTROLMIND]]; //confused for about 10 seconds*/ if (traceEnt - > ghoul2.size() && traceEnt - > headBolt != -1) { //FIXME: what if already playing effect? G_PlayEffect(G_EffectIndex("force/charm"), traceEnt - > playerModel, traceEnt - > headBolt, traceEnt - > s.number, traceEnt - > currentOrigin, FreezeTime[self - > client - > ps.forcePowerLevel[FP_CONTROLMIND]], qtrue); G_SoundOnEnt(self, CHAN_ITEM, "sound/weapons/force/charm.mp3"); } } } else { NPC_Jedi_PlayConfusionSound(traceEnt); } WP_ForcePowerStart(self, FP_CONTROLMIND, override); } /*else if ( traceEnt->client->playerTeam == self->client->playerTeam ) {//an ally //maybe just have him look at you? Respond? Take your enemy? if ( traceEnt->client->ps.pm_type < PM_DEAD && traceEnt->NPC!=NULL && !(traceEnt->NPC->scriptFlags&SCF_NO_RESPONSE) ) { NPC_UseResponse( traceEnt, self, qfalse ); WP_ForcePowerStart( self, FP_CONTROLMIND, 1 ); } }*/ //NOTE: no effect on TEAM_NEUTRAL? charmed enemy can be possessed again. vec3_t eyeDir; AngleVectors(traceEnt - > client - > renderInfo.eyeAngles, eyeDir, NULL, NULL); VectorNormalize(eyeDir); G_PlayEffect("force/controlmind_touch", traceEnt - > client - > renderInfo.eyePoint, eyeDir); //make sure this plays and that you cannot press fire for about 1 second after this //FIXME: BOTH_FORCEMINDTRICK or BOTH_FORCEDISTRACT NPC_SetAnim(self, SETANIM_TORSO, BOTH_FORCEPUSH, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); //FIXME: build-up or delay this until in proper part of anim } else { if (self - > client - > ps.forcePowerLevel[FP_CONTROLMIND] == FORCE_LEVEL_1 && tr.fraction * 2048 > 64) { //don't create a diversion less than 64 from you of if at power level 1 //use distraction anim instead G_PlayEffect(G_EffectIndex("force/force_controlmind_fail"), tr.endpos, tr.plane.normal); //FIXME: these events don't seem to always be picked up...? AddSoundEvent(self, tr.endpos, 512, AEL_SUSPICIOUS, qtrue, qtrue); AddSightEvent(self, tr.endpos, 512, AEL_SUSPICIOUS, 50); WP_ForcePowerStart(self, FP_CONTROLMIND, 0); } NPC_SetAnim(self, SETANIM_TORSO, BOTH_FORCEPUSH, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); } self - > client - > ps.saberMove = self - > client - > ps.saberBounceMove = LS_READY; //don't finish whatever saber anim you may have been in self - > client - > ps.saberBlocked = BLOCKED_NONE; self - > client - > ps.weaponTime = 1000; if (self - > client - > ps.forcePowersActive & (1 << FP_SPEED)) { self - > client - > ps.weaponTime = floor(self - > client - > ps.weaponTime * g_timescale - > value); } } THIS is the key for the possession: G_SetViewEntity( self, traceEnt ); This function change your point of view with the NPC you affected with power. TraceEnt is the function that allow you to slam your force power against your foe. A power with traceent, hit NPCs and other clients. A power with self, instead is casted on the player and the jedi. Understood? At the start of my power code there is this function: WP_CheckBreakControlMind This control the camera, exaclty as the WP_Checkbreakcontrol of mindtrick. You can found wp_cheackbreakcontrol upon void forcetelepathy code. Here the original: boolean WP_CheckBreakControl(gentity_t * self) { if (!self) { return qfalse; } if (!self - > s.number) { //player if (self - > client && self - > client - > ps.forcePowerLevel[FP_TELEPATHY] > FORCE_LEVEL_3) { //control-level if (self - > client - > ps.viewEntity > 0 && self - > client - > ps.viewEntity < ENTITYNUM_WORLD) { //we are in a viewentity gentity_t * controlled = & g_entities[self - > client - > ps.viewEntity]; if (controlled - > NPC && controlled - > NPC - > controlledTime > level.time) { //it is an NPC we controlled //clear it and return G_ClearViewEntity(self); return qtrue; } } } } else { //NPC if (self - > NPC && self - > NPC - > controlledTime > level.time) { //being controlled gentity_t * controller = & g_entities[0]; if (controller - > client && controller - > client - > ps.viewEntity == self - > s.number) { //we are being controlled by player if (controller - > client - > ps.forcePowerLevel[FP_TELEPATHY] > FORCE_LEVEL_3) { //control-level mind trick //clear the control and return G_ClearViewEntity(controller); return qtrue; } } } } return qfalse; } here my called function: qboolean WP_CheckBreakControlMind(gentity_t * self) { if (!self) { return qfalse; } if (!self - > s.number) { //player if (self - > client && self - > client - > ps.forcePowerLevel[FP_CONTROLMIND] > FORCE_LEVEL_0) { //control-level if (self - > client - > ps.viewEntity > 0 && self - > client - > ps.viewEntity < ENTITYNUM_WORLD) { //we are in a viewentity gentity_t * controlled = & g_entities[self - > client - > ps.viewEntity]; if (controlled - > NPC && controlled - > NPC - > controlledTime > level.time) { //it is an NPC we controlled //clear it and return G_ClearViewEntity(self); return qtrue; } } } } else { //NPC if (self - > NPC && self - > NPC - > controlledTime > level.time) { //being controlled gentity_t * controller = & g_entities[0]; if (controller - > client && controller - > client - > ps.viewEntity == self - > s.number) { //we are being controlled by player if (controller - > client - > ps.forcePowerLevel[FP_CONTROLMIND] > FORCE_LEVEL_0) { //control-level mind trick //clear the control and return G_ClearViewEntity(controller); return qtrue; } } } } return qfalse; } but this is NOT sufficient. You need also that the Jump key will work for allow you to return into your body. If not, you can stay undefinitley locked into NPC body until he die! For allow this, you need to go into g_active.cpp and find the ClientThink function. /* ================== ClientThink A new command has arrived from the client ================== */ extern void PM_CheckForceUseButton(gentity_t * ent, usercmd_t * ucmd); extern qboolean PM_GentCantJump(gentity_t * gent); extern qboolean PM_WeaponOkOnVehicle(int weapon); void ClientThink(int clientNum, usercmd_t * ucmd) { gentity_t * ent; qboolean restore_ucmd = qfalse; usercmd_t sav_ucmd = { 0 }; ent = g_entities + clientNum; if (ent - > s.number < MAX_CLIENTS) { if (ent - > client - > ps.viewEntity > 0 && ent - > client - > ps.viewEntity < ENTITYNUM_WORLD) { //you're controlling another NPC gentity_t * controlled = & g_entities[ent - > client - > ps.viewEntity]; qboolean freed = qfalse; if (controlled - > NPC && controlled - > NPC - > controlledTime && ent - > client - > ps.forcePowerLevel[FP_TELEPATHY] > FORCE_LEVEL_3 || controlled - > NPC && controlled - > NPC - > controlledTime && ent - > client - > ps.forcePowerLevel[FP_CONTROLMIND] > FORCE_LEVEL_0) { //An NPC I'm controlling with mind trick if (controlled - > NPC - > controlledTime < level.time) { //time's up! G_ClearViewEntity(ent); freed = qtrue; } as you can see, i added the FP_CONTROLMIND to the if definition related this. If the npc is controlled by player, when controlled time value is lesser of leveltime of CONTROLMNIND, camera free the NPC and will return to player body. else if (controlled - > client //an NPC && PM_GentCantJump(controlled) //that cannot jump && controlled - > client - > moveType != MT_FLYSWIM) //and does not use upmove to fly { //these types use jump to get out if (ucmd - > upmove > 0) { //jumping gets you out of it FIXME: check some other button instead... like ESCAPE... so you could even have total control over an NPC? G_ClearViewEntity(ent); ucmd - > upmove = 0; //ucmd->buttons = 0; //stop player from doing anything for a half second after ent - > aimDebounceTime = level.time + 500; freed = qtrue; } } as you can see, just below is definied this: when player press JUMp button ucmd->upmove > 0 the Npc is released and camera turn back to player. Now the FP_CONTROLMIND works! Techniclaly we have end. But making a power like force telepathy,m an istant power, is pretty easy, more difficult is to make an power like grip, heal, rage, or protect. They are more complicated. I see chunks of code of FP_PROTECT into g_combat.cpp too! Some powers are pretty had to do because they functions are scattered into all the codes. So be careful. Making a complex force power can be really hard and long, respect at this example. So keep up and get patiente and strenght. For this, i wanna tell you a last hint. go to the end of WP_SABER.CPP you can find four voids: WP_forcepowerstart, WP_forcepowerstop, wp_forcepowerrun and wp_checkforcepowers. These function define special event that happened at the start, the end, the running of force power. They are all switches and you need to add your power setting into the cases. In my case i make this: void WP_ForcePowerStart(gentity_t * self, forcePowers_t forcePower, int overrideAmt) { int duration = 0; //FIXME: debounce some of these? self - > client - > ps.forcePowerDebounce[forcePower] = 0; //and it in //set up duration time switch ((int) forcePower) { case FP_HEAL: self - > client - > ps.forcePowersActive |= (1 << forcePower); self - > client - > ps.forceHealCount = 0; WP_StartForceHealEffects(self); break; case FP_LEVITATION: self - > client - > ps.forcePowersActive |= (1 << forcePower); break; case FP_SPEED: //duration is always 5 seconds, player time duration = ceil(FORCE_SPEED_DURATION * forceSpeedValue[self - > client - > ps.forcePowerLevel[FP_SPEED]]); //FIXME: because the timescale scales down (not instant), this doesn't end up being exactly right... self - > client - > ps.forcePowersActive |= (1 << forcePower); self - > s.loopSound = G_SoundIndex("sound/weapons/force/speedloop.wav"); if (self - > client - > ps.forcePowerLevel[FP_SPEED] > FORCE_LEVEL_2) { //HACK: just using this as a timestamp for when the power started, setting debounce to current time shouldn't adversely affect anything else self - > client - > ps.forcePowerDebounce[FP_SPEED] = level.time; } break; case FP_PUSH: break; case FP_PULL: self - > client - > ps.forcePowersActive |= (1 << forcePower); break; case FP_TELEPATHY: break; case FP_STUN: break; case FP_HATE: break; case FP_CONTROLMIND: break; qboolean WP_ForcePowerAvailable(gentity_t * self, forcePowers_t forcePower, int overrideAmt) { if (forcePower == FP_LEVITATION) { return qtrue; } int drain = overrideAmt ? overrideAmt : forcePowerNeeded[forcePower]; if (!drain) { return qtrue; } if (self - > client - > ps.forcePower < drain) { //G_AddEvent( self, EV_NOAMMO, 0 ); return qfalse; } return qtrue; } void WP_ForcePowerStop(gentity_t * self, forcePowers_t forcePower) { gentity_t * gripEnt; gentity_t * drainEnt; if (!(self - > client - > ps.forcePowersActive & (1 << forcePower))) { //umm, wasn't doing it, so... return; } self - > client - > ps.forcePowersActive &= ~(1 << forcePower); switch ((int) forcePower) { case FP_HEAL: //if ( self->client->ps.forcePowerLevel[FP_HEAL] < FORCE_LEVEL_3 ) { //wasn't an instant heal and heal is now done if (self - > client - > ps.forcePowerLevel[FP_HEAL] < FORCE_LEVEL_2) { //if in meditation pose, must come out of it //FIXME: BOTH_FORCEHEAL_STOP if (self - > client - > ps.legsAnim == BOTH_FORCEHEAL_START) { NPC_SetAnim(self, SETANIM_LEGS, BOTH_FORCEHEAL_STOP, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } if (self - > client - > ps.torsoAnim == BOTH_FORCEHEAL_START) { NPC_SetAnim(self, SETANIM_TORSO, BOTH_FORCEHEAL_STOP, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } self - > client - > ps.saberMove = self - > client - > ps.saberBounceMove = LS_READY; //don't finish whatever saber anim you may have been in self - > client - > ps.saberBlocked = BLOCKED_NONE; } } WP_StopForceHealEffects(self); if (self - > health >= self - > client - > ps.stats[STAT_MAX_HEALTH] / 3) { gi.G2API_ClearSkinGore(self - > ghoul2); } break; case FP_LEVITATION: self - > client - > ps.forcePowerDebounce[FP_LEVITATION] = 0; break; case FP_SPEED: if (!self - > s.number) { //player using force speed if (g_timescale - > value != 1.0) { if (!(self - > client - > ps.forcePowersActive & (1 << FP_RAGE)) || self - > client - > ps.forcePowerLevel[FP_RAGE] < FORCE_LEVEL_2) { //not slowed down because of force rage gi.cvar_set("timescale", "1"); } } } //FIXME: reset my current anim, keeping current frame, but with proper anim speed // otherwise, the anim will continue playing at high speed self - > s.loopSound = 0; break; case FP_PUSH: break; case FP_PULL: break; case FP_TELEPATHY: break; case FP_STUN: break; case FP_HATE: break; case FP_CONTROLMIND: break; extern qboolean PM_ForceJumpingUp( gentity_t *gent ); static void WP_ForcePowerRun( gentity_t *self, forcePowers_t forcePower, usercmd_t *cmd ) { float speed, newSpeed; gentity_t *gripEnt, *drainEnt; vec3_t angles, dir, gripOrg, gripEntOrg; float dist; extern usercmd_t ucmd; switch( (int)forcePower ) { case FP_HEAL: if ( self->client->ps.forceHealCount >= FP_MaxForceHeal(self) || self->health >= self->client->ps.stats[STAT_MAX_HEALTH] ) {//fully healed or used up all 25 if ( !Q3_TaskIDPending( self, TID_CHAN_VOICE ) ) { int index = Q_irand( 1, 4 ); if ( self->s.number < MAX_CLIENTS ) { G_SoundOnEnt( self, CHAN_VOICE, va( "sound/weapons/force/heal%d_%c.mp3", index, g_sex->string[0] ) ); } else if ( self->NPC ) { if ( self->NPC->blockedSpeechDebounceTime <= level.time ) {//enough time has passed since our last speech if ( Q3_TaskIDPending( self, TID_CHAN_VOICE ) ) {//not playing a scripted line //say "Ahhh...." if ( self->NPC->stats.sex == SEX_MALE || self->NPC->stats.sex == SEX_NEUTRAL ) { G_SoundOnEnt( self, CHAN_VOICE, va( "sound/weapons/force/heal%d_m.mp3", index ) ); } else//all other sexes use female sounds { G_SoundOnEnt( self, CHAN_VOICE, va( "sound/weapons/force/heal%d_f.mp3", index ) ); } } } } } WP_ForcePowerStop( self, forcePower ); } else if ( self->client->ps.forcePowerLevel[FP_HEAL] < FORCE_LEVEL_3 && ( (cmd->buttons&BUTTON_ATTACK) || (cmd->buttons&BUTTON_ALT_ATTACK) || self->painDebounceTime > level.time || (self->client->ps.weaponTime&&self->client->ps.weapon!=WP_NONE) ) ) {//attacked or was hit while healing... //stop healing WP_ForcePowerStop( self, forcePower ); } else if ( self->client->ps.forcePowerLevel[FP_HEAL] < FORCE_LEVEL_2 && ( cmd->rightmove || cmd->forwardmove || cmd->upmove > 0 ) ) {//moved while healing... FIXME: also, in WP_ForcePowerStart, stop healing if any other force power is used //stop healing WP_ForcePowerStop( self, forcePower ); } else if ( self->client->ps.forcePowerDebounce[FP_HEAL] < level.time ) {//time to heal again if ( WP_ForcePowerAvailable( self, forcePower, 4 ) ) {//have available power int healInterval = FP_ForceHealInterval( self ); int healAmount = 1;//hard, normal healing rate if ( self->s.number < MAX_CLIENTS ) { if ( g_spskill->integer == 1 ) {//medium, heal twice as fast healAmount *= 2; } else if ( g_spskill->integer == 0 ) {//easy, heal 3 times as fast... healAmount *= 3; } } if ( self->health + healAmount > self->client->ps.stats[STAT_MAX_HEALTH] ) { healAmount = self->client->ps.stats[STAT_MAX_HEALTH] - self->health; } self->health += healAmount; self->client->ps.forceHealCount += healAmount; self->client->ps.forcePowerDebounce[FP_HEAL] = level.time + healInterval; WP_ForcePowerDrain( self, forcePower, 4 ); } else {//stop WP_ForcePowerStop( self, forcePower ); } } break; case FP_LEVITATION: if ( self->client->ps.groundEntityNum != ENTITYNUM_NONE && !self->client->ps.forceJumpZStart ) {//done with jump WP_ForcePowerStop( self, forcePower ); } else { if ( PM_ForceJumpingUp( self ) ) {//holding jump in air if ( cmd->upmove > 10 ) {//still trying to go up if ( WP_ForcePowerAvailable( self, FP_LEVITATION, 1 ) ) { if ( self->client->ps.forcePowerDebounce[FP_LEVITATION] < level.time ) { WP_ForcePowerDrain( self, FP_LEVITATION, 5 ); self->client->ps.forcePowerDebounce[FP_LEVITATION] = level.time + 100; } self->client->ps.forcePowersActive |= ( 1 << FP_LEVITATION ); self->client->ps.forceJumpCharge = 1;//just used as a flag for the player, cleared when he lands } else {//cut the jump short WP_ForcePowerStop( self, forcePower ); } } else {//cut the jump short WP_ForcePowerStop( self, forcePower ); } } else { WP_ForcePowerStop( self, forcePower ); } } break; case FP_SPEED: speed = forceSpeedValue[self->client->ps.forcePowerLevel[FP_SPEED]]; if ( !self->s.number ) {//player using force speed if ( !(self->client->ps.forcePowersActive&(1<<FP_RAGE)) || self->client->ps.forcePowerLevel[FP_SPEED] >= self->client->ps.forcePowerLevel[FP_RAGE] ) {//either not using rage or rage is at a lower level than speed gi.cvar_set("timescale", va("%4.2f", speed)); if ( g_timescale->value > speed ) { newSpeed = g_timescale->value - 0.05; if ( newSpeed < speed ) { newSpeed = speed; } gi.cvar_set("timescale", va("%4.2f", newSpeed)); } } } break; case FP_PUSH: break; case FP_PULL: break; case FP_TELEPATHY: break; case FP_STUN: break; case FP_HATE: break; case FP_CONTROLMIND: break; case FP_FEAR: break; void WP_CheckForcedPowers(gentity_t * self, usercmd_t * ucmd) { for (int forcePower = FP_FIRST; forcePower < NUM_FORCE_POWERS; forcePower++) { if ((self - > client - > ps.forcePowersForced & (1 << forcePower))) { switch (forcePower) { case FP_HEAL: ForceHeal(self); //do only once self - > client - > ps.forcePowersForced &= ~(1 << forcePower); break; case FP_LEVITATION: //nothing break; case FP_SPEED: ForceSpeed(self); //do only once self - > client - > ps.forcePowersForced &= ~(1 << forcePower); break; case FP_PUSH: ForceThrow(self, qfalse); //do only once self - > client - > ps.forcePowersForced &= ~(1 << forcePower); break; case FP_PULL: ForceThrow(self, qtrue); //do only once self - > client - > ps.forcePowersForced &= ~(1 << forcePower); break; case FP_TELEPATHY: //FIXME: target at enemy? ForceTelepathy(self); //do only once self - > client - > ps.forcePowersForced &= ~(1 << forcePower); break; case FP_FREEZE: //FIXME: target at enemy? ForceFreeze(self); //do only once self - > client - > ps.forcePowersForced &= ~(1 << forcePower); break; case FP_GRIP: ucmd - > buttons &= ~(BUTTON_ATTACK | BUTTON_ALT_ATTACK | BUTTON_FORCE_FOCUS | BUTTON_FORCE_DRAIN | BUTTON_FORCE_LIGHTNING); ucmd - > buttons |= BUTTON_FORCEGRIP; //holds until cleared break; case FP_LIGHTNING: ucmd - > buttons &= ~(BUTTON_ATTACK | BUTTON_ALT_ATTACK | BUTTON_FORCE_FOCUS | BUTTON_FORCEGRIP | BUTTON_FORCE_DRAIN); ucmd - > buttons |= BUTTON_FORCE_LIGHTNING; //holds until cleared break; case FP_SABERTHROW: ucmd - > buttons &= ~(BUTTON_ATTACK | BUTTON_FORCE_FOCUS | BUTTON_FORCEGRIP | BUTTON_FORCE_DRAIN | BUTTON_FORCE_LIGHTNING); ucmd - > buttons |= BUTTON_ALT_ATTACK; //holds until cleared? break; case FP_SABER_DEFENSE: //nothing break; case FP_SABER_OFFENSE: //nothing break; case FP_RAGE: ForceRage(self); //do only once self - > client - > ps.forcePowersForced &= ~(1 << forcePower); break; case FP_PROTECT: ForceProtect(self); //do only once self - > client - > ps.forcePowersForced &= ~(1 << forcePower); break; case FP_ABSORB: ForceAbsorb(self); //do only once self - > client - > ps.forcePowersForced &= ~(1 << forcePower); break; case FP_DRAIN: ucmd - > buttons &= ~(BUTTON_ATTACK | BUTTON_ALT_ATTACK | BUTTON_FORCE_FOCUS | BUTTON_FORCEGRIP | BUTTON_FORCE_LIGHTNING); ucmd - > buttons |= BUTTON_FORCE_DRAIN; //holds until cleared break; case FP_SEE: //nothing break; case FP_STUN: //FIXME: target at enemy? ForceStun(self); //do only once self - > client - > ps.forcePowersForced &= ~(1 << forcePower); break; case FP_HATE: //FIXME: target at enemy? ForceHate(self); //do only once self - > client - > ps.forcePowersForced &= ~(1 << forcePower); break; case FP_CONTROLMIND: //FIXME: target at enemy? ForceControlMind(self); //do only once self - > client - > ps.forcePowersForced &= ~(1 << forcePower); break; case FP_FEAR: //FIXME: target at enemy? ForceFear(self); //do only once self - > client - > ps.forcePowersForced &= ~(1 << forcePower); break; } } } } At the end, there is also the WP_Initforcepower. What did it... i am not much sure. I think it setting a preset value of force power level for various force power when you start a map with devmapall command. If you want to get your new power by the start of a level, you need to add it here, and also with the level value you desire. void WP_InitForcePowers(gentity_t * ent) { if (!ent || !ent - > client) { return; } if (!ent - > client - > ps.forcePowerMax) { ent - > client - > ps.forcePowerMax = FORCE_POWER_MAX; } if (!ent - > client - > ps.forcePowerRegenRate) { ent - > client - > ps.forcePowerRegenRate = 100; } ent - > client - > ps.forcePower = ent - > client - > ps.forcePowerMax; ent - > client - > ps.forcePowerRegenDebounceTime = 0; ent - > client - > ps.forceGripEntityNum = ent - > client - > ps.forceDrainEntityNum = ent - > client - > ps.pullAttackEntNum = ENTITYNUM_NONE; ent - > client - > ps.forceRageRecoveryTime = 0; ent - > client - > ps.forceDrainTime = 0; ent - > client - > ps.pullAttackTime = 0; if (ent - > s.number < MAX_CLIENTS) { //player if (!g_cheats - > integer) //devmaps give you all the FP { ent - > client - > ps.forcePowerLevel[FP_SABER_DEFENSE] = FORCE_LEVEL_1; ent - > client - > ps.forcePowerLevel[FP_SABER_OFFENSE] = FORCE_LEVEL_1; } else { ent - > client - > ps.forcePowersKnown = (1 << FP_HEAL) | (1 << FP_LEVITATION) | (1 << FP_SPEED) | (1 << FP_PUSH) | (1 << FP_PULL) | (1 << FP_TELEPATHY) | (1 << FP_GRIP) | (1 << FP_LIGHTNING) | (1 << FP_SABERTHROW) | (1 << FP_SABER_DEFENSE) | (1 << FP_SABER_OFFENSE) | (1 << FP_RAGE) | (1 << FP_DRAIN) | (1 << FP_PROTECT) | (1 << FP_ABSORB) | (1 << FP_SEE); ent - > client - > ps.forcePowerLevel[FP_HEAL] = FORCE_LEVEL_2; ent - > client - > ps.forcePowerLevel[FP_LEVITATION] = FORCE_LEVEL_2; ent - > client - > ps.forcePowerLevel[FP_PUSH] = FORCE_LEVEL_1; ent - > client - > ps.forcePowerLevel[FP_PULL] = FORCE_LEVEL_1; ent - > client - > ps.forcePowerLevel[FP_SABERTHROW] = FORCE_LEVEL_2; ent - > client - > ps.forcePowerLevel[FP_SPEED] = FORCE_LEVEL_2; ent - > client - > ps.forcePowerLevel[FP_LIGHTNING] = FORCE_LEVEL_1; ent - > client - > ps.forcePowerLevel[FP_TELEPATHY] = FORCE_LEVEL_2; ent - > client - > ps.forcePowerLevel[FP_RAGE] = FORCE_LEVEL_1; ent - > client - > ps.forcePowerLevel[FP_PROTECT] = FORCE_LEVEL_1; ent - > client - > ps.forcePowerLevel[FP_ABSORB] = FORCE_LEVEL_1; ent - > client - > ps.forcePowerLevel[FP_DRAIN] = FORCE_LEVEL_1; ent - > client - > ps.forcePowerLevel[FP_SEE] = FORCE_LEVEL_1; ent - > client - > ps.forcePowerLevel[FP_SABER_DEFENSE] = FORCE_LEVEL_3; ent - > client - > ps.forcePowerLevel[FP_SABER_OFFENSE] = FORCE_LEVEL_3; ent - > client - > ps.forcePowerLevel[FP_GRIP] = FORCE_LEVEL_2; ent - > client - > ps.forcePowerLevel[FP_STUN] = FORCE_LEVEL_1; ent - > client - > ps.forcePowerLevel[FP_HATE] = FORCE_LEVEL_1; ent - > client - > ps.forcePowerLevel[FP_CONTROLMIND] = FORCE_LEVEL_1; ent - > client - > ps.forcePowerLevel[FP_FREEZE] = FORCE_LEVEL_1; ent - > client - > ps.forcePowerLevel[FP_FEAR] = FORCE_LEVEL_1; } } } Well. We have end! Good luck and May Force be with you. Build the openjk_sp.86.dll and jagamex86.dll and have fun with your new power.
-
Yep @@Lancelot is right i am talking about this https://jkhub.org/topic/9904-where-i-can-find-tfu-expansion-pack/ the mod have an exe installer. i hope i can setup after i will paste into gamedata KOTF 2.0. i fear it pretend the original KOTF (i remember the oldest kotf was with an installer .-. ) however if i remember fine (i played many years ago i need again to re-setup) kotf tfu mod was 3 complete SP levels with battles and also a dogfight of fighters. i wanna study how is set and scripted and programmed the dogfight between vader and starfighters. for do that, i need the TFU mod assets.
-
Understood. Well, not problem. with a lot of pain, i downloaded today with help of my father. o hope The force unleashed expansion setup will works after i will place kotf into gamedata. without kotf setup is rejected. lol. Merry Christmas to alls Jkhub, KOTF and other mod staffs!
-
Hello, i am trying by days to download knight of the force 2.0 alpha from https://jkhub.org/files/file/3121-knights-of-the-force/ but download ever fail after half hour. the file is really BIG (7 giga) someone can be so kindful to split file into more pieces that i can download separately?
-
where i can find TFU expansion pack?
Asgarath83 replied to Asgarath83's topic in Knights of the Force
Yes, i now... when i played the part, i get some headache and head flips. lol fighter turn too fast, battle space is too small. but this can be improved. what i am interessed is how to recreate this on JKA or OPENJK. for that, i will need to check the decompiled map, scripts, assets, etc for see how was work this thing. but is only a start point. 1 years ago i coded a fighter AI very primitive merging boba fett and sentry AI but with potentializy. it miss just the dogfight flight manouvre. i not know how to program with vectors and the other way to do that shoul call on the code the roff files on t3_byss script about tie fighter battle. but i was thinking also to other solution and i remembered of this mod and of that battle. i hope to discover some useful. -
where i can find TFU expansion pack?
Asgarath83 replied to Asgarath83's topic in Knights of the Force
thanks, i didn't know that D: woah, that's cool. -
where i can find TFU expansion pack?
Asgarath83 replied to Asgarath83's topic in Knights of the Force
i Agree, we should contact KOTF2.0 staff for asking this. Lancelot, many thanks for answer. @@swegmaster thanks for your upload! now peoples can play again it (should work also on kotf 2? ) and i can study assets of mod for see how work this map and these fighters. i wanna see if they are moved by roff or by some code did at the time by mod creator (but i doubt these because SP source code was not again released ) -
Sorry if i disturb. years ago i played with old KOTF (yes, the version with "malware" -.- , but i know now there is a new KOTF for JKA without malware! Yeah! ) and i was really happy when i found these expansion: that recreate a single player campaign for make on JA The force unleashed game, that's only for xbox and not for pc for what i know. i have not an xbox and i haven not more this expansion so i was wondering if someone on JKHUB know where i can re-download the demo with kashyyyk levels. i ask also for another motivation. by months i was trying to archive a code work on open jk single player about space combat mod (like asteroid and siege destroyer, but for single player) so i was working on an AI for fighters and playable fighters. on the TFU mod of old KOTF there was a nice fighter battle: on fifth minutes of this video you can see vader on a tie fighter against jedi starfighters. i am curios to find and get the assets of this mod also for study how work this fighter combat, for my coding project. thanks for every help or answer.
-
MAP - Vengeance on Kothlis (Request)
Asgarath83 replied to dark_apprentice's topic in Mod Requests & Suggestions
I am very interessed on that. let me know when plug in is ready D: it's wonderful! the smallest space of gtk radiant map are ever a big limitation. .-. (also a vertex paint terrain on radiant should really amazing. easygen is not much user friendly) -
-
-
Yes you win a medal. i know the animation override function because i used many times on my custom force power. your work is interessing. i think a day i will remove this tedious glitch also by my custom code. thanks for your work and your idea!
-
lol, so was simply icarus up to date, these cheats and commands are already working on JO code? good to know. bye
-
I see, do you create this cheat with code hacking or with default exe game? o.o
-
the Playermodel cheat work on JO on the consolle command for play like other NPCs?
-
i am little better, but pretty far again by the past. mmm on JO is problematic because is not prevented that player can be changed... you play only as kyle for all game lol, so playermodel is not implemented. but you can change the playermodel also in another way: by loading cvars models by menu before starting at level. is a lot complex and you need a custom creation of a menu screen. but should works also on JO i guess.
-
for set specific parameters into entites on gtk radiant, select the enctity and press N key. on key you make the command and on value you put the value of command. for example... key spawnscript value : empire/start1 (path of the script, the script will be placed on scripts folder and into a folder called "empire" that you need to custom create for work, and need to be compiled with icarus on IBI format. .
-
- 24 comments
-
- halloween
- contest entry
-
(and 1 more)
Tagged with: