X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=blobdiff_plain;f=qcsrc%2Fcommon%2Fweapons%2Fweapon%2Fdevastator.qc;h=3049225c4ffef66b20f13adfce03616ee2ac13e2;hp=1454a501298fa316b4983027be15add3fe801a84;hb=90f4259bf39b41bb370dbc2efc6017ad0aa596e7;hpb=9e92e0e5bdad9052ff3463f67f3fb5a2ab812ed2 diff --git a/qcsrc/common/weapons/weapon/devastator.qc b/qcsrc/common/weapons/weapon/devastator.qc index 1454a5012..3049225c4 100644 --- a/qcsrc/common/weapons/weapon/devastator.qc +++ b/qcsrc/common/weapons/weapon/devastator.qc @@ -14,50 +14,53 @@ CLASS(Devastator, Weapon) /* wepimg */ ATTRIB(Devastator, model2, string, "weaponrocketlauncher"); /* refname */ ATTRIB(Devastator, netname, string, "devastator"); /* wepname */ ATTRIB(Devastator, m_name, string, _("Devastator")); + +#define X(BEGIN, P, END, class, prefix) \ + BEGIN(class) \ + P(class, prefix, ammo, float, NONE) \ + P(class, prefix, animtime, float, NONE) \ + P(class, prefix, damageforcescale, float, NONE) \ + P(class, prefix, damage, float, NONE) \ + P(class, prefix, detonatedelay, float, NONE) \ + P(class, prefix, edgedamage, float, NONE) \ + P(class, prefix, force, float, NONE) \ + P(class, prefix, guidedelay, float, NONE) \ + P(class, prefix, guidegoal, float, NONE) \ + P(class, prefix, guideratedelay, float, NONE) \ + P(class, prefix, guiderate, float, NONE) \ + P(class, prefix, guidestop, float, NONE) \ + P(class, prefix, health, float, NONE) \ + P(class, prefix, lifetime, float, NONE) \ + P(class, prefix, radius, float, NONE) \ + P(class, prefix, refire, float, NONE) \ + P(class, prefix, reload_ammo, float, NONE) \ + P(class, prefix, reload_time, float, NONE) \ + P(class, prefix, remote_damage, float, NONE) \ + P(class, prefix, remote_edgedamage, float, NONE) \ + P(class, prefix, remote_force, float, NONE) \ + P(class, prefix, remote_jump_damage, float, NONE) \ + P(class, prefix, remote_jump_radius, float, NONE) \ + P(class, prefix, remote_jump_velocity_z_add, float, NONE) \ + P(class, prefix, remote_jump_velocity_z_max, float, NONE) \ + P(class, prefix, remote_jump_velocity_z_min, float, NONE) \ + P(class, prefix, remote_radius, float, NONE) \ + P(class, prefix, speedaccel, float, NONE) \ + P(class, prefix, speedstart, float, NONE) \ + P(class, prefix, speed, float, NONE) \ + P(class, prefix, switchdelay_drop, float, NONE) \ + P(class, prefix, switchdelay_raise, float, NONE) \ + P(class, prefix, weaponreplace, string,NONE) \ + P(class, prefix, weaponstartoverride, float, NONE) \ + P(class, prefix, weaponstart, float, NONE) \ + P(class, prefix, weaponthrowable, float, NONE) \ + END() + W_PROPS(X, Devastator, devastator) +#undef X + ENDCLASS(Devastator) -REGISTER_WEAPON(DEVASTATOR, NEW(Devastator)); - -#define DEVASTATOR_SETTINGS(w_cvar,w_prop) DEVASTATOR_SETTINGS_LIST(w_cvar, w_prop, DEVASTATOR, devastator) -#define DEVASTATOR_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ - w_cvar(id, sn, NONE, ammo) \ - w_cvar(id, sn, NONE, animtime) \ - w_cvar(id, sn, NONE, damage) \ - w_cvar(id, sn, NONE, damageforcescale) \ - w_cvar(id, sn, NONE, detonatedelay) \ - w_cvar(id, sn, NONE, edgedamage) \ - w_cvar(id, sn, NONE, force) \ - w_cvar(id, sn, NONE, guidedelay) \ - w_cvar(id, sn, NONE, guidegoal) \ - w_cvar(id, sn, NONE, guiderate) \ - w_cvar(id, sn, NONE, guideratedelay) \ - w_cvar(id, sn, NONE, guidestop) \ - w_cvar(id, sn, NONE, health) \ - w_cvar(id, sn, NONE, lifetime) \ - w_cvar(id, sn, NONE, radius) \ - w_cvar(id, sn, NONE, refire) \ - w_cvar(id, sn, NONE, remote_damage) \ - w_cvar(id, sn, NONE, remote_edgedamage) \ - w_cvar(id, sn, NONE, remote_force) \ - w_cvar(id, sn, NONE, remote_jump_damage) \ - w_cvar(id, sn, NONE, remote_jump_radius) \ - w_cvar(id, sn, NONE, remote_jump_velocity_z_add) \ - w_cvar(id, sn, NONE, remote_jump_velocity_z_max) \ - w_cvar(id, sn, NONE, remote_jump_velocity_z_min) \ - w_cvar(id, sn, NONE, remote_radius) \ - w_cvar(id, sn, NONE, speed) \ - w_cvar(id, sn, NONE, speedaccel) \ - w_cvar(id, sn, NONE, speedstart) \ - w_prop(id, sn, float, reloading_ammo, reload_ammo) \ - w_prop(id, sn, float, reloading_time, reload_time) \ - w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ - w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ - w_prop(id, sn, string, weaponreplace, weaponreplace) \ - w_prop(id, sn, float, weaponstart, weaponstart) \ - w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ - w_prop(id, sn, float, weaponthrowable, weaponthrowable) +REGISTER_WEAPON(DEVASTATOR, devastator, NEW(Devastator)); #ifdef SVQC -DEVASTATOR_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) .float rl_release; .float rl_detonate_later; #endif @@ -69,76 +72,82 @@ spawnfunc(weapon_rocketlauncher) { spawnfunc_weapon_devastator(this); } .entity lastrocket; -void W_Devastator_Unregister(void) -{SELFPARAM(); - if(self.realowner && self.realowner.lastrocket == self) +void W_Devastator_Unregister(entity this) +{ + if(this.realowner && this.realowner.lastrocket == this) { - self.realowner.lastrocket = world; - // self.realowner.rl_release = 1; + this.realowner.lastrocket = NULL; + // this.realowner.rl_release = 1; } } -void W_Devastator_Explode(void) -{SELFPARAM(); - W_Devastator_Unregister(); +void W_Devastator_Explode(entity this, entity directhitentity) +{ + W_Devastator_Unregister(this); - if(other.takedamage == DAMAGE_AIM) - if(IS_PLAYER(other)) - if(DIFF_TEAM(self.realowner, other)) - if(other.deadflag == DEAD_NO) - if(IsFlying(other)) - Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); + if(directhitentity.takedamage == DAMAGE_AIM) + if(IS_PLAYER(directhitentity)) + if(DIFF_TEAM(this.realowner, directhitentity)) + if(!IS_DEAD(directhitentity)) + if(IsFlying(directhitentity)) + Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); - self.event_damage = func_null; - self.takedamage = DAMAGE_NO; + this.event_damage = func_null; + this.takedamage = DAMAGE_NO; RadiusDamage( - self, - self.realowner, + this, + this.realowner, WEP_CVAR(devastator, damage), WEP_CVAR(devastator, edgedamage), WEP_CVAR(devastator, radius), - world, - world, + NULL, + NULL, WEP_CVAR(devastator, force), - self.projectiledeathtype, - other + this.projectiledeathtype, + directhitentity ); - if(self.realowner.weapon == WEP_DEVASTATOR.m_id) + Weapon thiswep = WEP_DEVASTATOR; + if(PS(this.realowner).m_weapon == thiswep) { - if(self.realowner.WEP_AMMO(DEVASTATOR) < WEP_CVAR(devastator, ammo)) - if(!(self.realowner.items & IT_UNLIMITED_WEAPON_AMMO)) + if(this.realowner.(thiswep.ammo_field) < WEP_CVAR(devastator, ammo)) + if(!(this.realowner.items & IT_UNLIMITED_WEAPON_AMMO)) { - self.realowner.cnt = WEP_DEVASTATOR.m_id; + this.realowner.cnt = WEP_DEVASTATOR.m_id; int slot = 0; // TODO: unhardcode - ATTACK_FINISHED(self.realowner, slot) = time; - self.realowner.switchweapon = w_getbestweapon(self.realowner); + ATTACK_FINISHED(this.realowner, slot) = time; + PS(this.realowner).m_switchweapon = w_getbestweapon(this.realowner); } } - remove(self); + remove(this); +} + +void W_Devastator_Explode_think(entity this) +{ + W_Devastator_Explode(this, NULL); } -void W_Devastator_DoRemoteExplode(.entity weaponentity) -{SELFPARAM(); - W_Devastator_Unregister(); +void W_Devastator_DoRemoteExplode(entity this, .entity weaponentity) +{ + W_Devastator_Unregister(this); - self.event_damage = func_null; - self.takedamage = DAMAGE_NO; + this.event_damage = func_null; + this.takedamage = DAMAGE_NO; float handled_as_rocketjump = false; entity head = WarpZone_FindRadius( - self.origin, + this.origin, WEP_CVAR(devastator, remote_jump_radius), false ); while(head) { - if(head.takedamage && (head == self.realowner)) + if(head.takedamage && (head == this.realowner)) { - float distance_to_head = vlen(self.origin - head.WarpZone_findradius_nearest); + float distance_to_head = vlen(this.origin - head.WarpZone_findradius_nearest); if(distance_to_head <= WEP_CVAR(devastator, remote_jump_radius)) { // we handled this as a rocketjump :) @@ -155,16 +164,16 @@ void W_Devastator_DoRemoteExplode(.entity weaponentity) // now do the damage RadiusDamage( - self, + this, head, WEP_CVAR(devastator, remote_jump_damage), WEP_CVAR(devastator, remote_jump_damage), WEP_CVAR(devastator, remote_jump_radius), - world, + NULL, head, 0, - self.projectiledeathtype | HITTYPE_BOUNCE, - world + this.projectiledeathtype | HITTYPE_BOUNCE, + NULL ); break; } @@ -173,43 +182,44 @@ void W_Devastator_DoRemoteExplode(.entity weaponentity) } RadiusDamage( - self, - self.realowner, + this, + this.realowner, WEP_CVAR(devastator, remote_damage), WEP_CVAR(devastator, remote_edgedamage), WEP_CVAR(devastator, remote_radius), - (handled_as_rocketjump ? head : world), - world, + (handled_as_rocketjump ? head : NULL), + NULL, WEP_CVAR(devastator, remote_force), - self.projectiledeathtype | HITTYPE_BOUNCE, - world + this.projectiledeathtype | HITTYPE_BOUNCE, + NULL ); - if(self.realowner.weapon == WEP_DEVASTATOR.m_id) + Weapon thiswep = WEP_DEVASTATOR; + if(PS(this.realowner).m_weapon == thiswep) { - if(self.realowner.WEP_AMMO(DEVASTATOR) < WEP_CVAR(devastator, ammo)) - if(!(self.realowner.items & IT_UNLIMITED_WEAPON_AMMO)) + if(this.realowner.(thiswep.ammo_field) < WEP_CVAR(devastator, ammo)) + if(!(this.realowner.items & IT_UNLIMITED_WEAPON_AMMO)) { - self.realowner.cnt = WEP_DEVASTATOR.m_id; + this.realowner.cnt = WEP_DEVASTATOR.m_id; int slot = weaponslot(weaponentity); - ATTACK_FINISHED(self.realowner, slot) = time; - self.realowner.switchweapon = w_getbestweapon(self.realowner); + ATTACK_FINISHED(this.realowner, slot) = time; + PS(this.realowner).m_switchweapon = w_getbestweapon(this.realowner); } } - remove(self); + remove(this); } -void W_Devastator_RemoteExplode(.entity weaponentity) -{SELFPARAM(); - if(self.realowner.deadflag == DEAD_NO) - if(self.realowner.lastrocket) +void W_Devastator_RemoteExplode(entity this, .entity weaponentity) +{ + if(!IS_DEAD(this.realowner)) + if(this.realowner.lastrocket) { - if((self.spawnshieldtime >= 0) - ? (time >= self.spawnshieldtime) // timer - : (vlen(NearestPointOnBox(self.realowner, self.origin) - self.origin) > WEP_CVAR(devastator, remote_radius)) // safety device + if((this.spawnshieldtime >= 0) + ? (time >= this.spawnshieldtime) // timer + : (vdist(NearestPointOnBox(this.realowner, this.origin) - this.origin, >, WEP_CVAR(devastator, remote_radius))) // safety device ) { - W_Devastator_DoRemoteExplode(weaponentity); + W_Devastator_DoRemoteExplode(this, weaponentity); } } } @@ -247,114 +257,110 @@ vector W_Devastator_SteerTo(vector thisdir, vector goaldir, float maxturn_cos) // normalize(thisdir + goaldir) // normalize(0) -void W_Devastator_Think(void) -{SELFPARAM(); +void W_Devastator_Think(entity this) +{ vector desireddir, olddir, newdir, desiredorigin, goal; float velspeed, f; - self.nextthink = time; - if(time > self.cnt) + this.nextthink = time; + if(time > this.cnt) { - other = world; - self.projectiledeathtype |= HITTYPE_BOUNCE; - W_Devastator_Explode(); + this.projectiledeathtype |= HITTYPE_BOUNCE; + W_Devastator_Explode(this, NULL); return; } // accelerate - makevectors(self.angles.x * '-1 0 0' + self.angles.y * '0 1 0'); - velspeed = WEP_CVAR(devastator, speed) * W_WeaponSpeedFactor() - (self.velocity * v_forward); + makevectors(this.angles.x * '-1 0 0' + this.angles.y * '0 1 0'); + velspeed = WEP_CVAR(devastator, speed) * W_WeaponSpeedFactor(this.realowner) - (this.velocity * v_forward); if(velspeed > 0) - self.velocity = self.velocity + v_forward * min(WEP_CVAR(devastator, speedaccel) * W_WeaponSpeedFactor() * frametime, velspeed); + this.velocity = this.velocity + v_forward * min(WEP_CVAR(devastator, speedaccel) * W_WeaponSpeedFactor(this.realowner) * frametime, velspeed); // laser guided, or remote detonation - if(self.realowner.weapon == WEP_DEVASTATOR.m_id) + if(PS(this.realowner).m_weapon == WEP_DEVASTATOR) { - if(self == self.realowner.lastrocket) - if(!self.realowner.rl_release) - if(!self.BUTTON_ATCK2) + if(this == this.realowner.lastrocket) + if(!this.realowner.rl_release) + if(!PHYS_INPUT_BUTTON_ATCK2(this)) if(WEP_CVAR(devastator, guiderate)) - if(time > self.pushltime) - if(self.realowner.deadflag == DEAD_NO) + if(time > this.pushltime) + if(!IS_DEAD(this.realowner)) { f = WEP_CVAR(devastator, guideratedelay); if(f) - f = bound(0, (time - self.pushltime) / f, 1); + f = bound(0, (time - this.pushltime) / f, 1); else f = 1; - velspeed = vlen(self.velocity); + velspeed = vlen(this.velocity); - makevectors(self.realowner.v_angle); - desireddir = WarpZone_RefSys_TransformVelocity(self.realowner, self, v_forward); - desiredorigin = WarpZone_RefSys_TransformOrigin(self.realowner, self, self.realowner.origin + self.realowner.view_ofs); - olddir = normalize(self.velocity); + makevectors(this.realowner.v_angle); + desireddir = WarpZone_RefSys_TransformVelocity(this.realowner, this, v_forward); + desiredorigin = WarpZone_RefSys_TransformOrigin(this.realowner, this, this.realowner.origin + this.realowner.view_ofs); + olddir = normalize(this.velocity); // now it gets tricky... we want to move like some curve to approximate the target direction // but we are limiting the rate at which we can turn! - goal = desiredorigin + ((self.origin - desiredorigin) * desireddir + WEP_CVAR(devastator, guidegoal)) * desireddir; - newdir = W_Devastator_SteerTo(olddir, normalize(goal - self.origin), cos(WEP_CVAR(devastator, guiderate) * f * frametime * DEG2RAD)); + goal = desiredorigin + ((this.origin - desiredorigin) * desireddir + WEP_CVAR(devastator, guidegoal)) * desireddir; + newdir = W_Devastator_SteerTo(olddir, normalize(goal - this.origin), cos(WEP_CVAR(devastator, guiderate) * f * frametime * DEG2RAD)); - self.velocity = newdir * velspeed; - self.angles = vectoangles(self.velocity); + this.velocity = newdir * velspeed; + this.angles = vectoangles(this.velocity); - if(!self.count) + if(!this.count) { - Send_Effect(EFFECT_ROCKET_GUIDE, self.origin, self.velocity, 1); + Send_Effect(EFFECT_ROCKET_GUIDE, this.origin, this.velocity, 1); // TODO add a better sound here - sound(self.realowner, CH_WEAPON_B, SND_ROCKET_MODE, VOL_BASE, ATTN_NORM); - self.count = 1; + sound(this.realowner, CH_WEAPON_B, SND_ROCKET_MODE, VOL_BASE, ATTN_NORM); + this.count = 1; } } .entity weaponentity = weaponentities[0]; // TODO: unhardcode - if(self.rl_detonate_later) - W_Devastator_RemoteExplode(weaponentity); + if(this.rl_detonate_later) + W_Devastator_RemoteExplode(this, weaponentity); } - if(self.csqcprojectile_clientanimate == 0) - UpdateCSQCProjectile(self); + if(this.csqcprojectile_clientanimate == 0) + UpdateCSQCProjectile(this); } -void W_Devastator_Touch(void) -{SELFPARAM(); - if(WarpZone_Projectile_Touch()) +void W_Devastator_Touch(entity this, entity toucher) +{ + if(WarpZone_Projectile_Touch(this, toucher)) { - if(wasfreed(self)) - W_Devastator_Unregister(); + if(wasfreed(this)) + W_Devastator_Unregister(this); return; } - W_Devastator_Unregister(); - W_Devastator_Explode(); + W_Devastator_Unregister(this); + W_Devastator_Explode(this, toucher); } -void W_Devastator_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{SELFPARAM(); - if(self.health <= 0) +void W_Devastator_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{ + if(this.health <= 0) return; - if(!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions + if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_projectiles_damage says to halt - self.health = self.health - damage; - self.angles = vectoangles(self.velocity); + this.health = this.health - damage; + this.angles = vectoangles(this.velocity); - if(self.health <= 0) - W_PrepareExplosionByDamage(attacker, W_Devastator_Explode); + if(this.health <= 0) + W_PrepareExplosionByDamage(this, attacker, W_Devastator_Explode_think); } -void W_Devastator_Attack(Weapon thiswep) -{SELFPARAM(); - entity missile; - entity flash; - - W_DecreaseAmmo(thiswep, self, WEP_CVAR(devastator, ammo)); +void W_Devastator_Attack(Weapon thiswep, entity actor) +{ + W_DecreaseAmmo(thiswep, actor, WEP_CVAR(devastator, ammo)); - W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', false, 5, SND(ROCKET_FIRE), CH_WEAPON_A, WEP_CVAR(devastator, damage)); + W_SetupShot_ProjectileSize(actor, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR(devastator, damage)); Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - missile = WarpZone_RefSys_SpawnSameRefSys(self); - missile.owner = missile.realowner = self; - self.lastrocket = missile; + entity missile = WarpZone_RefSys_SpawnSameRefSys(actor); + missile.owner = missile.realowner = actor; + actor.lastrocket = missile; if(WEP_CVAR(devastator, detonatedelay) >= 0) missile.spawnshieldtime = time + WEP_CVAR(devastator, detonatedelay); else @@ -370,7 +376,7 @@ void W_Devastator_Attack(Weapon thiswep) missile.event_damage = W_Devastator_Damage; missile.damagedbycontents = true; - missile.movetype = MOVETYPE_FLY; + set_movetype(missile, MOVETYPE_FLY); PROJECTILE_MAKETRIGGER(missile); missile.projectiledeathtype = WEP_DEVASTATOR.m_id; setsize(missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot @@ -379,8 +385,8 @@ void W_Devastator_Attack(Weapon thiswep) W_SetupProjVelocity_Basic(missile, WEP_CVAR(devastator, speedstart), 0); missile.angles = vectoangles(missile.velocity); - missile.touch = W_Devastator_Touch; - missile.think = W_Devastator_Think; + settouch(missile, W_Devastator_Touch); + setthink(missile, W_Devastator_Think); missile.nextthink = time; missile.cnt = time + WEP_CVAR(devastator, lifetime); missile.flags = FL_PROJECTILE; @@ -389,261 +395,240 @@ void W_Devastator_Attack(Weapon thiswep) CSQCProjectile(missile, WEP_CVAR(devastator, guiderate) == 0 && WEP_CVAR(devastator, speedaccel) == 0, PROJECTILE_ROCKET, false); // because of fly sound // muzzle flash for 1st person view - flash = spawn(); + entity flash = spawn(); setmodel(flash, MDL_DEVASTATOR_MUZZLEFLASH); // precision set below SUB_SetFade(flash, time, 0.1); flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - W_AttachToShotorg(self, flash, '5 0 0'); + W_AttachToShotorg(actor, flash, '5 0 0'); // common properties - MUTATOR_CALLHOOK(EditProjectile, self, missile); + MUTATOR_CALLHOOK(EditProjectile, actor, missile); } - #if 0 - METHOD(Devastator, wr_aim, void(entity thiswep)) - { - // aim and decide to fire if appropriate - self.BUTTON_ATCK = bot_aim(WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), false); - if(skill >= 2) // skill 0 and 1 bots won't detonate rockets! - { - // decide whether to detonate rockets - entity missile, targetlist, targ; - targetlist = findchainfloat(bot_attack, true); - for(missile = world; (missile = find(missile, classname, "rocket")); ) if(missile.realowner == self) - { - targ = targetlist; - while(targ) - { - if(targ != missile.realowner && vlen(targ.origin - missile.origin) < WEP_CVAR(devastator, radius)) - { - self.BUTTON_ATCK2 = true; - break; - } - targ = targ.chain; - } - } - - if(self.BUTTON_ATCK2) self.BUTTON_ATCK = false; - } - } - #else - METHOD(Devastator, wr_aim, void(entity thiswep)) - { - // aim and decide to fire if appropriate - self.BUTTON_ATCK = bot_aim(WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), false); - if(skill >= 2) // skill 0 and 1 bots won't detonate rockets! - { - // decide whether to detonate rockets - entity missile, targetlist, targ; - float edgedamage, coredamage, edgeradius, recipricoledgeradius, d; - float selfdamage, teamdamage, enemydamage; - edgedamage = WEP_CVAR(devastator, edgedamage); - coredamage = WEP_CVAR(devastator, damage); - edgeradius = WEP_CVAR(devastator, radius); - recipricoledgeradius = 1 / edgeradius; - selfdamage = 0; - teamdamage = 0; - enemydamage = 0; - targetlist = findchainfloat(bot_attack, true); - missile = find(world, classname, "rocket"); - while(missile) - { - if(missile.realowner != self) - { - missile = find(missile, classname, "rocket"); - continue; - } - targ = targetlist; - while(targ) - { - d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - missile.origin); - d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000); - // count potential damage according to type of target - if(targ == self) - selfdamage = selfdamage + d; - else if(targ.team == self.team && teamplay) - teamdamage = teamdamage + d; - else if(bot_shouldattack(targ)) - enemydamage = enemydamage + d; - targ = targ.chain; - } - missile = find(missile, classname, "rocket"); - } - float desirabledamage; - desirabledamage = enemydamage; - if(time > self.invincible_finished && time > self.spawnshieldtime) - desirabledamage = desirabledamage - selfdamage * autocvar_g_balance_selfdamagepercent; - if(teamplay && self.team) - desirabledamage = desirabledamage - teamdamage; - - missile = find(world, classname, "rocket"); - while(missile) - { - if(missile.realowner != self) - { - missile = find(missile, classname, "rocket"); - continue; - } - makevectors(missile.v_angle); - targ = targetlist; - if(skill > 9) // normal players only do this for the target they are tracking - { - targ = targetlist; - while(targ) - { - if( - (v_forward * normalize(missile.origin - targ.origin)< 0.1) - && desirabledamage > 0.1*coredamage - )self.BUTTON_ATCK2 = true; - targ = targ.chain; - } - }else{ - float distance; distance= bound(300,vlen(self.origin-self.enemy.origin),30000); - //As the distance gets larger, a correct detonation gets near imposible - //Bots are assumed to use the rocket spawnfunc_light to see if the rocket gets near a player - if(v_forward * normalize(missile.origin - self.enemy.origin)< 0.1) - if(IS_PLAYER(self.enemy)) - if(desirabledamage >= 0.1*coredamage) - if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1)) - self.BUTTON_ATCK2 = true; - // dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n"); - } - - missile = find(missile, classname, "rocket"); - } - // if we would be doing at X percent of the core damage, detonate it - // but don't fire a new shot at the same time! - if(desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events - self.BUTTON_ATCK2 = true; - if((skill > 6.5) && (selfdamage > self.health)) - self.BUTTON_ATCK2 = false; - //if(self.BUTTON_ATCK2 == true) - // dprint(ftos(desirabledamage),"\n"); - if(self.BUTTON_ATCK2 == true) self.BUTTON_ATCK = false; - } - } - #endif - METHOD(Devastator, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) - { - if(WEP_CVAR(devastator, reload_ammo) && actor.clip_load < WEP_CVAR(devastator, ammo)) { // forced reload - Weapon w = get_weaponinfo(actor.weapon); - w.wr_reload(w); - } else { - if(fire & 1) - { - if(actor.rl_release || WEP_CVAR(devastator, guidestop)) - if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(devastator, refire))) - { - W_Devastator_Attack(thiswep); - weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready); - actor.rl_release = 0; - } - } - else - actor.rl_release = 1; - - if(fire & 2) - if(actor.switchweapon == WEP_DEVASTATOR.m_id) - { - entity rock; - bool rockfound = false; - for(rock = world; (rock = find(rock, classname, "rocket")); ) if(rock.realowner == actor) - { - if(!rock.rl_detonate_later) - { - rock.rl_detonate_later = true; - rockfound = true; - } - } - if(rockfound) - sound(actor, CH_WEAPON_B, SND_ROCKET_DET, VOL_BASE, ATTN_NORM); - } - } - } - METHOD(Devastator, wr_init, void(entity thiswep)) - { - DEVASTATOR_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); - } - METHOD(Devastator, wr_setup, void(entity thiswep)) - { - self.rl_release = 1; - } - METHOD(Devastator, wr_checkammo1, bool(entity thiswep)) - { - #if 0 - // don't switch while guiding a missile - if(ATTACK_FINISHED(self, slot) <= time || self.weapon != WEP_DEVASTATOR.m_id) - { - ammo_amount = false; - if(WEP_CVAR(devastator, reload_ammo)) - { - if(self.WEP_AMMO(DEVASTATOR) < WEP_CVAR(devastator, ammo) && self.(weapon_load[WEP_DEVASTATOR.m_id]) < WEP_CVAR(devastator, ammo)) - ammo_amount = true; - } - else if(self.WEP_AMMO(DEVASTATOR) < WEP_CVAR(devastator, ammo)) - ammo_amount = true; - return !ammo_amount; - } - #endif - #if 0 - if(self.rl_release == 0) - { - LOG_INFOF("W_Devastator(WR_CHECKAMMO1): %d, %.2f, %d: TRUE\n", self.rl_release, self.WEP_AMMO(DEVASTATOR), WEP_CVAR(devastator, ammo)); - return true; - } - else - { - ammo_amount = self.WEP_AMMO(DEVASTATOR) >= WEP_CVAR(devastator, ammo); - ammo_amount += self.(weapon_load[WEP_DEVASTATOR.m_id]) >= WEP_CVAR(devastator, ammo); - LOG_INFOF("W_Devastator(WR_CHECKAMMO1): %d, %.2f, %d: %s\n", self.rl_release, self.WEP_AMMO(DEVASTATOR), WEP_CVAR(devastator, ammo), (ammo_amount ? "TRUE" : "FALSE")); - return ammo_amount; - } - #else - float ammo_amount = self.WEP_AMMO(DEVASTATOR) >= WEP_CVAR(devastator, ammo); - ammo_amount += self.(weapon_load[WEP_DEVASTATOR.m_id]) >= WEP_CVAR(devastator, ammo); - return ammo_amount; - #endif - } - METHOD(Devastator, wr_checkammo2, bool(entity thiswep)) - { - return false; - } - METHOD(Devastator, wr_config, void(entity thiswep)) - { - DEVASTATOR_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS); - } - METHOD(Devastator, wr_resetplayer, void(entity thiswep)) - { - self.lastrocket = NULL; // stop rocket guiding, no revenge from the grave! - self.rl_release = 0; - } - METHOD(Devastator, wr_reload, void(entity thiswep)) - { - W_Reload(self, WEP_CVAR(devastator, ammo), SND(RELOAD)); - } - METHOD(Devastator, wr_suicidemessage, int(entity thiswep)) - { - return WEAPON_DEVASTATOR_SUICIDE; - } - METHOD(Devastator, wr_killmessage, int(entity thiswep)) - { - if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH)) - return WEAPON_DEVASTATOR_MURDER_SPLASH; - else - return WEAPON_DEVASTATOR_MURDER_DIRECT; - } +#if 0 +METHOD(Devastator, wr_aim, void(entity thiswep, entity actor)) +{ + entity this = actor; + // aim and decide to fire if appropriate + PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), false); + if(skill >= 2) // skill 0 and 1 bots won't detonate rockets! + { + // decide whether to detonate rockets + entity missile, targetlist, targ; + targetlist = findchainfloat(bot_attack, true); + for(missile = NULL; (missile = find(missile, classname, "rocket")); ) if(missile.realowner == actor) + { + targ = targetlist; + while(targ) + { + if(targ != missile.realowner && vlen(targ.origin - missile.origin) < WEP_CVAR(devastator, radius)) + { + PHYS_INPUT_BUTTON_ATCK2(actor) = true; + break; + } + targ = targ.chain; + } + } + + if(PHYS_INPUT_BUTTON_ATCK2(actor)) PHYS_INPUT_BUTTON_ATCK(actor) = false; + } +} +#else +METHOD(Devastator, wr_aim, void(entity thiswep, entity actor)) +{ + // aim and decide to fire if appropriate + PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), false); + if(skill >= 2) // skill 0 and 1 bots won't detonate rockets! + { + // decide whether to detonate rockets + float edgedamage, coredamage, edgeradius, recipricoledgeradius; + float selfdamage, teamdamage, enemydamage; + edgedamage = WEP_CVAR(devastator, edgedamage); + coredamage = WEP_CVAR(devastator, damage); + edgeradius = WEP_CVAR(devastator, radius); + recipricoledgeradius = 1 / edgeradius; + selfdamage = 0; + teamdamage = 0; + enemydamage = 0; + FOREACH_ENTITY_ENT(realowner, actor, + { + if(it.classname != "rocket") continue; + + entity rocket = it; + FOREACH_ENTITY_FLOAT(bot_attack, true, + { + float d = vlen(it.origin + (it.mins + it.maxs) * 0.5 - rocket.origin); + d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000); + // count potential damage according to type of target + if(it == actor) + selfdamage = selfdamage + d; + else if(SAME_TEAM(it, actor)) + teamdamage = teamdamage + d; + else if(bot_shouldattack(actor, it)) + enemydamage = enemydamage + d; + }); + }); + float desirabledamage; + desirabledamage = enemydamage; + if(time > actor.invincible_finished && time > actor.spawnshieldtime) + desirabledamage = desirabledamage - selfdamage * autocvar_g_balance_selfdamagepercent; + if(teamplay && actor.team) + desirabledamage = desirabledamage - teamdamage; + + makevectors(actor.v_angle); + FOREACH_ENTITY_ENT(realowner, actor, + { + if(it.classname != "rocket") continue; + + if(skill > 9) // normal players only do this for the target they are tracking + { + entity rocket = it; + FOREACH_ENTITY_FLOAT(bot_attack, true, + { + if((v_forward * normalize(rocket.origin - it.origin) < 0.1) + && desirabledamage > 0.1 * coredamage + ) PHYS_INPUT_BUTTON_ATCK2(actor) = true; + }); + } + else + { + //As the distance gets larger, a correct detonation gets near imposible + //Bots are assumed to use the rocket spawnfunc_light to see if the rocket gets near a player + if((v_forward * normalize(it.origin - actor.enemy.origin) < 0.1) + && IS_PLAYER(actor.enemy) + && (desirabledamage >= 0.1 * coredamage) + ) + { + float distance = bound(300, vlen(actor.origin - actor.enemy.origin), 30000); + if(random() / distance * 300 > frametime * bound(0, (10 - skill) * 0.2, 1)) + PHYS_INPUT_BUTTON_ATCK2(actor) = true; + } + } + }); + // if we would be doing at X percent of the core damage, detonate it + // but don't fire a new shot at the same time! + if(desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events + PHYS_INPUT_BUTTON_ATCK2(actor) = true; + if((skill > 6.5) && (selfdamage > actor.health)) + PHYS_INPUT_BUTTON_ATCK2(actor) = false; + //if(PHYS_INPUT_BUTTON_ATCK2(actor) == true) + // dprint(ftos(desirabledamage),"\n"); + if(PHYS_INPUT_BUTTON_ATCK2(actor)) PHYS_INPUT_BUTTON_ATCK(actor) = false; + } +} +#endif +METHOD(Devastator, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) +{ + if(WEP_CVAR(devastator, reload_ammo) && actor.clip_load < WEP_CVAR(devastator, ammo)) { // forced reload + thiswep.wr_reload(thiswep, actor, weaponentity); + } else { + if(fire & 1) + { + if(actor.rl_release || WEP_CVAR(devastator, guidestop)) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(devastator, refire))) + { + W_Devastator_Attack(thiswep, actor); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready); + actor.rl_release = 0; + } + } + else + actor.rl_release = 1; + + if(fire & 2) + if(PS(actor).m_switchweapon == WEP_DEVASTATOR) + { + entity rock; + bool rockfound = false; + for(rock = NULL; (rock = find(rock, classname, "rocket")); ) if(rock.realowner == actor) + { + if(!rock.rl_detonate_later) + { + rock.rl_detonate_later = true; + rockfound = true; + } + } + if(rockfound) + sound(actor, CH_WEAPON_B, SND_ROCKET_DET, VOL_BASE, ATTN_NORM); + } + } +} +METHOD(Devastator, wr_setup, void(entity thiswep, entity actor)) +{ + actor.rl_release = 1; +} +METHOD(Devastator, wr_checkammo1, bool(entity thiswep, entity actor)) +{ + #if 0 + // don't switch while guiding a missile + if(ATTACK_FINISHED(actor, slot) <= time || PS(actor).m_weapon != WEP_DEVASTATOR) + { + ammo_amount = false; + if(WEP_CVAR(devastator, reload_ammo)) + { + if(actor.(thiswep.ammo_field) < WEP_CVAR(devastator, ammo) && actor.(weapon_load[WEP_DEVASTATOR.m_id]) < WEP_CVAR(devastator, ammo)) + ammo_amount = true; + } + else if(actor.(thiswep.ammo_field) < WEP_CVAR(devastator, ammo)) + ammo_amount = true; + return !ammo_amount; + } + #endif + #if 0 + if(actor.rl_release == 0) + { + LOG_INFOF("W_Devastator(WR_CHECKAMMO1): %d, %.2f, %d: TRUE\n", actor.rl_release, actor.(thiswep.ammo_field), WEP_CVAR(devastator, ammo)); + return true; + } + else + { + ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(devastator, ammo); + ammo_amount += actor.(weapon_load[WEP_DEVASTATOR.m_id]) >= WEP_CVAR(devastator, ammo); + LOG_INFOF("W_Devastator(WR_CHECKAMMO1): %d, %.2f, %d: %s\n", actor.rl_release, actor.(thiswep.ammo_field), WEP_CVAR(devastator, ammo), (ammo_amount ? "TRUE" : "FALSE")); + return ammo_amount; + } + #else + float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(devastator, ammo); + ammo_amount += actor.(weapon_load[WEP_DEVASTATOR.m_id]) >= WEP_CVAR(devastator, ammo); + return ammo_amount; + #endif +} +METHOD(Devastator, wr_checkammo2, bool(entity thiswep, entity actor)) +{ + return false; +} +METHOD(Devastator, wr_resetplayer, void(entity thiswep, entity actor)) +{ + actor.lastrocket = NULL; // stop rocket guiding, no revenge from the grave! + actor.rl_release = 0; +} +METHOD(Devastator, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) +{ + W_Reload(actor, WEP_CVAR(devastator, ammo), SND_RELOAD); +} +METHOD(Devastator, wr_suicidemessage, Notification(entity thiswep)) +{ + return WEAPON_DEVASTATOR_SUICIDE; +} +METHOD(Devastator, wr_killmessage, Notification(entity thiswep)) +{ + if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH)) + return WEAPON_DEVASTATOR_MURDER_SPLASH; + else + return WEAPON_DEVASTATOR_MURDER_DIRECT; +} #endif #ifdef CSQC - METHOD(Devastator, wr_impacteffect, void(entity thiswep)) - { - vector org2; - org2 = w_org + w_backoff * 12; - pointparticles(EFFECT_ROCKET_EXPLODE, org2, '0 0 0', 1); - if(!w_issilent) - sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTN_NORM); - } +METHOD(Devastator, wr_impacteffect, void(entity thiswep, entity actor)) +{ + vector org2; + org2 = w_org + w_backoff * 12; + pointparticles(EFFECT_ROCKET_EXPLODE, org2, '0 0 0', 1); + if(!w_issilent) + sound(actor, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTN_NORM); +} #endif #endif