From: Mario Date: Sat, 31 Dec 2016 05:08:32 +0000 (+0000) Subject: Merge branch 'martin-t/maxshotdist' into 'master' X-Git-Tag: xonotic-v0.8.2~341 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=78a337e337b8f08759b210146cdb8d2fd534e923;hp=fca1fc158c44eff425d73fa279900144249aca16 Merge branch 'martin-t/maxshotdist' into 'master' Fix fireBullet(), increase MAX_SHOT_DISTANCE See merge request !395 --- diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index 5f88e78edf..2521548e15 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -653,7 +653,7 @@ float TrueAimCheck() mv = MOVE_NORMAL; if(zoomscript_caught) { - tracebox(view_origin, '0 0 0', '0 0 0', view_origin + view_forward * MAX_SHOT_DISTANCE, mv, ta); + tracebox(view_origin, '0 0 0', '0 0 0', view_origin + view_forward * max_shot_distance, mv, ta); return EnemyHitCheck(); } break; @@ -679,7 +679,7 @@ float TrueAimCheck() vecs = decompressShotOrigin(STAT(SHOTORG)); - traceline(traceorigin, traceorigin + view_forward * MAX_SHOT_DISTANCE, mv, ta); + traceline(traceorigin, traceorigin + view_forward * max_shot_distance, mv, ta); trueaimpoint = trace_endpos; if(vdist((trueaimpoint - traceorigin), <, g_trueaim_minrange)) @@ -972,7 +972,7 @@ void HUD_Crosshair(entity this) float shottype; // wcross_origin = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight; - wcross_origin = project_3d_to_2d(view_origin + MAX_SHOT_DISTANCE * view_forward); + wcross_origin = project_3d_to_2d(view_origin + max_shot_distance * view_forward); wcross_origin.z = 0; if(autocvar_crosshair_hittest) { diff --git a/qcsrc/common/debug.qh b/qcsrc/common/debug.qh index 6d580bd2d6..10a69a7068 100644 --- a/qcsrc/common/debug.qh +++ b/qcsrc/common/debug.qh @@ -280,7 +280,7 @@ MUTATOR_HOOKFUNCTION(trace, SV_StartFrame) vector forward = '0 0 0'; vector right = '0 0 0'; vector up = '0 0 0'; MAKEVECTORS(makevectors, it.v_angle, forward, right, up); vector pos = it.origin + it.view_ofs; - traceline(pos, pos + forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, it); + traceline(pos, pos + forward * max_shot_distance, MOVE_NORMAL, it); FOREACH_ENTITY(true, { it.solid = it.solid_prev; it.solid_prev = 0; diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc index a0017f94df..ee75728630 100644 --- a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc @@ -1,6 +1,6 @@ #include "sv_spawn_near_teammate.qh" -const float FLOAT_MAX = 340282346638528859811704183484516925440.0f; +#include float autocvar_g_spawn_near_teammate_distance; int autocvar_g_spawn_near_teammate_ignore_spawnpoint; diff --git a/qcsrc/common/turrets/sv_turrets.qc b/qcsrc/common/turrets/sv_turrets.qc index c90eaff1fe..83db075b42 100644 --- a/qcsrc/common/turrets/sv_turrets.qc +++ b/qcsrc/common/turrets/sv_turrets.qc @@ -1224,13 +1224,13 @@ void turret_initparams(entity tur) tur.shot_force = bound(0.001, (TRY(tur.shot_force) : tur.shot_dmg * 0.5 + tur.shot_radius * 0.5 ), 5000); tur.shot_volly = bound(1, (TRY(tur.shot_volly) : 1 ), floor(tur.ammo_max / tur.shot_dmg)); tur.shot_volly_refire = bound(tur.shot_refire, (TRY(tur.shot_volly_refire) : tur.shot_refire * tur.shot_volly ), 60); - tur.target_range = bound(0, (TRY(tur.target_range) : tur.shot_speed * 0.5 ), MAX_SHOT_DISTANCE); - tur.target_range_min = bound(0, (TRY(tur.target_range_min) : tur.shot_radius * 2 ), MAX_SHOT_DISTANCE); - tur.target_range_optimal = bound(0, (TRY(tur.target_range_optimal) : tur.target_range * 0.5 ), MAX_SHOT_DISTANCE); + tur.target_range = bound(0, (TRY(tur.target_range) : tur.shot_speed * 0.5 ), max_shot_distance); + tur.target_range_min = bound(0, (TRY(tur.target_range_min) : tur.shot_radius * 2 ), max_shot_distance); + tur.target_range_optimal = bound(0, (TRY(tur.target_range_optimal) : tur.target_range * 0.5 ), max_shot_distance); tur.aim_maxrotate = bound(0, (TRY(tur.aim_maxrotate) : 90 ), 360); tur.aim_maxpitch = bound(0, (TRY(tur.aim_maxpitch) : 20 ), 90); tur.aim_speed = bound(0.1, (TRY(tur.aim_speed) : 36 ), 1000); - tur.aim_firetolerance_dist = bound(0.1, (TRY(tur.aim_firetolerance_dist) : 5 + (tur.shot_radius * 2) ), MAX_SHOT_DISTANCE); + tur.aim_firetolerance_dist = bound(0.1, (TRY(tur.aim_firetolerance_dist) : 5 + (tur.shot_radius * 2) ), max_shot_distance); tur.target_select_rangebias = bound(-10, (TRY(tur.target_select_rangebias) : 1 ), 10); tur.target_select_samebias = bound(-10, (TRY(tur.target_select_samebias) : 1 ), 10); tur.target_select_anglebias = bound(-10, (TRY(tur.target_select_anglebias) : 1 ), 10); @@ -1270,7 +1270,7 @@ bool turret_initialize(entity this, Turret tur) this.netname = tur.netname; load_unit_settings(this, 0); - if(!this.team || !teamplay) { this.team = MAX_SHOT_DISTANCE; } + if(!this.team || !teamplay) { this.team = FLOAT_MAX; } if(!this.ticrate) { this.ticrate = ((this.turret_flags & TUR_FLAG_SUPPORT) ? 0.2 : 0.1); } if(!this.health) { this.health = 1000; } if(!this.shot_refire) { this.shot_refire = 1; } diff --git a/qcsrc/common/turrets/turret/plasma.qc b/qcsrc/common/turrets/turret/plasma.qc index d161436ab0..283cf6e514 100644 --- a/qcsrc/common/turrets/turret/plasma.qc +++ b/qcsrc/common/turrets/turret/plasma.qc @@ -10,7 +10,7 @@ METHOD(PlasmaTurret, tr_attack, void(PlasmaTurret this, entity it)) { if(g_instagib) { - FireRailgunBullet (it, it.tur_shotorg, it.tur_shotorg + it.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000, + FireRailgunBullet (it, it.tur_shotorg, it.tur_shotorg + it.tur_shotdir_updated * max_shot_distance, 10000000000, 800, 0, 0, 0, 0, DEATH_TURRET_PLASMA.m_id); Send_Effect(EFFECT_VORTEX_MUZZLEFLASH, it.tur_shotorg, it.tur_shotdir_updated * 1000, 1); diff --git a/qcsrc/common/turrets/turret/plasma_dual.qc b/qcsrc/common/turrets/turret/plasma_dual.qc index 9e6d80b2fd..7859d26e34 100644 --- a/qcsrc/common/turrets/turret/plasma_dual.qc +++ b/qcsrc/common/turrets/turret/plasma_dual.qc @@ -9,7 +9,7 @@ spawnfunc(turret_plasma_dual) { if (!turret_initialize(this, TUR_PLASMA_DUAL)) d METHOD(DualPlasmaTurret, tr_attack, void(DualPlasmaTurret thistur, entity it)) { if (g_instagib) { - FireRailgunBullet (it, it.tur_shotorg, it.tur_shotorg + it.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000, + FireRailgunBullet (it, it.tur_shotorg, it.tur_shotorg + it.tur_shotdir_updated * max_shot_distance, 10000000000, 800, 0, 0, 0, 0, DEATH_TURRET_PLASMA.m_id); diff --git a/qcsrc/common/vehicles/vehicle/bumblebee.qc b/qcsrc/common/vehicles/vehicle/bumblebee.qc index 573e46aa77..ad376e809b 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee.qc +++ b/qcsrc/common/vehicles/vehicle/bumblebee.qc @@ -167,7 +167,7 @@ bool bumblebee_gunner_frame(entity this, float dt) VEHICLE_UPDATE_PLAYER(this, vehic, shield, bumblebee); ad = gettaginfo(gun, gettagindex(gun, "fire")); - traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, gun); + traceline(ad, ad + v_forward * max_shot_distance, MOVE_NORMAL, gun); UpdateAuxiliaryXhair(this, trace_endpos, ('1 0 0' * this.vehicle_reload1) + ('0 1 0' *(1 - this.vehicle_reload1)), 0); diff --git a/qcsrc/common/vehicles/vehicle/raptor.qc b/qcsrc/common/vehicles/vehicle/raptor.qc index 7c433f9c76..cd2335665d 100644 --- a/qcsrc/common/vehicles/vehicle/raptor.qc +++ b/qcsrc/common/vehicles/vehicle/raptor.qc @@ -351,7 +351,7 @@ bool raptor_frame(entity this, float dt) /* ad = ad * 0.5; v_forward = vf * 0.5; - traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, vehic); + traceline(ad, ad + v_forward * max_shot_distance, MOVE_NORMAL, vehic); UpdateAuxiliaryXhair(this, trace_endpos, '0 1 0', 0); */ diff --git a/qcsrc/common/vehicles/vehicle/spiderbot.qc b/qcsrc/common/vehicles/vehicle/spiderbot.qc index 6ad46946d7..a259e0c1eb 100644 --- a/qcsrc/common/vehicles/vehicle/spiderbot.qc +++ b/qcsrc/common/vehicles/vehicle/spiderbot.qc @@ -72,15 +72,15 @@ bool spiderbot_frame(entity this, float dt) vf += v_forward; ad = ad * 0.5; v_forward = vf * 0.5; - traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, vehic); + traceline(ad, ad + v_forward * max_shot_distance, MOVE_NORMAL, vehic); UpdateAuxiliaryXhair(this, trace_endpos, ('1 0 0' * this.vehicle_reload1) + ('0 1 0' * (1 - this.vehicle_reload1)), 0); #else vector ad = gettaginfo(vehic.gun1, gettagindex(vehic.gun1, "barrels")); - traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, vehic); + traceline(ad, ad + v_forward * max_shot_distance, MOVE_NORMAL, vehic); UpdateAuxiliaryXhair(this, trace_endpos, ('1 0 0' * this.vehicle_reload1) + ('0 1 0' * (1 - this.vehicle_reload1)), 0); vector vf = ad; ad = gettaginfo(vehic.gun2, gettagindex(vehic.gun2, "barrels")); - traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, vehic); + traceline(ad, ad + v_forward * max_shot_distance, MOVE_NORMAL, vehic); UpdateAuxiliaryXhair(this, trace_endpos, ('1 0 0' * this.vehicle_reload1) + ('0 1 0' * (1 - this.vehicle_reload1)), 1); ad = 0.5 * (ad + vf); #endif diff --git a/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc b/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc index 5f7a2c31c8..1fde99b3fc 100644 --- a/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc +++ b/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc @@ -221,11 +221,11 @@ void spiderbot_rocket_do(entity this) rocket.pos1 = trace_endpos + randomvec() * (0.75 * autocvar_g_vehicle_spiderbot_rocket_radius); rocket.pos1_z = trace_endpos_z; - traceline(v, v + '0 0 1' * MAX_SHOT_DISTANCE, MOVE_WORLDONLY, this); + traceline(v, v + '0 0 1' * max_shot_distance, MOVE_WORLDONLY, this); float h1 = 0.75 * vlen(v - trace_endpos); //v = trace_endpos; - traceline(v , rocket.pos1 + '0 0 1' * MAX_SHOT_DISTANCE, MOVE_WORLDONLY, this); + traceline(v , rocket.pos1 + '0 0 1' * max_shot_distance, MOVE_WORLDONLY, this); float h2 = 0.75 * vlen(rocket.pos1 - v); rocket.velocity = spiberbot_calcartillery(v, rocket.pos1, ((h1 < h2) ? h1 : h2)); diff --git a/qcsrc/common/weapons/weapon.qh b/qcsrc/common/weapons/weapon.qh index b287b148f3..6220a0cea9 100644 --- a/qcsrc/common/weapons/weapon.qh +++ b/qcsrc/common/weapons/weapon.qh @@ -175,7 +175,7 @@ ENDCLASS(OffhandWeapon) .OffhandWeapon offhand; #endif -const int MAX_SHOT_DISTANCE = 32768; +int max_shot_distance = 32768; // determined by world mins/maxs when map loads // weapon flags const int WEP_TYPE_OTHER = 0x00; // not for damaging people diff --git a/qcsrc/common/weapons/weapon/vaporizer.qc b/qcsrc/common/weapons/weapon/vaporizer.qc index 91042b613b..79892920ab 100644 --- a/qcsrc/common/weapons/weapon/vaporizer.qc +++ b/qcsrc/common/weapons/weapon/vaporizer.qc @@ -193,7 +193,7 @@ void W_Vaporizer_Attack(Weapon thiswep, entity actor, .entity weaponentity) yoda = 0; damage_goodhits = 0; - FireRailgunBullet(actor, w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, vaporizer_damage, 800, 0, 0, 0, 0, WEP_VAPORIZER.m_id); + FireRailgunBullet(actor, w_shotorg, w_shotorg + w_shotdir * max_shot_distance, vaporizer_damage, 800, 0, 0, 0, 0, WEP_VAPORIZER.m_id); // do this now, as goodhits is disabled below SendCSQCVaporizerBeamParticle(actor, damage_goodhits); diff --git a/qcsrc/common/weapons/weapon/vortex.qc b/qcsrc/common/weapons/weapon/vortex.qc index bf3ce8185f..f7f20d2e8d 100644 --- a/qcsrc/common/weapons/weapon/vortex.qc +++ b/qcsrc/common/weapons/weapon/vortex.qc @@ -193,7 +193,7 @@ void W_Vortex_Attack(Weapon thiswep, entity actor, .entity weaponentity, float i yoda = 0; damage_goodhits = 0; - FireRailgunBullet(actor, w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, WEP_VORTEX.m_id); + FireRailgunBullet(actor, w_shotorg, w_shotorg + w_shotdir * max_shot_distance, mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, WEP_VORTEX.m_id); if(yoda && flying) Send_Notification(NOTIF_ONE, actor, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); diff --git a/qcsrc/lib/float.qh b/qcsrc/lib/float.qh new file mode 100644 index 0000000000..946319e443 --- /dev/null +++ b/qcsrc/lib/float.qh @@ -0,0 +1,3 @@ +#pragma once + +const float FLOAT_MAX = 340282346638528859811704183484516925440.0f; diff --git a/qcsrc/server/cheats.qc b/qcsrc/server/cheats.qc index 3d569222cd..631f382278 100644 --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@ -324,7 +324,7 @@ float CheatCommand(entity this, int argc) // effectname effectnum = _particleeffectnum(argv(1)); W_SetupShot(this, weaponentities[0], false, false, SND_Null, CH_WEAPON_A, 0); - traceline(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, MOVE_NORMAL, this); + traceline(w_shotorg, w_shotorg + w_shotdir * max_shot_distance, MOVE_NORMAL, this); __trailparticles(this, effectnum, w_shotorg, trace_endpos); DID_CHEAT(); break; diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index 39c42a5569..634530244e 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -32,7 +32,7 @@ void crosshair_trace(entity pl) { - traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl)); + traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * max_shot_distance, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl)); } .bool ctrace_solidchanged; void crosshair_trace_plusvisibletriggers(entity pl) @@ -56,7 +56,7 @@ void crosshair_trace_plusvisibletriggers(entity pl) } void WarpZone_crosshair_trace(entity pl) { - WarpZone_traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl)); + WarpZone_traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * max_shot_distance, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl)); } diff --git a/qcsrc/server/mutators/mutator/gamemode_assault.qc b/qcsrc/server/mutators/mutator/gamemode_assault.qc index 3998741434..28e91f26a3 100644 --- a/qcsrc/server/mutators/mutator/gamemode_assault.qc +++ b/qcsrc/server/mutators/mutator/gamemode_assault.qc @@ -1,5 +1,7 @@ #include "gamemode_assault.qh" +#include + .entity sprite; #define AS_ROUND_DELAY 5 @@ -549,7 +551,7 @@ MUTATOR_HOOKFUNCTION(as, TurretSpawn) { entity turret = M_ARGV(0, entity); - if(!turret.team || turret.team == MAX_SHOT_DISTANCE) + if(!turret.team || turret.team == FLOAT_MAX) turret.team = 5; // this gets reversed when match starts? } diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index 0918130159..fd9f340f5f 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -52,6 +52,11 @@ void InitGameplayMode() get_mi_min_max(1); world.mins = mi_min; world.maxs = mi_max; + // currently, NetRadiant's limit is 131072 qu for each side + // distance from one corner of a 131072qu cube to the opposite corner is approx. 227023 qu + // set the distance according to map size but don't go over the limit to avoid issues with float precision + // in case somebody makes extremely large maps + max_shot_distance = min(230000, vlen(world.maxs - world.mins)); MapInfo_LoadMapSettings(mapname); serverflags &= ~SERVERFLAG_TEAMPLAY; diff --git a/qcsrc/server/weapons/hitplot.qc b/qcsrc/server/weapons/hitplot.qc index 372f7357b4..355b3afbb8 100644 --- a/qcsrc/server/weapons/hitplot.qc +++ b/qcsrc/server/weapons/hitplot.qc @@ -66,7 +66,7 @@ void W_HitPlotAnalysis(entity player, vector screenforward, vector screenright, lag = 0; // only antilag for clients org = player.origin + player.view_ofs; - traceline_antilag_force(player, org, org + screenforward * MAX_SHOT_DISTANCE, MOVE_NORMAL, player, lag); + traceline_antilag_force(player, org, org + screenforward * max_shot_distance, MOVE_NORMAL, player, lag); if(IS_CLIENT(trace_ent) || IS_MONSTER(trace_ent)) { entity store = IS_CLIENT(trace_ent) ? CS(trace_ent) : trace_ent; diff --git a/qcsrc/server/weapons/tracing.qc b/qcsrc/server/weapons/tracing.qc index b49ed88f59..fd78c9120a 100644 --- a/qcsrc/server/weapons/tracing.qc +++ b/qcsrc/server/weapons/tracing.qc @@ -354,7 +354,7 @@ void fireBullet(entity this, vector start, vector dir, float spread, float max_s vector end; dir = normalize(dir + randomvec() * spread); - end = start + dir * MAX_SHOT_DISTANCE; + end = start + dir * max_shot_distance; fireBullet_last_hit = NULL; float solid_penetration_left = 1; @@ -399,6 +399,10 @@ void fireBullet(entity this, vector start, vector dir, float spread, float max_s start = trace_endpos; entity hit = trace_ent; + // traced up to max_shot_distance and didn't hit anything at all + if (trace_fraction == 1.0) + break; + // When hitting sky, stop. if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) break; diff --git a/qcsrc/server/weapons/tracing.qh b/qcsrc/server/weapons/tracing.qh index 21f12c1e4c..ed046465bc 100644 --- a/qcsrc/server/weapons/tracing.qh +++ b/qcsrc/server/weapons/tracing.qh @@ -9,7 +9,7 @@ vector w_shotend; // make sure you call makevectors first (FIXME?) void W_SetupShot_Dir_ProjectileSize_Range(entity ent, .entity weaponentity, vector s_forward, vector mi, vector ma, float antilag, float recoil, Sound snd, float chan, float maxdamage, float range); -#define W_SetupShot_Dir_ProjectileSize(ent,wepent,s_forward,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize_Range(ent, wepent, s_forward, mi, ma, antilag, recoil, snd, chan, maxdamage, MAX_SHOT_DISTANCE) +#define W_SetupShot_Dir_ProjectileSize(ent,wepent,s_forward,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize_Range(ent, wepent, s_forward, mi, ma, antilag, recoil, snd, chan, maxdamage, max_shot_distance) #define W_SetupShot_ProjectileSize(ent,wepent,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, wepent, v_forward, mi, ma, antilag, recoil, snd, chan, maxdamage) #define W_SetupShot_Dir(ent,wepent,s_forward,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, wepent, s_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage) #define W_SetupShot(ent,wepent,antilag,recoil,snd,chan,maxdamage) W_SetupShot_ProjectileSize(ent, wepent, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage)