Jump to content

SP new game styles (RPG - Space \ air dogfight battles)


Recommended Posts

drivable pretty far by working. when i spawn the "drivable" with npc tiefighter4 it follow player like a dog and is not usable...

also bounding box is totally wrong...

uff, i think i will take a break from that. i cannot solve the problems without an expert help. :(

Link to comment

uff, i think i will take a break from that. i cannot solve the problems without an expert help. :(

 

It doesn't matter if you take a break, what matters is that now you know much more than you did when you started this :)

Link to comment

It doesn't matter if you take a break, what matters is that now you know much more than you did when you started this :)

 

well, i think is time to show my progress for moment. so i paste here the code edit. i hope someone can use that for some mod.

almost can do some tiefighter nice boss for a end of a level or fighter NPC that chase and follow player in some canyon or in an open field shooting and bombard him.

(just remove WP_POISON, BLOODGUN and WP_JAWA by stuff, is a my custom creation\ hacking)

NPC file of tiefighters:

first off, you need a tie fighter rigged to humanoid glm skeleton for a full working npc. is just necessary to import model on blender or max and rig all mesh to pelvis bone. bolt_l_hand, bolt_r_hand need to be placed into the muzzle flash bolt points of cannons.

 

// Fighter class
TieFighter
// Test AI
{
    playerModel    tie
    weapon        WP_BLOODGUN
    weapon        WP_BRYAR_PISTOL
    weapon        WP_JAWA
    weapon        WP_POISON
    weapon        WP_BLASTER_PISTOL
    weapon        WP_BLASTER
//    weapon        WP_DISRUPTOR
    weapon        WP_DEMP2
    weapon        WP_BOWCASTER
    weapon        WP_REPEATER
    weapon        WP_FLECHETTE    
    weapon        WP_ROCKET_LAUNCHER
    weapon        WP_THERMAL
//d    weapon        WP_CONCUSSION
//    weapon        WP_ATST_MAIN
//    weapon        WP_ATST_SIDE
    FP_HEAL    0
    FP_LEVITATION    0
    FP_SPEED    0
    FP_PUSH    0
    FP_PULL        0
    FP_TELEPATHY    0
    FP_GRIP        0
    FP_LIGHTNING    0
    FP_RAGE    0
    FP_PROTECT        0
    FP_ABSORB        0
    FP_DRAIN        0
    FP_SEE            0
    FP_SABERTHROW    0
    FP_SABER_DEFENSE     0
    FP_SABER_OFFENSE     0
    FP_STUN            0
    FP_HATE            0
    FP_HEALOTHER            0
    FP_CONTROLMIND        0
    FP_WRACK        0
    FP_FREEZE        0
    FP_FORCEGLYPH        0
    FP_STONEGLYPH        0
    FP_FIREGLYPH        0
    FP_WATERGLYPH        0
    FP_DARKGLYPH        0
    FP_WINDGLYPH        0
    FP_SUNGLYPH        0
    FP_HOLYGLYPH        0
    FP_NECROGLYPH        0
    FP_SOUNDGLYPH        0    
    forceRegenAmount    0
    forcePowerMax        0
    rank            captain
    aggression        5
    aim            5
    intelligence        5
    reactions            5
    move            5
    evasion            5
    playerTeam        TEAM_ENEMY
    enemyTeam        TEAM_PLAYER
    class            CLASS_FIGHTER
    health            1000
width        140
    height        300
    snd            tie-fighter
    sndcombat        tie-fighter
    sndextra        tie-fighter
    sndjedi            tie-fighter
    dismemberProbArms    0
    dismemberProbHands    0
    dismemberProbHead    0
    dismemberProbLegs    0
    dismemberProbWaist    0
}


TieFighter1
// Test AI
{
    playerModel    tie
//    weapon         WP_BLASTER_PISTOL
//    weapon        WP_BRYAR_PISTOL
//    weapon        WP_BLASTER
    weapon        WP_DISRUPTOR
    weapon        WP_ROCKET_LAUNCHER
//    weapon        WP_REPEATER
//    weapon        WP_FLECHETTE

//    weapon        WP_BOWCASTER
//    weapon        WP_DEMP2
//    weapon        WP_THERMAL
//    weapon        WP_CONCUSSION
//    weapon        WP_ATST_MAIN
//    weapon        WP_ATST_SIDE
    FP_HEAL    0
    FP_LEVITATION    0
    FP_SPEED    0
    FP_PUSH    0
    FP_PULL        0
    FP_TELEPATHY    0
    FP_GRIP        0
    FP_LIGHTNING    0
    FP_RAGE    0
    FP_PROTECT        0
    FP_ABSORB        0
    FP_DRAIN        0
    FP_SEE            0
    FP_SABERTHROW    0
    FP_SABER_DEFENSE     0
    FP_SABER_OFFENSE     0
    FP_STUN            0
    FP_HATE            0
    FP_HEALOTHER            0
    FP_CONTROLMIND        0
    FP_WRACK        0
    FP_FREEZE        0
    FP_FORCEGLYPH        0
    FP_STONEGLYPH        0
    FP_FIREGLYPH        0
    FP_WATERGLYPH        0
    FP_DARKGLYPH        0
    FP_WINDGLYPH        0
    FP_SUNGLYPH        0
    FP_HOLYGLYPH        0
    FP_NECROGLYPH        0
    FP_SOUNDGLYPH        0    
    forceRegenAmount    0
    forcePowerMax        0
    rank            captain
    aggression        5
    aim            5
    intelligence        5
    reactions            5
    move            5
    evasion            5
    playerTeam        TEAM_ENEMY
    enemyTeam        TEAM_PLAYER
    class            CLASS_FIGHTER
    health            1000
width        140
    height        300
    snd            tie-fighter
    sndcombat        tie-fighter
    sndextra        tie-fighter
    sndjedi            tie-fighter
    dismemberProbArms    0
    dismemberProbHands    0
    dismemberProbHead    0
    dismemberProbLegs    0
    dismemberProbWaist    0
}


TieFighter2
// For player
{
    playerModel    tie
    weapon        WP_BLOODGUN
    weapon        WP_BRYAR_PISTOL
    weapon        WP_JAWA
    weapon        WP_POISON
    weapon        WP_BLASTER_PISTOL
    weapon        WP_BLASTER
    weapon        WP_DISRUPTOR
    weapon        WP_DEMP2
    weapon        WP_BOWCASTER
    weapon        WP_REPEATER
    weapon        WP_FLECHETTE    
    weapon        WP_ROCKET_LAUNCHER
    weapon        WP_THERMAL
//d    weapon        WP_CONCUSSION
//    weapon        WP_ATST_MAIN
//    weapon        WP_ATST_SIDE
    FP_HEAL    0
    FP_LEVITATION    0
    FP_SPEED    0
    FP_PUSH    0
    FP_PULL        0
    FP_TELEPATHY    0
    FP_GRIP        0
    FP_LIGHTNING    0
    FP_RAGE    0
    FP_PROTECT        0
    FP_ABSORB        0
    FP_DRAIN        0
    FP_SEE            0
    FP_SABERTHROW    0
    FP_SABER_DEFENSE     0
    FP_SABER_OFFENSE     0
    FP_STUN            0
    FP_HATE            0
    FP_HEALOTHER            0
    FP_CONTROLMIND        0
    FP_WRACK        0
    FP_FREEZE        0
    FP_FORCEGLYPH        0
    FP_STONEGLYPH        0
    FP_FIREGLYPH        0
    FP_WATERGLYPH        0
    FP_DARKGLYPH        0
    FP_WINDGLYPH        0
    FP_SUNGLYPH        0
    FP_HOLYGLYPH        0
    FP_NECROGLYPH        0
    FP_SOUNDGLYPH        0    
    forceRegenAmount    0
    forcePowerMax        0
    rank            captain
    aggression        5
    aim            5
    intelligence        5
    reactions            5
    move            5
    evasion            5
    playerTeam        TEAM_PLAYER
    enemyTeam        TEAM_ENEMY
    class            CLASS_FIGHTER
    health            1000
    width        140
    height        300
    snd            tie-fighter
    sndcombat        tie-fighter
    sndextra        tie-fighter
    sndjedi            tie-fighter
    dismemberProbArms    0
    dismemberProbHands    0
    dismemberProbHead    0
    dismemberProbLegs    0
    dismemberProbWaist    0
}

TieFighter3
// For fun
{
    playerModel    tie
    weapon            WP_EMPLACED_GUN
    FP_HEAL    0
    FP_LEVITATION    0
    FP_SPEED    0
    FP_PUSH    0
    FP_PULL        0
    FP_TELEPATHY    0
    FP_GRIP        0
    FP_LIGHTNING    0
    FP_RAGE    0
    FP_PROTECT        0
    FP_ABSORB        0
    FP_DRAIN        0
    FP_SEE            0
    FP_SABERTHROW    0
    FP_SABER_DEFENSE     0
    FP_SABER_OFFENSE     0
    FP_STUN            0
    FP_HATE            0
    FP_HEALOTHER            0
    FP_CONTROLMIND        0
    FP_WRACK        0
    FP_FREEZE        0
    FP_FORCEGLYPH        0
    FP_STONEGLYPH        0
    FP_FIREGLYPH        0
    FP_WATERGLYPH        0
    FP_DARKGLYPH        0
    FP_WINDGLYPH        0
    FP_SUNGLYPH        0
    FP_HOLYGLYPH        0
    FP_NECROGLYPH        0
    FP_SOUNDGLYPH        0    
    forceRegenAmount    0
    forcePowerMax        0
    rank            captain
    aggression        5
    aim            5
    intelligence        5
    reactions            5
    move            5
    evasion            5
    playerTeam        TEAM_ENEMY
    enemyTeam        TEAM_PLAYER
    class            CLASS_FIGHTER
    health            1000
//    width        140
//    height        300
    snd            tie-fighter
    sndcombat        tie-fighter
    sndextra        tie-fighter
    sndjedi            tie-fighter
    dismemberProbArms    0
    dismemberProbHands    0
    dismemberProbHead    0
    dismemberProbLegs    0
    dismemberProbWaist    0
}

TieFighter4
// DRIVABLE For fun
{
    playerModel    tie
    FP_HEAL    0
    FP_LEVITATION    0
    FP_SPEED    0
    FP_PUSH    0
    FP_PULL        0
    FP_TELEPATHY    0
    FP_GRIP        0
    FP_LIGHTNING    0
    FP_RAGE    0
    FP_PROTECT        0
    FP_ABSORB        0
    FP_DRAIN        0
    FP_SEE            0
    FP_SABERTHROW    0
    FP_SABER_DEFENSE     0
    FP_SABER_OFFENSE     0
    FP_STUN            0
    FP_HATE            0
    FP_HEALOTHER            0
    FP_CONTROLMIND        0
    FP_WRACK        0
    FP_FREEZE        0
    FP_FORCEGLYPH        0
    FP_STONEGLYPH        0
    FP_FIREGLYPH        0
    FP_WATERGLYPH        0
    FP_DARKGLYPH        0
    FP_WINDGLYPH        0
    FP_SUNGLYPH        0
    FP_HOLYGLYPH        0
    FP_NECROGLYPH        0
    FP_SOUNDGLYPH        0    
    forceRegenAmount    0
    forcePowerMax        0
    rank            captain
    aggression        5
    aim            5
    intelligence        5
    reactions            5
    move            5
    evasion            5
    playerTeam        TEAM_NEUTRAL
    enemyTeam        TEAM_NEUTRAL
    class            CLASS_FIGHTER
    health            1000
//    width        140
//    height        300
    snd            tie-fighter
    sndcombat        tie-fighter
    sndextra        tie-fighter
    sndjedi            tie-fighter
    dismemberProbArms    0
    dismemberProbHands    0
    dismemberProbHead    0
    dismemberProbLegs    0
    dismemberProbWaist    0
}

 

edit on code:

teams.h - add on the end of class_t

 

CLASS_FIGHTER, // Fighter npc class for air \ space battles. - generic fig
 

q3_interace.cpp

 

ENUM2STRING(CLASS_FIGHTER),
on same enum value of class

npc.cpp - after boba fett definitation behavour

 

    else if ( NPC->client->NPC_class == CLASS_ROCKETTROOPER )
    {//bounty hunter
        if ( RT_Flying( NPC ) || NPC->enemy != NULL )
        {
            NPC_BSRT_Default();
        }
        else
        {
            NPC_BehaviorSet_Stormtrooper( bState );
        }
        G_CheckCharmed( NPC );
        dontSetAim = qtrue;
    }
    else if ( NPC->client->NPC_class == CLASS_RANCOR ||
         NPC->client->NPC_class == CLASS_SLUAGH ||
          NPC->client->NPC_class == CLASS_BEAST    )
    {
        NPC_BehaviorSet_Rancor( bState );
    }
    else if ( NPC->client->NPC_class == CLASS_SAND_CREATURE )
    {
        NPC_BehaviorSet_SandCreature( bState );
    }
    else if ( NPC->client->NPC_class == CLASS_WAMPA )
    {
        NPC_BehaviorSet_Wampa( bState );
        G_CheckCharmed( NPC );
    }
    else if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH )
    {//being forced to march
        NPC_BSDefault();
    }
    else if ( NPC->client->ps.weapon == WP_TUSKEN_RIFLE )
    {
        if ( (NPCInfo->scriptFlags & SCF_ALT_FIRE) )
        {
            NPC_BehaviorSet_Sniper( bState );
            G_CheckCharmed( NPC );
            return;
        }
        else
        {
            NPC_BehaviorSet_Tusken( bState );
            G_CheckCharmed( NPC );
            return;
        }
    }
    else if ( NPC->client->ps.weapon == WP_TUSKEN_STAFF )
    {
        NPC_BehaviorSet_Tusken( bState );
        G_CheckCharmed( NPC );
        return;
    }
    else if ( NPC->client->ps.weapon == WP_NOGHRI_STICK )
    {
        NPC_BehaviorSet_Stormtrooper( bState );
        G_CheckCharmed( NPC );
    }
    else if ( NPC->client->NPC_class == CLASS_FIGHTER )
    {
        
            Fighter_Update();        
            if (NPCInfo->surrenderTime)
            {
                Fighter_Flee();
            }
            else
            {
                if (!Fighter_Tactics())
                {
                    if ( Fighter_Flying( NPC ) )
                    {
                        NPC_BSFighter_Default();
                    }
                    else
                    {
                        NPC_BehaviorSet_Jedi( bState );
                    }
                }
                dontSetAim = qtrue;
            }
    }

npc_spawn.cpp - work in progress

 

switch(ent->client->playerTeam)
    {
    case TEAM_PLAYER:
        //ent->flags |= FL_NO_KNOCKBACK;
        if ( ent->client->NPC_class == CLASS_SEEKER )
        {        
            ent->NPC->defaultBehavior = BS_DEFAULT;
            ent->client->ps.gravity = 0;
            ent->svFlags |= SVF_CUSTOM_GRAVITY;
            ent->client->moveType = MT_FLYSWIM;
            ent->count = 30; // SEEKER shot ammo count
            return;
        }
        // An allied Fighter is drivable if you are sufficiently close!
        else if ( ent->client->NPC_class == CLASS_FIGHTER )
        {        
                ent->NPC->defaultBehavior = BS_DEFAULT;
                ent->client->ps.gravity = 0;
                ent->svFlags |= SVF_CUSTOM_GRAVITY;
                ent->client->moveType = MT_FLYSWIM;
                ent->s.loopSound = G_SoundIndex( "sound/vehicles/tie-bomber/loop.wav" );
                // Spawn Floating until see an enemy!
                NPC_SetAnim( ent, SETANIM_LEGS, BOTH_SWIM_IDLE1, SETANIM_FLAG_NORMAL );
                NPC_SetAnim( ent, SETANIM_TORSO, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
                ent->client->ps.torsoAnimTimer = level.time + cg.time;
        }
        else if ( ent->client->NPC_class == CLASS_JEDI
            || ent->client->NPC_class == CLASS_KYLE
            || ent->client->NPC_class == CLASS_LUKE )
        {//good jedi
            ent->client->enemyTeam = TEAM_ENEMY;
            if ( ent->spawnflags & JSF_AMBUSH )
            {//ambusher
                ent->NPC->scriptFlags |= SCF_IGNORE_ALERTS;
                ent->client->noclip = true;//hang
            }
        }
        else
        {
            if (ent->client->ps.weapon != WP_NONE
                && ent->client->ps.weapon != WP_SABER //sabers done above
                && (!(ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON)||!ent->weaponModel[0]) )//they do this themselves
            {
                G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 );
            }
            switch ( ent->client->ps.weapon )
            {
            case WP_BRYAR_PISTOL://FIXME: new weapon: imp blaster pistol
            case WP_BLASTER_PISTOL:
            case WP_DISRUPTOR:
            case WP_BOWCASTER:
            case WP_REPEATER:
            case WP_DEMP2:
            case WP_FLECHETTE:
            case WP_ROCKET_LAUNCHER:
            case WP_CONCUSSION:
            default:
                break;
            case WP_THERMAL:
            case WP_BLASTER:
                //FIXME: health in NPCs.cfg, and not all blaster users are stormtroopers
                //ent->health = 25;
                //FIXME: not necc. a ST
                ST_ClearTimers( ent );
                if ( ent->NPC->rank >= RANK_LT || ent->client->ps.weapon == WP_THERMAL )
                {//officers, grenade-throwers use alt-fire
                    //ent->health = 50;
                    //ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                }
                break;
            }
        }
        if ( ent->client->NPC_class == CLASS_PLAYER
            || ent->client->NPC_class == CLASS_VEHICLE
            || (ent->spawnflags & SFB_CINEMATIC) )
        {
            ent->NPC->defaultBehavior = BS_CINEMATIC;
        }
        else
        {
            ent->NPC->defaultBehavior = BS_FOLLOW_LEADER;
            ent->client->leader = &g_entities[0];//player
        }
        break;

    case TEAM_NEUTRAL:

        if ( Q_stricmp( ent->NPC_type, "gonk" ) == 0 )
        {
            // I guess we generically make them player usable
            ent->svFlags |= SVF_PLAYER_USABLE;

            // Not even sure if we want to give different levels of batteries?  ...Or even that these are the values we'd want to use.
            switch ( g_spskill->integer )
            {
            case 0:    //    EASY
                ent->client->ps.batteryCharge = MAX_BATTERIES * 0.8f;
                break;
            case 1:    //    MEDIUM
                ent->client->ps.batteryCharge = MAX_BATTERIES * 0.75f;
                break;
            default :
            case 2:    //    HARD
                ent->client->ps.batteryCharge = MAX_BATTERIES * 0.5f;
                break;
            }
        }
        break;
        if ( ent->client->NPC_class == CLASS_FIGHTER )
        {// Drivable fighter vehicle
                ent->NPC->defaultBehavior = BS_CINEMATIC;
                ent->client->ps.gravity = 0;
                ent->svFlags |= SVF_CUSTOM_GRAVITY;
                ent->client->moveType = MT_FLYSWIM;
                ent->s.loopSound = G_SoundIndex( "sound/vehicles/tie-bomber/loop.wav" );
                ent->svFlags |= SVF_PLAYER_USABLE;

                // HERE PAST CODE FOR MAKING DRIVABLE THE CLASS


                // Spawn Floating until see an enemy!
                NPC_SetAnim( ent, SETANIM_LEGS, BOTH_SWIM_IDLE1, SETANIM_FLAG_NORMAL );
                NPC_SetAnim( ent, SETANIM_TORSO, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
                ent->client->ps.torsoAnimTimer = level.time + cg.time;

                /*ent->e_ThinkFunc = thinkF_TieBomberThink;
                ent->e_ThinkFunc = thinkF_TieFighterThink;
                ent->nextthink = level.time + FRAMETIME;
                ent->attackDebounceTime = level.time + 1000;
                // We only take damage from a heavy weapon class missiles.
                ent->s.eFlags |= EF_LESS_ATTEN;
                extern void NPC_Fighter_Precache(void);
                //extern void NPC_PrecacheAnimationCFG( const char *NPC_type );
                ent->s.modelindex = G_ModelIndex( "models/players/atst/model.glm" );
                ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, "models/players/atst/model.glm", ent->s.modelindex, NULL_HANDLE, NULL_HANDLE, 0, 0 );
                ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "model_root", qtrue );
                ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );    //FIXME: need to somehow set the anim/frame to the equivalent of BOTH_STAND1...  use to be that BOTH_STAND1 was the first frame of the glm, but not anymore
                //register my weapons, sounds and model
                RegisterItem( FindItemForWeapon( WP_ATST_MAIN ));    //precache the weapon
                RegisterItem( FindItemForWeapon( WP_ATST_SIDE ));    //precache the weapon
                //HACKHACKHACKTEMP - until ATST gets real weapons of it's own?
                RegisterItem( FindItemForWeapon( WP_EMPLACED_GUN ));    //precache the weapon
                RegisterItem( FindItemForWeapon( WP_ROCKET_LAUNCHER ));    //precache the weapon
                RegisterItem( FindItemForWeapon( WP_BOWCASTER ));    //precache the weapon
                //HACKHACKHACKTEMP - until ATST gets real weapons of it's own?
                G_SoundIndex( "sound/chars/fighter/fighter_hatch_open" );
                G_SoundIndex( "sound/chars/fighter/fighter_hatch_close" );

                NPC_Fighter_Precache();
                ent->NPC_type = (char *)"atst";
                //NPC_PrecacheAnimationCFG( ent->NPC_type );
                //open the hatch
                gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "head_hatchcover", 0 );
                ent->contents = CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;
                ent->max_health = ent->health; // cg_draw needs this
                G_SetOrigin( ent, ent->s.origin );
                G_SetAngles( ent, ent->s.angles );
                VectorCopy( ent->currentAngles, ent->s.angles2 );
                gi.linkentity ( ent );
                //FIXME: test the origin to make sure I'm clear?
                ent->e_UseFunc = useF_misc_atst_use;
                ent->svFlags |= SVF_PLAYER_USABLE;
                //make it able to take damage and die when you're not in it...
                //do an explosion and play the death anim, remove use func.
                ent->e_DieFunc = dieF_misc_atst_die;*/
            }
    case TEAM_ENEMY:
        {
            ent->NPC->defaultBehavior = BS_DEFAULT;
            if ( ent->client->NPC_class == CLASS_SHADOWTROOPER
                && Q_stricmpn("shadowtrooper", ent->NPC_type, 13 ) == 0 ||
                ent->client->NPC_class == CLASS_DARK ||
                ent->client->NPC_class == CLASS_GOLEM_DARK ||
                ent->client->NPC_class == CLASS_GOLEM_VOID ||
                ent->client->NPC_class == CLASS_VOID ||
                ent->client->NPC_class == CLASS_BLEED ||
                ent->client->NPC_class == CLASS_SHIFTER ||
                ent->client->NPC_class == CLASS_VAE )
            {//FIXME: a spawnflag?
                Jedi_Cloak( ent );
            }
             if( ent->client->NPC_class == CLASS_TAVION ||
                ent->client->NPC_class == CLASS_ALORA ||
                (ent->client->NPC_class == CLASS_REBORN && ent->client->ps.weapon == WP_SABER) ||
                ent->client->NPC_class == CLASS_DESANN ||
                ent->client->NPC_class == CLASS_SHADOWTROOPER )
            {
                ent->client->enemyTeam = TEAM_PLAYER;
                if ( ent->spawnflags & JSF_AMBUSH )
                {//ambusher
                    ent->NPC->scriptFlags |= SCF_IGNORE_ALERTS;
                    ent->client->noclip = true;//hang
                }
            }
            else if( ent->client->NPC_class == CLASS_PROBE || ent->client->NPC_class == CLASS_REMOTE ||
                        ent->client->NPC_class == CLASS_INTERROGATOR || ent->client->NPC_class == CLASS_SENTRY  ||
                        ent->client->NPC_class == CLASS_POLTER ||
                        ent->client->NPC_class == CLASS_BIRD ||
                        ent->client->NPC_class == CLASS_REAPER ||
                        ent->client->NPC_class == CLASS_DARK ||
                        ent->client->NPC_class == CLASS_LIGHT ||
                        ent->client->NPC_class == CLASS_FIRE ||
                        ent->client->NPC_class == CLASS_AIR ||
                        ent->client->NPC_class == CLASS_VOID )
            {        
                ent->NPC->defaultBehavior = BS_DEFAULT;
                ent->client->ps.gravity = 0;
                ent->svFlags |= SVF_CUSTOM_GRAVITY;
                ent->client->moveType = MT_FLYSWIM;
            }
            else if ( ent->client->NPC_class == CLASS_FIGHTER )
            {        
                ent->NPC->defaultBehavior = BS_DEFAULT;
                ent->client->ps.gravity = 0;
                ent->svFlags |= SVF_CUSTOM_GRAVITY;
                ent->client->moveType = MT_FLYSWIM;
                ent->s.loopSound = G_SoundIndex( "sound/vehicles/tie-bomber/loop.wav" );
                // Spawn Floating until see an enemy!
                NPC_SetAnim( ent, SETANIM_LEGS, BOTH_SWIM_IDLE1, SETANIM_FLAG_NORMAL );
                NPC_SetAnim( ent, SETANIM_TORSO, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
                ent->client->ps.torsoAnimTimer = level.time + cg.time;
            }
            else
            {
                if ( ent->client->ps.weapon != WP_NONE
                    && ent->client->ps.weapon != WP_SABER//sabers done above
                    && (!(ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON)||!ent->weaponModel[0]) )//they do this themselves
                {
                    G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 );
                }
                // ALT FIRE CODE!
                switch ( ent->client->ps.weapon )
                {
                case WP_BRYAR_PISTOL:
                    NPCInfo->scriptFlags |= SCF_PILOT;
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                case WP_BLOODGUN:
                    NPCInfo->scriptFlags |= SCF_PILOT;
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    if ( ent->client->NPC_class == CLASS_IMPERIAL
                        && ent->NPC->rank >= RANK_LT_COMM
                        && (!(ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON)||!ent->weaponModel[0]) )//they do this themselves
                    {//dual bloodgun pistols, so add the left-hand one, too
                        G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handLBolt, 1 );
                    }
                    break;
                case WP_CANNON:
                    NPCInfo->scriptFlags |= SCF_PILOT;
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                case WP_POISON:
                    NPCInfo->scriptFlags |= SCF_PILOT;
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                case WP_JAWA:
                    NPCInfo->scriptFlags |= SCF_PILOT;
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                case WP_BLASTER_PISTOL:
                    NPCInfo->scriptFlags |= SCF_PILOT;
                    if ( ent->client->NPC_class == CLASS_REBORN
                        && ent->NPC->rank >= RANK_LT_COMM
                        && (!(ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON)||!ent->weaponModel[0]) )//they do this themselves
                    {//dual blaster pistols, so add the left-hand one, too
                        G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handLBolt, 1 );
                    }
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                case WP_DISRUPTOR:
                    //Sniper
                    //ent->NPC->scriptFlags |= SCF_ALT_FIRE;//FIXME: use primary fire sometimes?  Up close?  Different class of NPC?
                    break;
                case WP_BOWCASTER:
                    NPCInfo->scriptFlags |= SCF_PILOT;
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                case WP_REPEATER:
                    NPCInfo->scriptFlags |= SCF_PILOT;
                    //machine-gunner
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                case WP_DEMP2:
                    NPCInfo->scriptFlags |= SCF_PILOT;
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                case WP_FLECHETTE:
                    NPCInfo->scriptFlags |= SCF_PILOT;
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                    //shotgunner
                    if ( !Q_stricmp( "stofficeralt", ent->NPC_type ) )
                    {
                        //ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                case WP_ROCKET_LAUNCHER:
                    NPCInfo->scriptFlags |= SCF_PILOT;
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                case WP_CONCUSSION:
                    NPCInfo->scriptFlags |= SCF_PILOT;
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                case WP_THERMAL:
                    NPCInfo->scriptFlags |= SCF_PILOT;
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                    //Gran, use main, bouncy fire
//                    ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                case WP_MELEE:
                    NPCInfo->scriptFlags |= SCF_PILOT;
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                case WP_NOGHRI_STICK:
                    break;
                default:
                case WP_BLASTER:
                    //FIXME: health in NPCs.cfg, and not all blaster users are stormtroopers
                    //FIXME: not necc. a ST
                    NPCInfo->scriptFlags |= SCF_PILOT;

                    ST_ClearTimers( ent );
                    if ( ent->NPC->rank >= RANK_COMMANDER && ent->s.weapon != WP_SABER )
                    {//commanders use alt-fire
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    if ( !Q_stricmp( "alora3", ent->NPC_type ) )
                    {
                        ent->NPC->scriptFlags |= SCF_ALT_FIRE;
                    }
                    break;
                }
            }
        }
        break;
    default:
        ent->NPC->defaultBehavior = BS_DEFAULT;
        if ( ent->client->ps.weapon != WP_NONE
            && ent->client->ps.weapon != WP_MELEE
            && ent->client->ps.weapon != WP_SABER//sabers done above
            && (!(ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON)||!ent->weaponModel[0]) )//they do this themselves
        {
            G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 );
        }
        break;
    }


    if ( ent->client->NPC_class == CLASS_ATST || ent->client->NPC_class == CLASS_MARK1 ) // chris/steve/kevin requested that the mark1 be shielded also
    {
        ent->flags |= (FL_SHIELDED|FL_NO_KNOCKBACK);
    }

    // Set CAN FLY Flag for Navigation On The Following Classes
    //----------------------------------------------------------
    if (ent->client->NPC_class==CLASS_PROBE ||
        ent->client->NPC_class==CLASS_REMOTE ||
        ent->client->NPC_class==CLASS_SEEKER ||
        ent->client->NPC_class==CLASS_SENTRY ||
        ent->client->NPC_class==CLASS_GLIDER ||
        ent->client->NPC_class==CLASS_IMPWORKER ||
        ent->client->NPC_class==CLASS_BOBAFETT ||
        ent->client->NPC_class==CLASS_ROCKETTROOPER ||
        ent->client->NPC_class == CLASS_BIRD ||
        ent->client->NPC_class == CLASS_POLTER ||
        ent->client->NPC_class == CLASS_REAPER ||
        ent->client->NPC_class == CLASS_DARK ||
        ent->client->NPC_class == CLASS_LIGHT ||
        ent->client->NPC_class == CLASS_FIRE ||
        ent->client->NPC_class == CLASS_VOID ||
        ent->client->NPC_class == CLASS_FIGHTER     )
    {
        ent->NPC->scriptFlags |= SCF_NAV_CAN_FLY;
    }

    if (ent->client->NPC_class==CLASS_VEHICLE )
    {
        Vehicle_Register(ent);
    }

    if ( ent->client->ps.stats[STAT_WEAPONS]&(1<<WP_SCEPTER) )
    {
        if ( !ent->weaponModel[1] )
        {//we have the scepter, so put it in our left hand if we don't already have a second weapon
            G_CreateG2AttachedWeaponModel( ent, weaponData[WP_SCEPTER].weaponMdl, ent->handLBolt, 1 );
        }
        ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->weaponModel[1]], "*flash");
    }

    if ( ent->client->ps.saber[0].type == SABER_SITH_SWORD )
    {
        ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->weaponModel[0]], "*flash");
        G_PlayEffect( G_EffectIndex( "scepter/sword.efx" ), ent->weaponModel[0], ent->genericBolt1, ent->s.number, ent->currentOrigin, qtrue, qtrue );
        //how many times can she recharge?
        ent->count = g_spskill->integer*2;
        //To make sure she can do it at least once
        ent->flags |= FL_UNDYING;
    }

    if ( ent->client->ps.weapon == WP_NOGHRI_STICK
        && ent->weaponModel[0] )
    {
        ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->weaponModel[0]], "*flash");
    }

    G_ClassSetDontFlee( ent );
}
Link to comment

AI: AI_SENTRY

at the end of file.

WIP: missing properly flight movement.

 

 

 
void NPC_BSSentry_Default( void )
{
    if ( NPC->targetname )
    {
        NPC->e_UseFunc = useF_sentry_use;
    }

    if (( NPC->enemy ) && (NPCInfo->localState != LSTATE_WAKEUP))
    {
        // Don't attack if waking up or if no enemy
        Sentry_AttackDecision();
    }
    else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
    {
        NPC_Sentry_Patrol();
    }
    else
    {
        Sentry_Idle();
    }
}

////////////////////////////////////////// FIGHTER AI ///////////////////////////////
/////////////////////////////////////// BASIC FIGHTER AI LIKE AI DEFAULT ////////////
void        Fighter_Precache( void );
void        Fighter_DustFallNear(const vec3_t origin, int dustcount);
void        Fighter_ChangeWeapon2(int wp);
qboolean    Fighter_StopKnockdown(gentity_t *self, gentity_t *pusher, const vec3_t pushDir, qboolean forceKnockdown = qfalse);
// Flight Related Functions (used Only by flying classes)
//--------------------------------------------------------
extern qboolean    Fighter_Flying( gentity_t *self );
extern void        Fighter_FlyStart( gentity_t *self );
extern void        Fighter_FlyStop( gentity_t *self );
// Called From Fighter_Pain()
//-----------------------------
void        Fighter_Pain( gentity_t *self, gentity_t *inflictor, int damage, int mod);


// Local: Other Tactics
//----------------------
void        Fighter_DoAmbushWait( gentity_t *self);
// Local: Respawning
//-------------------
//bool        Fighter_Respawn();

// Called From Within AI_Jedi && AI_Seeker
//-----------------------------------------
void        Fighter_Fire();
void        Fighter_FireDecide();

// Local: Called From Tactics()
//----------------------------
void        Fighter_TacticsSelect();
bool        Fighter_CanSeeEnemy( gentity_t *self );


// Called From Fighter_RunBehavior()
//-------------------------------
void        Fighter_Update();        // Always Called First, Before Any Other Thinking
bool        Fighter_Tactics();        // If returns true, Jedi and Seeker AI not used
bool        Fighter_Flee();        // If returns true, Jedi and Seeker AI not used
//////////////////////////////////////////////////////////////////////////////
// FLY FUNCTIONS CALLED FOR FLYING!
////////////////////////////////
void Fighter_MaintainHeight();
void Fighter_Idle();
void Fighter_Strafe();
void Fighter_Hunt( qboolean visible, qboolean advance );
void NPC_Fighter_Patrol();
void Fighter_RangedAttack( qboolean visible, qboolean advance );
void Fighter_AttackDecision();
qboolean NPC_CheckPlayerTeamStealth();
void NPC_BSFighter_Default();
void Fighter_use( gentity_t *self, gentity_t *other, gentity_t *activator);
void NPC_Fighter_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc );
////////////////////////////////////////////////////////////////////////////////////////
// External Functions
////////////////////////////////////////////////////////////////////////////////////////
extern void        G_SoundAtSpot( vec3_t org, int soundIndex, qboolean broadcast );
extern void        G_CreateG2AttachedWeaponModel( gentity_t *ent, const char *weaponModel, int boltNum, int weaponNum );
extern void        ChangeWeapon( gentity_t *ent, int newWeapon );
extern void        WP_ResistForcePush( gentity_t *self, gentity_t *pusher, qboolean noPenalty );
extern void        ForceJump( gentity_t *self, usercmd_t *ucmd );
extern void        G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
extern void WP_SaberAddG2SaberModels( gentity_t *ent, int specificSaberNum );
// DUSTY ADD
//extern qboolean heavyWeap(int);
//extern qboolean blasterWeap(int);
extern int ChooseBestWeapon(gentity_t* ent);
extern int ChooseWeaponRandom(gentity_t *ent, int wpnGroup);
//extern qboolean lightBlasterWeap(int);
//extern qboolean heavyBlasterWeap(int);
//extern qboolean meleeWeap(int);

// FIGHTER WEAPON CATEGORIES
extern qboolean seekerWeap(int);//seeker missiles WP_ROCKET // WP_BOWCASTER
extern qboolean bombsWeap(int);//REPEATER, POISON, THERMAL, TRIP, DETPACK  
extern qboolean laserWeap(int);//
// BLASTER PISTOL: green laser - short stunner
// BRYAR: red laser
// POISON // chemical weapon
// BLASTER  // green laser big
// JAWA // yellow
// BLOODGUN // Blue laser
extern qboolean chaffWeap(int);// FLECHETTE e DEMP2
extern qboolean specialWeap(int); // CONCUSSION AM ROCKET
// DISGREGATOR
// ATST MAIN
// ATST SIDE
// EMPLACED
////////////////////////////////////////////////////////////////////////////////////////
// Defines
////////////////////////////////////////////////////////////////////////////////////////
#define        FIGHTER_SHOOTRANGEMIN            1024 // Bombs and contromisure
#define        FIGHTER_SHOOTRANGEMED            2048 // blasters
#define        FIGHTER_SHOOTRANGEMAX            8192 // max seeker and antimatter distance atk
#define        FIGHTER_SPECIALRANGE            16384 // special weapon ranged used for cruisers class
#define FIGHTER_FORWARD_BASE_SPEED    75
#define FIGHTER_FORWARD_MULTIPLIER    5
#define FIGHTER_VELOCITY_DECAY    0.85f
#define FIGHTER_STRAFE_VEL        512
#define FIGHTER_STRAFE_DIS        512
#define FIGHTER_UPWARD_PUSH        32
#define FIGHTER_HOVER_HEIGHT        24
extern gitem_t    *FindItemForAmmo( ammo_t ammo );
#define FIGHTER_MIN_DISTANCE        1024
#define FIGHTER_MIN_DISTANCE_SQR    ( MIN_DISTANCE * MIN_DISTANCE )
//Local state enums
enum
{
    FSTATE_NONE = 0,
    FSTATE_ASLEEP,
    FSTATE_WAKEUP,
    FSTATE_ACTIVE,
    FSTATE_POWERING_UP,
    FSTATE_ATTACKING,
};



////////////////////////////////////////////////////////////////////////////////////////
// Global Data
////////////////////////////////////////////////////////////////////////////////////////
bool    FighterHadDeathScript = false;
bool    FighterActive = false;
vec3_t    AverageEnemyDirection3;
int        AverageEnemyDirectionSamples3;



////////////////////////////////////////////////////////////////////////////////////////
// NPC NEW AGILITY SYSTEM FOR AVOID ATTACKS!
////////////////////////////////////////////////////////////////////////////////////////

enum    FighterTacticsState
{
    FIGHTER_NONE,

    // Attack
    //--------
    FIGHTER_CHAFF,    // chaff and flare attack for enemy close
    FIGHTER_BOMBER,        // bombardment mode against strategics targets  
    FIGHTER_LASER,     // laser standard atk   
    FIGHTER_SEEKER,    // seeker ranged atk  
    FIGHTER_SPECIAL,    // special and super weapons atk by lond distance (cruisers, \ star destroyers )
    /// Waiting
    //---------
    FIGHTER_AMBUSHWAIT,        // Goto CP & Wait
    FIGHTER_MAX
};

void Fighter_Precache( void )
{
    G_SoundIndex( "sound/chars/fighter/departure.wav" );
    G_SoundIndex( "sound/chars/fighter/engine_lp.wav" );
    G_SoundIndex( "sound/chars/fighter/landing.wav" );
    G_EffectIndex( "volumetric/black_smoke" );
    G_EffectIndex( "chunks/dustFall" );
    G_SoundIndex( "sound/chars/fighter/misc/fighter_explo" );
    G_SoundIndex( "sound/chars/fighter/misc/fighter_pain" );
    G_SoundIndex( "sound/chars/fighter/misc/fighter_shield_open" );
    G_SoundIndex( "sound/chars/fighter/misc/fighter_shield_close" );
    G_SoundIndex( "sound/chars/fighter/misc/fighter_hover_1_lp" );
    G_SoundIndex( "sound/chars/fighter/misc/fighter_hover_2_lp" );
    G_SoundIndex( "sound/chars/fighter/misc/fighter_explo" );
    G_SoundIndex( "sound/chars/fighter/misc/fighter_pain" );
    for ( int i = 1; i < 4; i++)
    {
        G_SoundIndex( va( "sound/chars/fighter/misc/talk%d", i ) );
    }

    AverageEnemyDirectionSamples3 = 0;
    VectorClear(AverageEnemyDirection3);
    FighterHadDeathScript            = false;
    FighterActive                    = true;
}

////////////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////////////
void    Fighter_DustFallNear(const vec3_t origin, int dustcount)
{
    if (!FighterActive)
    {
        return;
    }

    trace_t        testTrace;
    vec3_t        testDirection;
    vec3_t        testStartPos;
    vec3_t        testEndPos;

    VectorCopy(origin, testStartPos);
    for (int i=0; i<dustcount; i++)
    {
        testDirection[0] = (random() * 2.0f) - 1.0f;
        testDirection[1] = (random() * 2.0f) - 1.0f;
        testDirection[2] = 1.0f;

        VectorMA(origin, 1000.0f, testDirection, testEndPos);
        gi.trace (&testTrace, origin, NULL, NULL, testEndPos, (player && player->inuse)?(0):(ENTITYNUM_NONE), MASK_SHOT, (EG2_Collision)0, 0 );

        if (!testTrace.startsolid &&
            !testTrace.allsolid &&
            testTrace.fraction>0.1f &&
            testTrace.fraction<0.9f)
        {
            G_PlayEffect( "chunks/dustFall", testTrace.endpos, testTrace.plane.normal );
        }
    }
}


qboolean Fighter_StopKnockdown( gentity_t *self, gentity_t *pusher, const vec3_t pushDir, qboolean forceKnockdown )
{
    if ( self->client->NPC_class == CLASS_BOBAFETT )// for others, not for boba.
    {
        return qfalse;
    }

    if ( self->client->moveType == MT_FLYSWIM )
    {//can't knock me down when I'm flying
        return qtrue;
    }

    vec3_t    pDir, fwd, right, ang = {0, self->currentAngles[YAW], 0};
    float    fDot, rDot;
    int        strafeTime = Q_irand( 1000, 2000 );

    AngleVectors( ang, fwd, right, NULL );
    VectorNormalize2( pushDir, pDir );
    fDot = DotProduct( pDir, fwd );
    rDot = DotProduct( pDir, right );

    if ( Q_irand( 0, 2 ) )
    {//flip or roll with it
        usercmd_t    tempCmd;
        if ( fDot >= 0.4f )
        {
            tempCmd.forwardmove = 127;
            TIMER_Set( self, "moveforward", strafeTime );
        }
        else if ( fDot <= -0.4f )
        {
            tempCmd.forwardmove = -127;
            TIMER_Set( self, "moveback", strafeTime );
        }
        else if ( rDot > 0 )
        {
            tempCmd.rightmove = 127;
            TIMER_Set( self, "strafeRight", strafeTime );
            TIMER_Set( self, "strafeLeft", -1 );
        }
        else
        {
            tempCmd.rightmove = -127;
            TIMER_Set( self, "strafeLeft", strafeTime );
            TIMER_Set( self, "strafeRight", -1 );
        }
        G_AddEvent( self, EV_JUMP, 0 );
        if ( !Q_irand( 0, 1 ) )
        {//flip
            self->client->ps.forceJumpCharge = 280;//FIXME: calc this intelligently?
            //ForceJump( self, &tempCmd );
        }
        else
        {//roll
            TIMER_Set( self, "duck", strafeTime );
        }
        self->painDebounceTime = 0;//so we do something
    }
    else
    {//fall down
        return qfalse;
    }

    return qtrue;
}

qboolean Fighter_Flying( gentity_t *self )
{
    assert(self && self->client && self->client->NPC_class==CLASS_FIGHTER );//self->NPC &&
    return ((qboolean)(self->client->moveType==MT_FLYSWIM));
// ONLY FOR FLYING CLASSES
}

////////////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////////////
bool    Fighter_CanSeeEnemy( gentity_t *self )
{
    assert(self && self->NPC && self->client && self->client->NPC_class==CLASS_FIGHTER );
     return ((level.time - self->NPC->enemyLastSeenTime)<1000);
}

////////////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////////////
void    Fighter_Pain( gentity_t *self, gentity_t *inflictor, int damage, int mod)
{
    if ( mod==MOD_SABER || mod !=MOD_SABER /*!(NPCInfo->aiFlags&NPCAI_FLAMETHROW)*/)
    {
        TIMER_Set( self, "NPC_TacticsSelect", 0);    // Hurt By The Saber, Time To Try Something New
    }
    
}

////////////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////////////
void Fighter_FlyStart( gentity_t *self )
{//switch to seeker AI for a while
         
    
    if ( TIMER_Done( self, "jetRecharge" )
        && !Fighter_Flying( self ) )
    {
        self->client->ps.gravity = 0;
        self->svFlags |= SVF_CUSTOM_GRAVITY;
        self->client->moveType = MT_FLYSWIM;
        //start jet effect
        self->client->jetPackTime = level.time + Q_irand( 3000, 10000 );
            //take-off sound
        G_SoundOnEnt( self, CHAN_ITEM, "sound/chars/fighter/departure.wav" );
        //boost loop sound
        self->s.loopSound = G_SoundIndex( "sound/chars/fighter/engine_lp.wav" );
    }
}

////////////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////////////
void Fighter_FlyStop( gentity_t *self )
{
    self->client->ps.gravity = g_gravity->value;
    self->svFlags &= ~SVF_CUSTOM_GRAVITY;
    self->client->moveType = MT_FLYSWIM;
    //Stop effect
    self->client->jetPackTime = 0;
    //stop jet loop sound
    G_SoundOnEnt( self, CHAN_ITEM, "sound/chars/fighter/landing.wav" );
    self->s.loopSound = 0;
    if ( self->NPC )
    {
        self->count = 0; // SEEKER shot ammo count
        TIMER_Set( self, "jetRecharge", Q_irand( 1000, 5000 ) );
        TIMER_Set( self, "jumpChaseDebounce", Q_irand( 500, 2000 ) );
    }
}

////////////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////////////
void        Fighter_DoAmbushWait( gentity_t *self)
{
  Fighter_TacticsSelect();
}

////////////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
// Call This function to make Boba actually shoot his current weapon
////////////////////////////////////////////////////////////////////////////////////////
void    Fighter_Fire()
{
    int            bolt;

    WeaponThink(qtrue);
// Which muzzle to fire from?
    int which = NPCInfo->burstCount % 3;
    switch( which )
    {
    case 0:
        bolt = NPC->handLBolt;
        break;
    case 1:
        bolt = NPC->handRBolt;
        break;
    case 2:
    default:
        bolt = NPC->genericBolt3;
    }
    // If Actually Fired, Decide To Apply Alt Fire And Calc Next Attack Delay
    //------------------------------------------------------------------------
    if (ucmd.buttons&BUTTON_ATTACK)
    {
        // DUSTY ADD - THIS WORK WITHOUT TROUBLE!

        if (seekerWeap(NPC->s.weapon))
        {
        if (NPC->s.weapon == WP_ROCKET_LAUNCHER|| NPC->s.weapon == WP_BOWCASTER )
                TIMER_Set(NPC, "nextAttackDelay", Q_irand(1000, 2000));

            // Half of your rocket and bowcasters are Homing\Grapple Missile
            //-------------------------------------
            if (!Q_irand(0, 1))
            {
                ucmd.buttons &= ~BUTTON_ATTACK;
                ucmd.buttons |= BUTTON_ALT_ATTACK;
                if (NPC->s.weapon == WP_ROCKET_LAUNCHER)
                    {
                    NPC->client->fireDelay = Q_irand(1000, 3000);
                    }
                else if (NPC->s.weapon == WP_BOWCASTER )
                    {
                    NPC->client->fireDelay = Q_irand(750, 2000);
                    }
            }
                        
        }
        else if (chaffWeap(NPC->s.weapon))
        {
        if (NPC->s.weapon == WP_FLECHETTE|| NPC->s.weapon == WP_DEMP2 )
                TIMER_Set(NPC, "nextAttackDelay", Q_irand(1000, 2000));

            // Half of your rocket and bowcasters are Homing\Grapple Missile
            //-------------------------------------
            if (!Q_irand(0, 5))
            {
                ucmd.buttons &= ~BUTTON_ATTACK;
                ucmd.buttons |= BUTTON_ALT_ATTACK;
                if (NPC->s.weapon == WP_FLECHETTE)
                    {
                    NPC->client->fireDelay = Q_irand(1000, 3000);
                    }
                else if (NPC->s.weapon == WP_DEMP2 )
                    {
                    NPC->client->fireDelay = Q_irand(1000, 1500);
                    }
            }
                        
        }
        else if (laserWeap(NPC->s.weapon))
        {
        if (NPC->s.weapon == WP_BLASTER|| NPC->s.weapon == WP_BRYAR_PISTOL ||
            NPC->s.weapon == WP_POISON || NPC->s.weapon == WP_JAWA ||
            NPC->s.weapon == WP_BLOODGUN     )
                TIMER_Set(NPC, "nextAttackDelay", Q_irand(500, 1000));

            // Laser weapons
            //-------------------------------------
            if (!Q_irand(0, 6))
            {
                ucmd.buttons &= ~BUTTON_ATTACK;
                ucmd.buttons |= BUTTON_ALT_ATTACK;
                if (NPC->s.weapon == WP_BLASTER_PISTOL ||
                    NPC->s.weapon == WP_BRYAR_PISTOL ) // allied and enemy stunner lasers
                    {
                        NPC->client->fireDelay = Q_irand(250, 500);
                    }
                else if (NPC->s.weapon == WP_BLASTER || WP_JAWA )// medium lasers
                    {
                        NPC->client->fireDelay = Q_irand(300, 750);
                    }
                else if (NPC->s.weapon == WP_POISON || WP_BLOODGUN ) // strong lasers
                    {
                        NPC->client->fireDelay = Q_irand(1000, 1500);
                    }

            }
                        
        }
        else if (bombsWeap(NPC->s.weapon))
        {
        if (NPC->s.weapon == WP_REPEATER|| NPC->s.weapon == WP_THERMAL ||
            NPC->s.weapon == WP_POISON )
                TIMER_Set(NPC, "nextAttackDelay", Q_irand(1000, 1500));

            // These weapons are shooted ever with half fire. Ever!
            //-------------------------------------
            NPCInfo->scriptFlags |= SCF_ALT_FIRE;
        
        }        
        else if (specialWeap(NPC->s.weapon))
        {
        if (NPC->s.weapon == WP_CONCUSSION|| NPC->s.weapon == WP_DISRUPTOR ||
            NPC->s.weapon == WP_ATST_MAIN || NPC->s.weapon == WP_ATST_SIDE ||
            NPC->s.weapon == WP_EMPLACED_GUN     )
                TIMER_Set(NPC, "nextAttackDelay", Q_irand(1500, 2000));

            // Special destructive massival weapons
            //-------------------------------------
            if (!Q_irand(0, 5))// very rare alt fire
            {
                ucmd.buttons &= ~BUTTON_ATTACK;
                ucmd.buttons |= BUTTON_ALT_ATTACK;
                if (NPC->s.weapon == WP_CONCUSSION ) // allied and enemy stunner lasers
                    {
                        NPC->client->fireDelay = Q_irand(1000, 5000);
                    }
                else if (NPC->s.weapon == WP_DISRUPTOR )// medium lasers
                    {
                        NPC->client->fireDelay = Q_irand(1000, 2000);
                    }
                else if (NPC->s.weapon == WP_ATST_MAIN || WP_ATST_SIDE ) // strong lasers
                    {
                        NPC->client->fireDelay = Q_irand(1000, 2000);
                    }
                else if (NPC->s.weapon == WP_EMPLACED_GUN ) // strong lasers
                    {
                        NPC->client->fireDelay = Q_irand(750, 850);
                    }

            }
                        
        }
        
    }
}


////////////////////////////////////////////////////////////////////////////////////////
// Call this function to see if Fett should fire his current weapon
////////////////////////////////////////////////////////////////////////////////////////
//helper function
qboolean isFighterClass(int className)
{
    if (NPC->client->NPC_class == CLASS_FIGHTER )// Add other special classes )
        return qtrue;

    return qfalse;
}

void Fighter_FireDecide( void )
{
    // Any Reason Not To Shoot?
    //--------------------------
    if (!NPC ||                                            // Only NPCs
        !NPC->client ||                                    // Only Clients
         //NPC->client->NPC_class!=CLASS_BOBAFETT ||        // Only Boba
        !isFighterClass(NPC->client->NPC_class) ||            // Only Boba
        !NPC->enemy ||                                    // Only If There Is An Enemy
        NPC->s.weapon == WP_NONE ||                // Only If Using A Valid Weapon
        !TIMER_Done(NPC, "nextAttackDelay") ||            // Only If Ready To Shoot Again
        !Fighter_CanSeeEnemy(NPC)        // Only If Enemy Recently Seen
        )
    {
        return;
    }

    // Now Check Weapon Specific Parameters To See If We Should Shoot Or Not
    //-----------------------------------------------------------------------
    // GOOD CODE
    // DUSTY VARIANTE
    if (laserWeap(NPC->s.weapon) || bombsWeap(NPC->s.weapon) ||  
        chaffWeap(NPC->s.weapon) || seekerWeap(NPC->s.weapon)||
        specialWeap(NPC->s.weapon))
    {
    
    // WHEN FIRE?
    if (Distance(NPC->currentOrigin, NPC->enemy->currentOrigin) > 400.0f)
        {
            Fighter_Fire();
        }
        else
        {
            Fighter_Fire();
        }
    }
    else
    {
        Fighter_Fire();
    }
}

////////////////////////////////////////////////////////////////////////////////////////
// Tactics avaliable to Boba Fett:
// --------------------------------
//    FIGHTER_LASER,            // Uses laser weapons and close \ medium combat
//    FIGHTER_BOMBER,        // Close / medium distance, bombardment.
//    FIGHTER_CHAFF         // Defensive action for close combat
//  FIGHTER_SEEKER,     // medium \ long range seeker and rockets
//  FIGHTER_SPECIAL,    // special weapons for cruisers, turbolasers, turrets etc  
//    FIGHTER_AMBUSHWAIT,    // Goto CP & Wait
//
extern void WP_DeactivateSaber( gentity_t *self, qboolean clearLength );
////////////////////////////////////////////////////////////////////////////////////////
void    Fighter_TacticsSelect()
{
    // Don't Change Tactics For A Little While
    //------------------------------------------
    TIMER_Set(NPC, "NPC_TacticsSelect", Q_irand(8000, 15000));
    int        nextState = NPCInfo->localState;


    // Get Some Data That Will Help With The Selection Of The Next Tactic
    //--------------------------------------------------------------------
    bool    enemyAlive3            = (NPC->enemy->health>0);
    float    fighterDistance        = Distance(NPC->currentOrigin, NPC->enemy->currentOrigin);
    bool    enemyChaffRange    = (fighterDistance<FIGHTER_SHOOTRANGEMIN);
    bool    enemyBombRange    = (fighterDistance>FIGHTER_SHOOTRANGEMIN && fighterDistance<FIGHTER_SHOOTRANGEMED);
    bool    enemyLaserRange    = (fighterDistance>FIGHTER_SHOOTRANGEMED && fighterDistance<FIGHTER_SHOOTRANGEMAX);
    bool    enemySeekRange    = (fighterDistance>FIGHTER_SHOOTRANGEMAX && fighterDistance<FIGHTER_SPECIALRANGE);
    bool    enemySpecialRange = (fighterDistance>FIGHTER_SPECIALRANGE);

    bool    enemyRecentlySeen3    = Fighter_CanSeeEnemy(NPC);
        
    if (!enemyAlive3)
    {
        nextState = FIGHTER_LASER;
    }
    else if (enemyChaffRange)// Enemy is pretty near, so use Saber!  
    {
        if ( HaveWeapon(NPC, WEAPS_CHAFF ))// If Enemy is near, and NPC have a sword in his inventory, using that sword.
        {
            nextState = FIGHTER_CHAFF;
        }
        else if ( HaveWeapon(NPC, WEAPS_LASER ))// If Enemy is near, and NPC have a sword in his inventory, using that sword.
        {
            nextState = FIGHTER_LASER;
        }
    }
    
    else if (enemyBombRange && !enemyChaffRange )// Enemy is distance enought to bombardment. on alternative, you can use lasers.
    {
        
        if ( HaveWeapon(NPC, WEAPS_BOMBS ))// Enemy is distance, you are a wizard? shoot with magic
        {
            nextState = FIGHTER_BOMBER;
        }
        else if ( HaveWeapon(NPC, WEAPS_LASER ))// Enemy is distance? You are an Hunter? use glyphic weapons
        {
            nextState = FIGHTER_LASER;
        }
    }    
    else if (!enemyBombRange && !enemyChaffRange && enemyLaserRange)// Enemy is distance so use shoot atk
    {
        
        if ( HaveWeapon(NPC, WEAPS_LASER ))// Enemy is distance, you are a wizard? shoot with magic
        {
            nextState = FIGHTER_LASER;
        }
        else if ( HaveWeapon(NPC, WEAPS_SEEKER ))// Enemy is distance? You are an Hunter? use glyphic weapons
        {
            nextState = FIGHTER_SEEKER;
        }
    }
    else if (!enemyBombRange && !enemyChaffRange && !enemyLaserRange && enemySeekRange)// Enemy is distance so use shoot atk
    {
        
        if ( HaveWeapon(NPC, WEAPS_LASER ))// Enemy is distance, you are a wizard? shoot with magic
        {
            nextState = FIGHTER_LASER;
        }
        else if ( HaveWeapon(NPC, WEAPS_SEEKER ))// Enemy is distance? You are an Hunter? use glyphic weapons
        {
            nextState = FIGHTER_SEEKER;
        }
    }    
    else if (!enemyBombRange && !enemyChaffRange && !enemyLaserRange && !enemySeekRange && enemySpecialRange) // Enemy is pretty far, try distance atk
    {
        // If It's Been Long Enough Since Our Last Flame Blast, Try To Torch The Enemy
        //-----------------------------------------------------------------------------
        //if (TIMER_Done(NPC, "nextFlameDelay"))
        // DUSTY ADD
        if ( HaveWeapon(NPC, WEAPS_BLASTER ))// Enemy is distance, you are a wizard? shoot with magic
        {
            nextState = FIGHTER_LASER;
        }
        else if ( HaveWeapon(NPC, WEAPS_SPECIAL ))// Enemy is distance? You are an Hunter? use glyphic weapons
        {
            nextState = FIGHTER_SPECIAL;
        }
        else if ( HaveWeapon(NPC, WEAPS_SEEKER ))// You are an archer? use arrows!  
        {
            nextState = FIGHTER_SEEKER;
        }
    }


    // Recently Saw The Enemy, Time For Some Good Ole Fighten!
    //---------------------------------------------------------
    /*else if (enemyRecentlySeen3)
    {
        // At First, Boba will prefer to use his blaster against the player, but
        //  the more times he is driven away (NPC->count), he will be less likely to
        //  choose the blaster, and more likely to go for the missile launcher
        if (!enemyChaffRange && NPC->client->ps.weapon != WP_FLECHETTE )
        {
          nextState = FIGHTER_LASER;
        }
        nextState = (!enemyChaffRange || Q_irand(0, NPC->count)<1)?(FIGHTER_BOMBER):(FIGHTER_LASER);  
        //nextState = (!enemyInShootRange || Q_irand(0, NPC->count)<1)?(NPC_MAGIC):(NPC_FENCER);


    }*/

    
    NPCInfo->localState = nextState;
    int weapon = 0;

    switch (NPCInfo->localState)
     {
            
    case FIGHTER_LASER:    
        weapon = ChooseWeaponRandom(NPC, WEAPS_LASER);
        if (weapon)
        {
             //NPC_Printf("NEW TACTIC: Rifle");
            NPC_ChangeWeapon(WEAPS_LASER);
            G_RemoveWeaponModels( NPC );
             break;
        }
    case FIGHTER_CHAFF:
        weapon = ChooseWeaponRandom(NPC, WEAPS_CHAFF);
        if (weapon)
        {
             //NPC_Printf("NEW TACTIC: Rocket Launcher");
            NPC_ChangeWeapon(WEAPS_CHAFF);
            G_RemoveWeaponModels( NPC );
             break;
        }
        
    case FIGHTER_BOMBER: //kinda stuck at this point if doesn't have flamethrower
        //NPC_Printf("NEW TACTIC: Flame Thrower");
        weapon = ChooseWeaponRandom(NPC, WEAPS_BOMBS);
        if (weapon)
        {
             //NPC_Printf("NEW TACTIC: Rocket Launcher");
            NPC_ChangeWeapon(WEAPS_BOMBS);
            G_RemoveWeaponModels( NPC );
             break;
        }
    case FIGHTER_SPECIAL: //kinda stuck at this point if doesn't have flamethrower
        //NPC_Printf("NEW TACTIC: Flame Thrower");
        weapon = ChooseWeaponRandom(NPC, WEAPS_SPECIAL);
        if (weapon)
        {
             //NPC_Printf("NEW TACTIC: Rocket Launcher");
            NPC_ChangeWeapon(WEAPS_SPECIAL);
            G_RemoveWeaponModels( NPC );
             break;
        }
    case FIGHTER_SEEKER: //kinda stuck at this point if doesn't have flamethrower
        //NPC_Printf("NEW TACTIC: Flame Thrower");
        weapon = ChooseWeaponRandom(NPC, WEAPS_SEEKER);
        if (weapon)
        {
             //NPC_Printf("NEW TACTIC: Rocket Launcher");
            NPC_ChangeWeapon(WEAPS_SEEKER);
            G_RemoveWeaponModels( NPC );
             break;
        }
        
    case FIGHTER_AMBUSHWAIT:
        //NPC_Printf("NEW TACTIC: Ambush");
        weapon = ChooseWeaponRandom(NPC, WEAPS_ALL);
        if (weapon)
        {
             //NPC_Printf("NEW TACTIC: Rocket Launcher");
            NPC_ChangeWeapon(weapon);
            G_RemoveWeaponModels( NPC );
             break;
        }
    }    
 }

////////////////////////////////////////////////////////////////////////////////////////
// Tactics
//
// This function is called right after Update()
// If returns true, Jedi and Seeker AI not used for movement
//
////////////////////////////////////////////////////////////////////////////////////////
bool    Fighter_Tactics()
{
    if (!NPC->enemy)
    {
        return false;
    }

    // Think About Changing Tactics
    //------------------------------
    if (TIMER_Done(NPC, "NPC_TacticsSelect"))
    {
        Fighter_TacticsSelect();
    }

    // These Tactics Require Seeker & Jedi Movement
    //----------------------------------------------
    if (!NPCInfo->localState ||
         NPCInfo->localState==FIGHTER_LASER ||
         NPCInfo->localState==FIGHTER_CHAFF ||
         NPCInfo->localState==FIGHTER_BOMBER||
         NPCInfo->localState==FIGHTER_SEEKER ||
         NPCInfo->localState==FIGHTER_SPECIAL )
    {
        return false;
    }

    // Flame Thrower - Locked In Place
    //---------------------------------
    if (NPCInfo->localState==FIGHTER_BOMBER ||
        NPCInfo->localState==FIGHTER_CHAFF ||
         NPCInfo->localState==FIGHTER_LASER ||
         NPCInfo->localState==FIGHTER_SEEKER ||
         NPCInfo->localState==FIGHTER_SPECIAL )
    {
        Fighter_TacticsSelect();
    }

    // Ambush Wait
    //------------
    else if (NPCInfo->localState==FIGHTER_AMBUSHWAIT)
    {
        Fighter_DoAmbushWait( NPC );
    }


    NPC_FacePosition( NPC->enemy->currentOrigin, qtrue);
    // chase enemy
    NPC_UpdateAngles(qtrue, qtrue);

    return true;            // Do Not Use Normal Jedi Or Seeker Movement
}


////////////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////////////
void    Fighter_Update()
{
    
    // Hey, That Tests The Trace All The Time
    //----------------------------------------------------
    if (NPC->enemy)
    {
        // Never Forget The Player... Never. NEVER!!!
        NPC->svFlags                |= SVF_LOCKEDENEMY;    // Don't forget about the enemy once you've found him
        if (!(NPC->svFlags&SVF_NOCLIENT))
        {
            trace_t        testTrace;
            vec3_t        eyes;
            CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes );
            gi.trace (&testTrace, eyes, NULL, NULL, NPC->enemy->currentOrigin, NPC->s.number, MASK_SHOT, (EG2_Collision)0, 0);

            bool    wasSeen = Fighter_CanSeeEnemy(NPC);

            if (!testTrace.startsolid &&
                !testTrace.allsolid &&
                testTrace.entityNum == NPC->enemy->s.number)
            {
                NPCInfo->enemyLastSeenTime    = level.time;
                NPCInfo->enemyLastHeardTime    = level.time;
                VectorCopy(NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation);
                VectorCopy(NPC->enemy->currentOrigin, NPCInfo->enemyLastHeardLocation);
            }
            else if (gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin))
            {
                NPCInfo->enemyLastHeardTime    = level.time;
                VectorCopy(NPC->enemy->currentOrigin, NPCInfo->enemyLastHeardLocation);
            }
        }
    }


    // Make Sure He Always Appears In The Last Area With Full Health When His Death Script Is Turned On
    //--------------------------------------------------------------------------------------------------
    if (!FighterHadDeathScript && NPC->behaviorSet[BSET_DEATH]!=0)
    {
        if (!gi.inPVS(NPC->enemy->currentOrigin, NPC->currentOrigin))
        {
            //NPC_Printf("Attempting Final Battle Spawn...");
            /*if (Fighter_Respawn())
            {
                FighterHadDeathScript = true;
            }
            else
            {
                //NPC_Printf("Failed");
            }*/
        }
    }
    // Occasionally A Jump Turns Into A Rocket Fly
    //---------------------------------------------
    if ( NPC->client->ps.groundEntityNum == ENTITYNUM_NONE
        && NPC->client->ps.forceJumpZStart && NPC->client->NPC_class == CLASS_FIGHTER
        && !Q_irand( 0, 10 ))
    {//take off
        Fighter_FlyStart( NPC );
    }
    
    // EDIT THIS WITH A FUNCTION FOR TAKE EVER A GOOD DISTANCE BY GROUND
    // CALL FIGHTER MAINTAINE HEIGHT
    // ADD

    // If Hurting, Try To Run Away
    //-----------------------------
    if (!NPCInfo->surrenderTime && (NPC->health<NPC->max_health/10))
    {
        // Find The Closest Flee Point That I Can Get To
        //-----------------------------------------------
        int cp = NPC_FindCombatPoint(NPC->currentOrigin, 0, NPC->currentOrigin, CP_FLEE|CP_HAS_ROUTE|CP_TRYFAR|CP_HORZ_DIST_COLL, 0, -1);
        if (cp!=-1)
        {
            NPC_SetCombatPoint( cp );
            NPC_SetMoveGoal( NPC, level.combatPoints[cp].origin, 8, qtrue, cp );
            if (NPC->count<6)
            {
                   NPCInfo->surrenderTime = level.time + Q_irand(5000, 10000) + 1000*(6-NPC->count);
            }
            else
            {
                  NPCInfo->surrenderTime = level.time + Q_irand(5000, 10000);
            }
        }
    }
}




////////////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////////////
bool    Fighter_Flee()
{
    bool    EnemyRecentlySeen    = ((level.time - NPCInfo->enemyLastSeenTime)<10000);
    bool    ReachedEscapePoint    = (Distance(level.combatPoints[NPCInfo->combatPoint].origin, NPC->currentOrigin)<50.0f);
    bool    HasBeenGoneEnough    = (level.time>NPCInfo->surrenderTime || (level.time - NPCInfo->enemyLastSeenTime)>400000);


    // Is It Time To Come Back For Some More?
    //----------------------------------------
     if (!EnemyRecentlySeen || ReachedEscapePoint)
    {
        NPC->svFlags |= SVF_NOCLIENT;
        if (HasBeenGoneEnough)
        {
            if ((level.time - NPCInfo->enemyLastSeenTime)>400000)
            {
                //NPC_Printf("  Gone Too Long, Attempting Respawn");
            }
        }
          else if (ReachedEscapePoint && (NPCInfo->surrenderTime - level.time)>3000)
        {
             if (TIMER_Done(NPC, "SpookPlayerTimer"))
            {
                vec3_t        testDirection;
                TIMER_Set(NPC, "SpookPlayerTimer", Q_irand(2000, 10000));
                switch(Q_irand(0, 1))
                {
                case 0:
                    //NPC_Printf("SPOOK: Dust");
                    Fighter_DustFallNear(NPC->enemy->currentOrigin, Q_irand(1,2));
                    break;

                case 1:
                    //NPC_Printf("SPOOK: Footsteps");
                      testDirection[0] =  (random() * 0.5f) - 1.0f;
                      testDirection[0] += (testDirection[0]>0.0f)?(0.5f)-0.5f);
                    testDirection[1] = (random() * 0.5f) - 1.0f;
                    testDirection[1] += (testDirection[1]>0.0f)?(0.5f)-0.5f);
                    testDirection[2] = 1.0f;
                      break;
                }
            }

             if (TIMER_Done(NPC, "ResampleEnemyDirection") && NPC->enemy->resultspeed>10.0f)
            {
                TIMER_Set(NPC, "ResampleEnemyDirection", Q_irand(500, 1000));
                AverageEnemyDirectionSamples3 ++;

                vec3_t    moveDir;
                VectorCopy(NPC->enemy->client->ps.velocity, moveDir);
                VectorNormalize(moveDir);

                VectorAdd(AverageEnemyDirection3, moveDir, AverageEnemyDirection3);
            }
        }
    }
    else
    {
        NPCInfo->surrenderTime += 100;
    }

    // Finish The Flame Thrower First...
    // FLAMETHROWER
    //-----------------------------------
    /*if (NPCInfo->aiFlags&NPCAI_FLAMETHROW)
    {
        Fighter_TacticsSelect();
        NPC_FacePosition( NPC->enemy->currentOrigin, qtrue);
        NPC_UpdateAngles(qtrue, qtrue);
        return true;
    }*/

    bool    IsOnAPath = !!NPC_MoveToGoal(qtrue);
    if (!ReachedEscapePoint &&
        NPCInfo->aiFlags&NPCAI_BLOCKED &&
        NPC->client->moveType!=MT_FLYSWIM &&
        ((level.time - NPCInfo->blockedDebounceTime)>1000)
        )
    {
        if (!Fighter_CanSeeEnemy(NPC) && Distance(NPC->currentOrigin, level.combatPoints[NPCInfo->combatPoint].origin)<200)
        {
            //NPC_Printf("BLOCKED: Just Teleporting There");
            G_SetOrigin(NPC, level.combatPoints[NPCInfo->combatPoint].origin);
        }
        else
        {
            //NPC_Printf("BLOCKED: Attempting Jump");

            if (IsOnAPath)
            {
                if (NPC_TryJump(NPCInfo->blockedTargetPosition))
                {
                }
                else
                {
                    //NPC_Printf("  Failed");
                }
            }
            else if (EnemyRecentlySeen)
            {
                if (NPC_TryJump(NPCInfo->enemyLastSeenLocation))
                {
                 }
                else
                {
                    //NPC_Printf("  Failed");
                }
            }
        }
    }


    NPC_UpdateAngles( qtrue, qtrue );
    return true;
}



 
Link to comment

flight code

 

///////////////////////// BY SENTRY CODE FLY FUNCTIONS//////////////////////////

void Fighter_MaintainHeight( void )
{    
    float    dif;

    NPC->s.loopSound = G_SoundIndex( "sound/chars/Fighter/misc/fighter_hover_1_lp" );

    // Update our angles regardless
    NPC_UpdateAngles( qtrue, qtrue );

    // If we have an enemy, we should try to hover at about enemy eye level
    if ( NPC->enemy )
    {
        // Find the height difference
        dif = (NPC->enemy->currentOrigin[2]+NPC->enemy->maxs[2]) - NPC->currentOrigin[2];

        // cap to prevent dramatic height shifts
        if ( fabs( dif ) > 8 )
        {
            if ( fabs( dif ) > FIGHTER_HOVER_HEIGHT )
            {
                dif = ( dif < 0 ? -24 : 24 );
            }

            NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
        }
    }
    else
    {
        gentity_t *goal = NULL;

        if ( NPCInfo->goalEntity )    // Is there a goal?
        {
            goal = NPCInfo->goalEntity;
        }
        else
        {
            goal = NPCInfo->lastGoalEntity;
        }

        if (goal)
        {
            dif = goal->currentOrigin[2] - NPC->currentOrigin[2];

            if ( fabs( dif ) > FIGHTER_HOVER_HEIGHT )
            {
                ucmd.upmove = ( ucmd.upmove < 0 ? -4 : 4 );
            }
            else
            {
                if ( NPC->client->ps.velocity[2] )
                {
                    NPC->client->ps.velocity[2] *= FIGHTER_VELOCITY_DECAY;

                    if ( fabs( NPC->client->ps.velocity[2] ) < 2 )
                    {
                        NPC->client->ps.velocity[2] = 0;
                    }
                }
            }
        }
        // Apply friction to Z
        else if ( NPC->client->ps.velocity[2] )
        {
            NPC->client->ps.velocity[2] *= FIGHTER_VELOCITY_DECAY;

            if ( fabs( NPC->client->ps.velocity[2] ) < 1 )
            {
                NPC->client->ps.velocity[2] = 0;
            }
        }
    }

    // Apply friction
    if ( NPC->client->ps.velocity[0] )
    {
        NPC->client->ps.velocity[0] *= FIGHTER_VELOCITY_DECAY;

        if ( fabs( NPC->client->ps.velocity[0] ) < 1 )
        {
            NPC->client->ps.velocity[0] = 0;
        }
    }

    if ( NPC->client->ps.velocity[1] )
    {
        NPC->client->ps.velocity[1] *= FIGHTER_VELOCITY_DECAY;

        if ( fabs( NPC->client->ps.velocity[1] ) < 1 )
        {
            NPC->client->ps.velocity[1] = 0;
        }
    }

    NPC_FaceEnemy( qtrue );
}

/*
-------------------------
Fighter_Idle
-------------------------
*/
void Fighter_Idle( void )
{
    /*float    distance, speed;
    vec3_t    forward;
    Fighter_MaintainHeight();
    VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, forward );
    distance = VectorNormalize( forward );
    speed = 0;*/
    
    // Is he waking up?
    if (NPCInfo->localState == FSTATE_WAKEUP)
    {
        if (NPC->client->ps.torsoAnimTimer<=0)
        {
            NPCInfo->scriptFlags |= SCF_LOOK_FOR_ENEMIES;
            NPCInfo->burstCount = 0;
        }
    }
    else
    {
        NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
        NPC->flags |= FL_SHIELDED;

        
        NPC_BSIdle();
    }
}

/*
-------------------------
Fighter_Strafe
-------------------------
*/
void Fighter_Strafe( void )
{
    int        dir;
    vec3_t    end, right;
    trace_t    tr;

    AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL );

    // Pick a random strafe direction, then check to see if doing a strafe would be
    //    reasonable valid
    dir = ( rand() & 1 ) ? -1 : 1;
    VectorMA( NPC->currentOrigin, FIGHTER_STRAFE_DIS * dir, right, end );

    gi.trace( &tr, NPC->currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID, (EG2_Collision)0, 0 );

    // Close enough
    if ( tr.fraction > 0.9f )
    {
        VectorMA( NPC->client->ps.velocity, FIGHTER_STRAFE_DIS * dir, right, NPC->client->ps.velocity );

        // Add a slight upward push
        NPC->client->ps.velocity[2] += FIGHTER_UPWARD_PUSH;

        // Set the strafe start time so we can do a controlled roll
        NPC->fx_time = level.time;
        NPCInfo->standTime = level.time + 3000 + random() * 500;
    }
}

/*
-------------------------
Fighter_Hunt
-------------------------
*/

#define            FIGHTER_ATTACK_FWD                0.95f
#define            FIGHTER_ATTACK_SIDE                0.20f
#define            FIGHTER_AIM_SIDE                0.60f
#define            FIGHTER_FUTURE_PRED_DIST        20.0f
#define            FIGHTER_FUTURE_SIDE_DIST        60.0f
#define            FIGHTER_ATTACK_FLANK_SLOWING    1000.0f
#define            FIGHTER_RAM_DIST                150.0f
#define            FIGHTER_MIN_STAY_VIEWABLE_TIME    20000
extern bool    VEH_StartStrafeRam(Vehicle_t *pVeh, bool Right);
void    Fighter_Steer()
{
    if (!NPC->enemy || !NPC->enemy->client)
    {
        return;
    }
// SETUP
//=======
    // Setup Actor Data
    //------------------
    CVec3        ActorPos(NPC->currentOrigin);
    CVec3        ActorAngles(NPC->currentAngles);
                ActorAngles[2]    = 0;
    Vehicle_t*    ActorVeh        = NPCInfo->greetEnt->m_pVehicle;
    bool        ActorInTurbo    = (ActorVeh->m_iTurboTime>level.time);
    float        ActorSpeed        = (ActorVeh)?(VectorLength(ActorVeh->m_pParentEntity->client->ps.velocity)):(NPC->client->ps.speed);


    // If my vehicle is spinning out of control, just hold on, we're going to die!!!!!
    //---------------------------------------------------------------------------------
    if (ActorVeh && (ActorVeh->m_ulFlags & VEH_OUTOFCONTROL))
    {
        if (NPC->client->ps.weapon!=WP_NONE)
        {
            NPC_ChangeWeapon(WP_NONE);
        }
        ucmd.buttons    &=~BUTTON_ATTACK;
        ucmd.buttons    &=~BUTTON_ALT_ATTACK;
        return;
    }

    CVec3        ActorDirection;
                AngleVectors(ActorAngles.v, ActorDirection.v, 0, 0);

    CVec3        ActorFuturePos(ActorPos);
                ActorFuturePos.ScaleAdd(ActorDirection, FIGHTER_FUTURE_PRED_DIST);

    bool        ActorDoTurbo    = false;
    bool        ActorAccelerate    = false;
    bool        ActorAimAtTarget= true;
    float        ActorYawOffset    = 0.0f;


    // Setup Enemy Data
    //------------------
    CVec3        EnemyPos(NPC->enemy->currentOrigin);
    CVec3        EnemyAngles(NPC->enemy->currentAngles);
                EnemyAngles[2]    = 0;
    Vehicle_t*    EnemyVeh        = (NPC->enemy->s.m_iVehicleNum)?(g_entities[NPC->enemy->s.m_iVehicleNum].m_pVehicle):(0);
    bool        EnemyInTurbo    = (EnemyVeh && EnemyVeh->m_iTurboTime>level.time);
    float        EnemySpeed        = (EnemyVeh)?(EnemyVeh->m_pParentEntity->client->ps.speed):(NPC->enemy->resultspeed);
    bool        EnemySlideBreak    = (EnemyVeh && (EnemyVeh->m_ulFlags&VEH_SLIDEBREAKING || EnemyVeh->m_ulFlags&VEH_STRAFERAM));
    bool        EnemyDead        = (NPC->enemy->health<=0);

    bool        ActorFlank        = (NPCInfo->lastAvoidSteerSideDebouncer>level.time && EnemyVeh && EnemySpeed>10.0f);

    CVec3        EnemyDirection;
    CVec3        EnemyRight;
                AngleVectors(EnemyAngles.v, EnemyDirection.v, EnemyRight.v, 0);

    CVec3        EnemyFuturePos(EnemyPos);
                EnemyFuturePos.ScaleAdd(EnemyDirection, FIGHTER_FUTURE_PRED_DIST);

    ESide        EnemySide        = ActorPos.LRTest(EnemyPos, EnemyFuturePos);
    CVec3        EnemyFlankPos(EnemyFuturePos);
                EnemyFlankPos.ScaleAdd(EnemyRight, (EnemySide==Side_Right)?(FIGHTER_FUTURE_SIDE_DIST)-FIGHTER_FUTURE_SIDE_DIST));

    // Debug Draw Enemy Data
    //-----------------------
    if (false)
    {
        CG_DrawEdge(EnemyPos.v,            EnemyFuturePos.v, EDGE_IMPACT_SAFE);
        CG_DrawEdge(EnemyFuturePos.v,    EnemyFlankPos.v, EDGE_IMPACT_SAFE);
    }


    // Setup Move And Aim Directions
    //-------------------------------
    CVec3        MoveDirection((ActorFlank)?(EnemyFlankPos):(EnemyFuturePos));
                MoveDirection    -= ActorPos;
    float        MoveDistance    = MoveDirection.SafeNorm();
    float        MoveAccuracy    = MoveDirection.Dot(ActorDirection);

    CVec3        AimDirection(EnemyPos);
                AimDirection    -= ActorPos;
    float        AimDistance        = AimDirection.SafeNorm();
    float        AimAccuracy        = AimDirection.Dot(ActorDirection);



    if (!ActorFlank && TIMER_Done(NPC, "FlankAttackCheck"))
    {
        TIMER_Set(NPC, "FlankAttackCheck", Q_irand(1000, 3000));
        if (MoveDistance<4000 && Q_irand(0, 1)==0)
        {
            NPCInfo->lastAvoidSteerSideDebouncer    = level.time + Q_irand(8000, 14000);
        }
    }



    // Fly By Sounds
    //---------------
    if ((ActorVeh->m_pVehicleInfo->soundFlyBy || ActorVeh->m_pVehicleInfo->soundFlyBy2) &&
        EnemyVeh &&
        MoveDistance<800 &&
        ActorSpeed>500.0f &&
        TIMER_Done(NPC, "FlybySoundDebouncer")
        )
    {
        if (EnemySpeed<100.0f || (ActorDirection.Dot(EnemyDirection)*(MoveDistance/800.0f))<-0.5f)
        {
            TIMER_Set(NPC, "FlybySoundDebouncer", 2000);
            int soundFlyBy = ActorVeh->m_pVehicleInfo->soundFlyBy;
            if (ActorVeh->m_pVehicleInfo->soundFlyBy2 && (!soundFlyBy || !Q_irand(0,1)))
            {
                soundFlyBy = ActorVeh->m_pVehicleInfo->soundFlyBy2;
            }
            G_Sound(ActorVeh->m_pParentEntity, soundFlyBy);
        }
    }



// FLY PAST BEHAVIOR
//===================
     if (EnemySlideBreak || !TIMER_Done(NPC, "MinHoldDirectionTime"))
    {
        if (TIMER_Done(NPC, "MinHoldDirectionTime"))
        {
            TIMER_Set(NPC, "MinHoldDirectionTime", 500);    // Hold For At Least 500 ms
        }
        ActorAccelerate        = true;                            // Go
        ActorAimAtTarget    = false;                        // Don't Alter Our Aim Direction
        ucmd.buttons        &=~BUTTON_VEH_SPEED;            // Let Normal Vehicle Controls Go
    }


// FLANKING BEHAVIOR
//===================
    else if (ActorFlank)
    {
          ActorAccelerate    = true;
        ActorDoTurbo    = (MoveDistance>2500 || EnemyInTurbo);
        ucmd.buttons    |= BUTTON_VEH_SPEED;            // Tells PMove to use the ps.speed we calculate here, not the one from g_vehicles.c


        // For Flanking, We Calculate The Speed By Hand, Rather Than Using Pure Accelerate / No Accelerate Functionality
        //---------------------------------------------------------------------------------------------------------------
        NPC->client->ps.speed = ActorVeh->m_pVehicleInfo->speedMax * ((ActorInTurbo)?(1.35f):(1.15f));


        // If In Slowing Distance, Scale Down The Speed As We Approach Our Move Target
        //-----------------------------------------------------------------------------
        if (MoveDistance<FIGHTER_ATTACK_FLANK_SLOWING)
        {
            NPC->client->ps.speed *= (MoveDistance/FIGHTER_ATTACK_FLANK_SLOWING);
            NPC->client->ps.speed += EnemySpeed;

            // Match Enemy Speed
            //-------------------
            if (NPC->client->ps.speed<5.0f && EnemySpeed<5.0f)
            {
                NPC->client->ps.speed = EnemySpeed;
            }

            // Extra Slow Down When Out In Front
            //-----------------------------------
             if  (MoveAccuracy<0.0f)
            {
                NPC->client->ps.speed *= (MoveAccuracy + 1.0f);
            }


            MoveDirection    *=        (MoveDistance/FIGHTER_ATTACK_FLANK_SLOWING);
            EnemyDirection    *= 1.0f - (MoveDistance/FIGHTER_ATTACK_FLANK_SLOWING);
            MoveDirection    += EnemyDirection;

            if (TIMER_Done(NPC, "RamCheck"))
            {
                TIMER_Set(NPC, "RamCheck", Q_irand(1000, 3000));
                if (MoveDistance<FIGHTER_RAM_DIST && Q_irand(0, 2)==0)
                {
                    VEH_StartStrafeRam(ActorVeh, (EnemySide==Side_Left));
                }
            }
        }
    }


// NORMAL CHASE BEHAVIOR
//=======================
    else
    {
        if (!EnemyVeh && AimAccuracy>0.99f && MoveDistance<500 && !EnemyDead)
        {
            ActorAccelerate = true;
            ActorDoTurbo    = false;
        }
        else
        {
            ActorAccelerate = ((MoveDistance>500 && EnemySpeed>20.0f) || MoveDistance>1000);
            ActorDoTurbo    = (MoveDistance>3000 && EnemySpeed>20.0f);
        }
        ucmd.buttons    &=~BUTTON_VEH_SPEED;
    }




// APPLY RESULTS
//=======================
    // Decide Turbo
    //--------------
    if (ActorDoTurbo || ActorInTurbo)
    {
        ucmd.buttons |= BUTTON_ALT_ATTACK;
    }
    else
    {
        ucmd.buttons &=~BUTTON_ALT_ATTACK;
    }

    // Decide Acceleration
    //---------------------
    ucmd.forwardmove = (ActorAccelerate)?(127):(0);



    // Decide To Shoot
    //-----------------
    ucmd.buttons    &=~BUTTON_ATTACK;
    ucmd.rightmove    = 0;
     if (AimDistance<2000 && !EnemyDead)
    {
        // If Doing A Ram Attack
        //-----------------------
        if (ActorYawOffset!=0)
        {
            if (NPC->client->ps.weapon!=WP_NONE)
            {
                NPC_ChangeWeapon(WP_NONE);
            }
            ucmd.buttons    &=~BUTTON_ATTACK;
        }
         else if (AimAccuracy>FIGHTER_ATTACK_FWD)
        {
            if (NPC->client->ps.weapon!=WP_NONE)
            {
                NPC_ChangeWeapon(WP_NONE);
            }
            ucmd.buttons    |= BUTTON_ATTACK;
        }
        else if (AimAccuracy<FIGHTER_AIM_SIDE && AimAccuracy>-FIGHTER_AIM_SIDE)
        {
            if (NPC->client->ps.weapon!=WP_BLASTER)
            {
                NPC_ChangeWeapon(WP_BLASTER);
            }

            if (AimAccuracy<FIGHTER_ATTACK_SIDE && AimAccuracy>-FIGHTER_ATTACK_SIDE)
            {
                //if (!TIMER_Done(NPC, "RiderAltAttack"))
                //{
                //    ucmd.buttons |= BUTTON_ALT_ATTACK;
                //}
                //else
                //{
                    ucmd.buttons |= BUTTON_ATTACK;

            /*        if (TIMER_Done(NPC, "RiderAltAttackCheck"))
                    {
                        TIMER_Set(NPC, "RiderAltAttackCheck", Q_irand(1000, 3000));
                        if (Q_irand(0, 2)==0)
                        {
                            TIMER_Set(NPC, "RiderAltAttack", 300);
                        }
                    }*/
                //}
                WeaponThink(true);
            }
            ucmd.rightmove = (EnemySide==Side_Left)?( 127)-127);
        }
        else
        {
            if (NPC->client->ps.weapon!=WP_NONE)
            {
                NPC_ChangeWeapon(WP_NONE);
            }
        }
    }
    else
    {
        if (NPC->client->ps.weapon!=WP_NONE)
        {
            NPC_ChangeWeapon(WP_NONE);
        }
    }


    // Aim At Target
    //---------------
    if (ActorAimAtTarget)
    {
        MoveDirection.VecToAng();
        NPCInfo->desiredPitch    = AngleNormalize360(MoveDirection[PITCH]);
        NPCInfo->desiredYaw        = AngleNormalize360(MoveDirection[YAW] + ActorYawOffset);
    }
    NPC_UpdateAngles(qtrue, qtrue);
}

void Fighter_Hunt( qboolean visible, qboolean advance )
{
    float    distance, speed;
    vec3_t    forward;

    //If we're not supposed to stand still, pursue the player
    if ( NPCInfo->standTime < level.time )
    {
        // Only strafe when we can see the player
        if ( visible )
        {
            Fighter_Strafe();
            return;
        }
    }

    //If we don't want to advance, stop here
    if ( !advance && visible )
        return;

    //Only try and navigate if the player is visible
    if ( visible == qfalse )
    {
        // Move towards our goal
        NPCInfo->goalEntity = NPC->enemy;
        NPCInfo->goalRadius = 12;

        NPC_MoveToGoal(qtrue);
        return;
    }
    else
    {
        VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, forward );
        distance = VectorNormalize( forward );
        // only for fighter classes not for cruisers
        // Acceleration! this allow a fighter to flight like a swoop.
        // friction: this allow a fighter to slow speed when go over the player
        // if a fighter go over the player, it brakes and turn back for purchase.
    //    Fighter_Steer(); CRASHA!

    }
    // If you disable that, fighter not fly and you get the bobbing movement!
    speed = FIGHTER_FORWARD_BASE_SPEED + FIGHTER_FORWARD_MULTIPLIER * g_spskill->integer;
    VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity );
}


/*
-------------------------
NPC_Fighter_Patrol
-------------------------
*/
void NPC_Fighter_Patrol( void )
{
    Fighter_MaintainHeight();

    //If we have somewhere to go, then do that
    if (!NPC->enemy)
    {
        if ( NPC_CheckPlayerTeamStealth() )
        {
            //NPC_AngerSound();
            NPC_UpdateAngles( qtrue, qtrue );
            return;
        }

        if ( UpdateGoal() )
        {
            //start loop sound once we move
            ucmd.buttons |= BUTTON_WALKING;
            NPC_MoveToGoal( qtrue );
        }

        //randomly talk
        if (TIMER_Done(NPC,"patrolNoise"))
        {
            G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/fighter/misc/talk%d", Q_irand(1, 3)) );

            TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
        }
    }

    NPC_UpdateAngles( qtrue, qtrue );
}


/*
-------------------------
Fighter_RangedAttack
-------------------------
*/
void Fighter_RangedAttack( qboolean visible, qboolean advance )
{
    if ( TIMER_Done( NPC, "attackDelay" ) && NPC->attackDebounceTime < level.time && visible )    // Attack?
    {
        if ( NPCInfo->burstCount > 6 )
        {
            if ( !NPC->fly_sound_debounce_time )
            {//delay closing down to give the player an opening
                NPC->fly_sound_debounce_time = level.time + Q_irand( 500, 2000 );
            }
            // YOU WANT TO SHIELD ONLY SOME CLASSES OF FIGHTER OR SPECIAL STARSHIP?
            /*else if ( NPC->fly_sound_debounce_time < level.time )
            {
                NPCInfo->localState = FSTATE_ACTIVE;
                NPC->fly_sound_debounce_time = NPCInfo->burstCount = 0;
                TIMER_Set( NPC, "attackDelay", Q_irand( 2000, 3500) );
                NPC->flags |= FL_SHIELDED;
                NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_FLY_SHIELDED, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
                G_SoundOnEnt( NPC, CHAN_AUTO, "sound/chars/fighter/misc/fighter_shield_close" );
            }*/
        }
        else
        {
            Fighter_Fire();
        }
    }

    if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
    {
        Fighter_Hunt( visible, advance );
    }
}

/*
-------------------------
Fighter_AttackDecision
-------------------------
*/
void Fighter_AttackDecision( void )
{
    // Always keep a good height off the ground
    Fighter_MaintainHeight();

    NPC->s.loopSound = G_SoundIndex( "sound/chars/sentry/fighter/sentry_hover_2_lp" );

    //randomly talk
    if ( TIMER_Done(NPC,"patrolNoise") )
    {
        if (TIMER_Done(NPC,"angerNoise"))
        {
            G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/fighter/misc/talk%d", Q_irand(1, 3)) );

            TIMER_Set( NPC, "patrolNoise", Q_irand( 4000, 10000 ) );
        }
    }

    // He's dead.
    if (NPC->enemy->health<1)
    {
        NPC->enemy = NULL;
        Fighter_Idle();
        return;
    }

    // If we don't have an enemy, just idle
    if ( NPC_CheckEnemyExt() == qfalse )
    {
        Fighter_Idle();
        return;
    }

    // Rate our distance to the target and visibilty
    float        distance    = (int) DistanceHorizontalSquared( NPC->currentOrigin, NPC->enemy->currentOrigin );    
    qboolean    visible        = NPC_ClearLOS( NPC->enemy );
    qboolean    advance        = (qboolean)(distance > FIGHTER_MIN_DISTANCE_SQR);

    // If we cannot see our target, move to see it
    if ( visible == qfalse )
    {
        if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
        {
            Fighter_Hunt( visible, advance );
            return;
        }
    }

    NPC_FaceEnemy( qtrue );

    Fighter_RangedAttack( visible, advance );
}

qboolean NPC_CheckPlayerTeamStealth( void );




void NPC_BSFighter_Default( void )
{
    //Fighter_Update();    
    if ( NPC->targetname )
    {
        //NPC->e_UseFunc = useF_fighter_use;
    }

    if (( NPC->enemy ) && (NPCInfo->localState != LSTATE_WAKEUP))
    {
        // Don't attack if waking up or if no enemy
        Fighter_AttackDecision();
    }
    else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
    {
        NPC_Fighter_Patrol();
    }
    else
    {
        Fighter_Idle();
    }
}



/*
================
Fighter_use
================
*/
void Fighter_use( gentity_t *self, gentity_t *other, gentity_t *activator)
{
    G_ActivateBehavior(self,BSET_USE);

    self->flags &= ~FL_SHIELDED;
    NPC_SetAnim( self, SETANIM_BOTH, BOTH_STAND1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
//    self->NPC->localState = LSTATE_WAKEUP;
    self->NPC->localState = FSTATE_ACTIVE;
}

/*
-------------------------
NPC_Fighter_Pain
-------------------------
*/
void NPC_Fighter_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc )
{        
    NPC_Pain( self, inflictor, other, point, damage, mod );

    if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT )
    {
        self->NPC->burstCount = 0;
        TIMER_Set( self, "attackDelay", Q_irand( 9000, 12000) );
        self->flags |= FL_SHIELDED;
        NPC_SetAnim( self, SETANIM_BOTH, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
        G_SoundOnEnt( self, CHAN_AUTO, "sound/chars/fighter/misc/fighter_pain" );        

        self->NPC->localState = FSTATE_ACTIVE;
    }

    // You got hit, go after the enemy
    if (self->NPC->localState == FSTATE_ASLEEP)
    {
        G_Sound( self, G_SoundIndex("sound/chars/Fighter/misc/shieldsopen.wav"));

        //self->flags &= ~FL_SHIELDED;
        NPC_SetAnim( self, SETANIM_BOTH, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
        self->NPC->localState = FSTATE_WAKEUP;
    }
}
Link to comment

Oh, ywes, i just missed up...

weapons groped into categories for fighter combact tacticts (fighter can switch weapon at secund of circustances and player distance. weapon available are the same that modder get to NPC into ext_data npc file.

on g_weapon.cpp

 

// WEAPONS CATEGORIES FOR STARFIGHTERS!
qboolean seekerWeap(int wp)//seeker missiles  
{
    switch (wp) {
    case WP_ROCKET_LAUNCHER:
    case WP_BOWCASTER:
        return qtrue;
    }

    return qfalse;
}

qboolean bombsWeap(int wp)//elementals
{
    switch (wp) {
    case WP_REPEATER:
    case WP_POISON:
    case WP_DET_PACK:
    case WP_THERMAL:
    case WP_TRIP_MINE:
        return qtrue;
    }

    return qfalse;
}

qboolean laserWeap(int wp)// Arrows
{
    switch (wp) {
    case WP_BLASTER_PISTOL: // green laser - short
    case WP_BRYAR_PISTOL: // red laser
    case WP_POISON: // chemical weapon
    case WP_BLASTER: // greenlaser
    case WP_JAWA: // yellow lasers
    case WP_BLOODGUN: // blue lasers
        return qtrue;
    }

    return qfalse;
}

qboolean chaffWeap(int wp)// Life&Necro
{
    switch (wp) {
    case WP_FLECHETTE:
    case WP_DEMP2:
        return qtrue;
    }

    return qfalse;
}

qboolean specialWeap(int wp) // Melee fight
{
    switch (wp) {
    case WP_CONCUSSION: // am rocket
    case WP_DISRUPTOR:
    case WP_ATST_MAIN: // blue laser
    case WP_ATST_SIDE:
    case WP_EMPLACED_GUN:
        return qtrue;
    }

    return qfalse;
}


// Weapon Helper Functions
float weaponSpeed[WP_NUM_WEAPONS][2] =
{
 

weapons.h

 

typedef enum {
WEAPS_FIGHTER,
WEAPS_LASER,
WEAPS_SEEKER,// Glifiche
WEAPS_BOMBS,// Arrows
WEAPS_CHAFF,// nec Holy
WEAPS_SPECIAL, // Explosive
WEAPS_OTHER2
} weaponGroup2;//for fighter
 

NPC_combat

assign weapon group for fighters to weapons headers.

 

extern qboolean seekerWeap(int wp);
extern qboolean    laserWeap(int wp);
extern qboolean bombsWeap(int wp);
extern qboolean chaffWeap(int wp);
extern qboolean specialWeap(int wp);

qboolean WeaponFighters(int weapon, int wpnGroup, int altFire = 0)
{//this and the funcs it calls do the heavy lifting for weapon grouping
    if ((seekerWeap(weapon) && wpnGroup == WEAPS_SEEKER) //
        || (laserWeap(weapon) && wpnGroup == WEAPS_LASER) //
        || (bombsWeap(weapon) && wpnGroup == WEAPS_BOMBS) //
        || (chaffWeap(weapon) && wpnGroup == WEAPS_CHAFF )//
        || ( specialWeap(weapon) && wpnGroup == WEAPS_SPECIAL )  //
        || wpnGroup == WEAPS_FIGHTER )
    {
            return qtrue;
    }
    return qfalse;
}
Link to comment

Okay, now i upgraded the fighter_steer function. flight is much better. is not acrobatic. but fighter chase enemy very fast and when they are close brake itself and fly around, strafing and shooting, and ever facing the enemy.

like swoop pilots, but with strafing of sentry,. noooot bad.

they need just a lot of acceleration of speed (but is facoltative: if player drive a fighter and is ever running forward, also if fighter reach target, they cannot completely round enemy because enemy is away pretty fast so they are forced to chase it again!

and so, for flight i think this AI is okay. they are static only against static objectives, so i think is okay.

 

just a lot of acceleration for avoid stupid swoopfights in case there are enemy fighter against teammate npc fighters.

 

two things to fix:

- weapons: there is some interference between this function and weapon tactive... fighters shoots rarely when they chase enemy and shoot nice and with a good rate of fire only when they are pretty close. i want more aggression!

- ground distance (they fly too near to ground is player is walking or running or ground -.- ) sometime i see the fighter landing and ... walking??? a tie fighter walking in the grass??? totally madness. need to tell to have more distance by ground and NEVER touch the ground or level geometry.

 

for other things, AI is completed so after i take a looong break... and after i will see if is possible to make something like a drivable fighter like a drivable atst... but honestly i not have much hope in that sense. is pretty complex like code and require multiple file working also for HUD and interface.

Link to comment

mmm a video with fraps? it's long time i not update a video on youtube, but,... okay.

really i need an hand with this fucked code for end the stuff. also, if is possible to contact who does the client fly code for jk2... maybe there is something useful that i use for fix AI and for client fly.

( not worry if in the video not listen all sound correctly because weapon system of fighter is WIP and also special effects of weapons are just placeholders. ) .

Link to comment

i take some snapshot and cutscene but is very laggy.

windows movie maker crash every 2 second so i need to found some other program for mount the clips, convert and host on youtube.

>.<

 

I use Open Broadcaster Software, it's much better than fraps and I can upload the clip directly to youtube. 

Link to comment

I use Open Broadcaster Software, it's much better than fraps and I can upload the clip directly to youtube. 

 

eh, my fraps is shareware too. i not know how to find a full version.

it's free your program?

can run on an old xp sp3?

 

 

tryed,... not run on xp, but on vista with direct x 10. >.<

i will search some other. i need just to mount 3 clipscenes.

Link to comment

uff, painful... i'll never be a youtuber.

disabled the audio for making the video more light as possible .

4 minutes of test. you can see fighter in fly hunting approach and strafing, some shooting (effects of shoot are placeholders for moments ) and a fighter destroyed by player shoots. and two fighters fight each others into the sky.

 

for moment, fly and shoot is the best part. dogfight is horrible. :P

 

 

making you some laughts:

see the video with this music:

 

Link to comment

Not related, but that legacy of kain custom look is looking pretty good!

thanks. the map is full of all soul reaver map objects on MD3 format. is a test map of md3 that i used for fly test because is great enough for flight zone. Kain use energy bolt and some kind of bleeding fire attack.

 

 

Honestly however, i hope in some mod feedback :P

yes i know video is too laggy and fighters not seems to shoot good.

was just for show the stupid flight behavour.

for shooting i have fixed efx only today.

Link to comment

shoot behavour is pretty close to complete.

now on fighter_tactictselect i add the field for flight behavour

 

 

/////// Fighter fly behavours/////////////////////////////////////////////////////////
    float    fighterflightDistance        = Distance(NPC->currentOrigin, NPC->enemy->currentOrigin);
    bool    fighterEvasionRange    = (fighterflightDistance<FIGHTER_SHOOTRANGEMIN); // If enemy is Near, try some evasion shooting chaffs
    bool    fighterRoundRange    = (fighterDistance>FIGHTER_SHOOTRANGEMIN && fighterDistance<FIGHTER_SHOOTRANGEMED); // if enemy is close, fly round him shoot him with laser
    bool    fighterBrakeRange    = (fighterDistance>FIGHTER_SHOOTRANGEMED && fighterDistance<FIGHTER_SHOOTRANGEMAX);// if you are a bomber nd you are sufficiently far from target. stay on target nnd bombarding.



    // Evasion: (only for fighter, not for cruisers or big ships) if enemy is shooted by player flee with evasion manouvre (rolling, strafing)

 

so we have 2 evasion behavour: one with  fighter equiped with chaff weapons, it shoot chaff and making evasion by a close enemy.

the other for fighter generically shooted. they rolled and strafe... if health of fighter is low, they will try to escape.

attack fly manouvre, instead is basically: for bombers tht carry bombs, they stay in front or upon target bombarding it. for fighter with lsers, they fly round target like an wp_rocket altfire homing missile, shooting him...

nd now is the hard task: how can i archive all that stuff????? o.o

ny help suggest? in JKA there are not much coding example about behavours like thats...

rolling is the more easy i think becuse the probe droid tilts they head and interrogator model tilt on itself entire model if you use a humanoid model with interrogator NPC default...

but the others... damn. :S

 

 

(if i have success AI of fighter is finished! )

Link to comment

Today work: :

- try to make a custom HUD drawing like atst but again HUD is not displayed and changed when i do "playermodel tiefighter" so it's again something missed up. but the function for drawing is wrote.

- worked on bg_pmove about movement. i have forced CLASS_FIGHTER to use the FLY_VEHICLE mode modality of fighter flying vehicle and this get some progress.

a playermodeled fighter cannot again leave by ground and fly (i need again to found how to set the command for do that i guess).

weapon switch work properly 50% (there is a glitch when a fighter get as weapon the lightsaber if i use playermodel before switch to a shoot weapon)

- NPC Fighter: much better with FLY_VEHICLE move that with default NPC fly move: they are slow as snails now so i think i need to set better the velocity paramater but they now float and fly properly without no more stupid walking in the ground (yes... fighter walks into the grounds until now if they touch ground :P XDD )

 

So what is missing:

NPC:

- properly velocity fix

- weapon bug fix (some time shoot 23942309423492342 repeater alt concussion together )

- evasion manouvre when hitted \ low health (rolling, flee? )

- better aggressive manouvre (fly round an enemy, strafing? )

 

playable fighter:

- correct hud draw display

- camera fix (insanely attacked to the ass of the fighter :P )

- correct "atst drivable" modality enabled for the class if is on playermodel

- correct fly movements.

- fix toggle weapons

 

after that... we have our space battle game! :D

i am at 50% of code work.

Lancelot and Noodle like this
Link to comment

great progress with client playable fighter!!! i create a specific class for players called OLON and i added on npc_spawn.cpp that a team player olon class get the drivable atst attributes...

 

results:

https://postimg.org/gallery/3g5jqqfg6/

 

now i need just a workaround to fix all bug and glitchs and change the moving by walking to flying.

    else if ( ent->client->NPC_class == CLASS_OLON )
        {// Drivable fighter vehicle
            ent->NPC->defaultBehavior = BS_CINEMATIC; // don't attack! damn it!
            ent->client->ps.gravity = 0;
            ent->svFlags |= SVF_CUSTOM_GRAVITY;
            ent->client->moveType = MT_FLYSWIM;
            ent->s.loopSound = G_SoundIndex( "sound/vehicles/tie-bomber/loop.wav" );
            extern void NPC_ATST_Precache(void);
            extern void NPC_PrecacheAnimationCFG( const char *NPC_type );

            ent->s.modelindex = G_ModelIndex( "models/players/atst/model.glm" );
            ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, "models/players/atst/model.glm", ent->s.modelindex, NULL_HANDLE, NULL_HANDLE, 0, 0 );
            ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "model_root", qtrue );
            ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );    //FIXME: need to somehow set the anim/frame to the equivalent of BOTH_STAND1...  use to be that BOTH_STAND1 was the first frame of the glm, but not anymore
            ent->s.radius = 320;
            VectorSet( ent->s.modelScale, 1.0f, 1.0f, 1.0f );

            //register my weapons, sounds and model
            RegisterItem( FindItemForWeapon( WP_ATST_MAIN ));    //precache the weapon
            RegisterItem( FindItemForWeapon( WP_ATST_SIDE ));    //precache the weapon
            //HACKHACKHACKTEMP - until ATST gets real weapons of it's own?
            RegisterItem( FindItemForWeapon( WP_EMPLACED_GUN ));    //precache the weapon
            //    RegisterItem( FindItemForWeapon( WP_ROCKET_LAUNCHER ));    //precache the weapon
            //    RegisterItem( FindItemForWeapon( WP_BOWCASTER ));    //precache the weapon
            //HACKHACKHACKTEMP - until ATST gets real weapons of it's own?

            G_SoundIndex( "sound/chars/atst/atst_hatch_open" );
            G_SoundIndex( "sound/chars/atst/atst_hatch_close" );

            NPC_ATST_Precache();
            ent->NPC_type = (char *)"atst";
            NPC_PrecacheAnimationCFG( ent->NPC_type );
            //open the hatch
            //misc_atst_setanim( ent, ent->rootBone, BOTH_STAND2 );
            gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "head_hatchcover", 0 );

            VectorSet( ent->mins, ATST_MINS0, ATST_MINS1, ATST_MINS2 );
            VectorSet( ent->maxs, ATST_MAXS0, ATST_MAXS1, ATST_MAXS2 );

            ent->contents = CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;
            ent->flags |= FL_SHIELDED;
            ent->takedamage = qtrue;
            if ( !ent->health )
    {
                ent->health = 800;
            }
            ent->s.radius = 320;

            ent->max_health = ent->health; // cg_draw needs this

            G_SetOrigin( ent, ent->s.origin );
            G_SetAngles( ent, ent->s.angles );
            VectorCopy( ent->currentAngles, ent->s.angles2 );

            gi.linkentity ( ent );

            //FIXME: test the origin to make sure I'm clear?

            ent->e_UseFunc = useF_misc_atst_use;
            ent->svFlags |= SVF_PLAYER_USABLE;

            //make it able to take damage and die when you're not in it...
            //do an explosion and play the death anim, remove use func.
            ent->e_DieFunc = dieF_misc_atst_die;
        }

just a copy paste of SP_misc_atst_drivable function by g_misc.cpp , but it works!

https://s2.postimg.org/weuxlyont/LOL1.jpg

https://s2.postimg.org/e0kehzcd5/LOL2.jpg

https://s2.postimg.org/sxsvizpll/LOL3.jpg

https://s2.postimg.org/gkg1c2zx5/LOL4.jpg

Noodle likes this
Link to comment

great progress with client playable fighter!!! i create a specific class for players called OLON and i added on npc_spawn.cpp that a team player olon class get the drivable atst attributes...

 

results:

https://postimg.org/gallery/3g5jqqfg6/

 

now i need just a workaround to fix all bug and glitchs and change the moving by walking to flying.

    else if ( ent->client->NPC_class == CLASS_OLON )
        {// Drivable fighter vehicle
            ent->NPC->defaultBehavior = BS_CINEMATIC; // don't attack! damn it!
            ent->client->ps.gravity = 0;
            ent->svFlags |= SVF_CUSTOM_GRAVITY;
            ent->client->moveType = MT_FLYSWIM;
            ent->s.loopSound = G_SoundIndex( "sound/vehicles/tie-bomber/loop.wav" );
            extern void NPC_ATST_Precache(void);
            extern void NPC_PrecacheAnimationCFG( const char *NPC_type );

            ent->s.modelindex = G_ModelIndex( "models/players/atst/model.glm" );
            ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, "models/players/atst/model.glm", ent->s.modelindex, NULL_HANDLE, NULL_HANDLE, 0, 0 );
            ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "model_root", qtrue );
            ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );    //FIXME: need to somehow set the anim/frame to the equivalent of BOTH_STAND1...  use to be that BOTH_STAND1 was the first frame of the glm, but not anymore
            ent->s.radius = 320;
            VectorSet( ent->s.modelScale, 1.0f, 1.0f, 1.0f );

            //register my weapons, sounds and model
            RegisterItem( FindItemForWeapon( WP_ATST_MAIN ));    //precache the weapon
            RegisterItem( FindItemForWeapon( WP_ATST_SIDE ));    //precache the weapon
            //HACKHACKHACKTEMP - until ATST gets real weapons of it's own?
            RegisterItem( FindItemForWeapon( WP_EMPLACED_GUN ));    //precache the weapon
            //    RegisterItem( FindItemForWeapon( WP_ROCKET_LAUNCHER ));    //precache the weapon
            //    RegisterItem( FindItemForWeapon( WP_BOWCASTER ));    //precache the weapon
            //HACKHACKHACKTEMP - until ATST gets real weapons of it's own?

            G_SoundIndex( "sound/chars/atst/atst_hatch_open" );
            G_SoundIndex( "sound/chars/atst/atst_hatch_close" );

            NPC_ATST_Precache();
            ent->NPC_type = (char *)"atst";
            NPC_PrecacheAnimationCFG( ent->NPC_type );
            //open the hatch
            //misc_atst_setanim( ent, ent->rootBone, BOTH_STAND2 );
            gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "head_hatchcover", 0 );

            VectorSet( ent->mins, ATST_MINS0, ATST_MINS1, ATST_MINS2 );
            VectorSet( ent->maxs, ATST_MAXS0, ATST_MAXS1, ATST_MAXS2 );

            ent->contents = CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;
            ent->flags |= FL_SHIELDED;
            ent->takedamage = qtrue;
            if ( !ent->health )
    {
                ent->health = 800;
            }
            ent->s.radius = 320;

            ent->max_health = ent->health; // cg_draw needs this

            G_SetOrigin( ent, ent->s.origin );
            G_SetAngles( ent, ent->s.angles );
            VectorCopy( ent->currentAngles, ent->s.angles2 );

            gi.linkentity ( ent );

            //FIXME: test the origin to make sure I'm clear?

            ent->e_UseFunc = useF_misc_atst_use;
            ent->svFlags |= SVF_PLAYER_USABLE;

            //make it able to take damage and die when you're not in it...
            //do an explosion and play the death anim, remove use func.
            ent->e_DieFunc = dieF_misc_atst_die;
        }

just a copy paste of SP_misc_atst_drivable function by g_misc.cpp , but it works!

https://s2.postimg.org/weuxlyont/LOL1.jpg

https://s2.postimg.org/e0kehzcd5/LOL2.jpg

https://s2.postimg.org/sxsvizpll/LOL3.jpg

https://s2.postimg.org/gkg1c2zx5/LOL4.jpg

Should be hard for first person view enabled \ disenable a cockpit but i will think to that when AI and client playable are alls okay! :3

 

otherwise, i found also a way for make that fighters and olons fly around to player... this is niceful! (but i need to do that only for enemy fighters they are really silly in move,ment and orientations but it works. they seems like sharks that swim around their lunch.

well, at this point i get all features... i need just to recode all for fix 3000 thousand of glitches.

Noodle likes this
Link to comment

@@Noodle i see only now you question about first person of the swoop... mmm again not . i will implement the first person stuff after i understand i work Atst drivable HUD stuff and swoop stuff. is not much easy to do a cockpit displaying... but i think that the way where in force sense you can see the crop aura effect when you active the force power can be a good hint about that draw \ hide this.

about cockpit... if someone can do a nice cockpit fighter image tga \ png and a radar icon fighters i can use i will be thankful. i am not much good with paint shop pro\ photoshop stuff. the health bar is okay and is like any other playable vehicle now.

should be wonderful maybre a cockpit like X wing Alliance game? they was pretty cool! :D

Noodle likes this
Link to comment

about cockpit... if someone can do a nice cockpit fighter image tga \ png and a radar icon fighters i can use i will be thankful. i am not much good with paint shop pro\ photoshop stuff. the health bar is okay and is like any other playable vehicle now.

should be wonderful maybre a cockpit like X wing Alliance game? they was pretty cool! :D

 

I'm hoping someone can do a nice interior HUD!

Link to comment

Create an account or sign in to comment

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

Create an account

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

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...