]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/monsters/monster/soldier.qc
Fx soldier uzi & shotgun attacks
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / monsters / monster / soldier.qc
index 7c90d595faf12cb6f9e644f2af2f334c02f029d1..ac5f316127e95de61d0a697c66e18f51d57213a0 100644 (file)
@@ -1,7 +1,14 @@
+#ifndef MENUQC
 // size
 const vector SOLDIER_MIN = '-16 -16 -30';
 const vector SOLDIER_MAX = '16 16 32';
 
+// model
+string SOLDIER_MODEL = "models/monsters/soldier.zym";
+
+#endif
+
+#ifdef SVQC
 // cvars
 float autocvar_g_monster_soldier;
 float autocvar_g_monster_soldier_health;
@@ -14,47 +21,42 @@ float autocvar_g_monster_soldier_weapon_shotgun_chance;
 float autocvar_g_monster_soldier_weapon_machinegun_chance;
 float autocvar_g_monster_soldier_weapon_rocketlauncher_chance;
 float autocvar_g_monster_soldier_attack_uzi_bullets;
+float autocvar_g_monster_soldier_attack_uzi_damage;
+float autocvar_g_monster_soldier_attack_uzi_force;
+float autocvar_g_monster_soldier_attack_shotgun_damage;
+float autocvar_g_monster_soldier_attack_shotgun_force;
+float autocvar_g_monster_soldier_attack_shotgun_spread;
+float autocvar_g_monster_soldier_attack_shotgun_bullets;
 
 // animations
-#define soldier_anim_die1                      0
-#define soldier_anim_die2                      1
-#define soldier_anim_draw                      2
-#define soldier_anim_duck                      3
-#define soldier_anim_duckwalk          4
-#define soldier_anim_duckjump          5
-#define soldier_anim_duckidle          6
-#define soldier_anim_idle                      7
-#define soldier_anim_jump                      8
-#define soldier_anim_pain1                     9
-#define soldier_anim_pain2                     10
-#define soldier_anim_shoot                     11
-#define soldier_anim_taunt                     12
-#define soldier_anim_run                       13
-#define soldier_anim_runbackwards      14
-#define soldier_anim_strafeleft        15
-#define soldier_anim_straferight       16
-#define soldier_anim_dead1                     17
-#define soldier_anim_dead2                     18
-#define soldier_anim_forwardright      19
-#define soldier_anim_forwardleft       20
-#define soldier_anim_backright                 21
-#define soldier_anim_backleft          22
-
-//#define soldier_anim_stand   0
-//#define soldier_anim_death1 1
-//#define soldier_anim_death2 2
-//#define soldier_anim_reload 3
-//#define soldier_anim_pain1   4
-//#define soldier_anim_pain2   5
-//#define soldier_anim_pain3   6
-//#define soldier_anim_run     7
-//#define soldier_anim_shoot   8
-//#define soldier_anim_prowl   9
+const float soldier_anim_die1                  = 0;
+const float soldier_anim_die2                  = 1;
+const float soldier_anim_draw                  = 2;
+const float soldier_anim_duck                  = 3;
+const float soldier_anim_duckwalk              = 4;
+const float soldier_anim_duckjump              = 5;
+const float soldier_anim_duckidle              = 6;
+const float soldier_anim_idle                  = 7;
+const float soldier_anim_jump                  = 8;
+const float soldier_anim_pain1                         = 9;
+const float soldier_anim_pain2                         = 10;
+const float soldier_anim_shoot                         = 11;
+const float soldier_anim_taunt                         = 12;
+const float soldier_anim_run                   = 13;
+const float soldier_anim_runbackwards  = 14;
+const float soldier_anim_strafeleft    = 15;
+const float soldier_anim_straferight   = 16;
+const float soldier_anim_dead1                         = 17;
+const float soldier_anim_dead2                         = 18;
+const float soldier_anim_forwardright  = 19;
+const float soldier_anim_forwardleft   = 20;
+const float soldier_anim_backright             = 21;
+const float soldier_anim_backleft              = 22;
 
 void soldier_think ()
 {
        self.think = soldier_think;
-       self.nextthink = time + 0.1;
+       self.nextthink = time + self.ticrate;
        
        if(self.delay != -1)
                self.nextthink = self.delay;
@@ -67,104 +69,23 @@ void soldier_think ()
 
 void soldier_reload ()
 {
-       self.frame = soldier_anim_draw;
+       self.monster_delayedattack = func_null; // out of ammo, don't keep attacking
+       self.delay = -1;
+       monsters_setframe(soldier_anim_draw);
        self.attack_finished_single = time + 2;
        self.currentammo = autocvar_g_monster_soldier_ammo;
        sound (self, CH_SHOTS, "weapons/reload.wav", VOL_BASE, ATTN_LARGE);
 }
 
-float SoldierCheckAttack ()
-{
-       local vector spot1 = '0 0 0', spot2 = '0 0 0';
-       local entity targ = self.enemy;
-       local float chance = 0;
-
-       if (self.health <= 0 || targ.health < 1 || targ == world)
-               return FALSE;
-
-       if (vlen(targ.origin - self.origin) > 2000) // long traces are slow
-               return FALSE;
-
-       // see if any entities are in the way of the shot
-       spot1 = self.origin + self.view_ofs;
-       spot2 = targ.origin + targ.view_ofs;
-
-       traceline (spot1, spot2, FALSE, self);
-
-       if (trace_ent != targ)
-               return FALSE; // don't have a clear shot
-
-       if (trace_inwater)
-       if (trace_inopen)
-               return FALSE; // sight line crossed contents
-               
-       if(self.monster_delayedattack && self.delay != -1)
-       {
-               if(time < self.delay)
-                       return FALSE;
-                       
-               self.monster_delayedattack();
-       }
-
-       // missile attack
-       if (time < self.attack_finished_single)
-               return FALSE;
-
-       if (enemy_range() >= 2000)
-               return FALSE;
-
-       if (enemy_range() <= 120)
-               chance = 0.9;
-       else if (enemy_range() <= 500)
-               chance = 0.6; // was 0.4
-       else if (enemy_range() <= 1000)
-               chance = 0.3; // was 0.05
-       else
-               chance = 0;
-
-       if (chance > 0)
-       if (chance > random())
-               return FALSE;
-               
-       if(self.currentammo <= 0 && enemy_range() <= 120)
-       {
-               monster_sound(self.msound_attack_melee, 0, FALSE); // no delay for attack sounds
-               self.attack_melee();
-               return TRUE;
-       }
-       
-       if(self.currentammo <= 0)
-       {
-               soldier_reload();
-               return FALSE;
-       }
-
-       if (self.attack_ranged())
-               return TRUE;
-
-       return FALSE;
-}
-
-void soldier_laser ()
-{
-       self.frame = soldier_anim_shoot;
-       self.attack_finished_single = time + 0.8;
-       W_Laser_Attack(0);
-}
-
-float soldier_missile_laser ()
-{
-       // FIXME: check if it would hit
-       soldier_laser();
-       return TRUE;
-}
-
 .float grunt_cycles;
 void soldier_uzi_fire ()
 {
        self.currentammo -= 1;
        if(self.currentammo <= 0)
+       {
+               soldier_reload();
                return;
+       }
                
        self.grunt_cycles += 1;
        
@@ -174,102 +95,85 @@ void soldier_uzi_fire ()
                self.delay = -1;
                return;
        }
-       W_UZI_Attack(DEATH_MONSTER_SOLDIER_NAIL);
-       self.delay = time + 0.1;
-       self.monster_delayedattack = soldier_uzi_fire;
-}
-
-void soldier_uzi ()
-{
-       if(self.currentammo <= 0)
-               return;
-               
-       self.frame = soldier_anim_shoot;
-       self.attack_finished_single = time + 0.8;
+       
+       W_SetupShot (self, autocvar_g_antilag_bullets && 18000 >= autocvar_g_antilag_bullets, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, autocvar_g_monster_soldier_attack_uzi_damage);
+       fireBallisticBullet(w_shotorg, w_shotdir, 0.02, 18000, 5, autocvar_g_monster_soldier_attack_uzi_damage, autocvar_g_monster_soldier_attack_uzi_force, DEATH_MONSTER_MARINE, 0, 1, 115);
+       endFireBallisticBullet();
+       
        self.delay = time + 0.1;
        self.monster_delayedattack = soldier_uzi_fire;
 }
 
-float soldier_missile_uzi ()
+void soldier_shotgun_fire()
 {
-       self.grunt_cycles = 0;
-       // FIXME: check if it would hit
-       soldier_uzi();
-       return TRUE;
+       float sc;
+       W_SetupShot (self, autocvar_g_antilag_bullets && 18000 >= autocvar_g_antilag_bullets, 5, "weapons/shotgun_fire.wav", CH_WEAPON_A, autocvar_g_monster_soldier_attack_shotgun_damage * autocvar_g_monster_soldier_attack_shotgun_bullets);
+       for (sc = 0;sc < autocvar_g_monster_soldier_attack_shotgun_bullets;sc = sc + 1)
+               fireBallisticBullet(w_shotorg, w_shotdir, autocvar_g_monster_soldier_attack_shotgun_spread, 18000, 5, autocvar_g_monster_soldier_attack_shotgun_damage, autocvar_g_monster_soldier_attack_shotgun_force, DEATH_MONSTER_MARINE, 0, 1, 115);
+       endFireBallisticBullet();
 }
 
-void soldier_shotgun ()
+float soldier_attack()
 {
-       self.currentammo -= 1;
+       monsters_setframe(soldier_anim_shoot);
+       makevectors(self.angles);
+       
        if(self.currentammo <= 0)
-               return;
-               
-       self.frame = soldier_anim_shoot;
-       self.attack_finished_single = time + 0.8;
-       W_Shotgun_Attack();
-}
-
-float soldier_missile_shotgun ()
-{
-       // FIXME: check if it would hit
+       {
+               soldier_reload();
+               return FALSE;
+       }
+       
        self.grunt_cycles = 0;
-       soldier_shotgun();
-       return TRUE;
-}
-
-void soldier_rl ()
-{
-       self.currentammo -= 1;
-       if(self.currentammo <= 0)
-               return;
-               
-       self.frame = soldier_anim_shoot;
-       self.attack_finished_single = time + 0.8;
-       W_Rocket_Attack();
-}
-
-float soldier_missile_rl ()
-{
-       // FIXME: check if it would hit
-       soldier_rl();
-       return TRUE;
+       
+       switch(self.weapon)
+       {
+               case WEP_ROCKET_LAUNCHER:
+               {
+                       self.currentammo -= 1;
+                       self.attack_finished_single = time + 0.8;
+                       W_Rocket_Attack();
+                       return TRUE;
+               }
+               case WEP_SHOTGUN:
+               {
+                       self.currentammo -= 1;
+                       self.attack_finished_single = time + 0.8;
+                       soldier_shotgun_fire();
+                       return TRUE;
+               }
+               case WEP_UZI:
+               {
+                       self.attack_finished_single = time + 0.8;
+                       self.delay = time + 0.1;
+                       self.monster_delayedattack = soldier_uzi_fire;
+                       return TRUE;
+               }
+               case WEP_LASER:
+               {
+                       self.attack_finished_single = time + 0.8;
+                       W_Laser_Attack(0);
+                       return TRUE;
+               }
+               default:
+                       return FALSE; // no weapon?
+       }
 }
 
-void soldier_bash ()
+void soldier_melee ()
 {
-       self.frame = soldier_anim_shoot;
+       monsters_setframe(soldier_anim_shoot);
        self.attack_finished_single = time + 0.8;
-       monster_melee(self.enemy, autocvar_g_monster_soldier_melee_damage, 70, DEATH_MONSTER_SOLDIER_NAIL);
+       monster_melee(self.enemy, autocvar_g_monster_soldier_melee_damage, 0.3, DEATH_MONSTER_MARINE_SLAP, TRUE);
 }
 
 void soldier_die()
 {
        Monster_CheckDropCvars ("soldier");
        
-       remove(self.weaponentity);
-       
-       self.solid                      = SOLID_NOT;
-       self.takedamage         = DAMAGE_NO;
-       self.event_damage   = func_null;
-       self.enemy                      = world;
-       self.movetype           = MOVETYPE_TOSS;
-       self.think                      = Monster_Fade;
-       self.nextthink          = time + 2.1;
-       self.weaponentity       = world;
-       
-       if (self.attack_ranged == soldier_missile_uzi)
-               W_ThrowNewWeapon(self, WEP_UZI, 0, self.origin, self.velocity);    
-       else if (self.attack_ranged == soldier_missile_shotgun)
-               W_ThrowNewWeapon(self, WEP_SHOTGUN, 0, self.origin, self.velocity);
-       else if (self.attack_ranged == soldier_missile_rl)
-               W_ThrowNewWeapon(self, WEP_ROCKET_LAUNCHER, 0, self.origin, self.velocity);
-       else
-               W_ThrowNewWeapon(self, WEP_LASER, 0, self.origin, self.velocity);
-
-       if (random() < 0.5)
-               self.frame = soldier_anim_die1;
-       else
-               self.frame = soldier_anim_die2;
+       self.think = Monster_Fade;
+       self.nextthink = time + 5;
+       monsters_setframe((random() > 0.5) ? soldier_anim_die1 : soldier_anim_die2);
                
        monster_hook_death(); // for post-death mods
 }
@@ -281,14 +185,21 @@ void soldier_spawn ()
 
        self.damageforcescale   = 0.003;
        self.classname                  = "monster_soldier";
-       self.checkattack                = SoldierCheckAttack;
-       self.attack_melee               = soldier_bash;
-       self.frame                              = soldier_anim_draw;
+       self.checkattack                = GenericCheckAttack;
+       self.attack_melee               = soldier_melee;
+       self.attack_ranged              = soldier_attack;
        self.nextthink                  = time + random() * 0.5 + 0.1;
        self.think                              = soldier_think;
-       self.sprite_height              = 45 * self.scale;
+       self.currentammo                = 3;
+       self.sprite_height              = 45;
        self.items                              = (IT_SHELLS | IT_ROCKETS | IT_NAILS);
        
+       monsters_setframe(soldier_anim_draw);
+       
+       monster_setupsounds("soldier");
+       
+       setmodel(self, SOLDIER_MODEL);
+       
        RandomSelection_Init();
        RandomSelection_Add(world, WEP_LASER, string_null, autocvar_g_monster_soldier_weapon_laser_chance, 1);
        RandomSelection_Add(world, WEP_SHOTGUN, string_null, autocvar_g_monster_soldier_weapon_shotgun_chance, 1);
@@ -303,34 +214,8 @@ void soldier_spawn ()
        setmodel(self.weaponentity, "models/weapons/v_seeker.md3");
        setattachment(self.weaponentity, self, "bip01 r hand");
        
-       if (RandomSelection_chosen_float == WEP_ROCKET_LAUNCHER)
-       {
-               self.weapon = WEP_ROCKET_LAUNCHER;
-               self.currentammo = self.ammo_rockets;
-               self.armorvalue = 10;
-               self.attack_ranged = soldier_missile_rl;
-       }
-       else if (RandomSelection_chosen_float == WEP_UZI)
-       {
-               self.weapon = WEP_UZI;
-               self.currentammo = self.ammo_nails;
-               self.armorvalue = 100;
-               self.attack_ranged = soldier_missile_uzi;
-       }
-       else if (RandomSelection_chosen_float == WEP_SHOTGUN)
-       {
-               self.weapon = WEP_SHOTGUN;
-               self.currentammo = self.ammo_shells;
-               self.armorvalue = 25;
-               self.attack_ranged = soldier_missile_shotgun;
-       }
-       else
-       {
-               self.weapon = WEP_LASER;
-               self.armorvalue = 60;
-               self.currentammo = self.ammo_none;
-               self.attack_ranged = soldier_missile_laser;
-       }
+       self.armorvalue = bound(0.5, random(), 1);
+       self.weapon = RandomSelection_chosen_float;
 
        monster_hook_spawn(); // for post-spawn mods
 }
@@ -341,17 +226,14 @@ void spawnfunc_monster_soldier ()
        
        self.monster_spawnfunc = spawnfunc_monster_soldier;
        
-       if(self.spawnflags & MONSTERFLAG_APPEAR)
-       {
-               self.think = func_null;
-               self.nextthink = -1;
-               self.use = Monster_Appear;
+       if(Monster_CheckAppearFlags(self))
                return;
-       }
+               
+       precache_model("models/weapons/v_seeker.md3");
+       precache_model(SOLDIER_MODEL);
        
        if not (monster_initialize(
-                        "Grunt",
-                        "models/monsters/soldier.zym",
+                        "Marine", MONSTER_MARINE,
                         SOLDIER_MIN, SOLDIER_MAX,
                         FALSE,
                         soldier_die, soldier_spawn))
@@ -359,12 +241,9 @@ void spawnfunc_monster_soldier ()
                remove(self);
                return;
        }
-       
-       precache_sound ("weapons/shotgun_fire.wav");
-       precache_sound ("weapons/uzi_fire.wav");
-       precache_sound ("weapons/laser_fire.wav");
-       precache_sound ("weapons/reload.wav");
 }
 
 // compatibility with old spawns
 void spawnfunc_monster_army () { spawnfunc_monster_soldier(); }
+
+#endif // SVQC