REGISTER_WEAPON(
/* WEP_##id */ DEVASTATOR,
/* function */ W_Devastator,
-/* ammotype */ IT_ROCKETS,
+/* ammotype */ ammo_rockets,
/* impulse */ 9,
/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
/* rating */ BOT_PICKUP_RATING_HIGH,
+/* color */ '1 1 0',
/* model */ "rl",
-/* shortname */ "devastator",
-/* fullname */ _("Devastator")
+/* crosshair */ "gfx/crosshairrocketlauncher 0.5875",
+/* refname */ "devastator",
+/* wepname */ _("Devastator")
);
-#define DEVASTATOR_SETTINGS(weapon) \
- WEP_ADD_CVAR(weapon, MO_NONE, ammo) \
- WEP_ADD_CVAR(weapon, MO_NONE, animtime) \
- WEP_ADD_CVAR(weapon, MO_NONE, damage) \
- WEP_ADD_CVAR(weapon, MO_NONE, damageforcescale) \
- WEP_ADD_CVAR(weapon, MO_NONE, detonatedelay) \
- WEP_ADD_CVAR(weapon, MO_NONE, edgedamage) \
- WEP_ADD_CVAR(weapon, MO_NONE, force) \
- WEP_ADD_CVAR(weapon, MO_NONE, guidedelay) \
- WEP_ADD_CVAR(weapon, MO_NONE, guidegoal) \
- WEP_ADD_CVAR(weapon, MO_NONE, guiderate) \
- WEP_ADD_CVAR(weapon, MO_NONE, guideratedelay) \
- WEP_ADD_CVAR(weapon, MO_NONE, guidestop) \
- WEP_ADD_CVAR(weapon, MO_NONE, health) \
- WEP_ADD_CVAR(weapon, MO_NONE, lifetime) \
- WEP_ADD_CVAR(weapon, MO_NONE, radius) \
- WEP_ADD_CVAR(weapon, MO_NONE, refire) \
- WEP_ADD_CVAR(weapon, MO_NONE, remote_damage) \
- WEP_ADD_CVAR(weapon, MO_NONE, remote_edgedamage) \
- WEP_ADD_CVAR(weapon, MO_NONE, remote_force) \
- WEP_ADD_CVAR(weapon, MO_NONE, remote_radius) \
- WEP_ADD_CVAR(weapon, MO_NONE, speed) \
- WEP_ADD_CVAR(weapon, MO_NONE, speedaccel) \
- WEP_ADD_CVAR(weapon, MO_NONE, speedstart) \
- WEP_ADD_PROP(weapon, reloading_ammo, reload_ammo) \
- WEP_ADD_PROP(weapon, reloading_time, reload_time) \
- WEP_ADD_PROP(weapon, switchdelay_raise, switchdelay_raise) \
- WEP_ADD_PROP(weapon, switchdelay_drop, switchdelay_drop)
+#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_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)
#ifdef SVQC
-DEVASTATOR_SETTINGS(devastator)
+DEVASTATOR_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
.float rl_release;
.float rl_detonate_later;
#endif
#else
#ifdef SVQC
-void spawnfunc_weapon_rocketlauncher()
-{
- weapon_defaultspawnfunc(WEP_DEVASTATOR);
-}
+void spawnfunc_weapon_devastator() { weapon_defaultspawnfunc(WEP_DEVASTATOR); }
+void spawnfunc_weapon_rocketlauncher() { spawnfunc_weapon_devastator(); }
void W_Devastator_Unregister()
{
if(other.takedamage == DAMAGE_AIM)
if(IS_PLAYER(other))
- if(IsDifferentTeam(self.realowner, 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 (self.realowner.weapon == WEP_DEVASTATOR)
{
- if(self.realowner.ammo_rockets < WEP_CVAR(devastator, ammo))
+ if(self.realowner.WEP_AMMO(DEVASTATOR) < WEP_CVAR(devastator, ammo))
{
self.realowner.cnt = WEP_DEVASTATOR;
ATTACK_FINISHED(self.realowner) = time;
if (self.realowner.weapon == WEP_DEVASTATOR)
{
- if(self.realowner.ammo_rockets < WEP_CVAR(devastator, ammo))
+ if(self.realowner.WEP_AMMO(DEVASTATOR) < WEP_CVAR(devastator, ammo))
{
self.realowner.cnt = WEP_DEVASTATOR;
ATTACK_FINISHED(self.realowner) = time;
if (self.realowner.weapon == WEP_DEVASTATOR)
{
if(self == self.realowner.lastrocket)
- if not(self.realowner.rl_release)
- if not(self.BUTTON_ATCK2)
+ if(!self.realowner.rl_release)
+ if(!self.BUTTON_ATCK2)
if(WEP_CVAR(devastator, guiderate))
if(time > self.pushltime)
if(self.realowner.deadflag == DEAD_NO)
entity missile;
entity flash;
- W_DecreaseAmmo(ammo_rockets, WEP_CVAR(devastator, ammo), WEP_CVAR(devastator, reload_ammo));
+ W_DecreaseAmmo(WEP_CVAR(devastator, ammo));
W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 5, "weapons/rocket_fire.wav", CH_WEAPON_A, WEP_CVAR(devastator, damage));
pointparticles(particleeffectnum("rocketlauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
setorigin (missile, w_shotorg - v_forward * 3); // move it back so it hits the wall at the right point
- W_SetupProjectileVelocity(missile, WEP_CVAR(devastator, speedstart), 0);
+ W_SetupProjVelocity_Basic(missile, WEP_CVAR(devastator, speedstart), 0);
missile.angles = vectoangles (missile.velocity);
missile.touch = W_Devastator_Touch;
entity rock;
float rockfound;
float ammo_amount;
-
- if (req == WR_AIM)
+ switch(req)
{
- // 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!
+ case WR_AIM: // WEAPONTODO: rewrite this, it's WAY too complicated for what it should be
{
- // 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)
+ // 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!
{
- 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
+ // 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)
+ while(targ)
{
- if (
- (v_forward * normalize(missile.origin - targ.origin)< 0.1)
- && desirabledamage > 0.1*coredamage
- )self.BUTTON_ATCK2 = TRUE;
+ if(targ != missile.realowner && vlen(targ.origin - missile.origin) < WEP_CVAR(devastator, radius))
+ {
+ self.BUTTON_ATCK2 = TRUE;
+ break;
+ }
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(self.BUTTON_ATCK2) self.BUTTON_ATCK = FALSE;
}
- // 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;
+
+ return TRUE;
}
- }
- else if (req == WR_THINK)
- {
- if(WEP_CVAR(devastator, reload_ammo) && self.clip_load < WEP_CVAR(devastator, ammo)) // forced reload
- weapon_action(self.weapon, WR_RELOAD);
- else
+ case WR_THINK:
{
- if (self.BUTTON_ATCK)
+ if(WEP_CVAR(devastator, reload_ammo) && self.clip_load < WEP_CVAR(devastator, ammo)) // forced reload
+ WEP_ACTION(self.weapon, WR_RELOAD);
+ else
{
- if(self.rl_release || WEP_CVAR(devastator, guidestop))
- if(weapon_prepareattack(0, WEP_CVAR(devastator, refire)))
+ if(self.BUTTON_ATCK)
{
- W_Devastator_Attack();
- weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready);
- self.rl_release = 0;
+ if(self.rl_release || WEP_CVAR(devastator, guidestop))
+ if(weapon_prepareattack(0, WEP_CVAR(devastator, refire)))
+ {
+ W_Devastator_Attack();
+ weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready);
+ self.rl_release = 0;
+ }
}
- }
- else
- self.rl_release = 1;
+ else
+ self.rl_release = 1;
- if (self.BUTTON_ATCK2)
- {
- rockfound = 0;
- for(rock = world; (rock = find(rock, classname, "rocket")); ) if(rock.realowner == self)
+ if(self.BUTTON_ATCK2)
{
- if(!rock.rl_detonate_later)
+ rockfound = 0;
+ for(rock = world; (rock = find(rock, classname, "rocket")); ) if(rock.realowner == self)
{
- rock.rl_detonate_later = TRUE;
- rockfound = 1;
+ if(!rock.rl_detonate_later)
+ {
+ rock.rl_detonate_later = TRUE;
+ rockfound = 1;
+ }
}
+ if(rockfound)
+ sound (self, CH_WEAPON_B, "weapons/rocket_det.wav", VOL_BASE, ATTN_NORM);
}
- if(rockfound)
- sound (self, CH_WEAPON_B, "weapons/rocket_det.wav", VOL_BASE, ATTN_NORM);
}
+
+ return TRUE;
}
- }
- else if (req == WR_PRECACHE)
- {
- if(autocvar_sv_precacheweapons)
+ case WR_INIT:
{
- precache_model ("models/flash.md3");
- precache_model ("models/weapons/g_rl.md3");
- precache_model ("models/weapons/v_rl.md3");
- precache_model ("models/weapons/h_rl.iqm");
- precache_sound ("weapons/rocket_det.wav");
- precache_sound ("weapons/rocket_fire.wav");
- precache_sound ("weapons/rocket_mode.wav");
+ if(autocvar_sv_precacheweapons)
+ {
+ precache_model("models/flash.md3");
+ precache_model("models/weapons/g_rl.md3");
+ precache_model("models/weapons/v_rl.md3");
+ precache_model("models/weapons/h_rl.iqm");
+ precache_sound("weapons/rocket_det.wav");
+ precache_sound("weapons/rocket_fire.wav");
+ precache_sound("weapons/rocket_mode.wav");
+ }
+ DEVASTATOR_SETTINGS(WEP_SKIPCVAR, WEP_SET_PROP)
+ return TRUE;
}
-
- #define WEP_ADD_CVAR(weapon,mode,name) /*nothing*/
- #define WEP_ADD_PROP(weapon,prop,name) get_weaponinfo(WEP_DEVASTATOR).##prop = autocvar_g_balance_##weapon##_##name;
- ARC_SETTINGS(devastator)
- #undef WEP_ADD_CVAR
- #undef WEP_ADD_PROP
- //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else
- }
- else if (req == WR_SETUP)
- {
- weapon_setup(WEP_DEVASTATOR);
- self.current_ammo = ammo_rockets;
- self.rl_release = 1;
- }
- else if (req == WR_CHECKAMMO1)
- {
- // don't switch while guiding a missile
- if (ATTACK_FINISHED(self) <= time || self.weapon != WEP_DEVASTATOR)
+ case WR_SETUP:
+ {
+ self.rl_release = 1;
+ return TRUE;
+ }
+ case WR_CHECKAMMO1:
{
- ammo_amount = FALSE;
- if(WEP_CVAR(devastator, reload_ammo))
+ // don't switch while guiding a missile
+ if (ATTACK_FINISHED(self) <= time || self.weapon != WEP_DEVASTATOR)
{
- if(self.ammo_rockets < WEP_CVAR(devastator, ammo) && self.(weapon_load[WEP_DEVASTATOR]) < WEP_CVAR(devastator, ammo))
+ ammo_amount = FALSE;
+ if(WEP_CVAR(devastator, reload_ammo))
+ {
+ if(self.WEP_AMMO(DEVASTATOR) < WEP_CVAR(devastator, ammo) && self.(weapon_load[WEP_DEVASTATOR]) < WEP_CVAR(devastator, ammo))
+ ammo_amount = TRUE;
+ }
+ else if(self.WEP_AMMO(DEVASTATOR) < WEP_CVAR(devastator, ammo))
ammo_amount = TRUE;
+ return !ammo_amount;
}
- else if(self.ammo_rockets < WEP_CVAR(devastator, ammo))
- ammo_amount = TRUE;
- return !ammo_amount;
+
+ return TRUE;
+ }
+ case WR_CHECKAMMO2:
+ {
+ return FALSE;
+ }
+ case WR_CONFIG:
+ {
+ DEVASTATOR_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS)
+ return TRUE;
+ }
+ case WR_RESETPLAYER:
+ {
+ self.rl_release = 0;
+ return TRUE;
+ }
+ case WR_RELOAD:
+ {
+ W_Reload(WEP_CVAR(devastator, ammo), "weapons/reload.wav");
+ return TRUE;
+ }
+ case WR_SUICIDEMESSAGE:
+ {
+ return WEAPON_ROCKETLAUNCHER_SUICIDE;
+ }
+ case WR_KILLMESSAGE:
+ {
+ if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH))
+ return WEAPON_ROCKETLAUNCHER_MURDER_SPLASH;
+ else
+ return WEAPON_ROCKETLAUNCHER_MURDER_DIRECT;
}
- }
- else if (req == WR_CHECKAMMO2)
- return FALSE;
- else if (req == WR_RESETPLAYER)
- {
- self.rl_release = 0;
- }
- else if (req == WR_RELOAD)
- {
- W_Reload(WEP_CVAR(devastator, ammo), WEP_CVAR(devastator, reload_ammo), WEP_CVAR(devastator, reload_time), "weapons/reload.wav");
- }
- else if (req == WR_SUICIDEMESSAGE)
- {
- return WEAPON_ROCKETLAUNCHER_SUICIDE;
- }
- else if (req == WR_KILLMESSAGE)
- {
- if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH))
- return WEAPON_ROCKETLAUNCHER_MURDER_SPLASH;
- else
- return WEAPON_ROCKETLAUNCHER_MURDER_DIRECT;
}
return TRUE;
}
#ifdef CSQC
float W_Devastator(float req)
{
- if(req == WR_IMPACTEFFECT)
+ switch(req)
{
- vector org2;
- org2 = w_org + w_backoff * 12;
- pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1);
- if(!w_issilent)
- sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
- }
- else if(req == WR_PRECACHE)
- {
- precache_sound("weapons/rocket_impact.wav");
+ case WR_IMPACTEFFECT:
+ {
+ vector org2;
+ org2 = w_org + w_backoff * 12;
+ pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1);
+ if(!w_issilent)
+ sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+
+ return TRUE;
+ }
+ case WR_INIT:
+ {
+ precache_sound("weapons/rocket_impact.wav");
+ return TRUE;
+ }
+ case WR_ZOOMRETICLE:
+ {
+ // no weapon specific image for this weapon
+ return FALSE;
+ }
}
return TRUE;
}