vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style)
{
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.
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)
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"))
// 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);
// 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;
{
vector mdirection;
float mspeed;
- float outspeed;
- float nstyle;
vector outvelocity;
mvelocity = mvelocity * g_weaponspeedfactor;
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;
}