]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
rewrite of newtonian projectiles, now also with a function that inverts the shot...
authorRudolf Polzer <divverent@alientrap.org>
Tue, 1 May 2012 08:43:18 +0000 (10:43 +0200)
committerRudolf Polzer <divverent@alientrap.org>
Tue, 1 May 2012 08:43:18 +0000 (10:43 +0200)
vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style)

qcsrc/common/campaign_file.qc
qcsrc/common/util.qc
qcsrc/common/util.qh
qcsrc/server/cl_weaponsystem.qc

index 067cd8054de6f311d562bf427190fd806ef1de79..5ab3da67cff47e58a34f3731b1d330d256bb3526 100644 (file)
@@ -38,13 +38,12 @@ float CampaignFile_Load(float offset, float n)
                        {
                                entlen = tokenize(l); // using insane tokenizer for CSV
 
-#define CAMPAIGN_GETARG0                  if(i >= entlen)
-#define CAMPAIGN_GETARG1 CAMPAIGN_GETARG0     error("syntax error in campaign file: line has not enough fields");
-#define CAMPAIGN_GETARG2 CAMPAIGN_GETARG1 a = argv(++i);
-#define CAMPAIGN_GETARG3 CAMPAIGN_GETARG2 if(a == ",")
-#define CAMPAIGN_GETARG4 CAMPAIGN_GETARG3     a = "";
-#define CAMPAIGN_GETARG5 CAMPAIGN_GETARG4 else
-#define CAMPAIGN_GETARG  CAMPAIGN_GETARG5     ++i
+#define CAMPAIGN_GETARG \
+       a = argv(++i); \
+       if(a == ",") \
+               a = ""; \
+       else \
+               ++i
 // What you're seeing here is what people will do when your compiler supports
 // C-style macros but no line continuations.
 
@@ -58,6 +57,10 @@ float CampaignFile_Load(float offset, float n)
                                CAMPAIGN_GETARG; campaign_mutators[campaign_entries] = strzone(a);
                                CAMPAIGN_GETARG; campaign_shortdesc[campaign_entries] = strzone(a);
                                CAMPAIGN_GETARG; campaign_longdesc[campaign_entries] = strzone(strreplace("\\n", "\n", a));
+
+                               if(i > entlen)
+                                       error("syntax error in campaign file: line has not enough fields");
+
                                campaign_entries = campaign_entries + 1;
 
                                if(campaign_entries >= n)                               
index a1e0595891b5bf723ee8a7c41d1ce6c4e8ee1acc..b973850cc9f23393062a1b60a781e6ead3052d3f 100644 (file)
@@ -1580,6 +1580,109 @@ vector solve_quadratic(float a, float b, float c) // ax^2 + bx + c = 0
        return v;
 }
 
+vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style)
+{
+       vector ret;
+
+       // make origin and speed relative
+       eorg -= myorg;
+       if(newton_style)
+               evel -= myvel;
+
+       // now solve for ret, ret normalized:
+       //   eorg + t * evel == t * ret * spd
+       // or, rather, solve for t:
+       //   |eorg + t * evel| == t * spd
+       //   eorg^2 + t^2 * evel^2 + 2 * t * (eorg * evel) == t^2 * spd^2
+       //   t^2 * (evel^2 - spd^2) + t * (2 * (eorg * evel)) + eorg^2 == 0
+       vector solution = solve_quadratic(evel * evel - spd * spd, 2 * (eorg * evel), eorg * eorg);
+       // p = 2 * (eorg * evel) / (evel * evel - spd * spd)
+       // q = (eorg * eorg) / (evel * evel - spd * spd)
+       if(!solution_z) // no real solution
+       {
+               // happens if D < 0
+               // (eorg * evel)^2 < (evel^2 - spd^2) * eorg^2
+               // (eorg * evel)^2 / eorg^2 < evel^2 - spd^2
+               // spd^2 < ((evel^2 * eorg^2) - (eorg * evel)^2) / eorg^2
+               // spd^2 < evel^2 * (1 - cos^2 angle(evel, eorg))
+               // spd^2 < evel^2 * sin^2 angle(evel, eorg)
+               // spd < |evel| * sin angle(evel, eorg)
+               return '0 0 0';
+       }
+       else if(solution_x > 0)
+       {
+               // both solutions > 0: take the smaller one
+               // happens if p < 0 and q > 0
+               ret = normalize(eorg + solution_x * evel);
+       }
+       else if(solution_y > 0)
+       {
+               // one solution > 0: take the larger one
+               // happens if q < 0 or q == 0 and p < 0
+               ret = normalize(eorg + solution_y * evel);
+       }
+       else
+       {
+               // no solution > 0: reject
+               // happens if p > 0 and q >= 0
+               // 2 * (eorg * evel) / (evel * evel - spd * spd) > 0
+               // (eorg * eorg) / (evel * evel - spd * spd) >= 0
+               //
+               // |evel| >= spd
+               // eorg * evel > 0
+               //
+               // "Enemy is moving away from me at more than spd"
+               return '0 0 0';
+       }
+
+       // NOTE: we always got a solution if spd > |evel|
+
+       if(newton_style == 2)
+               ret = normalize(ret * spd + myvel);
+
+       return ret;
+}
+
+vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_style, float mi, float ma)
+{
+       if(!newton_style)
+               return spd * mydir;
+
+       if(newton_style == 2)
+       {
+               // true Newtonian projectiles with automatic aim adjustment
+               //
+               // solve: |outspeed * mydir - myvel| = spd
+               // outspeed^2 - 2 * outspeed * (mydir * myvel) + myvel^2 - spd^2 = 0
+               // outspeed = (mydir * myvel) +- sqrt((mydir * myvel)^2 - myvel^2 + spd^2)
+               // PLUS SIGN!
+               // not defined?
+               // then...
+               // myvel^2 - (mydir * myvel)^2 > spd^2
+               // velocity without mydir component > spd
+               // fire at smallest possible spd that works?
+               // |(mydir * myvel) * myvel - myvel| = spd
+
+               vector solution = solve_quadratic(1, -2 * (mydir * myvel), myvel * myvel - spd * spd);
+
+               float outspeed;
+               if(solution_z)
+                       outspeed = solution_y; // the larger one
+               else
+               {
+                       //outspeed = 0; // slowest possible shot
+                       outspeed = solution_x; // the real part (that is, the average!)
+                       //dprint("impossible shot, adjusting\n");
+               }
+
+               outspeed = bound(spd * mi, outspeed, spd * ma);
+               return mydir * outspeed;
+       }
+
+       // real Newtonian
+       return myvel + spd * mydir;
+}
+
 void check_unacceptable_compiler_bugs()
 {
        if(cvar("_allow_unacceptable_compiler_bugs"))
index 64830ca1d73330931919e01e2b5d537f8c5141ab..c409de3544578c00acc39b6b4e0c2248fabe65f3 100644 (file)
@@ -168,6 +168,9 @@ vector solve_quadratic(float a, float b, float c);
 // z = 1 if a real solution exists, 0 if not
 // if no real solution exists, x contains the real part and y the imaginary part of the complex solutions x+iy and x-iy
 
+vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style);
+vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_style, float mi, float ma);
+
 void check_unacceptable_compiler_bugs();
 
 float compressShotOrigin(vector v);
@@ -314,3 +317,6 @@ float ReadApproxPastTime();
 // execute-stuff-next-frame subsystem
 void execute_next_frame();
 void queue_to_execute_next_frame(string s);
+
+// for marking written-to values as unused where it's a good idea to do this
+noref float unused_float;
index cbbe3d547ff4bcabedf3c6a1e8086fc8415f153f..402b692af1230fba959a89c1fce3aef5a999ab13 100644 (file)
@@ -1077,8 +1077,6 @@ vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity, float f
 {
        vector mdirection;
        float mspeed;
-       float outspeed;
-       float nstyle;
        vector outvelocity;
 
        mvelocity = mvelocity * g_weaponspeedfactor;
@@ -1086,48 +1084,7 @@ vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity, float f
        mdirection = normalize(mvelocity);
        mspeed = vlen(mvelocity);
 
-       nstyle = autocvar_g_projectiles_newton_style;
-       if(nstyle == 0 || forceAbsolute)
-       {
-               // absolute velocity
-               outvelocity = mvelocity;
-       }
-       else if(nstyle == 1)
-       {
-               // true Newtonian projectiles
-               outvelocity = pvelocity + mvelocity;
-       }
-       else if(nstyle == 2)
-       {
-               // true Newtonian projectiles with automatic aim adjustment
-               //
-               // solve: |outspeed * mdirection - pvelocity| = mspeed
-               // outspeed^2 - 2 * outspeed * (mdirection * pvelocity) + pvelocity^2 - mspeed^2 = 0
-               // outspeed = (mdirection * pvelocity) +- sqrt((mdirection * pvelocity)^2 - pvelocity^2 + mspeed^2)
-               // PLUS SIGN!
-               // not defined?
-               // then...
-               // pvelocity^2 - (mdirection * pvelocity)^2 > mspeed^2
-               // velocity without mdirection component > mspeed
-               // fire at smallest possible mspeed that works?
-               // |(mdirection * pvelocity) * pvelocity - pvelocity| = mspeed
-
-               vector solution;
-               solution = solve_quadratic(1, -2 * (mdirection * pvelocity), pvelocity * pvelocity - mspeed * mspeed);
-               if(solution_z)
-                       outspeed = solution_y; // the larger one
-               else
-               {
-                       //outspeed = 0; // slowest possible shot
-                       outspeed = solution_x; // the real part (that is, the average!)
-                       //dprint("impossible shot, adjusting\n");
-               }
-
-               outspeed = bound(mspeed * autocvar_g_projectiles_newton_style_2_minfactor, outspeed, mspeed * autocvar_g_projectiles_newton_style_2_maxfactor);
-               outvelocity = mdirection * outspeed;
-       }
-       else
-               error("g_projectiles_newton_style must be 0 (absolute), 1 (Newtonian) or 2 (Newtonian + aimfix)!");
+       outvelocity = get_shotvelocity(pvelocity, mdirection, mspeed, (forceAbsolute ? 0 : autocvar_g_projectiles_newton_style), autocvar_g_projectiles_newton_style_2_minfactor, autocvar_g_projectiles_newton_style_2_maxfactor);
 
        return outvelocity;
 }