]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/weapons/weapon/arc.qc
Use an attack think loop to keep the attack going instead of spamming weapon think...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / weapon / arc.qc
index 3b71d9207580baa0542b6b1176907686e78d8747..ef4e3eb7d7709e19b709396f0a30cf0d234820e4 100644 (file)
@@ -110,7 +110,7 @@ void W_Arc_Bolt_Damage(entity this, entity inflictor, entity attacker, float dam
        if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1))
                return; // g_projectiles_damage says to halt
 
-       SetResourceAmountExplicit(this, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH) - damage);
+       TakeResource(this, RESOURCE_HEALTH, damage);
        this.angles = vectoangles(this.velocity);
 
        if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0)
@@ -191,11 +191,9 @@ void W_Arc_Beam_Think(entity this)
        if(
                !IS_PLAYER(own)
                ||
-               (!thiswep.wr_checkammo1(thiswep, own, weaponentity) && !(own.items & IT_UNLIMITED_WEAPON_AMMO))
-               ||
                IS_DEAD(own)
                ||
-               forbidWeaponUse(own)
+               !weapon_prepareattack_check(thiswep, own, weaponentity, this.beam_bursting, -1)
                ||
                own.(weaponentity).m_switchweapon != WEP_ARC
                ||
@@ -238,8 +236,8 @@ void W_Arc_Beam_Think(entity this)
                {
                        // note: this doesn't force the switch
                        W_SwitchToOtherWeapon(own, weaponentity);
-                       own.(weaponentity).arc_BUTTON_ATCK_prev = false; // hax
                }
+               own.(weaponentity).arc_BUTTON_ATCK_prev = false; // allow switching weapons
                delete(this);
                return;
        }
@@ -267,14 +265,14 @@ void W_Arc_Beam_Think(entity this)
 
        W_SetupShot_Range(
                own,
-               weaponentity, // TODO
+               weaponentity,
                true,
                0,
                SND_Null,
                0,
                WEP_CVAR(arc, beam_damage) * coefficient,
                WEP_CVAR(arc, beam_range),
-               WEP_ARC.m_id
+               thiswep.m_id
        );
 
        // After teleport, "lock" the beam until the teleport is confirmed.
@@ -324,7 +322,10 @@ void W_Arc_Beam_Think(entity this)
                                (1 - (WEP_CVAR(arc, beam_returnspeed) * frametime)),
                                min(WEP_CVAR(arc, beam_maxangle) / angle, 1)
                        );
-                       this.beam_dir = normalize((w_shotdir * (1 - blendfactor)) + (this.beam_dir * blendfactor));
+                       if(vdist(this.beam_dir - w_shotdir, <, 0.01))
+                               this.beam_dir = w_shotdir;
+                       else
+                               this.beam_dir = normalize((w_shotdir * (1 - blendfactor)) + (this.beam_dir * blendfactor));
                }
                else
                {
@@ -334,7 +335,10 @@ void W_Arc_Beam_Think(entity this)
                                (1 - (WEP_CVAR(arc, beam_returnspeed) * frametime)),
                                1
                        );
-                       this.beam_dir = normalize((w_shotdir * (1 - blendfactor)) + (this.beam_dir * blendfactor));
+                       if(vdist(this.beam_dir - w_shotdir, <, 0.01))
+                               this.beam_dir = w_shotdir;
+                       else
+                               this.beam_dir = normalize((w_shotdir * (1 - blendfactor)) + (this.beam_dir * blendfactor));
                }
 
                // network information: beam direction
@@ -410,7 +414,7 @@ void W_Arc_Beam_Think(entity this)
                beam_endpos = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_endpos);
                new_dir = WarpZone_TransformVelocity(WarpZone_trace_transform, new_dir);
 
-               float is_player = (
+               bool is_player = (
                        IS_PLAYER(trace_ent)
                        ||
                        trace_ent.classname == "body"
@@ -418,129 +422,42 @@ void W_Arc_Beam_Think(entity this)
                        IS_MONSTER(trace_ent)
                );
 
-               // TODO: takedamage flag for things that can be healed?
-               if(trace_ent && (trace_ent.takedamage || trace_ent.classname == "onslaught_generator" || trace_ent.classname == "onslaught_controlpoint_icon") && (is_player || WEP_CVAR(arc, beam_nonplayerdamage)))
+               if(trace_ent)
                {
-                       // calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?)
-                       // NO. trace_endpos should be just fine. If not,
-                       // that's an engine bug that needs proper debugging.
-                       vector hitorigin = trace_endpos;
-
-                       float falloff = ExponentialFalloff(
-                               WEP_CVAR(arc, beam_falloff_mindist),
-                               WEP_CVAR(arc, beam_falloff_maxdist),
-                               WEP_CVAR(arc, beam_falloff_halflifedist),
-                               vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, hitorigin) - w_shotorg)
-                       );
-
-                       // TODO: api or event for things that can be healed
-                       if(IS_VEHICLE(trace_ent) && SAME_TEAM(own, trace_ent))
-                       {
-                               float roothealth = ((burst) ? WEP_CVAR(arc, burst_healing_hps) : WEP_CVAR(arc, beam_healing_hps));
-                               // not handling shield, since it's not exactly a defensive stat
-
-                               if(trace_ent.vehicle_health <= trace_ent.max_health && roothealth)
-                               {
-                                       trace_ent.vehicle_health = min(
-                                                       trace_ent.vehicle_health + (roothealth * coefficient),
-                                                       trace_ent.max_health
-                                               );
-                                       if(trace_ent.owner)
-                                               trace_ent.owner.vehicle_health = (trace_ent.vehicle_health / trace_ent.max_health) * 100;
-                                       new_beam_type = ARC_BT_HEAL;
-                               }
-                       }
-                       else if(trace_ent.classname == "onslaught_generator" && SAME_TEAM(own, trace_ent))
-                       {
-                               float roothealth = ((burst) ? WEP_CVAR(arc, burst_healing_hps) : WEP_CVAR(arc, beam_healing_hps));
-                               if(roothealth)
-                               {
-                                       if(GetResourceAmount(trace_ent, RESOURCE_HEALTH) <= trace_ent.max_health)
-                                       {
-                                               GiveResourceWithLimit(trace_ent, RESOURCE_HEALTH, (roothealth * coefficient), trace_ent.max_health);
-                                               WaypointSprite_UpdateHealth(trace_ent.sprite, GetResourceAmount(trace_ent, RESOURCE_HEALTH));
-                                               trace_ent.frame = 10 * bound(0, (1 - GetResourceAmount(trace_ent, RESOURCE_HEALTH) / trace_ent.max_health), 1);
-                                               trace_ent.lasthealth = GetResourceAmount(trace_ent, RESOURCE_HEALTH);
-                                               trace_ent.SendFlags |= GSF_STATUS;
-                                       }
-                                       new_beam_type = ARC_BT_HEAL;
-                               }
-                       }
-                       else if(trace_ent.classname == "onslaught_controlpoint_icon" && SAME_TEAM(own, trace_ent))
+                       if(SAME_TEAM(own, trace_ent))
                        {
                                float roothealth = ((burst) ? WEP_CVAR(arc, burst_healing_hps) : WEP_CVAR(arc, beam_healing_hps));
-                               if(roothealth)
-                               {
-                                       if(GetResourceAmount(trace_ent, RESOURCE_HEALTH) <= trace_ent.max_health)
-                                       {
-                                               GiveResourceWithLimit(trace_ent, RESOURCE_HEALTH, (roothealth * coefficient), trace_ent.max_health);
-                                               if(trace_ent.owner.iscaptured)
-                                                       WaypointSprite_UpdateHealth(trace_ent.owner.sprite, GetResourceAmount(trace_ent, RESOURCE_HEALTH));
-                                               else
-                                                       WaypointSprite_UpdateBuildFinished(trace_ent.owner.sprite, time + (trace_ent.max_health - GetResourceAmount(trace_ent, RESOURCE_HEALTH)) / (trace_ent.count / ONS_CP_THINKRATE));
-                                       }
-                                       new_beam_type = ARC_BT_HEAL;
-                               }
-                       }
-                       else if(trace_ent.classname == "func_assault_destructible" && SAME_TEAM(own, trace_ent))
-                       {
-                               float roothealth = ((burst) ? WEP_CVAR(arc, burst_healing_hps) : WEP_CVAR(arc, beam_healing_hps));
-
-                               if(roothealth)
+                               float rootarmor = ((burst) ? WEP_CVAR(arc, burst_healing_aps) : WEP_CVAR(arc, beam_healing_aps));
+                               float hplimit = ((IS_PLAYER(trace_ent)) ? WEP_CVAR(arc, beam_healing_hmax) : RESOURCE_LIMIT_NONE);
+                               Heal(trace_ent, own, (roothealth * coefficient), hplimit);
+                               if(IS_PLAYER(trace_ent) && rootarmor)
                                {
-                                       if(GetResourceAmount(trace_ent, RESOURCE_HEALTH) <= trace_ent.max_health)
+                                       if(GetResourceAmount(trace_ent, RESOURCE_ARMOR) <= WEP_CVAR(arc, beam_healing_amax))
                                        {
-                                               GiveResourceWithLimit(trace_ent, RESOURCE_HEALTH, (roothealth * coefficient), trace_ent.max_health);
-                                               if(trace_ent.sprite)
-                                               {
-                                                       WaypointSprite_UpdateHealth(trace_ent.sprite, GetResourceAmount(trace_ent, RESOURCE_HEALTH));
-                                               }
-                                               func_breakable_colormod(trace_ent);
+                                               GiveResourceWithLimit(trace_ent, RESOURCE_ARMOR, (rootarmor * coefficient), WEP_CVAR(arc, beam_healing_amax));
+                                               trace_ent.pauserotarmor_finished = max(
+                                                       trace_ent.pauserotarmor_finished,
+                                                       time + autocvar_g_balance_pause_armor_rot
+                                               );
                                        }
-                                       new_beam_type = ARC_BT_HEAL;
-                               }
-                       }
-                       else if(is_player && SAME_TEAM(own, trace_ent))
-                       {
-                               float roothealth, rootarmor;
-                               float maxhp;
-                               if(burst)
-                               {
-                                       roothealth = WEP_CVAR(arc, burst_healing_hps);
-                                       rootarmor = WEP_CVAR(arc, burst_healing_aps);
                                }
-                               else
-                               {
-                                       roothealth = WEP_CVAR(arc, beam_healing_hps);
-                                       rootarmor = WEP_CVAR(arc, beam_healing_aps);
-                               }
-                               maxhp = ((IS_MONSTER(trace_ent)) ? trace_ent.max_health : WEP_CVAR(arc, beam_healing_hmax));
-
-                               if(GetResourceAmount(trace_ent, RESOURCE_HEALTH) <= maxhp && roothealth)
-                               {
-                                       GiveResourceWithLimit(trace_ent, RESOURCE_HEALTH, (roothealth * coefficient), maxhp);
-                               }
-                               if(GetResourceAmount(trace_ent, RESOURCE_ARMOR) <= WEP_CVAR(arc, beam_healing_amax) && rootarmor && !IS_MONSTER(trace_ent))
-                               {
-                                       GiveResourceWithLimit(trace_ent, RESOURCE_ARMOR, (rootarmor * coefficient), WEP_CVAR(arc, beam_healing_amax));
-                               }
-
-                               // stop rot, set visual effect
                                if(roothealth || rootarmor)
-                               {
-                                       trace_ent.pauserothealth_finished = max(
-                                               trace_ent.pauserothealth_finished,
-                                               time + autocvar_g_balance_pause_health_rot
-                                       );
-                                       trace_ent.pauserotarmor_finished = max(
-                                               trace_ent.pauserotarmor_finished,
-                                               time + autocvar_g_balance_pause_armor_rot
-                                       );
                                        new_beam_type = ARC_BT_HEAL;
-                               }
                        }
-                       else
+                       else if(trace_ent.takedamage && (is_player || WEP_CVAR(arc, beam_nonplayerdamage)))
                        {
+                               // calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?)
+                               // NO. trace_endpos should be just fine. If not,
+                               // that's an engine bug that needs proper debugging.
+                               vector hitorigin = trace_endpos;
+
+                               float falloff = ExponentialFalloff(
+                                       WEP_CVAR(arc, beam_falloff_mindist),
+                                       WEP_CVAR(arc, beam_falloff_maxdist),
+                                       WEP_CVAR(arc, beam_falloff_halflifedist),
+                                       vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, hitorigin) - w_shotorg)
+                               );
+
                                float rootdamage;
                                if(is_player)
                                {
@@ -556,7 +473,7 @@ void W_Arc_Beam_Think(entity this)
                                {
                                        accuracy_add(
                                                own,
-                                               WEP_ARC.m_id,
+                                               WEP_ARC,
                                                0,
                                                rootdamage * coefficient * falloff
                                        );
@@ -622,17 +539,33 @@ void W_Arc_Beam(float burst, entity actor, .entity weaponentity)
 
        getthink(beam)(beam);
 }
-void Arc_Smoke(entity actor, .entity weaponentity)
+void W_Arc_Attack(Weapon thiswep, entity actor, .entity weaponentity, int fire)
 {
+       if(!actor.(weaponentity).arc_beam || wasfreed(actor.(weaponentity).arc_beam))
+       {
+               w_ready(thiswep, actor, weaponentity, fire);
+               return;
+       }
+
+       // attack handled by the beam itself, this is just a loop to keep the attack happening!
+
+       // NOTE: arc doesn't use a refire
+       //ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR_PRI(arc, refire) * W_WeaponRateFactor(actor);
+       actor.(weaponentity).wframe = WFRAME_FIRE1;
+       weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, WEP_CVAR(arc, beam_animtime), W_Arc_Attack);
+}
+void Arc_Smoke(Weapon thiswep, entity actor, .entity weaponentity, int fire)
+{
+       // TODO: spamming this without checking any refires is asking for trouble!
        makevectors(actor.v_angle);
-       W_SetupShot_Range(actor,weaponentity,true,0,SND_Null,0,0,0,WEP_ARC.m_id); // TODO: probably doesn't need deathtype, since this is just a prefire effect
+       W_SetupShot_Range(actor,weaponentity,false,0,SND_Null,0,0,0,thiswep.m_id); // TODO: probably doesn't need deathtype, since this is just a prefire effect
 
        vector smoke_origin = w_shotorg + actor.velocity*frametime;
        if ( actor.arc_overheat > time )
        {
                if ( random() < actor.(weaponentity).arc_heat_percent )
                        Send_Effect(EFFECT_ARC_SMOKE, smoke_origin, '0 0 0', 1 );
-               if ( PHYS_INPUT_BUTTON_ATCK(actor) || PHYS_INPUT_BUTTON_ATCK2(actor) )
+               if ( (fire & 1) || (fire & 2) )
                {
                        Send_Effect(EFFECT_ARC_OVERHEAT_FIRE, smoke_origin, w_shotdir, 1 );
                        if ( !actor.arc_smoke_sound )
@@ -651,7 +584,7 @@ void Arc_Smoke(entity actor, .entity weaponentity)
        }
 
        if (  actor.arc_smoke_sound && ( actor.arc_overheat <= time ||
-               !( PHYS_INPUT_BUTTON_ATCK(actor) || PHYS_INPUT_BUTTON_ATCK2(actor) ) ) || actor.(weaponentity).m_switchweapon != WEP_ARC )
+               !( PHYS_INPUT_BUTTON_ATCK(actor) || PHYS_INPUT_BUTTON_ATCK2(actor) ) ) || actor.(weaponentity).m_switchweapon != thiswep )
        {
                actor.arc_smoke_sound = 0;
                sound(actor, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM);
@@ -686,14 +619,14 @@ METHOD(Arc, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
 METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
 {
     Arc_Player_SetHeat(actor, weaponentity);
-    Arc_Smoke(actor, weaponentity);
+    Arc_Smoke(thiswep, actor, weaponentity, fire);
 
     bool beam_fire2 = ((fire & 2) && !WEP_CVAR(arc, bolt));
 
     if (time >= actor.arc_overheat)
     if ((fire & 1) || beam_fire2 || actor.(weaponentity).arc_beam.beam_bursting)
     {
-
+       #if 0
         if(actor.(weaponentity).arc_BUTTON_ATCK_prev)
         {
             #if 0
@@ -703,6 +636,7 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i
             #endif
                 weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, WEP_CVAR(arc, beam_animtime), w_ready);
         }
+        #endif
 
         if((!actor.(weaponentity).arc_beam) || wasfreed(actor.(weaponentity).arc_beam))
         {
@@ -712,7 +646,8 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i
 
                 if(!actor.(weaponentity).arc_BUTTON_ATCK_prev)
                 {
-                    weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
+                       actor.(weaponentity).wframe = WFRAME_FIRE1;
+                    weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, WEP_CVAR(arc, beam_animtime), W_Arc_Attack);
                     actor.(weaponentity).arc_BUTTON_ATCK_prev = true;
                 }
             }
@@ -722,7 +657,7 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i
     }
     else if(fire & 2)
     {
-        if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(arc, bolt_refire)))
+        if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR(arc, bolt_refire)))
         {
             W_Arc_Attack_Bolt(thiswep, actor, weaponentity);
             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, bolt_refire), w_ready);
@@ -731,10 +666,9 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i
 
     if(actor.(weaponentity).arc_BUTTON_ATCK_prev)
     {
-       int slot = weaponslot(weaponentity);
         sound(actor, CH_WEAPON_A, SND_ARC_STOP, VOL_BASE, ATTN_NORM);
         weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
-        ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor(actor);
+        ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor(actor);
     }
     actor.(weaponentity).arc_BUTTON_ATCK_prev = false;