From: Rudolf Polzer Date: Tue, 1 May 2012 08:43:18 +0000 (+0200) Subject: rewrite of newtonian projectiles, now also with a function that inverts the shot... X-Git-Tag: xonotic-v0.7.0~260^2~3 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=4f3a2f7b2c54a0d424e61266b406abfa71ab3c10;p=xonotic%2Fxonotic-data.pk3dir.git rewrite of newtonian projectiles, now also with a function that inverts the shot direction adjustment for bot/turret purposes vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style) --- diff --git a/qcsrc/common/campaign_file.qc b/qcsrc/common/campaign_file.qc index 067cd8054..5ab3da67c 100644 --- a/qcsrc/common/campaign_file.qc +++ b/qcsrc/common/campaign_file.qc @@ -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) diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index a1e059589..b973850cc 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -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")) diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index 64830ca1d..c409de354 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -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; diff --git a/qcsrc/server/cl_weaponsystem.qc b/qcsrc/server/cl_weaponsystem.qc index cbbe3d547..402b692af 100644 --- a/qcsrc/server/cl_weaponsystem.qc +++ b/qcsrc/server/cl_weaponsystem.qc @@ -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; }