Jump to content

Asgarath83

Members
  • Posts

    1,953
  • Joined

  • Last visited

2 Followers

Profile Information

  • Gender
    Male
  • Location
    Italy, Sardinia
  • Interests
    Star Wars, Star Trek, Reading, Writing, Fantasy, Science fiction, comics, music, art, science, astronomy, biology, history, misteries of life, magic, etc etc etc.

    Please, not ask me more for any kind of help or partecipation on modding project. I have an hell time with heal's problems, so i cannot manage no one more of these requests.

Recent Profile Visitors

3,695 profile views

Asgarath83's Achievements

  1. 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.
  2. Really? Exagonal face??? hilarous design. what's on the mind of pyke syndacate? nice model however, i like it, very fun!
  3. First off, thanks for provide the source. i really like the skybox and i really puzzle about how you did a skybox like that, is really perfect, a dinamic skybox with traffic tool! it provide the illusions that map is more larger of what is really. i love this kind of things. how you did this skybox? you used a misc_skyportal? the map is an interesting maze, nice works with patchs and caps mesh really you did all by hands and that is impressive. i love the waterfall effect! 10/10 edit: yes, you used a skyportal. smart choice! the best way for make wasteland, urbans and big maps enviroments in a realistic way :D
  4. https://github.com/JACoders/OpenJK Here. Download Zip, Enable Git, create your repository and have fun
  5. 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.
  6. I ever like when someone upload a good map. and thanks for source too. <3 great job man!
  7. Oh, so is sufficient to increase the amount of "storyinfo" necessary for unlock the main quest for solve this problem. very interesting! :D thanks again!!!
  8. Cool! So is sufficient to add new mission that NOT add the value on cvar or fix it (maybe subctracting instead of adding into some case) and that allow to add into a tier any kind of mission any want to do Man, you open me a world of possibilities! TY!
  9. Amazing... Is... Possible to do??? No code hacking? and how you avoid the fact that after the fifth tie you are forced to back to academy?
  10. and use SET_WEAPON NONE and the LOCK WEAPON command, so player is unabled to switch other weapons from weapon none? maybe also playing a sound of "dropping"
  11. Is possible, but you need some good guy that now well the vehicle code of multiplayer and singleplayer both. the asteroid mod of gummelt have a nice multiplayer dogfight space combat. the single player vehicles are horrid and hacked. animals and speeders are very good both Fighters handling and collision is very nasty. well, basically, a coder should past the source code of asteroid mod from MP to SP for fix the bad flight behavour of fighter. then you need another problem: you need to wrote an AI for NPC that use fighter vehicles for have realistic space combat mode. honestly i not know how to do that. At moment, i am not working on my fighers, i am doing a library for customize weapons as expernal *.GUN file that works exactly like *.SAB for sabers. and replace the limitated and frustrating weapons.DAT i am at good point but is a LONG job, the variables for fully customize model, icon, descripton, projectiles, damage, visual effects, ray shaders of disruptor, animation damage, splashdamage and ballistic of weapons are ton of stuff, and i am again working for accomplish it. when i will end this code i will host the edited code and i add some notification about the functions created and the files i hacked so amodder more expert of me can complete that an host on git and do some better improved job. i never got git working, sadly and no one never teached me step by step how to use it. however, yes, lancelot, is possible to do, every thing is possible to do with code. you need - source code - c++ knowledge - willpower - a nice coder. any thing of life is possible to do if you have the skills, the willpower and the tools for do it. the limitatiosn exists only on our mind.
  12. is all hardcoded. if you know c++ you can download opejk and visual studio and get a look. the file of code related to saber combat is wp_saber.cpp for single player .
  13. I not know how work. but why no try to see the script of jedi outcast level of nar shaadaa when kyle enter into the cantina and rodians ask hik to drop his weapons and is not possible to use weapons until you chat with barman?
  14. https://github.com/JACoders/OpenJK Here. Download Zip, Enable Git, create your repository and have fun
×
×
  • Create New...