From f963550195dc4a038b7ebb0c87f6ce996fbf20ed Mon Sep 17 00:00:00 2001 From: div0 Date: Wed, 24 Dec 2008 13:57:07 +0000 Subject: [PATCH] updated ballistics code and data, matches physics better now git-svn-id: svn://svn.icculus.org/nexuiz/trunk@5294 f962a42d-fe04-0410-a3ab-8c8b0445ebaa --- data/defaultNexuiz.cfg | 34 +++++++++++++++- data/qcsrc/server/constants.qh | 4 +- data/qcsrc/server/w_campingrifle.qc | 8 ++-- data/qcsrc/server/w_common.qc | 60 +++++++++++++++-------------- data/weapons.cfg | 6 ++- data/weaponsPro.cfg | 6 ++- 6 files changed, 77 insertions(+), 41 deletions(-) diff --git a/data/defaultNexuiz.cfg b/data/defaultNexuiz.cfg index 6773d3bf8..e48f913f6 100644 --- a/data/defaultNexuiz.cfg +++ b/data/defaultNexuiz.cfg @@ -16,7 +16,7 @@ alias if_dedicated "_detect_dedicated_$qport $*" seta g_configversion 0 // default.cfg versioning (update using update-cvarcount.sh; run that every time after adding a new cvar) -set cvar_check_default b58c84e3a08bf79ae880a0d4b081efbb +set cvar_check_default 3a4552d8adffd3ad69fec3f0c15c62d8 // Nexuiz version (formatted for machines) // used to determine if a client version is compatible @@ -1247,7 +1247,37 @@ set g_showweaponspawns 0 // 1: display sprites for weapon spawns found on the ma alias records "cmd records" -set g_ballistics_solidspeedhalflife 0.001 // note: bullets can travel this*velocity*1.44 far inside solid +// ballistics use physical units, but qu based +// Quake-Newton: 1 qN = 1 qu * 1 g / 1 s^2 +// Quake-Joule: 1 qJ = 1 qN * 1 qu +// Quake-Pascal: 1 qPa = 1 qN / 1 qu^2 + +set g_ballistics_materialconstant 600060006 // arbitrary: 2^22 +// unit: qJ / qu^3 (energy needed per volume unit of solid to push/burn away +// parameter: bullet constant: mass / area in g/qu^2 +// = mass / (pi/4 * caliber^2) +// with caliber in inches, mass in grams: +// = 1.273239544735163 * mass / caliber^2 +// with caliber in inches, mass in grains: +// = 0.082633246453312 * mass / caliber^2 + +// bullet max travel distance inside solid: +// 0.5 * v^2 * bulletconstant / g_ballistics_materialconstant + +// some bullet constants: +// http://hypertextbook.com/facts/2000/ShantayArmstrong.shtml +// second bullet: caliber .45, mass 16.2g, bullet constant 101.859163578813 +// third bullet: caliber .338, mass 16.2g, bullet constant 180.5476053421592 +// fourth bullet: caliber .25, mass 2.3g, bullet constant 46.85521524625399 +// http://en.wikipedia.org/wiki/.50_BMG +// caliber .5, 360 grains, bullet constant 118.9918748927693 +// AK-47: +// caliber .3, 62 grains, bullet constant 56.92512533450383 +// .3 winchester magnum: +// caliber .3, 150 grains, bullet constant 137.7220774221867 + set g_ballistics_force 0 // 1: make all bullets use the ballistics code instead of hitscan set g_ballistics_force_uzi_speed 10000 // speed of uzi bullets if g_ballistics_force is 1 +set g_ballistics_force_uzi_bulletconstant 80 // 6.7 qu set g_ballistics_force_shotgun_speed 6000 // speed of shotgun bullets if g_ballistics_force is 1 +set g_ballistics_force_shotgun_bulletconstant 50 // 1.5 qu diff --git a/data/qcsrc/server/constants.qh b/data/qcsrc/server/constants.qh index 174b7ae1b..39d0c845c 100644 --- a/data/qcsrc/server/constants.qh +++ b/data/qcsrc/server/constants.qh @@ -1,5 +1,5 @@ -string CVAR_CHECK_DEFAULT = "b58c84e3a08bf79ae880a0d4b081efbb"; -string CVAR_CHECK_WEAPONS = "5a724edde37f7db7608cf03f262a1f34"; +string CVAR_CHECK_DEFAULT = "3a4552d8adffd3ad69fec3f0c15c62d8"; +string CVAR_CHECK_WEAPONS = "7abbcf55140e618bfaa97b587fc7aef7"; float FALSE = 0; float TRUE = 1; diff --git a/data/qcsrc/server/w_campingrifle.qc b/data/qcsrc/server/w_campingrifle.qc index a06a16fa0..eb5ab4c46 100644 --- a/data/qcsrc/server/w_campingrifle.qc +++ b/data/qcsrc/server/w_campingrifle.qc @@ -20,7 +20,7 @@ void W_Campingrifle_CheckReload() W_Campingrifle_Reload(); } -void W_CampingRifle_FireBullet(float pSpread, float pDamage, float pHeadshotAddedDamage, float pForce, float pSpeed, float pLifetime, float pAmmo, float deathtype) +void W_CampingRifle_FireBullet(float pSpread, float pDamage, float pHeadshotAddedDamage, float pForce, float pSpeed, float pLifetime, float pAmmo, float deathtype, float pBulletConstant) { if not(self.items & IT_UNLIMITED_WEAPON_AMMO) self.ammo_nails -= pAmmo; @@ -28,7 +28,7 @@ void W_CampingRifle_FireBullet(float pSpread, float pDamage, float pHeadshotAdde W_SetupShot (self, '25 8 -8', FALSE, 2, "weapons/campingrifle_fire.wav"); pointparticles(particleeffectnum("shotgun_muzzleflash"), w_shotorg, w_shotdir * 2000, 1); - fireBallisticBullet(w_shotorg, v_forward /* no TrueAim for this weapon */, pSpread, pSpeed, pLifetime, pDamage, pHeadshotAddedDamage / pDamage, pForce, deathtype, EF_RED, 1); + fireBallisticBullet(w_shotorg, v_forward /* no TrueAim, so shooting through solid is easier */, pSpread, pSpeed, pLifetime, pDamage, pHeadshotAddedDamage / pDamage, pForce, deathtype, EF_RED, 1, pBulletConstant); if (cvar("g_casings") >= 2) SpawnCasing (w_shotorg + v_forward * 10, ((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3); @@ -39,12 +39,12 @@ void W_CampingRifle_FireBullet(float pSpread, float pDamage, float pHeadshotAdde void W_Campingrifle_Attack() { - W_CampingRifle_FireBullet(cvar("g_balance_campingrifle_primary_spread"), cvar("g_balance_campingrifle_primary_damage"), cvar("g_balance_campingrifle_primary_headshotaddeddamage"), cvar("g_balance_campingrifle_primary_force"), cvar("g_balance_campingrifle_primary_speed"), cvar("g_balance_campingrifle_primary_lifetime"), cvar("g_balance_campingrifle_primary_ammo"), WEP_CAMPINGRIFLE); + W_CampingRifle_FireBullet(cvar("g_balance_campingrifle_primary_spread"), cvar("g_balance_campingrifle_primary_damage"), cvar("g_balance_campingrifle_primary_headshotaddeddamage"), cvar("g_balance_campingrifle_primary_force"), cvar("g_balance_campingrifle_primary_speed"), cvar("g_balance_campingrifle_primary_lifetime"), cvar("g_balance_campingrifle_primary_ammo"), WEP_CAMPINGRIFLE, cvar("g_balance_campingrifle_primary_bulletconstant")); } void W_Campingrifle_Attack2() { - W_CampingRifle_FireBullet(cvar("g_balance_campingrifle_secondary_spread"), cvar("g_balance_campingrifle_secondary_damage"), cvar("g_balance_campingrifle_secondary_headshotaddeddamage"), cvar("g_balance_campingrifle_secondary_force"), cvar("g_balance_campingrifle_secondary_speed"), cvar("g_balance_campingrifle_secondary_lifetime"), cvar("g_balance_campingrifle_secondary_ammo"), WEP_CAMPINGRIFLE | HITTYPE_SECONDARY); + W_CampingRifle_FireBullet(cvar("g_balance_campingrifle_secondary_spread"), cvar("g_balance_campingrifle_secondary_damage"), cvar("g_balance_campingrifle_secondary_headshotaddeddamage"), cvar("g_balance_campingrifle_secondary_force"), cvar("g_balance_campingrifle_secondary_speed"), cvar("g_balance_campingrifle_secondary_lifetime"), cvar("g_balance_campingrifle_secondary_ammo"), WEP_CAMPINGRIFLE | HITTYPE_SECONDARY, cvar("g_balance_campingrifle_secondary_bulletconstant")); } void spawnfunc_weapon_campingrifle (void) diff --git a/data/qcsrc/server/w_common.qc b/data/qcsrc/server/w_common.qc index bf8cc25d8..e99033fa7 100644 --- a/data/qcsrc/server/w_common.qc +++ b/data/qcsrc/server/w_common.qc @@ -119,7 +119,7 @@ void W_BallisticBullet_Hit (void) vector org2; float f; - org2 = self.origin - 6 * normalize(self.oldvelocity); + org2 = self.origin - 6 * normalize(self.velocity); if (DEATH_ISWEAPON(self.projectiledeathtype, WEP_SHOTGUN)) pointparticles(particleeffectnum("shotgun_impact"), org2, normalize(self.velocity) * 1000, 1); else @@ -129,7 +129,7 @@ void W_BallisticBullet_Hit (void) { self.enemy = other; // don't hit the same player twice with the same bullet - f = vlen(self.velocity) / vlen(self.oldvelocity); + f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier headshot = 0; yoda = 0; @@ -170,7 +170,7 @@ void W_BallisticBullet_LeaveSolid_think() self.flags &~= FL_ONGROUND; self.effects &~= EF_NODRAW; - org2 = self.origin - 6 * normalize(self.oldvelocity); + org2 = self.origin + 6 * normalize(self.velocity); if (DEATH_ISWEAPON(self.projectiledeathtype, WEP_SHOTGUN)) pointparticles(particleeffectnum("shotgun_impact"), org2, normalize(self.velocity) * 1000, 1); else @@ -187,30 +187,22 @@ float log(float x) return 2 * log(sqrt(x)); } -float W_BallisticBullet_LeaveSolid(entity e, vector vel, float speedhalflife) +float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant) { // move the entity along its velocity until it's out of solid, then let it resume vector tracevel, org, skiporg, endorg, t; - float dt, dst, velfactor, v0; + float dt, dst, velfactor, v0, vs; float maxdist; + float E0_m, Es_m; - speedhalflife *= 1.442695040888963; // distance for 1/eth of the speed + // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass v0 = vlen(vel); - // maxdist: max distance that CAN be travelled using current velocity and speed halflife - // - // v(t) = v(0) * e^(-t / speedhalflife) - // integrate - // V(t) = - v(0) * e^(-t / speedhalflife) * speedhalflife - // s(t) = V(t) - V(0) - // s(t) = (speedhalflife * v(0)) * (1 - e^(-t / speedhalflife)) - // lim s = speedhalflife * v(0) - // t(s) = speedhalflife * log((speedhalflife * v(0)) / (speedhalflife * v(0) - s)) - // v(s) = (speedhalflife * v(0) - s) / speedhalflife - - maxdist = speedhalflife * v0; - //print("max dist = ", ftos(maxdist), "\n"); + E0_m = 0.5 * v0 * v0; + maxdist = E0_m / constant; + // maxdist = 0.5 * v0 * v0 / constant + dprint("max dist = ", ftos(maxdist), "\n"); if(maxdist <= 0) return 0; @@ -259,13 +251,22 @@ float W_BallisticBullet_LeaveSolid(entity e, vector vel, float speedhalflife) } dst = vlen(self.W_BallisticBullet_LeaveSolid_origin - org); - velfactor = (speedhalflife * v0 - dst) / (speedhalflife * v0); - - // t(s) = speedhalflife * log((speedhalflife * v(0)) / (speedhalflife * v(0) - s)) - dt = speedhalflife * log((speedhalflife * v0) / (speedhalflife * v0 - dst)); + // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass + Es_m = E0_m - constant * dst; + if(Es_m < 0) + { + print("ballistics: error condition Es_m < 0\n"); + print("dst = ", ftos(dst), "\n"); + print("maxdist = ", ftos(maxdist), "\n"); + Es_m = 0; + return 0; + } + vs = sqrt(2 * Es_m); + velfactor = vs / v0; - //print("slowdown by ", ftos(dst), " units = ", ftos(velfactor), "\n"); - //print("takes time ", ftos(dt), "\n"); + dt = dst / (0.5 * (v0 + vs)); + // this is not correct, but the differential equations have no analytic + // solution - and these times are very small anyway self.W_BallisticBullet_LeaveSolid_think_save = self.think; self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink; @@ -300,7 +301,7 @@ void W_BallisticBullet_Touch (void) self.projectiledeathtype |= HITTYPE_BOUNCE; } -void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, float lifetime, float damage, float headshotbonus, float force, float dtype, float tracereffects, float gravityfactor) +void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, float lifetime, float damage, float headshotbonus, float force, float dtype, float tracereffects, float gravityfactor, float bulletconstant) { entity proj; proj = spawn(); @@ -318,7 +319,8 @@ void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, f proj.velocity = (dir + randomvec() * spread) * pSpeed; W_SetupProjectileVelocity(proj); proj.angles = vectoangles(proj.velocity); - proj.dmg_radius = cvar("g_ballistics_solidspeedhalflife"); + proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant; + // so: bulletconstant = bullet mass / area of bullet circle setmodel(proj, "models/tracer.mdl"); setsize(proj, '0 0 0', '0 0 0'); setorigin(proj, start); @@ -342,9 +344,9 @@ void fireBullet (vector start, vector dir, float spread, float damage, float for if(cvar("g_ballistics_force")) { if (DEATH_ISWEAPON(dtype, WEP_SHOTGUN)) - fireBallisticBullet(start, dir, spread, cvar("g_ballistics_force_shotgun_speed"), 5, damage, 0, force, dtype, 0, 1); + fireBallisticBullet(start, dir, spread, cvar("g_ballistics_force_shotgun_speed"), 5, damage, 0, force, dtype, 0, 1, cvar("g_ballistics_force_shotgun_bulletconstant")); else - fireBallisticBullet(start, dir, spread, cvar("g_ballistics_force_uzi_speed"), 5, damage, 0, force, dtype, 0, 1); + fireBallisticBullet(start, dir, spread, cvar("g_ballistics_force_uzi_speed"), 5, damage, 0, force, dtype, 0, 1, cvar("g_ballistics_force_shotgun_bulletconstant")); return; } diff --git a/data/weapons.cfg b/data/weapons.cfg index ef09a58ab..0b9d82ef1 100644 --- a/data/weapons.cfg +++ b/data/weapons.cfg @@ -3,7 +3,7 @@ // // And... don't forget to edit weaponsPro.cfg too. -set cvar_check_weapons 5a724edde37f7db7608cf03f262a1f34 +set cvar_check_weapons 7abbcf55140e618bfaa97b587fc7aef7 set g_start_weapon_laser 1 set g_start_weapon_shotgun 1 @@ -350,11 +350,12 @@ set g_balance_campingrifle_primary_damage 90 set g_balance_campingrifle_primary_headshotaddeddamage 160 set g_balance_campingrifle_primary_spread 0 set g_balance_campingrifle_primary_force 2 -set g_balance_campingrifle_primary_speed 40000 +set g_balance_campingrifle_primary_speed 30000 set g_balance_campingrifle_primary_lifetime 5 set g_balance_campingrifle_primary_refire 0.7 set g_balance_campingrifle_primary_animtime 0.3 set g_balance_campingrifle_primary_ammo 10 +set g_balance_campingrifle_primary_bulletconstant 130 // 97.5 qu set g_balance_campingrifle_secondary_damage 60 set g_balance_campingrifle_secondary_headshotaddeddamage 0 set g_balance_campingrifle_secondary_spread 0.02 @@ -365,3 +366,4 @@ set g_balance_campingrifle_secondary_refire 0.12 set g_balance_campingrifle_secondary_animtime 0.06 set g_balance_campingrifle_secondary_ammo 10 set g_balance_campingrifle_secondary_health 5 +set g_balance_campingrifle_secondary_bulletconstant 130 // 6.9 qu diff --git a/data/weaponsPro.cfg b/data/weaponsPro.cfg index b15e36732..97920ce0b 100644 --- a/data/weaponsPro.cfg +++ b/data/weaponsPro.cfg @@ -1,4 +1,4 @@ -set cvar_check_weapons 5a724edde37f7db7608cf03f262a1f34 +set cvar_check_weapons 7abbcf55140e618bfaa97b587fc7aef7 set g_start_weapon_laser 1 set g_start_weapon_shotgun 1 @@ -345,11 +345,12 @@ set g_balance_campingrifle_primary_damage 90 set g_balance_campingrifle_primary_headshotaddeddamage 160 set g_balance_campingrifle_primary_spread 0 set g_balance_campingrifle_primary_force 2 -set g_balance_campingrifle_primary_speed 40000 +set g_balance_campingrifle_primary_speed 30000 set g_balance_campingrifle_primary_lifetime 5 set g_balance_campingrifle_primary_refire 0.7 set g_balance_campingrifle_primary_animtime 0.3 set g_balance_campingrifle_primary_ammo 10 +set g_balance_campingrifle_primary_bulletconstant 130 // 97.5 qu set g_balance_campingrifle_secondary_damage 60 set g_balance_campingrifle_secondary_headshotaddeddamage 0 set g_balance_campingrifle_secondary_spread 0.02 @@ -360,3 +361,4 @@ set g_balance_campingrifle_secondary_refire 0.12 set g_balance_campingrifle_secondary_animtime 0.06 set g_balance_campingrifle_secondary_ammo 10 set g_balance_campingrifle_secondary_health 5 +set g_balance_campingrifle_secondary_bulletconstant 130 // 6.9 qu -- 2.39.2