--- /dev/null
- void M_Ogre_Attack_Grenade_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+#ifdef REGISTER_MONSTER
+REGISTER_MONSTER(
+/* MON_##id */ OGRE,
+/* functions */ M_Ogre, M_Ogre_Attack,
+/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RANGED | MONSTER_SIZE_BROKEN,
+/* mins,maxs */ '-36 -36 -20', '36 36 50',
+/* model */ "ogre.mdl",
+/* netname */ "ogre",
+/* fullname */ _("Ogre")
+);
+
+#else
+#ifdef SVQC
+#include "../../effects.qh"
+
+float autocvar_g_monster_ogre_health;
+var float autocvar_g_monster_ogre_damageforcescale = 0.4;
+float autocvar_g_monster_ogre_attack_machinegun_spread;
+float autocvar_g_monster_ogre_attack_machinegun_solidpenetration;
+float autocvar_g_monster_ogre_attack_machinegun_damage;
+float autocvar_g_monster_ogre_attack_machinegun_force;
+float autocvar_g_monster_ogre_attack_grenade_damage;
+float autocvar_g_monster_ogre_attack_grenade_edgedamage;
+float autocvar_g_monster_ogre_attack_grenade_radius;
+float autocvar_g_monster_ogre_attack_grenade_speed;
+float autocvar_g_monster_ogre_attack_grenade_speed_up;
+float autocvar_g_monster_ogre_attack_grenade_speed_z;
+float autocvar_g_monster_ogre_attack_grenade_spread;
+float autocvar_g_monster_ogre_attack_grenade_force;
+float autocvar_g_monster_ogre_attack_grenade_bouncefactor;
+float autocvar_g_monster_ogre_attack_grenade_bouncestop;
+float autocvar_g_monster_ogre_attack_grenade_lifetime;
+float autocvar_g_monster_ogre_attack_grenade_health;
+float autocvar_g_monster_ogre_attack_grenade_damageforcescale;
+float autocvar_g_monster_ogre_attack_melee_damage;
+float autocvar_g_monster_ogre_attack_melee_nonplayerdamage;
+float autocvar_g_monster_ogre_attack_melee_delay;
+float autocvar_g_monster_ogre_attack_melee_time;
+float autocvar_g_monster_ogre_attack_melee_range;
+float autocvar_g_monster_ogre_attack_melee_traces;
+float autocvar_g_monster_ogre_attack_melee_swing_up;
+float autocvar_g_monster_ogre_attack_melee_swing_side;
+float autocvar_g_monster_ogre_speed_stop;
+float autocvar_g_monster_ogre_speed_run;
+float autocvar_g_monster_ogre_speed_walk;
+
+const float ogre_anim_idle = 0;
+const float ogre_anim_walk = 1;
+const float ogre_anim_run = 2;
+const float ogre_anim_swing = 3;
+const float ogre_anim_smash = 4;
+const float ogre_anim_shoot = 5;
+const float ogre_anim_pain1 = 6;
+const float ogre_anim_pain2 = 7;
+const float ogre_anim_pain3 = 8;
+const float ogre_anim_pain4 = 9;
+const float ogre_anim_pain5 = 10;
+const float ogre_anim_death1 = 11;
+const float ogre_anim_death2 = 12;
+const float ogre_anim_pull = 13;
+
+void M_Ogre_Attack_MachineGun()
+{
+ vector dir = normalize(self.enemy.origin - self.origin);
+ vector org = self.origin + self.view_ofs + v_forward * 14;
+ sound (self, CH_WEAPON_A, W_Sound("uzi_fire"), VOL_BASE, ATTEN_NORM);
+
+ fireBullet(org, dir, autocvar_g_monster_ogre_attack_machinegun_spread, autocvar_g_monster_ogre_attack_machinegun_solidpenetration, autocvar_g_monster_ogre_attack_machinegun_damage, autocvar_g_monster_ogre_attack_machinegun_force, DEATH_MONSTER_OGRE_MACHINEGUN, 0);
+
+ Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, org, dir * 1000, 1);
+
+ // casing code
+ if (autocvar_g_casings >= 2)
+ SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self);
+}
+
+void M_Ogre_Attack_Grenade_Explode()
+{
+ self.event_damage = func_null;
+ self.takedamage = DAMAGE_NO;
+
+ if(self.movetype == MOVETYPE_NONE)
+ self.velocity = self.oldvelocity;
+
+ sound (self, CH_WEAPON_A, W_Sound("grenade_impact"), VOL_BASE, ATTEN_NORM);
+ pointparticles(particleeffectnum("grenade_explode"), self.origin, '0 0 0', 1);
+ RadiusDamage (self, self.realowner, autocvar_g_monster_ogre_attack_grenade_damage, autocvar_g_monster_ogre_attack_grenade_edgedamage, autocvar_g_monster_ogre_attack_grenade_radius, world, world, autocvar_g_monster_ogre_attack_grenade_force, self.projectiledeathtype, other);
+
+ remove (self);
+}
+
++void M_Ogre_Attack_Grenade_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+ if (self.health <= 0)
+ return;
+
+ if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
+ return; // g_projectiles_damage says to halt
+
+ self.health -= damage;
+
+ if (self.health <= 0)
+ W_PrepareExplosionByDamage(attacker, self.use);
+}
+
+void M_Ogre_Attack_Grenade_Touch()
+{
+ PROJECTILE_TOUCH;
+
+ if (other.takedamage == DAMAGE_AIM)
+ {
+ self.use();
+ return;
+ }
+
+ float r;
+ r = random() * 6;
+ if(r < 1)
+ spamsound (self, CH_SHOTS, W_Sound("grenade_bounce1"), VOL_BASE, ATTEN_NORM);
+ else if(r < 2)
+ spamsound (self, CH_SHOTS, W_Sound("grenade_bounce2"), VOL_BASE, ATTEN_NORM);
+ else if(r < 3)
+ spamsound (self, CH_SHOTS, W_Sound("grenade_bounce3"), VOL_BASE, ATTEN_NORM);
+ else if(r < 4)
+ spamsound (self, CH_SHOTS, W_Sound("grenade_bounce4"), VOL_BASE, ATTEN_NORM);
+ else if(r < 5)
+ spamsound (self, CH_SHOTS, W_Sound("grenade_bounce5"), VOL_BASE, ATTEN_NORM);
+ else
+ spamsound (self, CH_SHOTS, W_Sound("grenade_bounce6"), VOL_BASE, ATTEN_NORM);
+ self.projectiledeathtype |= HITTYPE_BOUNCE;
+}
+
+void M_Ogre_Attack_Grenade()
+{
+ entity gren;
+
+ sound (self, CH_WEAPON_A, W_Sound("grenade_fire"), VOL_BASE, ATTEN_NORM);
+
+ vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
+ vector org = self.origin + v_forward * 14 + '0 0 30' + v_right * -14;
+
+ makevectors(self.angles);
+
+ Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, org, dir * 1000, 1);
+
+ gren = spawn ();
+ gren.owner = gren.realowner = self;
+ gren.classname = "grenade";
+ gren.bot_dodge = true;
+ gren.bot_dodgerating = autocvar_g_monster_ogre_attack_grenade_damage;
+ gren.movetype = MOVETYPE_BOUNCE;
+ gren.bouncefactor = autocvar_g_monster_ogre_attack_grenade_bouncefactor;
+ gren.bouncestop = autocvar_g_monster_ogre_attack_grenade_bouncestop;
+ PROJECTILE_MAKETRIGGER(gren);
+ gren.projectiledeathtype = DEATH_MONSTER_OGRE_GRENADE;
+ setorigin(gren, org);
+ setsize(gren, '-3 -3 -3', '3 3 3');
+
+ gren.nextthink = time + autocvar_g_monster_ogre_attack_grenade_lifetime;
+ gren.think = adaptor_think2use_hittype_splash;
+ gren.use = M_Ogre_Attack_Grenade_Explode;
+ gren.touch = M_Ogre_Attack_Grenade_Touch;
+
+ gren.takedamage = DAMAGE_YES;
+ gren.health = autocvar_g_monster_ogre_attack_grenade_health;
+ gren.damageforcescale = autocvar_g_monster_ogre_attack_grenade_damageforcescale;
+ gren.event_damage = M_Ogre_Attack_Grenade_Damage;
+ gren.damagedbycontents = true;
+ gren.missile_flags = MIF_SPLASH | MIF_ARC;
+ W_SetupProjVelocity_Explicit(gren, dir, v_up, autocvar_g_monster_ogre_attack_grenade_speed, autocvar_g_monster_ogre_attack_grenade_speed_up, autocvar_g_monster_ogre_attack_grenade_speed_z, autocvar_g_monster_ogre_attack_grenade_spread, false);
+
+ gren.angles = vectoangles (gren.velocity);
+ gren.flags = FL_PROJECTILE;
+
+ CSQCProjectile(gren, true, PROJECTILE_GRENADE_BOUNCING, true);
+
+ other = gren; MUTATOR_CALLHOOK(EditProjectile);
+
+ self.attack_finished_single = time + 0.7;
+ self.state = 0;
+}
+
+.float ogre_swing_prev;
+.entity ogre_swing_alreadyhit;
+void M_Ogre_Attack_Chainsaw()
+{
+ // declarations
+ float i, f, swing, swing_factor, swing_damage, meleetime, is_player, is_monster;
+ entity target_victim;
+ vector targpos;
+
+ if(!self.cnt) // set start time of melee
+ {
+ self.cnt = time;
+ }
+
+ makevectors(self.realowner.angles); // update values for v_* vectors
+
+ // calculate swing percentage based on time
+ meleetime = autocvar_g_monster_ogre_attack_melee_time;
+ swing = bound(0, (self.cnt + meleetime - time) / meleetime, 10);
+ f = ((1 - swing) * autocvar_g_monster_ogre_attack_melee_traces);
+
+ // check to see if we can still continue, otherwise give up now
+ if(self.realowner.deadflag != DEAD_NO)
+ {
+ remove(self);
+ return;
+ }
+
+ // if okay, perform the traces needed for this frame
+ for(i=self.ogre_swing_prev; i < f; ++i)
+ {
+ swing_factor = ((1 - (i / autocvar_g_monster_ogre_attack_melee_traces)) * 2 - 1);
+
+ targpos = (self.realowner.origin + self.realowner.view_ofs
+ + (v_forward * autocvar_g_monster_ogre_attack_melee_range)
+ + (v_up * swing_factor * autocvar_g_monster_ogre_attack_melee_swing_up)
+ + (v_right * swing_factor * autocvar_g_monster_ogre_attack_melee_swing_side));
+
+ WarpZone_traceline_antilag(self, self.realowner.origin + self.realowner.view_ofs, targpos, false, self, 0);
+
+ // draw lightning beams for debugging
+ te_lightning2(world, targpos, self.realowner.origin + self.realowner.view_ofs + v_forward * 5 - v_up * 5);
+ te_customflash(targpos, 40, 2, '1 1 1');
+
+ is_player = (IS_PLAYER(trace_ent) || trace_ent.classname == "body");
+ is_monster = IS_MONSTER(trace_ent);
+
+ if((trace_fraction < 1) // if trace is good, apply the damage and remove self
+ && (trace_ent.takedamage == DAMAGE_AIM)
+ && (trace_ent != self.ogre_swing_alreadyhit)
+ && ((is_player || is_monster) || autocvar_g_monster_ogre_attack_melee_nonplayerdamage))
+ {
+ target_victim = trace_ent; // so it persists through other calls
+
+ if(is_player || is_monster) // this allows us to be able to nerf the non-player damage done in e.g. assault or onslaught.
+ swing_damage = (autocvar_g_monster_ogre_attack_melee_damage * min(1, swing_factor + 1));
+ else
+ swing_damage = (autocvar_g_monster_ogre_attack_melee_nonplayerdamage * min(1, swing_factor + 1));
+
+ //print(strcat(self.realowner.netname, " hitting ", target_victim.netname, " with ", strcat(ftos(swing_damage), " damage (factor: ", ftos(swing_factor), ") at "), ftos(time), " seconds.\n"));
+
+ Damage(target_victim, self.realowner, self.realowner,
+ swing_damage, DEATH_MONSTER_OGRE_MELEE,
+ self.realowner.origin + self.realowner.view_ofs,
+ v_forward * 1);
+
+ // draw large red flash for debugging
+ //te_customflash(targpos, 200, 2, '15 0 0');
+
+ self.ogre_swing_alreadyhit = target_victim;
+ continue; // move along to next trace
+ }
+ }
+
+ if(time >= self.cnt + meleetime)
+ {
+ // melee is finished
+ self.realowner.frame = ogre_anim_idle;
+ remove(self);
+ return;
+ }
+ else
+ {
+ // set up next frame
+ self.ogre_swing_prev = i;
+ self.nextthink = time;
+ }
+}
+
+float M_Ogre_Attack(float attack_type)
+{
+ switch(attack_type)
+ {
+ case MONSTER_ATTACK_MELEE:
+ {
+ vector vdir = normalize(self.enemy.origin - self.origin);
+
+ if(vdir_z > 0.7)
+ {
+ self.attack_finished_single = time + 1.2;
+ self.frame = ogre_anim_shoot;
+ self.state = MONSTER_ATTACK_RANGED;
+ Monster_Delay(2, 0.1, 0.4, M_Ogre_Attack_MachineGun);
+ return 2;
+ }
+ entity meleetemp;
+ meleetemp = spawn();
+ meleetemp.realowner = self;
+ meleetemp.think = M_Ogre_Attack_Chainsaw;
+ meleetemp.nextthink = time + autocvar_g_monster_ogre_attack_melee_delay;
+ self.attack_finished_single = time + autocvar_g_monster_ogre_attack_melee_time + autocvar_g_monster_ogre_attack_melee_delay + 0.7;
+ self.anim_finished = self.attack_finished_single;
+ self.state = MONSTER_ATTACK_MELEE;
+ self.frame = ogre_anim_swing;
+
+ return true;
+ }
+ case MONSTER_ATTACK_RANGED:
+ {
+ Monster_Delay(0, 0, 0.5, M_Ogre_Attack_Grenade);
+ self.state = MONSTER_ATTACK_RANGED;
+ self.attack_finished_single = time + 1;
+ self.anim_finished = time + 0.5;
+ self.frame = ogre_anim_shoot;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void spawnfunc_monster_ogre() { Monster_Spawn(MON_OGRE); }
+
+float M_Ogre(float req)
+{
+ switch(req)
+ {
+ case MR_THINK:
+ {
+ return true;
+ }
+ case MR_PAIN:
+ {
+ switch(floor(random() * 6))
+ {
+ default:
+ case 1: self.frame = ogre_anim_pain1; self.anim_finished = time + 0.4; break;
+ case 2: self.frame = ogre_anim_pain2; self.anim_finished = time + 0.2; break;
+ case 3: self.frame = ogre_anim_pain3; self.anim_finished = time + 0.5; break;
+ case 4: self.frame = ogre_anim_pain4; self.anim_finished = time + 1.5; break;
+ case 5: self.frame = ogre_anim_pain5; self.anim_finished = time + 1.4; break;
+ }
+ return true;
+ }
+ case MR_DEATH:
+ {
+ self.frame = ((random() >= 0.5) ? ogre_anim_death1 : ogre_anim_death2);
+ return true;
+ }
+ case MR_SETUP:
+ {
+ if(!self.health) self.health = (autocvar_g_monster_ogre_health);
+ if(!self.speed) { self.speed = (autocvar_g_monster_ogre_speed_walk); }
+ if(!self.speed2) { self.speed2 = (autocvar_g_monster_ogre_speed_run); }
+ if(!self.stopspeed) { self.stopspeed = (autocvar_g_monster_ogre_speed_stop); }
+ if(!self.damageforcescale) { self.damageforcescale = (autocvar_g_monster_ogre_damageforcescale); }
+
+ self.m_anim_walk = ogre_anim_walk;
+ self.m_anim_run = ogre_anim_run;
+ self.m_anim_idle = ogre_anim_idle;
+
+ self.monster_loot = spawnfunc_item_rockets;
+ self.weapon = WEP_MACHINEGUN;
+ self.frame = ogre_anim_pull;
+ self.spawn_time = time + 1;
+ self.spawnshieldtime = self.spawn_time;
+
+ return true;
+ }
+ case MR_PRECACHE:
+ {
+ return true;
+ }
+ }
+
+ return true;
+}
+
+#endif // SVQC
+#endif // REGISTER_MONSTER
self.nextthink = time + 0.2;
}
- void M_Shambler_Attack_Lightning_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-void shambler_lightning_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
++void M_Shambler_Attack_Lightning_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
{
if (self.health <= 0)
return;
self.msound_delay = time + sound_delay;
}
-void monster_makevectors(entity e)
+
+// =======================
+// Monster attack handlers
+// =======================
+
- float Monster_Attack_Melee(entity targ, float damg, float anim, float er, float animtime, float deathtype, float dostop)
++float Monster_Attack_Melee(entity targ, float damg, float anim, float er, float animtime, int deathtype, float dostop)
{
- vector v;
+ if(dostop) { self.state = MONSTER_ATTACK_MELEE; }
- v = e.origin + (e.mins + e.maxs) * 0.5;
- self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
- self.v_angle_x = -self.v_angle.x;
+ self.frame = anim;
- makevectors(self.v_angle);
+ if(animtime > 0) { self.attack_finished_single = self.anim_finished = time + animtime; }
+
+ monster_makevectors(targ);
+
+ traceline(self.origin + self.view_ofs, self.origin + v_forward * er, 0, self);
+
+ if(trace_ent.takedamage)
+ Damage(trace_ent, self, self, damg * MONSTER_SKILLMOD(self), deathtype, trace_ent.origin, normalize(trace_ent.origin - self.origin));
+
+ return true;
+}
+
+float Monster_Attack_Leap_Check(vector vel)
+{
+ if(self.state)
+ return false; // already attacking
+ if(!(self.flags & FL_ONGROUND))
+ return false; // not on the ground
+ if(self.health <= 0)
+ return false; // called when dead?
+ if(time < self.attack_finished_single)
+ return false; // still attacking
+
+ vector old = self.velocity;
+
+ self.velocity = vel;
+ tracetoss(self, self);
+ self.velocity = old;
+ if (trace_ent != self.enemy)
+ return false;
+
+ return true;
+}
+
+bool Monster_Attack_Leap(int anm, void() touchfunc, vector vel, float animtime)
+{
+ if(!Monster_Attack_Leap_Check(vel))
+ return false;
+
+ self.frame = anm;
+ self.state = MONSTER_ATTACK_RANGED;
+ self.touch = touchfunc;
+ self.origin_z += 1;
+ self.velocity = vel;
+ self.flags &= ~FL_ONGROUND;
+
+ self.attack_finished_single = time + animtime;
+ self.anim_finished = self.attack_finished_single; // TODO: make these frame based
+
+ return true;
}
-float monster_melee(entity targ, float damg, float anim, float er, float anim_finished, int deathtype, float dostop)
+void Monster_Attack_Check(entity e, entity targ)
{
- if (self.health <= 0)
- return false; // attacking while dead?!
+ if((e == world || targ == world)
+ || (!e.monster_attackfunc)
+ || (time < e.attack_finished_single)
+ ) { return; }
+
+ float targ_vlen = vlen(targ.origin - e.origin);
- if(dostop)
+ if(targ_vlen <= e.attack_range)
{
- self.velocity_x = 0;
- self.velocity_y = 0;
- self.state = MONSTER_STATE_ATTACK_MELEE;
+ float attack_success = e.monster_attackfunc(MONSTER_ATTACK_MELEE);
+ if(attack_success == 1)
+ Monster_Sound(monstersound_melee, 0, false, CH_VOICE);
+ else if(attack_success > 0)
+ return;
}
- self.frame = anim;
+ if(targ_vlen > e.attack_range)
+ {
+ float attack_success = e.monster_attackfunc(MONSTER_ATTACK_RANGED);
+ if(attack_success == 1)
+ Monster_Sound(monstersound_melee, 0, false, CH_VOICE);
+ else if(attack_success > 0)
+ return;
+ }
+}
- if(anim_finished != 0)
- self.attack_finished_single = time + anim_finished;
- monster_makevectors(targ);
+// ======================
+// Main monster functions
+// ======================
- traceline(self.origin + self.view_ofs, self.origin + v_forward * er, 0, self);
+void Monster_Skin_Check()
+{
+ vector oldmin = self.mins, oldmax = self.maxs;
+ entity mon = get_monsterinfo(self.monsterid);
+ string trymodel = sprintf("%s_%d%s", substring(mon.model, 0, strlen(mon.model) - 4), self.skin, substring(mon.model, strlen(mon.model) - 4, strlen(mon.model)));
- if(trace_ent.takedamage)
- Damage(trace_ent, self, self, damg * Monster_SkillModifier(), deathtype, trace_ent.origin, normalize(trace_ent.origin - self.origin));
+ if(fexists(trymodel))
+ {
+ precache_model(trymodel);
+ setmodel(self, trymodel);
+ setsize(self, oldmin, oldmax);
+ CSQCMODEL_AUTOUPDATE(); // do a quick update
+ }
- return true;
+ self.oldskin = self.skin;
}
-void Monster_CheckMinibossFlag ()
+void Monster_Touch()
+{
+ if(other == world) { return; }
+
+ if(self.enemy != other)
+ if(!IS_MONSTER(other))
+ if(Monster_ValidTarget(self, other))
+ self.enemy = other;
+}
+
+void Monster_Miniboss_Check()
{
if(MUTATOR_CALLHOOK(MonsterCheckBossFlag))
return;
self.moveto = self.origin;
}
- void Monster_Dead_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-void monsters_corpse_damage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
++void Monster_Dead_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
{
self.health -= damage;
if(!((self.flags & FL_FLY) || (self.flags & FL_SWIM)))
self.velocity = '0 0 0';
+ CSQCModel_UnlinkEntity();
+
MON_ACTION(self.monsterid, MR_DEATH);
+
+ if(self.candrop && self.weapon)
+ W_ThrowNewWeapon(self, self.weapon, 0, self.origin, randomvec() * 150 + '0 0 325');
}
- void Monster_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-void monsters_damage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
++void Monster_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
{
- if(self.frozen && deathtype != DEATH_KILL && deathtype != DEATH_NADE_ICE_FREEZE)
- return;
-
if((self.spawnflags & MONSTERFLAG_INVINCIBLE) && deathtype != DEATH_KILL)
return;
#ifndef SV_MONSTERS_H
#define SV_MONSTERS_H
-.string spawnmob;
-.float monster_attack;
+// stats networking
+.int stat_monsters_killed;
+.int stat_monsters_total;
+int monsters_total;
+int monsters_killed;
+
+// monster properties
+.int monster_movestate; // move target priority
+.entity monster_follow; // follow target
+.float wander_delay; // logic delay between moving while idle
+.float wander_distance; // distance to move between wander delays
+.float monster_lifetime; // monster dies instantly after this delay, set from spawn
+.float attack_range; // melee attack if closer, ranged attack if further away (TODO: separate ranged attack range?)
+.float spawn_time; // delay monster thinking until spawn animation has completed
+.bool candrop; // toggle to allow disabling monster item drops
+.int monster_movestate; // will be phased out
+.int monster_moveflags;
+.string oldtarget2; // a copy of the original follow target string
+.float last_trace; // logic delay between target tracing
+.float last_enemycheck; // for checking enemy
+.float anim_finished; // will be phased out when we have proper animations system
+.vector monster_moveto; // custom destination for monster (reset to '0 0 0' when you're done!)
+.vector monster_face; // custom looking direction for monster (reset to '0 0 0' when you're done!)
+.float speed2; // run speed
+.float stopspeed;
+.int m_anim_run;
+.int m_anim_walk;
+.int m_anim_idle;
+.int oldskin;
+.string mdl_dead; // dead model for goombas
+
+#define MONSTER_SKILLMOD(mon) (0.5 + mon.monster_skill * ((1.2 - 0.3) / 10))
+
+// other properties
+.bool monster_attack; // indicates whether an entity can be attacked by monsters
+.float spider_slowness; // effect time of slowness inflicted by spiders
+
+// monster state declarations
+const int MONSTER_MOVE_FOLLOW = 1; // monster will follow if in range, or stand still
+const int MONSTER_MOVE_WANDER = 2; // monster will ignore owner & wander around
+const int MONSTER_MOVE_SPAWNLOC = 3; // monster will move to its spawn location when not attacking
+const int MONSTER_MOVE_NOMOVE = 4; // monster simply stands still
+const int MONSTER_MOVE_ENEMY = 5; // used only as a movestate
+const int MONSTER_ATTACK_MELEE = 6;
+const int MONSTER_ATTACK_RANGED = 7;
-.entity monster_owner; // new monster owner entity, fixes non-solid monsters
+// skill declarations
+const int MONSTER_SKILL_EASY = 1;
+const int MONSTER_SKILL_MEDIUM = 3;
+const int MONSTER_SKILL_HARD = 5;
+const int MONSTER_SKILL_INSANE = 7;
+const int MONSTER_SKILL_NIGHTMARE = 10;
-.float stat_monsters_killed; // stats
-.float stat_monsters_total;
-float monsters_total;
-float monsters_killed;
-void monsters_setstatus(); // monsters.qc
-.float monster_moveflags; // checks where to move when not attacking
+const int MONSTERSKILL_NOTEASY = 256; // monster will not spawn on skill <= 1
+const int MONSTERSKILL_NOTMEDIUM = 512; // monster will not spawn on skill 2
+const int MONSTERSKILL_NOTHARD = 1024; // monster will not spawn on skill >= 3
-.float wander_delay;
-.float wander_distance;
+// spawn flags
+const int MONSTERFLAG_APPEAR = 2; // delay spawn until triggered
+const int MONSTERFLAG_NORESPAWN = 4;
+const int MONSTERFLAG_FLY_VERTICAL = 8; // fly/swim vertically
+const int MONSTERFLAG_INFRONT = 32; // only check for enemies infront of us
+const int MONSTERFLAG_MINIBOSS = 64; // monster spawns as mini-boss (also has a chance of naturally becoming one)
+const int MONSTERFLAG_INVINCIBLE = 128; // monster doesn't take damage (may be used for map objects & temporary monsters)
+const int MONSTERFLAG_SPAWNED = 16384; // flag for spawned monsters
+const int MONSTERFLAG_RESPAWNED = 32768; // flag for re-spawned monsters
+// compatibility with old maps (soon to be removed)
.float monster_lifetime;
+.int monster_skill;
-.float spider_slowness; // special spider timer
+// functions used elsewhere
+void Monster_Remove(entity mon);
-void monster_remove(entity mon); // removes a monster
+void monsters_setstatus();
-.float(float attack_type) monster_attackfunc;
-const int MONSTER_ATTACK_MELEE = 1;
-const int MONSTER_ATTACK_RANGED = 2;
+bool Monster_Spawn(int mon_id);
-.float monster_skill;
-const float MONSTER_SKILL_EASY = 1;
-const float MONSTER_SKILL_MEDIUM = 3;
-const float MONSTER_SKILL_HARD = 5;
-const float MONSTER_SKILL_INSANE = 7;
-const float MONSTER_SKILL_NIGHTMARE = 10;
+void monster_setupcolors(entity mon);
+
+void Monster_Touch();
-.float fish_wasdrowning; // used to reset a drowning fish's angles if it reaches water again
+void Monster_Move_2D(float mspeed, float allow_jumpoff, float manim_walk, float manim_idle);
-.float candrop;
+void Monster_Delay(float repeat_count, float repeat_defer, float defer_amnt, void() func);
- float Monster_Attack_Melee(entity targ, float damg, float anim, float er, float animtime, float deathtype, float dostop);
-.float attack_range;
++float Monster_Attack_Melee(entity targ, float damg, float anim, float er, float animtime, int deathtype, float dostop);
-.float spawn_time; // stop monster from moving around right after spawning
+bool Monster_Attack_Leap(int anm, void() touchfunc, vector vel, float animtime);
-.string oldtarget2;
-.float lastshielded;
+entity Monster_FindTarget(entity mon);
-.vector oldangles;
+void monster_makevectors(entity e);
-.float m_armor_blockpercent;
+void Monster_Sound(.string samplefield, float sound_delay, float delaytoo, float chan);
// monster sounds
-// copied from player sounds
.float msound_delay; // temporary antilag system
#define ALLMONSTERSOUNDS \
_MSOUND(death) \
#undef _MSOUND
float GetMonsterSoundSampleField_notFound;
-const int MONSTERSKILL_NOTEASY = 256; // monster will not spawn on skill <= 1
-const int MONSTERSKILL_NOTMEDIUM = 512; // monster will not spawn on skill 2
-const int MONSTERSKILL_NOTHARD = 1024; // monster will not spawn on skill >= 3
-
-// new flags
-const int MONSTERFLAG_APPEAR = 2; // delay spawn until triggered
-const int MONSTERFLAG_NORESPAWN = 4;
-const int MONSTERFLAG_FLY_VERTICAL = 8; // fly/swim vertically
-const int MONSTERFLAG_INFRONT = 32; // only check for enemies infront of us
-const int MONSTERFLAG_MINIBOSS = 64; // monster spawns as mini-boss (also has a chance of naturally becoming one)
-const int MONSTERFLAG_INVINCIBLE = 128; // monster doesn't take damage (may be used for map objects & temporary monsters)
-const int MONSTERFLAG_SPAWNED = 16384; // flag for spawned monsters
-const int MONSTERFLAG_RESPAWNED = 32768; // flag for re-spawned monsters
-
-.int monster_movestate; // used to tell what the monster is currently doing
-const int MONSTER_MOVE_OWNER = 1; // monster will move to owner if in range, or stand still
-const int MONSTER_MOVE_WANDER = 2; // monster will ignore owner & wander around
-const int MONSTER_MOVE_SPAWNLOC = 3; // monster will move to its spawn location when not attacking
-const int MONSTER_MOVE_NOMOVE = 4; // monster simply stands still
-const int MONSTER_MOVE_ENEMY = 5; // used only as a movestate
-
-const int MONSTER_STATE_ATTACK_LEAP = 1;
-const int MONSTER_STATE_ATTACK_MELEE = 2;
-
-float monster_initialize(float mon_id);
-float monster_leap (float anm, void() touchfunc, vector vel, float anim_finished);
-void monster_makevectors(entity e);
-float monster_melee(entity targ, float damg, float anim, float er, float anim_finished, int deathtype, float dostop);
-void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_run, float manim_walk, float manim_idle);
-void monster_setupcolors(entity mon);
-float Monster_SkillModifier();
-void MonsterTouch ();
+
#endif
--- /dev/null
- void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+#ifdef SVQC
+// button and multiple button
+
+void() button_wait;
+void() button_return;
+
+void button_wait()
+{
+ self.state = STATE_TOP;
+ self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
+ self.SUB_THINK = button_return;
+ activator = self.enemy;
+ SUB_UseTargets();
+ self.frame = 1; // use alternate textures
+}
+
+void button_done()
+{
+ self.state = STATE_BOTTOM;
+}
+
+void button_return()
+{
+ self.state = STATE_DOWN;
+ SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, button_done);
+ self.frame = 0; // use normal textures
+ if (self.health)
+ self.takedamage = DAMAGE_YES; // can be shot again
+}
+
+
+void button_blocked()
+{
+ // do nothing, just don't come all the way back out
+}
+
+
+void button_fire()
+{
+ self.health = self.max_health;
+ self.takedamage = DAMAGE_NO; // will be reset upon return
+
+ if (self.state == STATE_UP || self.state == STATE_TOP)
+ return;
+
+ if (self.noise != "")
+ sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+
+ self.state = STATE_UP;
+ SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, button_wait);
+}
+
+void button_reset()
+{
+ self.health = self.max_health;
+ setorigin(self, self.pos1);
+ self.frame = 0; // use normal textures
+ self.state = STATE_BOTTOM;
+ if (self.health)
+ self.takedamage = DAMAGE_YES; // can be shot again
+}
+
+void button_use()
+{
+ if(self.active != ACTIVE_ACTIVE)
+ return;
+
+ self.enemy = activator;
+ button_fire ();
+}
+
+void button_touch()
+{
+ if (!other)
+ return;
+ if (!other.iscreature)
+ return;
+ if(other.velocity * self.movedir < 0)
+ return;
+ self.enemy = other;
+ if (other.owner)
+ self.enemy = other.owner;
+ button_fire ();
+}
+
++void button_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+ if(self.spawnflags & DOOR_NOSPLASH)
+ if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
+ return;
+ self.health = self.health - damage;
+ if (self.health <= 0)
+ {
+ self.enemy = damage_attacker;
+ button_fire ();
+ }
+}
+
+
+/*QUAKED spawnfunc_func_button (0 .5 .8) ?
+When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again.
+
+"angle" determines the opening direction
+"target" all entities with a matching targetname will be used
+"speed" override the default 40 speed
+"wait" override the default 1 second wait (-1 = never return)
+"lip" override the default 4 pixel lip remaining at end of move
+"health" if set, the button must be killed instead of touched. If set to -1, the button will fire on ANY attack, even damageless ones like the InstaGib laser
+"sounds"
+0) steam metal
+1) wooden clunk
+2) metallic click
+3) in-out
+*/
+void spawnfunc_func_button()
+{
+ SetMovedir ();
+
+ if (!InitMovingBrushTrigger())
+ return;
+ self.effects |= EF_LOWPRECISION;
+
+ self.blocked = button_blocked;
+ self.use = button_use;
+
+// if (self.health == 0) // all buttons are now shootable
+// self.health = 10;
+ if (self.health)
+ {
+ self.max_health = self.health;
+ self.event_damage = button_damage;
+ self.takedamage = DAMAGE_YES;
+ }
+ else
+ self.touch = button_touch;
+
+ if (!self.speed)
+ self.speed = 40;
+ if (!self.wait)
+ self.wait = 1;
+ if (!self.lip)
+ self.lip = 4;
+
+ if(self.noise != "")
+ precache_sound(self.noise);
+
+ self.active = ACTIVE_ACTIVE;
+
+ self.pos1 = self.origin;
+ self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
+ self.flags |= FL_NOTARGET;
+
+ button_reset();
+}
+#endif
--- /dev/null
- void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+/*
+
+Doors are similar to buttons, but can spawn a fat trigger field around them
+to open without a touch, and they link together to form simultanious
+double/quad doors.
+
+Door.owner is the master door. If there is only one door, it points to itself.
+If multiple doors, all will point to a single one.
+
+Door.enemy chains from the master door through all doors linked in the chain.
+
+*/
+
+
+/*
+=============================================================================
+
+THINK FUNCTIONS
+
+=============================================================================
+*/
+
+void() door_go_down;
+void() door_go_up;
+void() door_rotating_go_down;
+void() door_rotating_go_up;
+
+void door_blocked()
+{
+ if((self.spawnflags & 8)
+#ifdef SVQC
+ && (other.takedamage != DAMAGE_NO)
+#elif defined(CSQC)
+ && !PHYS_DEAD(other)
+#endif
+ )
+ { // KIll Kill Kill!!
+#ifdef SVQC
+ Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+#endif
+ }
+ else
+ {
+#ifdef SVQC
+ if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
+ Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+#endif
+
+ // don't change direction for dead or dying stuff
+ if(PHYS_DEAD(other)
+#ifdef SVQC
+ && (other.takedamage == DAMAGE_NO)
+#endif
+ )
+ {
+ if (self.wait >= 0)
+ {
+ if (self.state == STATE_DOWN)
+ if (self.classname == "door")
+ {
+ door_go_up ();
+ } else
+ {
+ door_rotating_go_up ();
+ }
+ else
+ if (self.classname == "door")
+ {
+ door_go_down ();
+ } else
+ {
+ door_rotating_go_down ();
+ }
+ }
+ }
+#ifdef SVQC
+ else
+ {
+ //gib dying stuff just to make sure
+ if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
+ Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+ }
+#endif
+ }
+}
+
+void door_hit_top()
+{
+ if (self.noise1 != "")
+ sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+ self.state = STATE_TOP;
+ if (self.spawnflags & DOOR_TOGGLE)
+ return; // don't come down automatically
+ if (self.classname == "door")
+ {
+ self.SUB_THINK = door_go_down;
+ } else
+ {
+ self.SUB_THINK = door_rotating_go_down;
+ }
+ self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
+}
+
+void door_hit_bottom()
+{
+ if (self.noise1 != "")
+ sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+ self.state = STATE_BOTTOM;
+}
+
+void door_go_down()
+{
+ if (self.noise2 != "")
+ sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+ if (self.max_health)
+ {
+ self.takedamage = DAMAGE_YES;
+ self.health = self.max_health;
+ }
+
+ self.state = STATE_DOWN;
+ SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, door_hit_bottom);
+}
+
+void door_go_up()
+{
+ if (self.state == STATE_UP)
+ return; // already going up
+
+ if (self.state == STATE_TOP)
+ { // reset top wait time
+ self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
+ return;
+ }
+
+ if (self.noise2 != "")
+ sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+ self.state = STATE_UP;
+ SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, door_hit_top);
+
+ string oldmessage;
+ oldmessage = self.message;
+ self.message = "";
+ SUB_UseTargets();
+ self.message = oldmessage;
+}
+
+
+/*
+=============================================================================
+
+ACTIVATION FUNCTIONS
+
+=============================================================================
+*/
+
+float door_check_keys(void)
+{
+ local entity door;
+
+
+ if (self.owner)
+ door = self.owner;
+ else
+ door = self;
+
+ // no key needed
+ if (!door.itemkeys)
+ return true;
+
+ // this door require a key
+ // only a player can have a key
+ if (!IS_PLAYER(other))
+ return false;
+
+#ifdef SVQC
+ if (item_keys_usekey(door, other))
+ {
+ // some keys were used
+ if (other.key_door_messagetime <= time)
+ {
+
+ play2(other, "misc/talk.wav");
+ Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(door.itemkeys));
+ other.key_door_messagetime = time + 2;
+ }
+ }
+ else
+ {
+ // no keys were used
+ if (other.key_door_messagetime <= time)
+ {
+ play2(other, "misc/talk.wav");
+ Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(door.itemkeys));
+
+ other.key_door_messagetime = time + 2;
+ }
+ }
+#endif
+
+ if (door.itemkeys)
+ {
+#ifdef SVQC
+ // door is now unlocked
+ play2(other, "misc/talk.wav");
+ Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_UNLOCKED);
+#endif
+ return true;
+ }
+ else
+ return false;
+}
+
+void door_fire()
+{
+ entity oself;
+ entity starte;
+
+ if (self.owner != self)
+ objerror ("door_fire: self.owner != self");
+
+ oself = self;
+
+ if (self.spawnflags & DOOR_TOGGLE)
+ {
+ if (self.state == STATE_UP || self.state == STATE_TOP)
+ {
+ starte = self;
+ do
+ {
+ if (self.classname == "door")
+ {
+ door_go_down ();
+ }
+ else
+ {
+ door_rotating_go_down ();
+ }
+ self = self.enemy;
+ } while ( (self != starte) && (self != world) );
+ self = oself;
+ return;
+ }
+ }
+
+// trigger all paired doors
+ starte = self;
+ do
+ {
+ if (self.classname == "door")
+ {
+ door_go_up ();
+ } else
+ {
+ // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
+ if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
+ {
+ self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
+ self.pos2 = '0 0 0' - self.pos2;
+ }
+ // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
+ if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
+ && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
+ {
+ door_rotating_go_up ();
+ }
+ }
+ self = self.enemy;
+ } while ( (self != starte) && (self != world) );
+ self = oself;
+}
+
+void door_use()
+{
+ entity oself;
+
+ //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
+
+ if (self.owner)
+ {
+ oself = self;
+ self = self.owner;
+ door_fire ();
+ self = oself;
+ }
+}
+
++void door_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+ entity oself;
+ if(self.spawnflags & DOOR_NOSPLASH)
+ if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
+ return;
+ self.health = self.health - damage;
+
+ if (self.itemkeys)
+ {
+ // don't allow opening doors through damage if keys are required
+ return;
+ }
+
+ if (self.health <= 0)
+ {
+ oself = self;
+ self = self.owner;
+ self.health = self.max_health;
+ self.takedamage = DAMAGE_NO; // wil be reset upon return
+ door_use ();
+ self = oself;
+ }
+}
+
+
+/*
+================
+door_touch
+
+Prints messages
+================
+*/
+
+void door_touch()
+{
+ if (!IS_PLAYER(other))
+ return;
+ if (self.owner.attack_finished_single > time)
+ return;
+
+ self.owner.attack_finished_single = time + 2;
+
+#ifdef SVQC
+ if (!(self.owner.dmg) && (self.owner.message != ""))
+ {
+ if (IS_CLIENT(other))
+ centerprint(other, self.owner.message);
+ play2(other, "misc/talk.wav");
+ }
+#endif
+}
+
+void door_generic_plat_blocked()
+{
+
+ if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
+#ifdef SVQC
+ Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+#endif
+ }
+ else
+ {
+
+#ifdef SVQC
+ if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
+ Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+#endif
+
+ //Dont chamge direction for dead or dying stuff
+ if(PHYS_DEAD(other) && (other.takedamage == DAMAGE_NO))
+ {
+ if (self.wait >= 0)
+ {
+ if (self.state == STATE_DOWN)
+ door_rotating_go_up ();
+ else
+ door_rotating_go_down ();
+ }
+ }
+#ifdef SVQC
+ else
+ {
+ //gib dying stuff just to make sure
+ if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
+ Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+ }
+#endif
+ }
+}
+
+void door_rotating_hit_top()
+{
+ if (self.noise1 != "")
+ sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+ self.state = STATE_TOP;
+ if (self.spawnflags & DOOR_TOGGLE)
+ return; // don't come down automatically
+ self.SUB_THINK = door_rotating_go_down;
+ self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
+}
+
+void door_rotating_hit_bottom()
+{
+ if (self.noise1 != "")
+ sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+ if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
+ {
+ self.pos2 = '0 0 0' - self.pos2;
+ self.lip = 0;
+ }
+ self.state = STATE_BOTTOM;
+}
+
+void door_rotating_go_down()
+{
+ if (self.noise2 != "")
+ sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+ if (self.max_health)
+ {
+ self.takedamage = DAMAGE_YES;
+ self.health = self.max_health;
+ }
+
+ self.state = STATE_DOWN;
+ SUB_CalcAngleMove (self.pos1, TSPEED_LINEAR, self.speed, door_rotating_hit_bottom);
+}
+
+void door_rotating_go_up()
+{
+ if (self.state == STATE_UP)
+ return; // already going up
+
+ if (self.state == STATE_TOP)
+ { // reset top wait time
+ self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
+ return;
+ }
+ if (self.noise2 != "")
+ sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+ self.state = STATE_UP;
+ SUB_CalcAngleMove (self.pos2, TSPEED_LINEAR, self.speed, door_rotating_hit_top);
+
+ string oldmessage;
+ oldmessage = self.message;
+ self.message = "";
+ SUB_UseTargets();
+ self.message = oldmessage;
+}
+
+
+/*
+=========================================
+door trigger
+
+Spawned if a door lacks a real activator
+=========================================
+*/
+
+void door_trigger_touch()
+{
+ if (other.health < 1)
+#ifdef SVQC
+ if (!((other.iscreature || (other.flags & FL_PROJECTILE)) && !PHYS_DEAD(other)))
+#elif defined(CSQC)
+ if(!((IS_CLIENT(other) || other.classname == "csqcprojectile") && !PHYS_DEAD(other)))
+#endif
+ return;
+
+ if (time < self.attack_finished_single)
+ return;
+
+ // check if door is locked
+ if (!door_check_keys())
+ return;
+
+ self.attack_finished_single = time + 1;
+
+ activator = other;
+
+ self = self.owner;
+ door_use ();
+}
+
+void spawn_field(vector fmins, vector fmaxs)
+{
+ entity trigger;
+ vector t1 = fmins, t2 = fmaxs;
+
+ trigger = spawn();
+ trigger.classname = "doortriggerfield";
+ trigger.movetype = MOVETYPE_NONE;
+ trigger.solid = SOLID_TRIGGER;
+ trigger.owner = self;
+#ifdef SVQC
+ trigger.touch = door_trigger_touch;
+#elif defined(CSQC)
+ trigger.trigger_touch = door_trigger_touch;
+ trigger.draw = trigger_draw_generic;
+ trigger.drawmask = MASK_NORMAL;
+#endif
+
+ setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
+}
+
+
+/*
+=============
+LinkDoors
+
+
+=============
+*/
+
+entity LinkDoors_nextent(entity cur, entity near, entity pass)
+{
+ while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy))
+ {
+ }
+ return cur;
+}
+
+bool LinkDoors_isconnected(entity e1, entity e2, entity pass)
+{
+ float DELTA = 4;
+ if((e1.absmin_x > e2.absmax_x + DELTA)
+ || (e1.absmin_y > e2.absmax_y + DELTA)
+ || (e1.absmin_z > e2.absmax_z + DELTA)
+ || (e2.absmin_x > e1.absmax_x + DELTA)
+ || (e2.absmin_y > e1.absmax_y + DELTA)
+ || (e2.absmin_z > e1.absmax_z + DELTA)
+ ) { return false; }
+ return true;
+}
+
+#ifdef SVQC
+void door_link();
+#endif
+void LinkDoors()
+{
+ entity t;
+ vector cmins, cmaxs;
+
+#ifdef SVQC
+ door_link();
+#endif
+
+ if (self.enemy)
+ return; // already linked by another door
+ if (self.spawnflags & 4)
+ {
+ self.owner = self.enemy = self;
+
+ if (self.health)
+ return;
+ IFTARGETED
+ return;
+ if (self.items)
+ return;
+
+ spawn_field(self.absmin, self.absmax);
+
+ return; // don't want to link this door
+ }
+
+ FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world);
+
+ // set owner, and make a loop of the chain
+ dprint("LinkDoors: linking doors:");
+ for(t = self; ; t = t.enemy)
+ {
+ dprint(" ", etos(t));
+ t.owner = self;
+ if(t.enemy == world)
+ {
+ t.enemy = self;
+ break;
+ }
+ }
+ dprint("\n");
+
+ // collect health, targetname, message, size
+ cmins = self.absmin;
+ cmaxs = self.absmax;
+ for(t = self; ; t = t.enemy)
+ {
+ if(t.health && !self.health)
+ self.health = t.health;
+ if((t.targetname != "") && (self.targetname == ""))
+ self.targetname = t.targetname;
+ if((t.message != "") && (self.message == ""))
+ self.message = t.message;
+ if (t.absmin_x < cmins_x)
+ cmins_x = t.absmin_x;
+ if (t.absmin_y < cmins_y)
+ cmins_y = t.absmin_y;
+ if (t.absmin_z < cmins_z)
+ cmins_z = t.absmin_z;
+ if (t.absmax_x > cmaxs_x)
+ cmaxs_x = t.absmax_x;
+ if (t.absmax_y > cmaxs_y)
+ cmaxs_y = t.absmax_y;
+ if (t.absmax_z > cmaxs_z)
+ cmaxs_z = t.absmax_z;
+ if(t.enemy == self)
+ break;
+ }
+
+ // distribute health, targetname, message
+ for(t = self; t; t = t.enemy)
+ {
+ t.health = self.health;
+ t.targetname = self.targetname;
+ t.message = self.message;
+ if(t.enemy == self)
+ break;
+ }
+
+ // shootable, or triggered doors just needed the owner/enemy links,
+ // they don't spawn a field
+
+ if (self.health)
+ return;
+ IFTARGETED
+ return;
+ if (self.items)
+ return;
+
+ spawn_field(cmins, cmaxs);
+}
+
+#ifdef SVQC
+/*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
+if two doors touch, they are assumed to be connected and operate as a unit.
+
+TOGGLE causes the door to wait in both the start and end states for a trigger event.
+
+START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors).
+
+GOLD_KEY causes the door to open only if the activator holds a gold key.
+
+SILVER_KEY causes the door to open only if the activator holds a silver key.
+
+"message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
+"angle" determines the opening direction
+"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
+"health" if set, door must be shot open
+"speed" movement speed (100 default)
+"wait" wait before returning (3 default, -1 = never return)
+"lip" lip remaining at end of move (8 default)
+"dmg" damage to inflict when blocked (2 default)
+"sounds"
+0) no sound
+1) stone
+2) base
+3) stone chain
+4) screechy metal
+FIXME: only one sound set available at the time being
+
+*/
+
+float door_send(entity to, float sf)
+{
+ WriteByte(MSG_ENTITY, ENT_CLIENT_DOOR);
+ WriteByte(MSG_ENTITY, sf);
+
+ if(sf & SF_TRIGGER_INIT)
+ {
+ WriteString(MSG_ENTITY, self.classname);
+ WriteByte(MSG_ENTITY, self.spawnflags);
+
+ WriteString(MSG_ENTITY, self.model);
+
+ trigger_common_write(true);
+
+ WriteCoord(MSG_ENTITY, self.pos1_x);
+ WriteCoord(MSG_ENTITY, self.pos1_y);
+ WriteCoord(MSG_ENTITY, self.pos1_z);
+ WriteCoord(MSG_ENTITY, self.pos2_x);
+ WriteCoord(MSG_ENTITY, self.pos2_y);
+ WriteCoord(MSG_ENTITY, self.pos2_z);
+
+ WriteCoord(MSG_ENTITY, self.size_x);
+ WriteCoord(MSG_ENTITY, self.size_y);
+ WriteCoord(MSG_ENTITY, self.size_z);
+
+ WriteShort(MSG_ENTITY, self.wait);
+ WriteShort(MSG_ENTITY, self.speed);
+ WriteByte(MSG_ENTITY, self.lip);
+ WriteByte(MSG_ENTITY, self.state);
+ WriteCoord(MSG_ENTITY, self.SUB_LTIME);
+ }
+
+ if(sf & SF_TRIGGER_RESET)
+ {
+ // client makes use of this, we do not
+ }
+
+ if(sf & SF_TRIGGER_UPDATE)
+ {
+ WriteCoord(MSG_ENTITY, self.origin_x);
+ WriteCoord(MSG_ENTITY, self.origin_y);
+ WriteCoord(MSG_ENTITY, self.origin_z);
+
+ WriteCoord(MSG_ENTITY, self.pos1_x);
+ WriteCoord(MSG_ENTITY, self.pos1_y);
+ WriteCoord(MSG_ENTITY, self.pos1_z);
+ WriteCoord(MSG_ENTITY, self.pos2_x);
+ WriteCoord(MSG_ENTITY, self.pos2_y);
+ WriteCoord(MSG_ENTITY, self.pos2_z);
+ }
+
+ return true;
+}
+
+void door_link()
+{
+ // set size now, as everything is loaded
+ //FixSize(self);
+ //Net_LinkEntity(self, false, 0, door_send);
+}
+#endif
+
+void door_init_startopen()
+{
+ SUB_SETORIGIN(self, self.pos2);
+ self.pos2 = self.pos1;
+ self.pos1 = self.origin;
+
+#ifdef SVQC
+ self.SendFlags |= SF_TRIGGER_UPDATE;
+#endif
+}
+
+void door_reset()
+{
+ SUB_SETORIGIN(self, self.pos1);
+ self.SUB_VELOCITY = '0 0 0';
+ self.state = STATE_BOTTOM;
+ self.SUB_THINK = func_null;
+ self.SUB_NEXTTHINK = 0;
+
+#ifdef SVQC
+ self.SendFlags |= SF_TRIGGER_RESET;
+#endif
+}
+
+#ifdef SVQC
+
+// spawnflags require key (for now only func_door)
+void spawnfunc_func_door()
+{
+ // Quake 1 keys compatibility
+ if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
+ self.itemkeys |= ITEM_KEY_BIT(0);
+ if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
+ self.itemkeys |= ITEM_KEY_BIT(1);
+
+ SetMovedir ();
+
+ self.max_health = self.health;
+ if (!InitMovingBrushTrigger())
+ return;
+ self.effects |= EF_LOWPRECISION;
+ self.classname = "door";
+
+ self.blocked = door_blocked;
+ self.use = door_use;
+
+ if(self.dmg && (self.message == ""))
+ self.message = "was squished";
+ if(self.dmg && (self.message2 == ""))
+ self.message2 = "was squished by";
+
+ if (self.sounds > 0)
+ {
+ precache_sound ("plats/medplat1.wav");
+ precache_sound ("plats/medplat2.wav");
+ self.noise2 = "plats/medplat1.wav";
+ self.noise1 = "plats/medplat2.wav";
+ }
+
+ if (!self.speed)
+ self.speed = 100;
+ if (!self.wait)
+ self.wait = 3;
+ if (!self.lip)
+ self.lip = 8;
+
+ self.pos1 = self.SUB_ORIGIN;
+ self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
+
+// DOOR_START_OPEN is to allow an entity to be lighted in the closed position
+// but spawn in the open position
+ if (self.spawnflags & DOOR_START_OPEN)
+ InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
+
+ self.state = STATE_BOTTOM;
+
+ if (self.health)
+ {
+ self.takedamage = DAMAGE_YES;
+ self.event_damage = door_damage;
+ }
+
+ if (self.items)
+ self.wait = -1;
+
+ self.touch = door_touch;
+
+// LinkDoors can't be done until all of the doors have been spawned, so
+// the sizes can be detected properly.
+ InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
+
+ self.reset = door_reset;
+}
+
+#elif defined(CSQC)
+
+void door_draw()
+{
+ Movetype_Physics_NoMatchServer();
+
+ trigger_draw_generic();
+}
+
+void ent_door()
+{
+ float sf = ReadByte();
+
+ if(sf & SF_TRIGGER_INIT)
+ {
+ self.classname = strzone(ReadString());
+ self.spawnflags = ReadByte();
+
+ self.mdl = strzone(ReadString());
+ setmodel(self, self.mdl);
+
+ trigger_common_read(true);
+
+ self.pos1_x = ReadCoord();
+ self.pos1_y = ReadCoord();
+ self.pos1_z = ReadCoord();
+ self.pos2_x = ReadCoord();
+ self.pos2_y = ReadCoord();
+ self.pos2_z = ReadCoord();
+
+ self.size_x = ReadCoord();
+ self.size_y = ReadCoord();
+ self.size_z = ReadCoord();
+
+ self.wait = ReadShort();
+ self.speed = ReadShort();
+ self.lip = ReadByte();
+ self.state = ReadByte();
+ self.SUB_LTIME = ReadCoord();
+
+ self.solid = SOLID_BSP;
+ self.movetype = MOVETYPE_PUSH;
+ self.trigger_touch = door_touch;
+ self.draw = door_draw;
+ self.drawmask = MASK_NORMAL;
+ self.use = door_use;
+
+ LinkDoors();
+
+ if(self.spawnflags & DOOR_START_OPEN)
+ door_init_startopen();
+
+ self.move_time = time;
+ self.move_origin = self.origin;
+ self.move_movetype = MOVETYPE_PUSH;
+ self.move_angles = self.angles;
+ self.move_blocked = door_blocked;
+ }
+
+ if(sf & SF_TRIGGER_RESET)
+ {
+ door_reset();
+ }
+
+ if(sf & SF_TRIGGER_UPDATE)
+ {
+ self.origin_x = ReadCoord();
+ self.origin_y = ReadCoord();
+ self.origin_z = ReadCoord();
+ setorigin(self, self.origin);
+ self.move_origin = self.origin;
+
+ self.pos1_x = ReadCoord();
+ self.pos1_y = ReadCoord();
+ self.pos1_z = ReadCoord();
+ self.pos2_x = ReadCoord();
+ self.pos2_y = ReadCoord();
+ self.pos2_z = ReadCoord();
+ }
+}
+
+#endif
--- /dev/null
- void fd_secret_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+#ifdef SVQC
+void() fd_secret_move1;
+void() fd_secret_move2;
+void() fd_secret_move3;
+void() fd_secret_move4;
+void() fd_secret_move5;
+void() fd_secret_move6;
+void() fd_secret_done;
+
+const float SECRET_OPEN_ONCE = 1; // stays open
+const float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
+const float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
+const float SECRET_NO_SHOOT = 8; // only opened by trigger
+const float SECRET_YES_SHOOT = 16; // shootable even if targeted
+
+void fd_secret_use()
+{
+ float temp;
+ string message_save;
+
+ self.health = 10000;
+ self.bot_attack = true;
+
+ // exit if still moving around...
+ if (self.origin != self.oldorigin)
+ return;
+
+ message_save = self.message;
+ self.message = ""; // no more message
+ SUB_UseTargets(); // fire all targets / killtargets
+ self.message = message_save;
+
+ self.velocity = '0 0 0';
+
+ // Make a sound, wait a little...
+
+ if (self.noise1 != "")
+ sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+ self.SUB_NEXTTHINK = self.SUB_LTIME + 0.1;
+
+ temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
+ makevectors(self.mangle);
+
+ if (!self.t_width)
+ {
+ if (self.spawnflags & SECRET_1ST_DOWN)
+ self.t_width = fabs(v_up * self.size);
+ else
+ self.t_width = fabs(v_right * self.size);
+ }
+
+ if (!self.t_length)
+ self.t_length = fabs(v_forward * self.size);
+
+ if (self.spawnflags & SECRET_1ST_DOWN)
+ self.dest1 = self.origin - v_up * self.t_width;
+ else
+ self.dest1 = self.origin + v_right * (self.t_width * temp);
+
+ self.dest2 = self.dest1 + v_forward * self.t_length;
+ SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move1);
+ if (self.noise2 != "")
+ sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+}
+
++void fd_secret_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+ fd_secret_use();
+}
+
+// Wait after first movement...
+void fd_secret_move1()
+{
+ self.SUB_NEXTTHINK = self.SUB_LTIME + 1.0;
+ self.think = fd_secret_move2;
+ if (self.noise3 != "")
+ sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
+}
+
+// Start moving sideways w/sound...
+void fd_secret_move2()
+{
+ if (self.noise2 != "")
+ sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+ SUB_CalcMove(self.dest2, TSPEED_LINEAR, self.speed, fd_secret_move3);
+}
+
+// Wait here until time to go back...
+void fd_secret_move3()
+{
+ if (self.noise3 != "")
+ sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
+ if (!(self.spawnflags & SECRET_OPEN_ONCE))
+ {
+ self.SUB_NEXTTHINK = self.SUB_LTIME + self.wait;
+ self.think = fd_secret_move4;
+ }
+}
+
+// Move backward...
+void fd_secret_move4()
+{
+ if (self.noise2 != "")
+ sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+ SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move5);
+}
+
+// Wait 1 second...
+void fd_secret_move5()
+{
+ self.SUB_NEXTTHINK = self.SUB_LTIME + 1.0;
+ self.think = fd_secret_move6;
+ if (self.noise3 != "")
+ sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
+}
+
+void fd_secret_move6()
+{
+ if (self.noise2 != "")
+ sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+ SUB_CalcMove(self.oldorigin, TSPEED_LINEAR, self.speed, fd_secret_done);
+}
+
+void fd_secret_done()
+{
+ if (self.spawnflags&SECRET_YES_SHOOT)
+ {
+ self.health = 10000;
+ self.takedamage = DAMAGE_YES;
+ //self.th_pain = fd_secret_use;
+ }
+ if (self.noise3 != "")
+ sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
+}
+
+void secret_blocked()
+{
+ if (time < self.attack_finished_single)
+ return;
+ self.attack_finished_single = time + 0.5;
+ //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
+}
+
+/*
+==============
+secret_touch
+
+Prints messages
+================
+*/
+void secret_touch()
+{
+ if (!other.iscreature)
+ return;
+ if (self.attack_finished_single > time)
+ return;
+
+ self.attack_finished_single = time + 2;
+
+ if (self.message)
+ {
+ if (IS_CLIENT(other))
+ centerprint(other, self.message);
+ play2(other, "misc/talk.wav");
+ }
+}
+
+void secret_reset()
+{
+ if (self.spawnflags&SECRET_YES_SHOOT)
+ {
+ self.health = 10000;
+ self.takedamage = DAMAGE_YES;
+ }
+ setorigin(self, self.oldorigin);
+ self.think = func_null;
+ self.SUB_NEXTTHINK = 0;
+}
+
+/*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
+Basic secret door. Slides back, then to the side. Angle determines direction.
+wait = # of seconds before coming back
+1st_left = 1st move is left of arrow
+1st_down = 1st move is down from arrow
+always_shoot = even if targeted, keep shootable
+t_width = override WIDTH to move back (or height if going down)
+t_length = override LENGTH to move sideways
+"dmg" damage to inflict when blocked (2 default)
+
+If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
+"sounds"
+1) medieval
+2) metal
+3) base
+*/
+
+void spawnfunc_func_door_secret()
+{
+ /*if (!self.deathtype) // map makers can override this
+ self.deathtype = " got in the way";*/
+
+ if (!self.dmg)
+ self.dmg = 2;
+
+ // Magic formula...
+ self.mangle = self.angles;
+ self.angles = '0 0 0';
+ self.classname = "door";
+ if (!InitMovingBrushTrigger())
+ return;
+ self.effects |= EF_LOWPRECISION;
+
+ self.touch = secret_touch;
+ self.blocked = secret_blocked;
+ self.speed = 50;
+ self.use = fd_secret_use;
+ IFTARGETED
+ {
+ }
+ else
+ self.spawnflags |= SECRET_YES_SHOOT;
+
+ if(self.spawnflags&SECRET_YES_SHOOT)
+ {
+ self.health = 10000;
+ self.takedamage = DAMAGE_YES;
+ self.event_damage = fd_secret_damage;
+ }
+ self.oldorigin = self.origin;
+ if (!self.wait)
+ self.wait = 5; // 5 seconds before closing
+
+ self.reset = secret_reset;
+ secret_reset();
+}
+#endif
--- /dev/null
- void multi_eventdamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+// NOTE: also contains trigger_once at bottom
+
+#ifdef SVQC
+// the wait time has passed, so set back up for another activation
+void multi_wait()
+{
+ if (self.max_health)
+ {
+ self.health = self.max_health;
+ self.takedamage = DAMAGE_YES;
+ self.solid = SOLID_BBOX;
+ }
+}
+
+
+// the trigger was just touched/killed/used
+// self.enemy should be set to the activator so it can be held through a delay
+// so wait for the delay time before firing
+void multi_trigger()
+{
+ if (self.nextthink > time)
+ {
+ return; // allready been triggered
+ }
+
+ if (self.classname == "trigger_secret")
+ {
+ if (!IS_PLAYER(self.enemy))
+ return;
+ found_secrets = found_secrets + 1;
+ WriteByte (MSG_ALL, SVC_FOUNDSECRET);
+ }
+
+ if (self.noise)
+ sound (self.enemy, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+
+// don't trigger again until reset
+ self.takedamage = DAMAGE_NO;
+
+ activator = self.enemy;
+ other = self.goalentity;
+ SUB_UseTargets();
+
+ if (self.wait > 0)
+ {
+ self.think = multi_wait;
+ self.nextthink = time + self.wait;
+ }
+ else if (self.wait == 0)
+ {
+ multi_wait(); // waiting finished
+ }
+ else
+ { // we can't just remove (self) here, because this is a touch function
+ // called wheil C code is looping through area links...
+ self.touch = func_null;
+ }
+}
+
+void multi_use()
+{
+ self.goalentity = other;
+ self.enemy = activator;
+ multi_trigger();
+}
+
+void multi_touch()
+{
+ if(!(self.spawnflags & 2))
+ if(!other.iscreature)
+ return;
+
+ if(self.team)
+ if(((self.spawnflags & 4) == 0) == (self.team != other.team))
+ return;
+
+// if the trigger has an angles field, check player's facing direction
+ if (self.movedir != '0 0 0')
+ {
+ makevectors (other.angles);
+ if (v_forward * self.movedir < 0)
+ return; // not facing the right way
+ }
+
+ // if the trigger has pressed keys, check that the player is pressing those keys
+ if(self.pressedkeys)
+ if(IS_PLAYER(other)) // only for players
+ if(!(other.pressedkeys & self.pressedkeys))
+ return;
+
+ EXACTTRIGGER_TOUCH;
+
+ self.enemy = other;
+ self.goalentity = other;
+ multi_trigger ();
+}
+
++void multi_eventdamage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+ if (!self.takedamage)
+ return;
+ if(self.spawnflags & DOOR_NOSPLASH)
+ if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
+ return;
+ self.health = self.health - damage;
+ if (self.health <= 0)
+ {
+ self.enemy = attacker;
+ self.goalentity = inflictor;
+ multi_trigger();
+ }
+}
+
+void multi_reset()
+{
+ if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
+ self.touch = multi_touch;
+ if (self.max_health)
+ {
+ self.health = self.max_health;
+ self.takedamage = DAMAGE_YES;
+ self.solid = SOLID_BBOX;
+ }
+ self.think = func_null;
+ self.nextthink = 0;
+ self.team = self.team_saved;
+}
+
+/*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
+Variable sized repeatable trigger. Must be targeted at one or more entities. If "health" is set, the trigger must be killed to activate each time.
+If "delay" is set, the trigger waits some time after activating before firing.
+"wait" : Seconds between triggerings. (.2 default)
+If notouch is set, the trigger is only fired by other entities, not by touching.
+NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
+sounds
+1) secret
+2) beep beep
+3) large switch
+4)
+set "message" to text string
+*/
+void spawnfunc_trigger_multiple()
+{
+ self.reset = multi_reset;
+ if (self.sounds == 1)
+ {
+ precache_sound ("misc/secret.wav");
+ self.noise = "misc/secret.wav";
+ }
+ else if (self.sounds == 2)
+ {
+ precache_sound ("misc/talk.wav");
+ self.noise = "misc/talk.wav";
+ }
+ else if (self.sounds == 3)
+ {
+ precache_sound ("misc/trigger1.wav");
+ self.noise = "misc/trigger1.wav";
+ }
+
+ if (!self.wait)
+ self.wait = 0.2;
+ else if(self.wait < -1)
+ self.wait = 0;
+ self.use = multi_use;
+
+ EXACTTRIGGER_INIT;
+
+ self.team_saved = self.team;
+
+ if (self.health)
+ {
+ if (self.spawnflags & SPAWNFLAG_NOTOUCH)
+ objerror ("health and notouch don't make sense\n");
+ self.max_health = self.health;
+ self.event_damage = multi_eventdamage;
+ self.takedamage = DAMAGE_YES;
+ self.solid = SOLID_BBOX;
+ setorigin (self, self.origin); // make sure it links into the world
+ }
+ else
+ {
+ if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
+ {
+ self.touch = multi_touch;
+ setorigin (self, self.origin); // make sure it links into the world
+ }
+ }
+}
+
+
+/*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
+Variable sized trigger. Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching
+"targetname". If "health" is set, the trigger must be killed to activate.
+If notouch is set, the trigger is only fired by other entities, not by touching.
+if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
+if "angle" is set, the trigger will only fire when someone is facing the direction of the angle. Use "360" for an angle of 0.
+sounds
+1) secret
+2) beep beep
+3) large switch
+4)
+set "message" to text string
+*/
+void spawnfunc_trigger_once()
+{
+ self.wait = -1;
+ spawnfunc_trigger_multiple();
+}
+#endif
--- /dev/null
- void turret_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
+#ifdef SVQC
+#include "../../server/autocvars.qh"
+
+// Generic aiming
+vector turret_aim_generic()
+{
+
+ vector pre_pos, prep;
+ float distance, impact_time = 0, i, mintime;
+
+ turret_tag_fire_update();
+
+ if(self.aim_flags & TFL_AIM_SIMPLE)
+ return real_origin(self.enemy);
+
+ mintime = max(self.attack_finished_single - time,0) + sys_frametime;
+
+ // Baseline
+ pre_pos = real_origin(self.enemy);
+
+ // Lead?
+ if (self.aim_flags & TFL_AIM_LEAD)
+ {
+ if (self.aim_flags & TFL_AIM_SHOTTIMECOMPENSATE) // Need to conpensate for shot traveltime
+ {
+ prep = pre_pos;
+
+ distance = vlen(prep - self.tur_shotorg);
+ impact_time = distance / self.shot_speed;
+
+ prep = pre_pos + (self.enemy.velocity * (impact_time + mintime));
+
+ if(self.aim_flags & TFL_AIM_ZPREDICT)
+ if(!(self.enemy.flags & FL_ONGROUND))
+ if(self.enemy.movetype == MOVETYPE_WALK || self.enemy.movetype == MOVETYPE_TOSS || self.enemy.movetype == MOVETYPE_BOUNCE)
+ {
+ float vz;
+ prep_z = pre_pos_z;
+ vz = self.enemy.velocity_z;
+ for(i = 0; i < impact_time; i += sys_frametime)
+ {
+ vz = vz - (autocvar_sv_gravity * sys_frametime);
+ prep_z = prep_z + vz * sys_frametime;
+ }
+ }
+ pre_pos = prep;
+ }
+ else
+ pre_pos = pre_pos + self.enemy.velocity * mintime;
+ }
+
+ if(self.aim_flags & TFL_AIM_SPLASH)
+ {
+ //tracebox(pre_pos + '0 0 32',self.enemy.mins,self.enemy.maxs,pre_pos -'0 0 64',MOVE_WORLDONLY,self.enemy);
+ traceline(pre_pos + '0 0 32',pre_pos -'0 0 64',MOVE_WORLDONLY,self.enemy);
+ if(trace_fraction != 1.0)
+ pre_pos = trace_endpos;
+ }
+
+ return pre_pos;
+}
+
+float turret_targetscore_support(entity _turret,entity _target)
+{
+ float score; // Total score
+ float s_score = 0, d_score;
+
+ if (_turret.enemy == _target) s_score = 1;
+
+ d_score = min(_turret.target_range_optimal,tvt_dist) / max(_turret.target_range_optimal,tvt_dist);
+
+ score = (d_score * _turret.target_select_rangebias) +
+ (s_score * _turret.target_select_samebias);
+
+ return score;
+}
+
+/*
+* Generic bias aware score system.
+*/
+float turret_targetscore_generic(entity _turret, entity _target)
+{
+ float d_dist; // Defendmode Distance
+ float score; // Total score
+ float d_score; // Distance score
+ float a_score; // Angular score
+ float m_score = 0; // missile score
+ float p_score = 0; // player score
+ float ikr; // ideal kill range
+
+ if (_turret.tur_defend)
+ {
+ d_dist = vlen(real_origin(_target) - _turret.tur_defend.origin);
+ ikr = vlen(_turret.origin - _turret.tur_defend.origin);
+ d_score = 1 - d_dist / _turret.target_range;
+ }
+ else
+ {
+ // Make a normlized value base on the targets distance from our optimal killzone
+ ikr = _turret.target_range_optimal;
+ d_score = min(ikr, tvt_dist) / max(ikr, tvt_dist);
+ }
+
+ a_score = 1 - tvt_thadf / _turret.aim_maxrotate;
+
+ if ((_turret.target_select_missilebias > 0) && (_target.flags & FL_PROJECTILE))
+ m_score = 1;
+
+ if ((_turret.target_select_playerbias > 0) && IS_CLIENT(_target))
+ p_score = 1;
+
+ d_score = max(d_score, 0);
+ a_score = max(a_score, 0);
+ m_score = max(m_score, 0);
+ p_score = max(p_score, 0);
+
+ score = (d_score * _turret.target_select_rangebias) +
+ (a_score * _turret.target_select_anglebias) +
+ (m_score * _turret.target_select_missilebias) +
+ (p_score * _turret.target_select_playerbias);
+
+ if(_turret.target_range < vlen(_turret.tur_shotorg - real_origin(_target)))
+ {
+ //dprint("Wtf?\n");
+ score *= 0.001;
+ }
+
+#ifdef TURRET_DEBUG
+ string sd,sa,sm,sp,ss;
+ string sdt,sat,smt,spt;
+
+ sd = ftos(d_score);
+ d_score *= _turret.target_select_rangebias;
+ sdt = ftos(d_score);
+
+ //sv = ftos(v_score);
+ //v_score *= _turret.target_select_samebias;
+ //svt = ftos(v_score);
+
+ sa = ftos(a_score);
+ a_score *= _turret.target_select_anglebias;
+ sat = ftos(a_score);
+
+ sm = ftos(m_score);
+ m_score *= _turret.target_select_missilebias;
+ smt = ftos(m_score);
+
+ sp = ftos(p_score);
+ p_score *= _turret.target_select_playerbias;
+ spt = ftos(p_score);
+
+
+ ss = ftos(score);
+ bprint("^3Target scores^7 \[ ",_turret.netname, " \] ^3for^7 \[ ", _target.netname," \]\n");
+ bprint("^5Range:\[ ",sd, " \]^2+bias:\[ ",sdt," \]\n");
+ bprint("^5Angle:\[ ",sa, " \]^2+bias:\[ ",sat," \]\n");
+ bprint("^5Missile:\[ ",sm," \]^2+bias:\[ ",smt," \]\n");
+ bprint("^5Player:\[ ",sp, " \]^2+bias:\[ ",spt," \]\n");
+ bprint("^3Total (w/bias):\[^1",ss,"\]\n");
+
+#endif
+
+ return score;
+}
+
+// Generic damage handling
+void turret_hide()
+{
+ self.effects |= EF_NODRAW;
+ self.nextthink = time + self.respawntime - 0.2;
+ self.think = turret_respawn;
+}
+
+void turret_die()
+{
+ self.deadflag = DEAD_DEAD;
+ self.tur_head.deadflag = self.deadflag;
+
+// Unsolidify and hide real parts
+ self.solid = SOLID_NOT;
+ self.tur_head.solid = self.solid;
+
+ self.event_damage = func_null;
+ self.takedamage = DAMAGE_NO;
+
+ self.health = 0;
+
+// Go boom
+ //RadiusDamage (self,self, min(self.ammo,50),min(self.ammo,50) * 0.25,250,world,min(self.ammo,50)*5,DEATH_TURRET,world);
+
+ if(self.damage_flags & TFL_DMG_DEATH_NORESPAWN)
+ {
+ TUR_ACTION(self.turretid, TR_DEATH);
+
+ remove(self.tur_head);
+ remove(self);
+ }
+ else
+ {
+ // Setup respawn
+ self.SendFlags |= TNSF_STATUS;
+ self.nextthink = time + 0.2;
+ self.think = turret_hide;
+
+ TUR_ACTION(self.turretid, TR_DEATH);
+ }
+}
+
- void turret_projectile_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
++void turret_damage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector vforce)
+{
+ // Enough already!
+ if(self.deadflag == DEAD_DEAD)
+ return;
+
+ // Inactive turrets take no damage. (hm..)
+ if(!self.active)
+ return;
+
+ if(SAME_TEAM(self, attacker))
+ {
+ if(autocvar_g_friendlyfire)
+ damage = damage * autocvar_g_friendlyfire;
+ else
+ return;
+ }
+
+ if(DEATH_WEAPONOFWEAPONDEATH(deathtype))
+ damage = damage * autocvar_g_turrets_weapon_damagerate;
+
+ self.health -= damage;
+
+ // thorw head slightly off aim when hit?
+ if (self.damage_flags & TFL_DMG_HEADSHAKE)
+ {
+ self.tur_head.angles_x = self.tur_head.angles_x + (-0.5 + random()) * damage;
+ self.tur_head.angles_y = self.tur_head.angles_y + (-0.5 + random()) * damage;
+
+ self.SendFlags |= TNSF_ANG;
+ }
+
+ if (self.turret_flags & TUR_FLAG_MOVE)
+ self.velocity = self.velocity + vforce;
+
+ if (self.health <= 0)
+ {
+ self.event_damage = func_null;
+ self.tur_head.event_damage = func_null;
+ self.takedamage = DAMAGE_NO;
+ self.nextthink = time;
+ self.think = turret_die;
+ }
+
+ self.SendFlags |= TNSF_STATUS;
+}
+
+void() turret_think;
+void turret_respawn()
+{
+ // Make sure all parts belong to the same team since
+ // this function doubles as "teamchange" function.
+ self.tur_head.team = self.team;
+ self.effects &= ~EF_NODRAW;
+ self.deadflag = DEAD_NO;
+ self.effects = EF_LOWPRECISION;
+ self.solid = SOLID_BBOX;
+ self.takedamage = DAMAGE_AIM;
+ self.event_damage = turret_damage;
+ self.avelocity = '0 0 0';
+ self.tur_head.avelocity = self.avelocity;
+ self.tur_head.angles = self.idle_aim;
+ self.health = self.max_health;
+ self.enemy = world;
+ self.volly_counter = self.shot_volly;
+ self.ammo = self.ammo_max;
+
+ self.nextthink = time + self.ticrate;
+ self.think = turret_think;
+
+ self.SendFlags = TNSF_FULL_UPDATE;
+
+ TUR_ACTION(self.turretid, TR_SETUP);
+}
+
+
+// Main functions
+#define cvar_base "g_turrets_unit_"
+.float clientframe;
+void turrets_setframe(float _frame, float client_only)
+{
+ if((client_only ? self.clientframe : self.frame ) != _frame)
+ {
+ self.SendFlags |= TNSF_ANIM;
+ self.anim_start_time = time;
+ }
+
+ if(client_only)
+ self.clientframe = _frame;
+ else
+ self.frame = _frame;
+
+}
+
+float turret_send(entity to, float sf)
+{
+
+ WriteByte(MSG_ENTITY, ENT_CLIENT_TURRET);
+ WriteByte(MSG_ENTITY, sf);
+ if(sf & TNSF_SETUP)
+ {
+ WriteByte(MSG_ENTITY, self.turretid);
+
+ WriteCoord(MSG_ENTITY, self.origin_x);
+ WriteCoord(MSG_ENTITY, self.origin_y);
+ WriteCoord(MSG_ENTITY, self.origin_z);
+
+ WriteAngle(MSG_ENTITY, self.angles_x);
+ WriteAngle(MSG_ENTITY, self.angles_y);
+ }
+
+ if(sf & TNSF_ANG)
+ {
+ WriteShort(MSG_ENTITY, rint(self.tur_head.angles_x));
+ WriteShort(MSG_ENTITY, rint(self.tur_head.angles_y));
+ }
+
+ if(sf & TNSF_AVEL)
+ {
+ WriteShort(MSG_ENTITY, rint(self.tur_head.avelocity_x));
+ WriteShort(MSG_ENTITY, rint(self.tur_head.avelocity_y));
+ }
+
+ if(sf & TNSF_MOVE)
+ {
+ WriteShort(MSG_ENTITY, rint(self.origin_x));
+ WriteShort(MSG_ENTITY, rint(self.origin_y));
+ WriteShort(MSG_ENTITY, rint(self.origin_z));
+
+ WriteShort(MSG_ENTITY, rint(self.velocity_x));
+ WriteShort(MSG_ENTITY, rint(self.velocity_y));
+ WriteShort(MSG_ENTITY, rint(self.velocity_z));
+
+ WriteShort(MSG_ENTITY, rint(self.angles_y));
+ }
+
+ if(sf & TNSF_ANIM)
+ {
+ WriteCoord(MSG_ENTITY, self.anim_start_time);
+ WriteByte(MSG_ENTITY, self.frame);
+ }
+
+ if(sf & TNSF_STATUS)
+ {
+ WriteByte(MSG_ENTITY, self.team);
+
+ if(self.health <= 0)
+ WriteByte(MSG_ENTITY, 0);
+ else
+ WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255));
+ }
+
+ return true;
+}
+
+void load_unit_settings(entity ent, string unitname, float is_reload)
+{
+ string sbase;
+
+ if (ent == world)
+ return;
+
+ if(!ent.turret_scale_damage) ent.turret_scale_damage = 1;
+ if(!ent.turret_scale_range) ent.turret_scale_range = 1;
+ if(!ent.turret_scale_refire) ent.turret_scale_refire = 1;
+ if(!ent.turret_scale_ammo) ent.turret_scale_ammo = 1;
+ if(!ent.turret_scale_aim) ent.turret_scale_aim = 1;
+ if(!ent.turret_scale_health) ent.turret_scale_health = 1;
+ if(!ent.turret_scale_respawn) ent.turret_scale_respawn = 1;
+
+ sbase = strcat(cvar_base,unitname);
+ if (is_reload)
+ {
+ ent.enemy = world;
+ ent.tur_head.avelocity = '0 0 0';
+
+ ent.tur_head.angles = '0 0 0';
+ }
+
+ ent.health = cvar(strcat(sbase,"_health")) * ent.turret_scale_health;
+ ent.respawntime = cvar(strcat(sbase,"_respawntime")) * ent.turret_scale_respawn;
+
+ ent.shot_dmg = cvar(strcat(sbase,"_shot_dmg")) * ent.turret_scale_damage;
+ ent.shot_refire = cvar(strcat(sbase,"_shot_refire")) * ent.turret_scale_refire;
+ ent.shot_radius = cvar(strcat(sbase,"_shot_radius")) * ent.turret_scale_damage;
+ ent.shot_speed = cvar(strcat(sbase,"_shot_speed"));
+ ent.shot_spread = cvar(strcat(sbase,"_shot_spread"));
+ ent.shot_force = cvar(strcat(sbase,"_shot_force")) * ent.turret_scale_damage;
+ ent.shot_volly = cvar(strcat(sbase,"_shot_volly"));
+ ent.shot_volly_refire = cvar(strcat(sbase,"_shot_volly_refire")) * ent.turret_scale_refire;
+
+ ent.target_range = cvar(strcat(sbase,"_target_range")) * ent.turret_scale_range;
+ ent.target_range_min = cvar(strcat(sbase,"_target_range_min")) * ent.turret_scale_range;
+ ent.target_range_optimal = cvar(strcat(sbase,"_target_range_optimal")) * ent.turret_scale_range;
+ //ent.target_range_fire = cvar(strcat(sbase,"_target_range_fire")) * ent.turret_scale_range;
+
+ ent.target_select_rangebias = cvar(strcat(sbase,"_target_select_rangebias"));
+ ent.target_select_samebias = cvar(strcat(sbase,"_target_select_samebias"));
+ ent.target_select_anglebias = cvar(strcat(sbase,"_target_select_anglebias"));
+ ent.target_select_playerbias = cvar(strcat(sbase,"_target_select_playerbias"));
+ //ent.target_select_fov = cvar(cvar_gets(sbase,"_target_select_fov"));
+
+ ent.ammo_max = cvar(strcat(sbase,"_ammo_max")) * ent.turret_scale_ammo;
+ ent.ammo_recharge = cvar(strcat(sbase,"_ammo_recharge")) * ent.turret_scale_ammo;
+
+ ent.aim_firetolerance_dist = cvar(strcat(sbase,"_aim_firetolerance_dist"));
+ ent.aim_speed = cvar(strcat(sbase,"_aim_speed")) * ent.turret_scale_aim;
+ ent.aim_maxrotate = cvar(strcat(sbase,"_aim_maxrot"));
+ ent.aim_maxpitch = cvar(strcat(sbase,"_aim_maxpitch"));
+
+ ent.track_type = cvar(strcat(sbase,"_track_type"));
+ ent.track_accel_pitch = cvar(strcat(sbase,"_track_accel_pitch"));
+ ent.track_accel_rotate = cvar(strcat(sbase,"_track_accel_rot"));
+ ent.track_blendrate = cvar(strcat(sbase,"_track_blendrate"));
+
+ if(is_reload)
+ TUR_ACTION(self.turretid, TR_SETUP);
+}
+
+void turret_projectile_explode()
+{
+
+ self.takedamage = DAMAGE_NO;
+ self.event_damage = func_null;
+#ifdef TURRET_DEBUG
+ float d;
+ d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
+ self.owner.tur_debug_dmg_t_h = self.owner.tur_debug_dmg_t_h + d;
+ self.owner.tur_debug_dmg_t_f = self.owner.tur_debug_dmg_t_f + self.owner.shot_dmg;
+#else
+ RadiusDamage (self, self.realowner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
+#endif
+ remove(self);
+}
+
+void turret_projectile_touch()
+{
+ PROJECTILE_TOUCH;
+ turret_projectile_explode();
+}
+
++void turret_projectile_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector vforce)
+{
+ self.velocity += vforce;
+ self.health -= damage;
+ //self.realowner = attacker; // Dont change realowner, it does not make much sense for turrets
+ if(self.health <= 0)
+ W_PrepareExplosionByDamage(self.owner, turret_projectile_explode);
+}
+
+entity turret_projectile(string _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim)
+{
+ entity proj;
+
+ sound (self, CH_WEAPON_A, _snd, VOL_BASE, ATTEN_NORM);
+ proj = spawn ();
+ setorigin(proj, self.tur_shotorg);
+ setsize(proj, '-0.5 -0.5 -0.5' * _size, '0.5 0.5 0.5' * _size);
+ proj.owner = self;
+ proj.realowner = self;
+ proj.bot_dodge = true;
+ proj.bot_dodgerating = self.shot_dmg;
+ proj.think = turret_projectile_explode;
+ proj.touch = turret_projectile_touch;
+ proj.nextthink = time + 9;
+ proj.movetype = MOVETYPE_FLYMISSILE;
+ proj.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed;
+ proj.flags = FL_PROJECTILE;
+ proj.enemy = self.enemy;
+ proj.totalfrags = _death;
+ PROJECTILE_MAKETRIGGER(proj);
+ if(_health)
+ {
+ proj.health = _health;
+ proj.takedamage = DAMAGE_YES;
+ proj.event_damage = turret_projectile_damage;
+ }
+ else
+ proj.flags |= FL_NOTARGET;
+
+ CSQCProjectile(proj, _cli_anim, _proj_type, _cull);
+
+ return proj;
+}
+
+/**
+** updates enemy distances, predicted impact point/time
+** and updated aim<->predict impact distance.
+**/
+void turret_do_updates(entity t_turret)
+{
+ vector enemy_pos;
+ entity oldself;
+
+ oldself = self;
+ self = t_turret;
+
+ enemy_pos = real_origin(self.enemy);
+
+ turret_tag_fire_update();
+
+ self.tur_shotdir_updated = v_forward;
+ self.tur_dist_enemy = vlen(self.tur_shotorg - enemy_pos);
+ self.tur_dist_aimpos = vlen(self.tur_shotorg - self.tur_aimpos);
+
+ /*if((self.firecheck_flags & TFL_FIRECHECK_VERIFIED) && (self.enemy))
+ {
+ oldpos = self.enemy.origin;
+ setorigin(self.enemy, self.tur_aimpos);
+ tracebox(self.tur_shotorg, '-1 -1 -1', '1 1 1', self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos), MOVE_NORMAL,self);
+ setorigin(self.enemy, oldpos);
+
+ if(trace_ent == self.enemy)
+ self.tur_dist_impact_to_aimpos = 0;
+ else
+ self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos);
+ }
+ else*/
+ tracebox(self.tur_shotorg, '-1 -1 -1','1 1 1', self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos), MOVE_NORMAL,self);
+
+ self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos) - (vlen(self.enemy.maxs - self.enemy.mins) * 0.5);
+ self.tur_impactent = trace_ent;
+ self.tur_impacttime = vlen(self.tur_shotorg - trace_endpos) / self.shot_speed;
+
+ self = oldself;
+}
+
+/**
+** Handles head rotation according to
+** the units .track_type and .track_flags
+**/
+.float turret_framecounter;
+void turret_track()
+{
+ vector target_angle; // This is where we want to aim
+ vector move_angle; // This is where we can aim
+ float f_tmp;
+ vector v1, v2;
+ v1 = self.tur_head.angles;
+ v2 = self.tur_head.avelocity;
+
+ if (self.track_flags == TFL_TRACK_NO)
+ return;
+
+ if(!self.active)
+ target_angle = self.idle_aim - ('1 0 0' * self.aim_maxpitch);
+ else if (self.enemy == world)
+ {
+ if(time > self.lip)
+ target_angle = self.idle_aim + self.angles;
+ else
+ target_angle = vectoangles(normalize(self.tur_aimpos - self.tur_shotorg));
+ }
+ else
+ {
+ target_angle = vectoangles(normalize(self.tur_aimpos - self.tur_shotorg));
+ }
+
+ self.tur_head.angles_x = anglemods(self.tur_head.angles_x);
+ self.tur_head.angles_y = anglemods(self.tur_head.angles_y);
+
+ // Find the diffrence between where we currently aim and where we want to aim
+ //move_angle = target_angle - (self.angles + self.tur_head.angles);
+ //move_angle = shortangle_vxy(move_angle,(self.angles + self.tur_head.angles));
+
+ move_angle = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(self.angles), AnglesTransform_FromAngles(target_angle))) - self.tur_head.angles;
+ move_angle = shortangle_vxy(move_angle, self.tur_head.angles);
+
+ switch(self.track_type)
+ {
+ case TFL_TRACKTYPE_STEPMOTOR:
+ f_tmp = self.aim_speed * self.ticrate; // dgr/sec -> dgr/tic
+ if (self.track_flags & TFL_TRACK_PITCH)
+ {
+ self.tur_head.angles_x += bound(-f_tmp,move_angle_x, f_tmp);
+ if(self.tur_head.angles_x > self.aim_maxpitch)
+ self.tur_head.angles_x = self.aim_maxpitch;
+
+ if(self.tur_head.angles_x < -self.aim_maxpitch)
+ self.tur_head.angles_x = self.aim_maxpitch;
+ }
+
+ if (self.track_flags & TFL_TRACK_ROTATE)
+ {
+ self.tur_head.angles_y += bound(-f_tmp, move_angle_y, f_tmp);
+ if(self.tur_head.angles_y > self.aim_maxrotate)
+ self.tur_head.angles_y = self.aim_maxrotate;
+
+ if(self.tur_head.angles_y < -self.aim_maxrotate)
+ self.tur_head.angles_y = self.aim_maxrotate;
+ }
+
+ // CSQC
+ self.SendFlags |= TNSF_ANG;
+
+ return;
+
+ case TFL_TRACKTYPE_FLUIDINERTIA:
+ f_tmp = self.aim_speed * self.ticrate; // dgr/sec -> dgr/tic
+ move_angle_x = bound(-self.aim_speed, move_angle_x * self.track_accel_pitch * f_tmp, self.aim_speed);
+ move_angle_y = bound(-self.aim_speed, move_angle_y * self.track_accel_rotate * f_tmp, self.aim_speed);
+ move_angle = (self.tur_head.avelocity * self.track_blendrate) + (move_angle * (1 - self.track_blendrate));
+ break;
+
+ case TFL_TRACKTYPE_FLUIDPRECISE:
+
+ move_angle_y = bound(-self.aim_speed, move_angle_y, self.aim_speed);
+ move_angle_x = bound(-self.aim_speed, move_angle_x, self.aim_speed);
+
+ break;
+ }
+
+ // pitch
+ if (self.track_flags & TFL_TRACK_PITCH)
+ {
+ self.tur_head.avelocity_x = move_angle_x;
+ if((self.tur_head.angles_x + self.tur_head.avelocity_x * self.ticrate) > self.aim_maxpitch)
+ {
+ self.tur_head.avelocity_x = 0;
+ self.tur_head.angles_x = self.aim_maxpitch;
+
+ self.SendFlags |= TNSF_ANG;
+ }
+
+ if((self.tur_head.angles_x + self.tur_head.avelocity_x * self.ticrate) < -self.aim_maxpitch)
+ {
+ self.tur_head.avelocity_x = 0;
+ self.tur_head.angles_x = -self.aim_maxpitch;
+
+ self.SendFlags |= TNSF_ANG;
+ }
+ }
+
+ // rot
+ if (self.track_flags & TFL_TRACK_ROTATE)
+ {
+ self.tur_head.avelocity_y = move_angle_y;
+
+ if((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) > self.aim_maxrotate)
+ {
+ self.tur_head.avelocity_y = 0;
+ self.tur_head.angles_y = self.aim_maxrotate;
+
+ self.SendFlags |= TNSF_ANG;
+ }
+
+ if((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) < -self.aim_maxrotate)
+ {
+ self.tur_head.avelocity_y = 0;
+ self.tur_head.angles_y = -self.aim_maxrotate;
+
+ self.SendFlags |= TNSF_ANG;
+ }
+ }
+
+ self.SendFlags |= TNSF_AVEL;
+
+ // Force a angle update every 10'th frame
+ self.turret_framecounter += 1;
+ if(self.turret_framecounter >= 10)
+ {
+ self.SendFlags |= TNSF_ANG;
+ self.turret_framecounter = 0;
+ }
+}
+
+/*
+ + TFL_TARGETSELECT_NO
+ + TFL_TARGETSELECT_LOS
+ + TFL_TARGETSELECT_PLAYERS
+ + TFL_TARGETSELECT_MISSILES
+ - TFL_TARGETSELECT_TRIGGERTARGET
+ + TFL_TARGETSELECT_ANGLELIMITS
+ + TFL_TARGETSELECT_RANGELIMITS
+ + TFL_TARGETSELECT_TEAMCHECK
+ - TFL_TARGETSELECT_NOBUILTIN
+ + TFL_TARGETSELECT_OWNTEAM
+*/
+
+/**
+** Evaluate a entity for target valitity based on validate_flags
+** NOTE: the caller must check takedamage before calling this, to inline this check.
+**/
+float turret_validate_target(entity e_turret, entity e_target, float validate_flags)
+{
+ vector v_tmp;
+
+ //if(!validate_flags & TFL_TARGETSELECT_NOBUILTIN)
+ // return -0.5;
+
+ if(!e_target)
+ return -2;
+
+ if(e_target.owner == e_turret)
+ return -0.5;
+
+ if(!checkpvs(e_target.origin, e_turret))
+ return -1;
+
+ if(e_target.alpha <= 0.3)
+ return -1;
+
+ if(g_onslaught)
+ if (substring(e_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job!
+ return - 3;
+
+ if (validate_flags & TFL_TARGETSELECT_NO)
+ return -4;
+
+ // If only this was used more..
+ if (e_target.flags & FL_NOTARGET)
+ return -5;
+
+ // Cant touch this
+ if(IS_VEHICLE(e_target))
+ {
+ if (e_target.vehicle_health <= 0)
+ return -6;
+ }
+ else if(Player_Trapped(e_target))
+ return -6;
+
+ // player
+ if (IS_CLIENT(e_target))
+ {
+ if(!(validate_flags & TFL_TARGETSELECT_PLAYERS))
+ return -7;
+
+ if (e_target.deadflag != DEAD_NO)
+ return -8;
+ }
+
+ // enemy turrets
+ if(validate_flags & TFL_TARGETSELECT_NOTURRETS)
+ if(e_target.owner.tur_head == e_target)
+ if(e_target.team != e_turret.team) // Dont break support units.
+ return -9;
+
+ // Missile
+ if (e_target.flags & FL_PROJECTILE)
+ if(!(validate_flags & TFL_TARGETSELECT_MISSILES))
+ return -10;
+
+ if (validate_flags & TFL_TARGETSELECT_MISSILESONLY)
+ if(!(e_target.flags & FL_PROJECTILE))
+ return -10.5;
+
+ // Team check
+ if (validate_flags & TFL_TARGETSELECT_TEAMCHECK)
+ {
+ if (validate_flags & TFL_TARGETSELECT_OWNTEAM)
+ {
+ if (e_target.team != e_turret.team)
+ return -11;
+
+ if (e_turret.team != e_target.owner.team)
+ return -12;
+ }
+ else
+ {
+ if (e_target.team == e_turret.team)
+ return -13;
+
+ if (e_turret.team == e_target.owner.team)
+ return -14;
+ }
+ }
+
+ // Range limits?
+ tvt_dist = vlen(e_turret.origin - real_origin(e_target));
+ if (validate_flags & TFL_TARGETSELECT_RANGELIMITS)
+ {
+ if (tvt_dist < e_turret.target_range_min)
+ return -15;
+
+ if (tvt_dist > e_turret.target_range)
+ return -16;
+ }
+
+ // Can we even aim this thing?
+ tvt_thadv = angleofs3(e_turret.tur_head.origin, e_turret.angles + e_turret.tur_head.angles, e_target);
+ tvt_tadv = shortangle_vxy(angleofs(e_turret, e_target), e_turret.angles);
+ tvt_thadf = vlen(tvt_thadv);
+ tvt_tadf = vlen(tvt_tadv);
+
+ /*
+ if(validate_flags & TFL_TARGETSELECT_FOV)
+ {
+ if(e_turret.target_select_fov < tvt_thadf)
+ return -21;
+ }
+ */
+
+ if (validate_flags & TFL_TARGETSELECT_ANGLELIMITS)
+ {
+ if (fabs(tvt_tadv_x) > e_turret.aim_maxpitch)
+ return -17;
+
+ if (fabs(tvt_tadv_y) > e_turret.aim_maxrotate)
+ return -18;
+ }
+
+ // Line of sight?
+ if (validate_flags & TFL_TARGETSELECT_LOS)
+ {
+ v_tmp = real_origin(e_target) + ((e_target.mins + e_target.maxs) * 0.5);
+
+ traceline(e_turret.origin + '0 0 16', v_tmp, 0, e_turret);
+
+ if (e_turret.aim_firetolerance_dist < vlen(v_tmp - trace_endpos))
+ return -19;
+ }
+
+ if (e_target.classname == "grapplinghook")
+ return -20;
+
+ /*
+ if (e_target.classname == "func_button")
+ return -21;
+ */
+
+#ifdef TURRET_DEBUG_TARGETSELECT
+ dprint("Target:",e_target.netname," is a valid target for ",e_turret.netname,"\n");
+#endif
+
+ return 1;
+}
+
+entity turret_select_target()
+{
+ entity e; // target looper entity
+ float score; // target looper entity score
+ entity e_enemy; // currently best scoreing target
+ float m_score; // currently best scoreing target's score
+
+ m_score = 0;
+ if(self.enemy && self.enemy.takedamage && turret_validate_target(self,self.enemy,self.target_validate_flags) > 0)
+ {
+ e_enemy = self.enemy;
+ m_score = self.turret_score_target(self,e_enemy) * self.target_select_samebias;
+ }
+ else
+ e_enemy = self.enemy = world;
+
+ e = findradius(self.origin, self.target_range);
+
+ // Nothing to aim at?
+ if (!e)
+ return world;
+
+ while (e)
+ {
+ if(e.takedamage)
+ {
+ float f = turret_validate_target(self, e, self.target_select_flags);
+ //dprint("F is: ", ftos(f), "\n");
+ if ( f > 0)
+ {
+ score = self.turret_score_target(self,e);
+ if ((score > m_score) && (score > 0))
+ {
+ e_enemy = e;
+ m_score = score;
+ }
+ }
+ }
+ e = e.chain;
+ }
+
+ return e_enemy;
+}
+
+
+/*
+ + = implemented
+ - = not implemented
+
+ + TFL_FIRECHECK_NO
+ + TFL_FIRECHECK_WORLD
+ + TFL_FIRECHECK_DEAD
+ + TFL_FIRECHECK_DISTANCES
+ - TFL_FIRECHECK_LOS
+ + TFL_FIRECHECK_AIMDIST
+ + TFL_FIRECHECK_REALDIST
+ - TFL_FIRECHECK_ANGLEDIST
+ - TFL_FIRECHECK_TEAMCECK
+ + TFL_FIRECHECK_AFF
+ + TFL_FIRECHECK_AMMO_OWN
+ + TFL_FIRECHECK_AMMO_OTHER
+ + TFL_FIRECHECK_REFIRE
+*/
+
+/**
+** Preforms pre-fire checks based on the uints firecheck_flags
+**/
+float turret_firecheck()
+{
+ // This one just dont care =)
+ if (self.firecheck_flags & TFL_FIRECHECK_NO)
+ return 1;
+
+ if (self.enemy == world)
+ return 0;
+
+ // Ready?
+ if (self.firecheck_flags & TFL_FIRECHECK_REFIRE)
+ if (self.attack_finished_single > time) return 0;
+
+ // Special case: volly fire turret that has to fire a full volly if a shot was fired.
+ if (self.shoot_flags & TFL_SHOOT_VOLLYALWAYS)
+ if (self.volly_counter != self.shot_volly)
+ if(self.ammo >= self.shot_dmg)
+ return 1;
+
+ // Lack of zombies makes shooting dead things unnecessary :P
+ if (self.firecheck_flags & TFL_FIRECHECK_DEAD)
+ if (self.enemy.deadflag != DEAD_NO)
+ return 0;
+
+ // Own ammo?
+ if (self.firecheck_flags & TFL_FIRECHECK_AMMO_OWN)
+ if (self.ammo < self.shot_dmg)
+ return 0;
+
+ // Other's ammo? (support-supply units)
+ if (self.firecheck_flags & TFL_FIRECHECK_AMMO_OTHER)
+ if (self.enemy.ammo >= self.enemy.ammo_max)
+ return 0;
+
+ // Target of opertunity?
+ if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0)
+ {
+ self.enemy = self.tur_impactent;
+ return 1;
+ }
+
+ if (self.firecheck_flags & TFL_FIRECHECK_DISTANCES)
+ {
+ // To close?
+ if (self.tur_dist_aimpos < self.target_range_min)
+ if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0)
+ return 1; // Target of opertunity?
+ else
+ return 0;
+ }
+
+ // Try to avoid FF?
+ if (self.firecheck_flags & TFL_FIRECHECK_AFF)
+ if (self.tur_impactent.team == self.team)
+ return 0;
+
+ // aim<->predicted impact
+ if (self.firecheck_flags & TFL_FIRECHECK_AIMDIST)
+ if (self.tur_dist_impact_to_aimpos > self.aim_firetolerance_dist)
+ return 0;
+
+ // Volly status
+ if (self.shot_volly > 1)
+ if (self.volly_counter == self.shot_volly)
+ if (self.ammo < (self.shot_dmg * self.shot_volly))
+ return 0;
+
+ /*if(self.firecheck_flags & TFL_FIRECHECK_VERIFIED)
+ if(self.tur_impactent != self.enemy)
+ return 0;*/
+
+ return 1;
+}
+
+void turret_fire()
+{
+ if (autocvar_g_turrets_nofire != 0)
+ return;
+
+ TUR_ACTION(self.turretid, TR_ATTACK);
+
+ self.attack_finished_single = time + self.shot_refire;
+ self.ammo -= self.shot_dmg;
+ self.volly_counter = self.volly_counter - 1;
+
+ if (self.volly_counter <= 0)
+ {
+ self.volly_counter = self.shot_volly;
+
+ if (self.shoot_flags & TFL_SHOOT_CLEARTARGET)
+ self.enemy = world;
+
+ if (self.shot_volly > 1)
+ self.attack_finished_single = time + self.shot_volly_refire;
+ }
+
+#ifdef TURRET_DEBUG
+ if (self.enemy) paint_target3(self.tur_aimpos, 64, self.tur_debug_rvec, self.tur_impacttime + 0.25);
+#endif
+}
+
+void turret_think()
+{
+ entity e;
+
+ self.nextthink = time + self.ticrate;
+
+ // ONS uses somewhat backwards linking.
+ if (teamplay)
+ {
+ if (g_onslaught)
+ if (self.target)
+ {
+ e = find(world, targetname,self.target);
+ if (e != world)
+ self.team = e.team;
+ }
+
+ if (self.team != self.tur_head.team)
+ turret_respawn();
+ }
+
+#ifdef TURRET_DEBUG
+ if (self.tur_debug_tmr1 < time)
+ {
+ if (self.enemy) paint_target (self.enemy,128,self.tur_debug_rvec,0.9);
+ paint_target(self,256,self.tur_debug_rvec,0.9);
+ self.tur_debug_tmr1 = time + 1;
+ }
+#endif
+
+ // Handle ammo
+ if (!(self.spawnflags & TSF_NO_AMMO_REGEN))
+ if (self.ammo < self.ammo_max)
+ self.ammo = min(self.ammo + self.ammo_recharge, self.ammo_max);
+
+ // Inactive turrets needs to run the think loop,
+ // So they can handle animation and wake up if need be.
+ if(!self.active)
+ {
+ turret_track();
+ return;
+ }
+
+ // This is typicaly used for zaping every target in range
+ // turret_fusionreactor uses this to recharge friendlys.
+ if (self.shoot_flags & TFL_SHOOT_HITALLVALID)
+ {
+ // Do a self.turret_fire for every valid target.
+ e = findradius(self.origin,self.target_range);
+ while (e)
+ {
+ if(e.takedamage)
+ {
+ if (turret_validate_target(self,e,self.target_validate_flags))
+ {
+ self.enemy = e;
+
+ turret_do_updates(self);
+
+ if (self.turret_firecheckfunc())
+ turret_fire();
+ }
+ }
+
+ e = e.chain;
+ }
+ self.enemy = world;
+ }
+ else if(self.shoot_flags & TFL_SHOOT_CUSTOM)
+ {
+ // This one is doing something.. oddball. assume its handles what needs to be handled.
+
+ // Predict?
+ if(!(self.aim_flags & TFL_AIM_NO))
+ self.tur_aimpos = turret_aim_generic();
+
+ // Turn & pitch?
+ if(!(self.track_flags & TFL_TRACK_NO))
+ turret_track();
+
+ turret_do_updates(self);
+
+ // Fire?
+ if (self.turret_firecheckfunc())
+ turret_fire();
+ }
+ else
+ {
+ // Special case for volly always. if it fired once it must compleate the volly.
+ if(self.shoot_flags & TFL_SHOOT_VOLLYALWAYS)
+ if(self.volly_counter != self.shot_volly)
+ {
+ // Predict or whatnot
+ if(!(self.aim_flags & TFL_AIM_NO))
+ self.tur_aimpos = turret_aim_generic();
+
+ // Turn & pitch
+ if(!(self.track_flags & TFL_TRACK_NO))
+ turret_track();
+
+ turret_do_updates(self);
+
+ // Fire!
+ if (self.turret_firecheckfunc() != 0)
+ turret_fire();
+
+ TUR_ACTION(self.turretid, TR_THINK);
+
+ return;
+ }
+
+ // Check if we have a vailid enemy, and try to find one if we dont.
+
+ // g_turrets_targetscan_maxdelay forces a target re-scan at least this often
+ float do_target_scan = 0;
+ if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time)
+ do_target_scan = 1;
+
+ // Old target (if any) invalid?
+ if(self.target_validate_time < time)
+ if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0)
+ {
+ self.enemy = world;
+ self.target_validate_time = time + 0.5;
+ do_target_scan = 1;
+ }
+
+ // But never more often then g_turrets_targetscan_mindelay!
+ if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time)
+ do_target_scan = 0;
+
+ if(do_target_scan)
+ {
+ self.enemy = turret_select_target();
+ self.target_select_time = time;
+ }
+
+ // No target, just go to idle, do any custom stuff and bail.
+ if (self.enemy == world)
+ {
+ // Turn & pitch
+ if(!(self.track_flags & TFL_TRACK_NO))
+ turret_track();
+
+ TUR_ACTION(self.turretid, TR_THINK);
+
+ // And bail.
+ return;
+ }
+ else
+ self.lip = time + autocvar_g_turrets_aimidle_delay; // Keep track of the last time we had a target.
+
+ // Predict?
+ if(!(self.aim_flags & TFL_AIM_NO))
+ self.tur_aimpos = turret_aim_generic();
+
+ // Turn & pitch?
+ if(!(self.track_flags & TFL_TRACK_NO))
+ turret_track();
+
+ turret_do_updates(self);
+
+ // Fire?
+ if (self.turret_firecheckfunc())
+ turret_fire();
+ }
+
+ TUR_ACTION(self.turretid, TR_THINK);
+}
+
+/*
+ When .used a turret switch team to activator.team.
+ If activator is world, the turret go inactive.
+*/
+void turret_use()
+{
+ dprint("Turret ",self.netname, " used by ", activator.classname, "\n");
+
+ self.team = activator.team;
+
+ if(self.team == 0)
+ self.active = ACTIVE_NOT;
+ else
+ self.active = ACTIVE_ACTIVE;
+
+}
+
+void turret_link()
+{
+ Net_LinkEntity(self, true, 0, turret_send);
+ self.think = turret_think;
+ self.nextthink = time;
+ self.tur_head.effects = EF_NODRAW;
+}
+
+void turrets_manager_think()
+{
+ self.nextthink = time + 1;
+
+ entity e;
+ if (autocvar_g_turrets_reloadcvars == 1)
+ {
+ e = nextent(world);
+ while (e)
+ {
+ if (IS_TURRET(e))
+ {
+ load_unit_settings(e,e.cvar_basename,1);
+ TUR_ACTION(self.turretid, TR_THINK);
+ }
+
+ e = nextent(e);
+ }
+ cvar_set("g_turrets_reloadcvars","0");
+ }
+}
+
+float turret_initialize(float tur_id)
+{
+ if(!autocvar_g_turrets)
+ return false;
+
+ entity e;
+ entity tur = get_turretinfo(tur_id);
+ if(tur.turretid == 0)
+ return false; // invalid turret
+
+ if(!self.tur_head) { TUR_ACTION(tur_id, TR_PRECACHE); } // if tur_head exists, we can assume this turret re-spawned
+
+ e = find(world, classname, "turret_manager");
+ if(!e)
+ {
+ e = spawn();
+ e.classname = "turret_manager";
+ e.think = turrets_manager_think;
+ e.nextthink = time + 2;
+ }
+
+ if(!(self.spawnflags & TSF_SUSPENDED))
+ builtin_droptofloor();
+
+ self.cvar_basename = tur.cvar_basename;
+ load_unit_settings(self, self.cvar_basename, 0);
+
+ if(!self.team || !teamplay) { self.team = MAX_SHOT_DISTANCE; }
+ if(!self.ticrate) { self.ticrate = ((self.turret_flags & TUR_FLAG_SUPPORT) ? 0.2 : 0.1); }
+ if(!self.health) { self.health = 1000; }
+ if(!self.shot_refire) { self.shot_refire = 1; }
+ if(!self.tur_shotorg) { self.tur_shotorg = '50 0 50'; }
+ if(!self.turret_flags) { self.turret_flags = TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER; }
+ if(!self.damage_flags) { self.damage_flags = TFL_DMG_YES | TFL_DMG_RETALIATE | TFL_DMG_AIMSHAKE; }
+ if(!self.aim_flags) { self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; }
+ if(!self.track_type) { self.track_type = TFL_TRACKTYPE_STEPMOTOR; }
+ if(!self.track_flags) { self.track_flags = TFL_TRACK_PITCH | TFL_TRACK_ROTATE; }
+ if(!self.ammo_flags) { self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; }
+ if(!self.target_select_flags) { self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_ANGLELIMITS; }
+ if(!self.firecheck_flags) { self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_LOS
+ | TFL_FIRECHECK_AIMDIST | TFL_FIRECHECK_TEAMCHECK | TFL_FIRECHECK_AMMO_OWN | TFL_FIRECHECK_REFIRE; }
+
+ if(self.track_type != TFL_TRACKTYPE_STEPMOTOR)
+ {
+ // Fluid / Ineria mode. Looks mutch nicer.
+ // Can reduce aim preformance alot, needs a bit diffrent aimspeed
+
+ self.aim_speed = bound(0.1, ((!self.aim_speed) ? 180 : self.aim_speed), 1000);
+
+ if(!self.track_accel_pitch) { self.track_accel_pitch = 0.5; }
+ if(!self.track_accel_rotate) { self.track_accel_rotate = 0.5; }
+ if(!self.track_blendrate) { self.track_blendrate = 0.35; }
+ }
+
+ self.respawntime = max(-1, ((!self.respawntime) ? 60 : self.respawntime));
+ self.shot_refire = bound(0.01, ((!self.shot_refire) ? 1 : self.shot_refire), 9999);
+ self.shot_dmg = max(1, ((!self.shot_dmg) ? self.shot_refire * 50 : self.shot_dmg));
+ self.shot_radius = max(1, ((!self.shot_radius) ? self.shot_dmg * 0.5 : self.shot_radius));
+ self.shot_speed = max(1, ((!self.shot_speed) ? 2500 : self.shot_speed));
+ self.shot_spread = bound(0.0001, ((!self.shot_spread) ? 0.0125 : self.shot_spread), 500);
+ self.shot_force = bound(0.001, ((!self.shot_force) ? self.shot_dmg * 0.5 + self.shot_radius * 0.5 : self.shot_force), 5000);
+ self.shot_volly = bound(1, ((!self.shot_volly) ? 1 : self.shot_volly), floor(self.ammo_max / self.shot_dmg));
+ self.shot_volly_refire = bound(self.shot_refire, ((!self.shot_volly_refire) ? self.shot_refire * self.shot_volly : self.shot_volly_refire), 60);
+ self.target_range = bound(0, ((!self.target_range) ? self.shot_speed * 0.5 : self.target_range), MAX_SHOT_DISTANCE);
+ self.target_range_min = bound(0, ((!self.target_range_min) ? self.shot_radius * 2 : self.target_range_min), MAX_SHOT_DISTANCE);
+ self.target_range_optimal = bound(0, ((!self.target_range_optimal) ? self.target_range * 0.5 : self.target_range_optimal), MAX_SHOT_DISTANCE);
+ self.aim_maxrotate = bound(0, ((!self.aim_maxrotate) ? 90 : self.aim_maxrotate), 360);
+ self.aim_maxpitch = bound(0, ((!self.aim_maxpitch) ? 20 : self.aim_maxpitch), 90);
+ self.aim_speed = bound(0.1, ((!self.aim_speed) ? 36 : self.aim_speed), 1000);
+ self.aim_firetolerance_dist = bound(0.1, ((!self.aim_firetolerance_dist) ? 5 + (self.shot_radius * 2) : self.aim_firetolerance_dist), MAX_SHOT_DISTANCE);
+ self.target_select_rangebias = bound(-10, ((!self.target_select_rangebias) ? 1 : self.target_select_rangebias), 10);
+ self.target_select_samebias = bound(-10, ((!self.target_select_samebias) ? 1 : self.target_select_samebias), 10);
+ self.target_select_anglebias = bound(-10, ((!self.target_select_anglebias) ? 1 : self.target_select_anglebias), 10);
+ self.target_select_missilebias = bound(-10, ((!self.target_select_missilebias) ? 1 : self.target_select_missilebias), 10);
+ self.target_select_playerbias = bound(-10, ((!self.target_select_playerbias) ? 1 : self.target_select_playerbias), 10);
+ self.ammo_max = max(self.shot_dmg, ((!self.ammo_max) ? self.shot_dmg * 10 : self.ammo_max));
+ self.ammo_recharge = max(0, ((!self.ammo_recharge) ? self.shot_dmg * 0.5 : self.ammo_recharge));
+
+ self.turret_flags = TUR_FLAG_ISTURRET | (tur.spawnflags);
+
+ if(self.turret_flags & TUR_FLAG_SPLASH)
+ self.aim_flags |= TFL_AIM_SPLASH;
+
+ if(self.turret_flags & TUR_FLAG_MISSILE)
+ self.target_select_flags |= TFL_TARGETSELECT_MISSILES;
+
+ if(self.turret_flags & TUR_FLAG_PLAYER)
+ self.target_select_flags |= TFL_TARGETSELECT_PLAYERS;
+
+ if(self.spawnflags & TSL_NO_RESPAWN)
+ self.damage_flags |= TFL_DMG_DEATH_NORESPAWN;
+
+ if (self.turret_flags & TUR_FLAG_SUPPORT)
+ self.turret_score_target = turret_targetscore_support;
+ else
+ self.turret_score_target = turret_targetscore_generic;
+
+ ++turret_count;
+
+ setmodel(self, tur.model);
+ setsize(self, tur.mins, tur.maxs);
+
+ self.turretid = tur_id;
+ self.classname = "turret_main";
+ self.active = ACTIVE_ACTIVE;
+ self.effects = EF_NODRAW;
+ self.netname = TUR_NAME(tur_id);
+ self.ticrate = bound(sys_frametime, self.ticrate, 60);
+ self.max_health = self.health;
+ self.target_validate_flags = self.target_select_flags;
+ self.ammo = self.ammo_max;
+ self.ammo_recharge *= self.ticrate;
+ self.solid = SOLID_BBOX;
+ self.takedamage = DAMAGE_AIM;
+ self.movetype = MOVETYPE_NOCLIP;
+ self.view_ofs = '0 0 0';
+ self.turret_firecheckfunc = turret_firecheck;
+ self.event_damage = turret_damage;
+ self.use = turret_use;
+ self.bot_attack = true;
+ self.nextthink = time + 1;
+ self.nextthink += turret_count * sys_frametime;
+
+ self.tur_head = spawn();
+ setmodel(self.tur_head, tur.head_model);
+ setsize(self.tur_head, '0 0 0', '0 0 0');
+ setorigin(self.tur_head, '0 0 0');
+ setattachment(self.tur_head, self, "tag_head");
+
+ self.tur_head.netname = self.tur_head.classname = "turret_head";
+ self.tur_head.team = self.team;
+ self.tur_head.owner = self;
+ self.tur_head.takedamage = DAMAGE_NO;
+ self.tur_head.solid = SOLID_NOT;
+ self.tur_head.movetype = self.movetype;
+
+ if(!self.tur_defend)
+ if(self.target != "")
+ {
+ self.tur_defend = find(world, targetname, self.target);
+ if (self.tur_defend == world)
+ {
+ self.target = "";
+ dprint("Turret has invalid defendpoint!\n");
+ }
+ }
+
+ if (self.tur_defend)
+ self.idle_aim = self.tur_head.angles + angleofs(self.tur_head, self.tur_defend);
+ else
+ self.idle_aim = '0 0 0';
+
+#ifdef TURRET_DEBUG
+ self.tur_debug_start = self.nextthink;
+ while (vlen(self.tur_debug_rvec) < 2)
+ self.tur_debug_rvec = randomvec() * 4;
+
+ self.tur_debug_rvec_x = fabs(self.tur_debug_rvec_x);
+ self.tur_debug_rvec_y = fabs(self.tur_debug_rvec_y);
+ self.tur_debug_rvec_z = fabs(self.tur_debug_rvec_z);
+#endif
+
+ turret_link();
+ turret_respawn();
+ turret_tag_fire_update();
+
+ TUR_ACTION(tur_id, TR_SETUP);
+
+ if(MUTATOR_CALLHOOK(TurretSpawn))
+ return false;
+
+ return true;
+}
+#endif
--- /dev/null
- void walker_rocket_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
+#ifdef REGISTER_TURRET
+REGISTER_TURRET(
+/* TUR_##id */ WALKER,
+/* function */ t_walker,
+/* spawnflags */ TUR_FLAG_PLAYER | TUR_FLAG_MOVE,
+/* mins,maxs */ '-70 -70 0', '70 70 95',
+/* model */ "walker_body.md3",
+/* head_model */ "walker_head_minigun.md3",
+/* netname */ "walker",
+/* fullname */ _("Walker Turret")
+);
+#else
+#ifdef SVQC
+#include "../../effects.qh"
+
+float autocvar_g_turrets_unit_walker_melee_damage;
+float autocvar_g_turrets_unit_walker_melee_force;
+float autocvar_g_turrets_unit_walker_melee_range;
+float autocvar_g_turrets_unit_walker_rocket_damage;
+float autocvar_g_turrets_unit_walker_rocket_radius;
+float autocvar_g_turrets_unit_walker_rocket_force;
+float autocvar_g_turrets_unit_walker_rocket_speed;
+float autocvar_g_turrets_unit_walker_rocket_range;
+float autocvar_g_turrets_unit_walker_rocket_range_min;
+float autocvar_g_turrets_unit_walker_rocket_refire;
+float autocvar_g_turrets_unit_walker_rocket_turnrate;
+float autocvar_g_turrets_unit_walker_speed_stop;
+float autocvar_g_turrets_unit_walker_speed_walk;
+float autocvar_g_turrets_unit_walker_speed_run;
+float autocvar_g_turrets_unit_walker_speed_jump;
+float autocvar_g_turrets_unit_walker_speed_swim;
+float autocvar_g_turrets_unit_walker_speed_roam;
+float autocvar_g_turrets_unit_walker_turn;
+float autocvar_g_turrets_unit_walker_turn_walk;
+float autocvar_g_turrets_unit_walker_turn_strafe;
+float autocvar_g_turrets_unit_walker_turn_swim;
+float autocvar_g_turrets_unit_walker_turn_run;
+
+#define ANIM_NO 0
+#define ANIM_TURN 1
+#define ANIM_WALK 2
+#define ANIM_RUN 3
+#define ANIM_STRAFE_L 4
+#define ANIM_STRAFE_R 5
+#define ANIM_JUMP 6
+#define ANIM_LAND 7
+#define ANIM_PAIN 8
+#define ANIM_MELEE 9
+#define ANIM_SWIM 10
+#define ANIM_ROAM 11
+
+.float animflag;
+.float idletime;
+
+#define WALKER_PATH(s,e) pathlib_astar(s,e)
+
+float walker_firecheck()
+{
+ if (self.animflag == ANIM_MELEE)
+ return 0;
+
+ return turret_firecheck();
+}
+
+void walker_melee_do_dmg()
+{
+ vector where;
+ entity e;
+
+ makevectors(self.angles);
+ where = self.origin + v_forward * 128;
+
+ e = findradius(where,32);
+ while (e)
+ {
+ if (turret_validate_target(self, e, self.target_validate_flags))
+ if (e != self && e.owner != self)
+ Damage(e, self, self, (autocvar_g_turrets_unit_walker_melee_damage), DEATH_TURRET_WALK_MELEE, '0 0 0', v_forward * (autocvar_g_turrets_unit_walker_melee_force));
+
+ e = e.chain;
+ }
+}
+
+void walker_setnoanim()
+{
+ turrets_setframe(ANIM_NO, false);
+ self.animflag = self.frame;
+}
+void walker_rocket_explode()
+{
+ RadiusDamage (self, self.owner, (autocvar_g_turrets_unit_walker_rocket_damage), 0, (autocvar_g_turrets_unit_walker_rocket_radius), self, world, (autocvar_g_turrets_unit_walker_rocket_force), DEATH_TURRET_WALK_ROCKET, world);
+ remove (self);
+}
+
++void walker_rocket_damage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector vforce)
+{
+ self.health = self.health - damage;
+ self.velocity = self.velocity + vforce;
+
+ if (self.health <= 0)
+ W_PrepareExplosionByDamage(self.owner, walker_rocket_explode);
+}
+
+#define WALKER_ROCKET_MOVE movelib_move_simple(newdir, (autocvar_g_turrets_unit_walker_rocket_speed), (autocvar_g_turrets_unit_walker_rocket_turnrate)); UpdateCSQCProjectile(self)
+void walker_rocket_loop();
+void walker_rocket_think()
+{
+ vector newdir;
+ float edist;
+ float itime;
+ float m_speed;
+
+ self.nextthink = time;
+
+ edist = vlen(self.enemy.origin - self.origin);
+
+ // Simulate crude guidance
+ if (self.cnt < time)
+ {
+ if (edist < 1000)
+ self.tur_shotorg = randomvec() * min(edist, 64);
+ else
+ self.tur_shotorg = randomvec() * min(edist, 256);
+
+ self.cnt = time + 0.5;
+ }
+
+ if (edist < 128)
+ self.tur_shotorg = '0 0 0';
+
+ if (self.max_health < time)
+ {
+ self.think = walker_rocket_explode;
+ self.nextthink = time;
+ return;
+ }
+
+ if (self.shot_dmg != 1337 && random() < 0.01)
+ {
+ walker_rocket_loop();
+ return;
+ }
+
+ m_speed = vlen(self.velocity);
+
+ // Enemy dead? just keep on the current heading then.
+ if (self.enemy == world || self.enemy.deadflag != DEAD_NO)
+ self.enemy = world;
+
+ if (self.enemy)
+ {
+ itime = max(edist / m_speed, 1);
+ newdir = steerlib_pull(self.enemy.origin + self.tur_shotorg);
+ }
+ else
+ newdir = normalize(self.velocity);
+
+ WALKER_ROCKET_MOVE;
+}
+
+void walker_rocket_loop3()
+{
+ vector newdir;
+ self.nextthink = time;
+
+ if (self.max_health < time)
+ {
+ self.think = walker_rocket_explode;
+ return;
+ }
+
+ if (vlen(self.origin - self.tur_shotorg) < 100 )
+ {
+ self.think = walker_rocket_think;
+ return;
+ }
+
+ newdir = steerlib_pull(self.tur_shotorg);
+ WALKER_ROCKET_MOVE;
+
+ self.angles = vectoangles(self.velocity);
+}
+
+void walker_rocket_loop2()
+{
+ vector newdir;
+
+ self.nextthink = time;
+
+ if (self.max_health < time)
+ {
+ self.think = walker_rocket_explode;
+ return;
+ }
+
+ if (vlen(self.origin - self.tur_shotorg) < 100 )
+ {
+ self.tur_shotorg = self.origin - '0 0 200';
+ self.think = walker_rocket_loop3;
+ return;
+ }
+
+ newdir = steerlib_pull(self.tur_shotorg);
+ WALKER_ROCKET_MOVE;
+}
+
+void walker_rocket_loop()
+{
+ self.nextthink = time;
+ self.tur_shotorg = self.origin + '0 0 300';
+ self.think = walker_rocket_loop2;
+ self.shot_dmg = 1337;
+}
+
+void walker_fire_rocket(vector org)
+{
+ entity rocket;
+
+ fixedmakevectors(self.angles);
+
+ te_explosion (org);
+
+ rocket = spawn ();
+ setorigin(rocket, org);
+
+ sound (self, CH_WEAPON_A, W_Sound("hagar_fire"), VOL_BASE, ATTEN_NORM);
+ setsize (rocket, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
+
+ rocket.classname = "walker_rocket";
+ rocket.owner = self;
+ rocket.bot_dodge = true;
+ rocket.bot_dodgerating = 50;
+ rocket.takedamage = DAMAGE_YES;
+ rocket.damageforcescale = 2;
+ rocket.health = 25;
+ rocket.tur_shotorg = randomvec() * 512;
+ rocket.cnt = time + 1;
+ rocket.enemy = self.enemy;
+
+ if (random() < 0.01)
+ rocket.think = walker_rocket_loop;
+ else
+ rocket.think = walker_rocket_think;
+
+ rocket.event_damage = walker_rocket_damage;
+
+ rocket.nextthink = time;
+ rocket.movetype = MOVETYPE_FLY;
+ rocket.velocity = normalize((v_forward + v_up * 0.5) + (randomvec() * 0.2)) * (autocvar_g_turrets_unit_walker_rocket_speed);
+ rocket.angles = vectoangles(rocket.velocity);
+ rocket.touch = walker_rocket_explode;
+ rocket.flags = FL_PROJECTILE;
+ rocket.solid = SOLID_BBOX;
+ rocket.max_health = time + 9;
+ rocket.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT;
+
+ CSQCProjectile(rocket, false, PROJECTILE_ROCKET, false); // no culling, has fly sound
+}
+
+.vector enemy_last_loc;
+.float enemy_last_time;
+void walker_move_to(vector _target, float _dist)
+{
+ switch (self.waterlevel)
+ {
+ case WATERLEVEL_NONE:
+ if (_dist > 500)
+ self.animflag = ANIM_RUN;
+ else
+ self.animflag = ANIM_WALK;
+ case WATERLEVEL_WETFEET:
+ case WATERLEVEL_SWIMMING:
+ if (self.animflag != ANIM_SWIM)
+ self.animflag = ANIM_WALK;
+ else
+ self.animflag = ANIM_SWIM;
+ break;
+ case WATERLEVEL_SUBMERGED:
+ self.animflag = ANIM_SWIM;
+ }
+
+ self.moveto = _target;
+ self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
+
+ if(self.enemy)
+ {
+ self.enemy_last_loc = _target;
+ self.enemy_last_time = time;
+ }
+}
+
+//#define WALKER_FANCYPATHING
+
+void walker_move_path()
+{
+#ifdef WALKER_FANCYPATHING
+ // Are we close enougth to a path node to switch to the next?
+ if (vlen(self.origin - self.pathcurrent.origin) < 64)
+ if (self.pathcurrent.path_next == world)
+ {
+ // Path endpoint reached
+ pathlib_deletepath(self.pathcurrent.owner);
+ self.pathcurrent = world;
+
+ if (self.pathgoal)
+ {
+ if (self.pathgoal.use)
+ self.pathgoal.use();
+
+ if (self.pathgoal.enemy)
+ {
+ self.pathcurrent = WALKER_PATH(self.pathgoal.origin,self.pathgoal.enemy.origin);
+ self.pathgoal = self.pathgoal.enemy;
+ }
+ }
+ else
+ self.pathgoal = world;
+ }
+ else
+ self.pathcurrent = self.pathcurrent.path_next;
+
+ self.moveto = self.pathcurrent.origin;
+ self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95);
+ walker_move_to(self.moveto, 0);
+
+#else
+ if (vlen(self.origin - self.pathcurrent.origin) < 64)
+ self.pathcurrent = self.pathcurrent.enemy;
+
+ if(!self.pathcurrent)
+ return;
+
+ self.moveto = self.pathcurrent.origin;
+ self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
+ walker_move_to(self.moveto, 0);
+#endif
+}
+
+void spawnfunc_turret_walker() { if(!turret_initialize(TUR_WALKER)) remove(self); }
+
+float t_walker(float req)
+{
+ switch(req)
+ {
+ case TR_ATTACK:
+ {
+ sound (self, CH_WEAPON_A, W_Sound("uzi_fire"), VOL_BASE, ATTEN_NORM);
+ fireBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, 0, self.shot_dmg, self.shot_force, DEATH_TURRET_WALK_GUN, 0);
+ Send_Effect(EFFECT_LASER_MUZZLEFLASH, self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
+
+ return true;
+ }
+ case TR_THINK:
+ {
+ fixedmakevectors(self.angles);
+
+ if (self.spawnflags & TSF_NO_PATHBREAK && self.pathcurrent)
+ walker_move_path();
+ else if (self.enemy == world)
+ {
+ if(self.pathcurrent)
+ walker_move_path();
+ else
+ {
+ if(self.enemy_last_time != 0)
+ {
+ if(vlen(self.origin - self.enemy_last_loc) < 128 || time - self.enemy_last_time > 10)
+ self.enemy_last_time = 0;
+ else
+ walker_move_to(self.enemy_last_loc, 0);
+ }
+ else
+ {
+ if(self.animflag != ANIM_NO)
+ {
+ traceline(self.origin + '0 0 64', self.origin + '0 0 64' + v_forward * 128, MOVE_NORMAL, self);
+
+ if(trace_fraction != 1.0)
+ self.tur_head.idletime = -1337;
+ else
+ {
+ traceline(trace_endpos, trace_endpos - '0 0 256', MOVE_NORMAL, self);
+ if(trace_fraction == 1.0)
+ self.tur_head.idletime = -1337;
+ }
+
+ if(self.tur_head.idletime == -1337)
+ {
+ self.moveto = self.origin + randomvec() * 256;
+ self.tur_head.idletime = 0;
+ }
+
+ self.moveto = self.moveto * 0.9 + ((self.origin + v_forward * 500) + randomvec() * 400) * 0.1;
+ self.moveto_z = self.origin_z + 64;
+ walker_move_to(self.moveto, 0);
+ }
+
+ if(self.idletime < time)
+ {
+ if(random() < 0.5 || !(self.spawnflags & TSL_ROAM))
+ {
+ self.idletime = time + 1 + random() * 5;
+ self.moveto = self.origin;
+ self.animflag = ANIM_NO;
+ }
+ else
+ {
+ self.animflag = ANIM_WALK;
+ self.idletime = time + 4 + random() * 2;
+ self.moveto = self.origin + randomvec() * 256;
+ self.tur_head.moveto = self.moveto;
+ self.tur_head.idletime = 0;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (self.tur_dist_enemy < (autocvar_g_turrets_unit_walker_melee_range) && self.animflag != ANIM_MELEE)
+ {
+ vector wish_angle;
+
+ wish_angle = angleofs(self, self.enemy);
+ if (self.animflag != ANIM_SWIM)
+ if (fabs(wish_angle_y) < 15)
+ {
+ self.moveto = self.enemy.origin;
+ self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
+ self.animflag = ANIM_MELEE;
+ }
+ }
+ else if (self.tur_head.attack_finished_single < time)
+ {
+ if(self.tur_head.shot_volly)
+ {
+ self.animflag = ANIM_NO;
+
+ self.tur_head.shot_volly = self.tur_head.shot_volly -1;
+ if(self.tur_head.shot_volly == 0)
+ self.tur_head.attack_finished_single = time + (autocvar_g_turrets_unit_walker_rocket_refire);
+ else
+ self.tur_head.attack_finished_single = time + 0.2;
+
+ if(self.tur_head.shot_volly > 1)
+ walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket01")));
+ else
+ walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket02")));
+ }
+ else
+ {
+ if (self.tur_dist_enemy > (autocvar_g_turrets_unit_walker_rocket_range_min))
+ if (self.tur_dist_enemy < (autocvar_g_turrets_unit_walker_rocket_range))
+ self.tur_head.shot_volly = 4;
+ }
+ }
+ else
+ {
+ if (self.animflag != ANIM_MELEE)
+ walker_move_to(self.enemy.origin, self.tur_dist_enemy);
+ }
+ }
+
+ {
+ vector real_angle;
+ float turny = 0, turnx = 0;
+ float vz;
+
+ real_angle = vectoangles(self.steerto) - self.angles;
+ vz = self.velocity_z;
+
+ switch (self.animflag)
+ {
+ case ANIM_NO:
+ movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
+ break;
+
+ case ANIM_TURN:
+ turny = (autocvar_g_turrets_unit_walker_turn);
+ movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
+ break;
+
+ case ANIM_WALK:
+ turny = (autocvar_g_turrets_unit_walker_turn_walk);
+ movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_walk), 0.6);
+ break;
+
+ case ANIM_RUN:
+ turny = (autocvar_g_turrets_unit_walker_turn_run);
+ movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_run), 0.6);
+ break;
+
+ case ANIM_STRAFE_L:
+ turny = (autocvar_g_turrets_unit_walker_turn_strafe);
+ movelib_move_simple(v_right * -1, (autocvar_g_turrets_unit_walker_speed_walk), 0.8);
+ break;
+
+ case ANIM_STRAFE_R:
+ turny = (autocvar_g_turrets_unit_walker_turn_strafe);
+ movelib_move_simple(v_right, (autocvar_g_turrets_unit_walker_speed_walk), 0.8);
+ break;
+
+ case ANIM_JUMP:
+ self.velocity += '0 0 1' * (autocvar_g_turrets_unit_walker_speed_jump);
+ break;
+
+ case ANIM_LAND:
+ break;
+
+ case ANIM_PAIN:
+ if(self.frame != ANIM_PAIN)
+ defer(0.25, walker_setnoanim);
+
+ break;
+
+ case ANIM_MELEE:
+ if(self.frame != ANIM_MELEE)
+ {
+ defer(0.41, walker_setnoanim);
+ defer(0.21, walker_melee_do_dmg);
+ }
+
+ movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop));
+ break;
+
+ case ANIM_SWIM:
+ turny = (autocvar_g_turrets_unit_walker_turn_swim);
+ turnx = (autocvar_g_turrets_unit_walker_turn_swim);
+
+ self.angles_x += bound(-10, shortangle_f(real_angle_x, self.angles_x), 10);
+ movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_swim), 0.3);
+ vz = self.velocity_z + sin(time * 4) * 8;
+ break;
+
+ case ANIM_ROAM:
+ turny = (autocvar_g_turrets_unit_walker_turn_walk);
+ movelib_move_simple(v_forward ,(autocvar_g_turrets_unit_walker_speed_roam), 0.5);
+ break;
+ }
+
+ if(turny)
+ {
+ turny = bound( turny * -1, shortangle_f(real_angle_y, self.angles_y), turny );
+ self.angles_y += turny;
+ }
+
+ if(turnx)
+ {
+ turnx = bound( turnx * -1, shortangle_f(real_angle_x, self.angles_x), turnx );
+ self.angles_x += turnx;
+ }
+
+ self.velocity_z = vz;
+ }
+
+
+ if(self.origin != self.oldorigin)
+ self.SendFlags |= TNSF_MOVE;
+
+ self.oldorigin = self.origin;
+ turrets_setframe(self.animflag, false);
+
+ return true;
+ }
+ case TR_DEATH:
+ {
+#ifdef WALKER_FANCYPATHING
+ if (self.pathcurrent)
+ pathlib_deletepath(self.pathcurrent.owner);
+#endif
+ self.pathcurrent = world;
+
+ return true;
+ }
+ case TR_SETUP:
+ {
+ self.ticrate = 0.05;
+
+ entity e;
+
+ // Respawn is called & first spawn to, to set team. need to make sure we do not move the initial spawn.
+ if(self.movetype == MOVETYPE_WALK)
+ {
+ if(self.pos1)
+ setorigin(self, self.pos1);
+ if(self.pos2)
+ self.angles = self.pos2;
+ }
+
+ self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE;
+ self.aim_flags = TFL_AIM_LEAD;
+ self.turret_flags |= TUR_FLAG_HITSCAN;
+
+ self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
+ self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
+ self.iscreature = true;
+ self.teleportable = TELEPORT_NORMAL;
+ self.damagedbycontents = true;
+ self.solid = SOLID_SLIDEBOX;
+ self.takedamage = DAMAGE_AIM;
+ if(self.movetype != MOVETYPE_WALK)
+ {
+ setorigin(self, self.origin);
+ tracebox(self.origin + '0 0 128', self.mins, self.maxs, self.origin - '0 0 10000', MOVE_NORMAL, self);
+ setorigin(self, trace_endpos + '0 0 4');
+ self.pos1 = self.origin;
+ self.pos2 = self.angles;
+ }
+ self.movetype = MOVETYPE_WALK;
+ self.idle_aim = '0 0 0';
+ self.turret_firecheckfunc = walker_firecheck;
+
+ if (self.target != "")
+ {
+ e = find(world, targetname, self.target);
+ if (!e)
+ {
+ dprint("Initital waypoint for walker does NOT exsist, fix your map!\n");
+ self.target = "";
+ }
+
+ if (e.classname != "turret_checkpoint")
+ dprint("Warning: not a turrret path\n");
+ else
+ {
+#ifdef WALKER_FANCYPATHING
+ self.pathcurrent = WALKER_PATH(self.origin, e.origin);
+ self.pathgoal = e;
+#else
+ self.pathcurrent = e;
+#endif
+ }
+ }
+
+ return true;
+ }
+ case TR_PRECACHE:
+ {
+ precache_model ("models/turrets/walker_body.md3");
+ precache_model ("models/turrets/walker_head_minigun.md3");
+ precache_model ("models/turrets/rocket.md3");
+ precache_sound (W_Sound("rocket_impact"));
+ return true;
+ }
+ }
+
+ return true;
+}
+
+#endif // SVQC
+#ifdef CSQC
+
+void walker_draw()
+{
+ float dt;
+
+ dt = time - self.move_time;
+ self.move_time = time;
+ if(dt <= 0)
+ return;
+
+ fixedmakevectors(self.angles);
+ movelib_groundalign4point(300, 100, 0.25, 45);
+ setorigin(self, self.origin + self.velocity * dt);
+ self.tur_head.angles += dt * self.tur_head.move_avelocity;
+ self.angles_y = self.move_angles_y;
+
+ if (self.health < 127)
+ if(random() < 0.15)
+ te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16);
+}
+
+float t_walker(float req)
+{
+ switch(req)
+ {
+ case TR_SETUP:
+ {
+ self.gravity = 1;
+ self.movetype = MOVETYPE_BOUNCE;
+ self.move_movetype = MOVETYPE_BOUNCE;
+ self.move_origin = self.origin;
+ self.move_time = time;
+ self.draw = walker_draw;
+
+ return true;
+ }
+ case TR_PRECACHE:
+ {
+ return true;
+ }
+ }
+
+ return true;
+}
+
+#endif // CSQC
+#endif // REGISTER_TURRET
--- /dev/null
- float bforce, float f_dmg, float f_velfactor, float deathtype)
+/*
+* Return a angle within +/- 360.
+*/
+float anglemods(float v)
+{
+ v = v - 360 * floor(v / 360);
+
+ if(v >= 180)
+ return v - 360;
+ else if(v <= -180)
+ return v + 360;
+ else
+ return v;
+}
+
+/*
+* Return the short angle
+*/
+float shortangle_f(float ang1, float ang2)
+{
+ if(ang1 > ang2)
+ {
+ if(ang1 > 180)
+ return ang1 - 360;
+ }
+ else
+ {
+ if(ang1 < -180)
+ return ang1 + 360;
+ }
+
+ return ang1;
+}
+
+vector shortangle_v(vector ang1, vector ang2)
+{
+ vector vtmp;
+
+ vtmp_x = shortangle_f(ang1_x,ang2_x);
+ vtmp_y = shortangle_f(ang1_y,ang2_y);
+ vtmp_z = shortangle_f(ang1_z,ang2_z);
+
+ return vtmp;
+}
+
+vector shortangle_vxy(vector ang1, vector ang2)
+{
+ vector vtmp = '0 0 0';
+
+ vtmp_x = shortangle_f(ang1_x,ang2_x);
+ vtmp_y = shortangle_f(ang1_y,ang2_y);
+
+ return vtmp;
+}
+
+
+/*
+* Get "real" origin, in worldspace, even if ent is attached to something else.
+*/
+vector real_origin(entity ent)
+{
+ entity e;
+ vector v = ((ent.absmin + ent.absmax) * 0.5);
+
+ e = ent.tag_entity;
+ while(e)
+ {
+ v = v + ((e.absmin + e.absmax) * 0.5);
+ e = e.tag_entity;
+ }
+
+ return v;
+}
+
+/*
+* Return the angle between two enteties
+*/
+vector angleofs(entity from, entity to)
+{
+ vector v_res;
+
+ v_res = normalize(to.origin - from.origin);
+ v_res = vectoangles(v_res);
+ v_res = v_res - from.angles;
+
+ if (v_res_x < 0) v_res_x += 360;
+ if (v_res_x > 180) v_res_x -= 360;
+
+ if (v_res_y < 0) v_res_y += 360;
+ if (v_res_y > 180) v_res_y -= 360;
+
+ return v_res;
+}
+
+vector angleofs3(vector from, vector from_a, entity to)
+{
+ vector v_res;
+
+ v_res = normalize(to.origin - from);
+ v_res = vectoangles(v_res);
+ v_res = v_res - from_a;
+
+ if (v_res_x < 0) v_res_x += 360;
+ if (v_res_x > 180) v_res_x -= 360;
+
+ if (v_res_y < 0) v_res_y += 360;
+ if (v_res_y > 180) v_res_y -= 360;
+
+ return v_res;
+}
+
+/*
+* Update self.tur_shotorg by getting up2date bone info
+* NOTICE this func overwrites the global v_forward, v_right and v_up vectors.
+*/
+float turret_tag_fire_update_s()
+{
+ if(!self.tur_head)
+ {
+ error("Call to turret_tag_fire_update with self.tur_head missing!\n");
+ self.tur_shotorg = '0 0 0';
+ return false;
+ }
+
+ self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire"));
+ v_forward = normalize(v_forward);
+
+ return true;
+}
+
+/*
+* Railgun-like beam, but has thickness and suppots slowing of target
+*/
+void FireImoBeam (vector start, vector end, vector smin, vector smax,
++ float bforce, float f_dmg, float f_velfactor, int deathtype)
+
+{
+ vector hitloc, force, endpoint, dir;
+ entity ent;
+
+ dir = normalize(end - start);
+ force = dir * bforce;
+
+ // go a little bit into the wall because we need to hit this wall later
+ end = end + dir;
+
+ // trace multiple times until we hit a wall, each obstacle will be made unsolid.
+ // note down which entities were hit so we can damage them later
+ while (1)
+ {
+ tracebox(start, smin, smax, end, false, self);
+
+ // if it is world we can't hurt it so stop now
+ if (trace_ent == world || trace_fraction == 1)
+ break;
+
+ if (trace_ent.solid == SOLID_BSP)
+ break;
+
+ // make the entity non-solid so we can hit the next one
+ trace_ent.railgunhit = true;
+ trace_ent.railgunhitloc = end;
+ trace_ent.railgunhitsolidbackup = trace_ent.solid;
+
+ // stop if this is a wall
+
+ // make the entity non-solid
+ trace_ent.solid = SOLID_NOT;
+ }
+
+ endpoint = trace_endpos;
+
+ // find all the entities the railgun hit and restore their solid state
+ ent = findfloat(world, railgunhit, true);
+ while (ent)
+ {
+ // restore their solid type
+ ent.solid = ent.railgunhitsolidbackup;
+ ent = findfloat(ent, railgunhit, true);
+ }
+
+ // find all the entities the railgun hit and hurt them
+ ent = findfloat(world, railgunhit, true);
+ while (ent)
+ {
+ // get the details we need to call the damage function
+ hitloc = ent.railgunhitloc;
+ ent.railgunhitloc = '0 0 0';
+ ent.railgunhitsolidbackup = SOLID_NOT;
+ ent.railgunhit = false;
+
+ // apply the damage
+ if (ent.takedamage)
+ {
+ Damage (ent, self, self, f_dmg, deathtype, hitloc, force);
+ ent.velocity = ent.velocity * f_velfactor;
+ //ent.alpha = 0.25 + random() * 0.75;
+ }
+
+ // advance to the next entity
+ ent = findfloat(ent, railgunhit, true);
+ }
+ trace_endpos = endpoint;
+}
+
+#ifdef TURRET_DEBUG
+void SUB_Remove();
+void marker_think()
+{
+ if(self.cnt)
+ if(self.cnt < time)
+ {
+ self.think = SUB_Remove;
+ self.nextthink = time;
+ return;
+ }
+
+ self.frame += 1;
+ if(self.frame > 29)
+ self.frame = 0;
+
+ self.nextthink = time;
+}
+
+void mark_error(vector where,float lifetime)
+{
+ entity err;
+
+ err = spawn();
+ err.classname = "error_marker";
+ setmodel(err,"models/marker.md3");
+ setorigin(err,where);
+ err.movetype = MOVETYPE_NONE;
+ err.think = marker_think;
+ err.nextthink = time;
+ err.skin = 0;
+ if(lifetime)
+ err.cnt = lifetime + time;
+}
+
+void mark_info(vector where,float lifetime)
+{
+ entity err;
+
+ err = spawn();
+ err.classname = "info_marker";
+ setmodel(err,"models/marker.md3");
+ setorigin(err,where);
+ err.movetype = MOVETYPE_NONE;
+ err.think = marker_think;
+ err.nextthink = time;
+ err.skin = 1;
+ if(lifetime)
+ err.cnt = lifetime + time;
+}
+
+entity mark_misc(vector where,float lifetime)
+{
+ entity err;
+
+ err = spawn();
+ err.classname = "mark_misc";
+ setmodel(err,"models/marker.md3");
+ setorigin(err,where);
+ err.movetype = MOVETYPE_NONE;
+ err.think = marker_think;
+ err.nextthink = time;
+ err.skin = 3;
+ if(lifetime)
+ err.cnt = lifetime + time;
+ return err;
+}
+
+/*
+* Paint a v_color colord circle on target onwho
+* that fades away over f_time
+*/
+void paint_target(entity onwho, float f_size, vector v_color, float f_time)
+{
+ entity e;
+
+ e = spawn();
+ setmodel(e, "models/turrets/c512.md3"); // precision set above
+ e.scale = (f_size/512);
+ //setsize(e, '0 0 0', '0 0 0');
+ //setattachment(e,onwho,"");
+ setorigin(e,onwho.origin + '0 0 1');
+ e.alpha = 0.15;
+ e.movetype = MOVETYPE_FLY;
+
+ e.velocity = (v_color * 32); // + '0 0 1' * 64;
+
+ e.colormod = v_color;
+ SUB_SetFade(e,time,f_time);
+}
+
+void paint_target2(entity onwho, float f_size, vector v_color, float f_time)
+{
+ entity e;
+
+ e = spawn();
+ setmodel(e, "models/turrets/c512.md3"); // precision set above
+ e.scale = (f_size/512);
+ setsize(e, '0 0 0', '0 0 0');
+
+ setorigin(e,onwho.origin + '0 0 1');
+ e.alpha = 0.15;
+ e.movetype = MOVETYPE_FLY;
+
+ e.velocity = (v_color * 32); // + '0 0 1' * 64;
+ e.avelocity_x = -128;
+
+ e.colormod = v_color;
+ SUB_SetFade(e,time,f_time);
+}
+
+void paint_target3(vector where, float f_size, vector v_color, float f_time)
+{
+ entity e;
+ e = spawn();
+ setmodel(e, "models/turrets/c512.md3"); // precision set above
+ e.scale = (f_size/512);
+ setsize(e, '0 0 0', '0 0 0');
+ setorigin(e,where+ '0 0 1');
+ e.movetype = MOVETYPE_NONE;
+ e.velocity = '0 0 0';
+ e.colormod = v_color;
+ SUB_SetFade(e,time,f_time);
+}
+#endif
--- /dev/null
- void FireImoBeam (vector start, vector end, vector smin, vector smax, float bforce, float f_dmg, float f_velfactor, float deathtype);
+#ifndef TURRETS_UTIL_H
+#define TURRETS_UTIL_H
+
+#define turret_tag_fire_update() self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire"));v_forward = normalize(v_forward)
+
+float shortangle_f(float ang1, float ang2);
+
+float anglemods(float v);
+
+vector shortangle_v(vector ang1, vector ang2);
+
+vector shortangle_vxy(vector ang1, vector ang2);
+
+vector real_origin(entity ent);
+
+vector angleofs(entity from, entity to);
+
+vector angleofs3(vector from, vector from_a, entity to);
+
++void FireImoBeam (vector start, vector end, vector smin, vector smax, float bforce, float f_dmg, float f_velfactor, int deathtype);
+
+#endif
void RandomSelection_Add(entity e, float f, string s, float weight, float priority);
#ifndef MENUQC
- vector healtharmor_maxdamage(float h, float a, float armorblock, float deathtype); // returns vector: maxdamage, armorideal, 1 if fully armored
- vector healtharmor_applydamage(float a, float armorblock, float deathtype, float damage, float bycount); // returns vector: take, save, 0
+ vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype); // returns vector: maxdamage, armorideal, 1 if fully armored
-vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage); // returns vector: take, save, 0
++vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage, bool bycount); // returns vector: take, save, 0
#endif
string getcurrentmod();
--- /dev/null
- void raptor_flare_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+#ifdef REGISTER_VEHICLE
+REGISTER_VEHICLE(
+/* VEH_##id */ RAPTOR,
+/* function */ v_raptor,
+/* spawnflags */ VHF_DMGSHAKE | VHF_DMGROLL,
+/* mins,maxs */ '-80 -80 0', '80 80 70',
+/* model */ "models/vehicles/raptor.dpm",
+/* head_model */ "",
+/* hud_model */ "models/vehicles/raptor_cockpit.dpm",
+/* tags */ "", "tag_hud", "tag_camera",
+/* netname */ "raptor",
+/* fullname */ _("Raptor")
+);
+#else
+
+const int RSM_FIRST = 1;
+const int RSM_BOMB = 1;
+const int RSM_FLARE = 2;
+const int RSM_LAST = 2;
+
+#ifdef SVQC
+#include "../../effects.qh"
+
+bool autocvar_g_vehicle_raptor;
+
+float autocvar_g_vehicle_raptor_respawntime;
+float autocvar_g_vehicle_raptor_takeofftime;
+
+float autocvar_g_vehicle_raptor_movestyle;
+float autocvar_g_vehicle_raptor_turnspeed;
+float autocvar_g_vehicle_raptor_pitchspeed;
+float autocvar_g_vehicle_raptor_pitchlimit;
+
+float autocvar_g_vehicle_raptor_speed_forward;
+float autocvar_g_vehicle_raptor_speed_strafe;
+float autocvar_g_vehicle_raptor_speed_up;
+float autocvar_g_vehicle_raptor_speed_down;
+float autocvar_g_vehicle_raptor_friction;
+
+float autocvar_g_vehicle_raptor_bomblets;
+float autocvar_g_vehicle_raptor_bomblet_alt;
+float autocvar_g_vehicle_raptor_bomblet_time;
+float autocvar_g_vehicle_raptor_bomblet_damage;
+float autocvar_g_vehicle_raptor_bomblet_spread;
+float autocvar_g_vehicle_raptor_bomblet_edgedamage;
+float autocvar_g_vehicle_raptor_bomblet_radius;
+float autocvar_g_vehicle_raptor_bomblet_force;
+float autocvar_g_vehicle_raptor_bomblet_explode_delay;
+float autocvar_g_vehicle_raptor_bombs_refire;
+
+float autocvar_g_vehicle_raptor_flare_refire;
+float autocvar_g_vehicle_raptor_flare_lifetime;
+float autocvar_g_vehicle_raptor_flare_chase;
+float autocvar_g_vehicle_raptor_flare_range;
+
+float autocvar_g_vehicle_raptor_cannon_turnspeed;
+float autocvar_g_vehicle_raptor_cannon_turnlimit;
+float autocvar_g_vehicle_raptor_cannon_pitchlimit_up;
+float autocvar_g_vehicle_raptor_cannon_pitchlimit_down;
+
+float autocvar_g_vehicle_raptor_cannon_locktarget;
+float autocvar_g_vehicle_raptor_cannon_locking_time;
+float autocvar_g_vehicle_raptor_cannon_locking_releasetime;
+float autocvar_g_vehicle_raptor_cannon_locked_time;
+float autocvar_g_vehicle_raptor_cannon_predicttarget;
+
+float autocvar_g_vehicle_raptor_cannon_cost;
+float autocvar_g_vehicle_raptor_cannon_damage;
+float autocvar_g_vehicle_raptor_cannon_radius;
+float autocvar_g_vehicle_raptor_cannon_refire;
+float autocvar_g_vehicle_raptor_cannon_speed;
+float autocvar_g_vehicle_raptor_cannon_spread;
+float autocvar_g_vehicle_raptor_cannon_force;
+
+float autocvar_g_vehicle_raptor_energy;
+float autocvar_g_vehicle_raptor_energy_regen;
+float autocvar_g_vehicle_raptor_energy_regen_pause;
+
+float autocvar_g_vehicle_raptor_health;
+float autocvar_g_vehicle_raptor_health_regen;
+float autocvar_g_vehicle_raptor_health_regen_pause;
+
+float autocvar_g_vehicle_raptor_shield;
+float autocvar_g_vehicle_raptor_shield_regen;
+float autocvar_g_vehicle_raptor_shield_regen_pause;
+
+float autocvar_g_vehicle_raptor_bouncefactor;
+float autocvar_g_vehicle_raptor_bouncestop;
+vector autocvar_g_vehicle_raptor_bouncepain;
+
+.entity bomb1;
+.entity bomb2;
+
+float raptor_altitude(float amax)
+{
+ tracebox(self.origin, self.mins, self.maxs, self.origin - ('0 0 1' * amax), MOVE_WORLDONLY, self);
+ return vlen(self.origin - trace_endpos);
+}
+
+void raptor_bomblet_boom()
+{
+ RadiusDamage (self, self.realowner, autocvar_g_vehicle_raptor_bomblet_damage,
+ autocvar_g_vehicle_raptor_bomblet_edgedamage,
+ autocvar_g_vehicle_raptor_bomblet_radius, world, world,
+ autocvar_g_vehicle_raptor_bomblet_force, DEATH_VH_RAPT_BOMB, world);
+ remove(self);
+}
+
+void raptor_bomblet_touch()
+{
+ if(other == self.owner)
+ return;
+
+ PROJECTILE_TOUCH;
+ self.think = raptor_bomblet_boom;
+ self.nextthink = time + random() * autocvar_g_vehicle_raptor_bomblet_explode_delay;
+}
+
+void raptor_bomb_burst()
+{
+ if(self.cnt > time)
+ if(autocvar_g_vehicle_raptor_bomblet_alt)
+ {
+ self.nextthink = time;
+ traceline(self.origin, self.origin + (normalize(self.velocity) * autocvar_g_vehicle_raptor_bomblet_alt), MOVE_NORMAL, self);
+ if((trace_fraction == 1.0) || (vlen(self.origin - self.owner.origin) < autocvar_g_vehicle_raptor_bomblet_radius))
+ {
+ UpdateCSQCProjectile(self);
+ return;
+ }
+ }
+
+ entity bomblet;
+ float i;
+
+ Damage_DamageInfo(self.origin, 0, 0, 0, '0 0 0', DEATH_VH_RAPT_FRAGMENT, 0, self);
+
+ for(i = 0; i < autocvar_g_vehicle_raptor_bomblets; ++i)
+ {
+ bomblet = spawn();
+ setorigin(bomblet, self.origin);
+
+ bomblet.movetype = MOVETYPE_TOSS;
+ bomblet.touch = raptor_bomblet_touch;
+ bomblet.think = raptor_bomblet_boom;
+ bomblet.nextthink = time + 5;
+ bomblet.owner = self.owner;
+ bomblet.realowner = self.realowner;
+ bomblet.velocity = normalize(normalize(self.velocity) + (randomvec() * autocvar_g_vehicle_raptor_bomblet_spread)) * vlen(self.velocity);
+
+ PROJECTILE_MAKETRIGGER(bomblet);
+ CSQCProjectile(bomblet, true, PROJECTILE_RAPTORBOMBLET, true);
+ }
+
+ remove(self);
+}
+
+void raptor_bombdrop()
+{
+ entity bomb_1, bomb_2;
+
+ bomb_1 = spawn();
+ bomb_2 = spawn();
+
+ setorigin(bomb_1, gettaginfo(self, gettagindex(self, "bombmount_left")));
+ setorigin(bomb_2, gettaginfo(self, gettagindex(self, "bombmount_right")));
+
+ bomb_1.movetype = bomb_2.movetype = MOVETYPE_BOUNCE;
+ bomb_1.velocity = bomb_2.velocity = self.velocity;
+ bomb_1.touch = bomb_2.touch = raptor_bomb_burst;
+ bomb_1.think = bomb_2.think = raptor_bomb_burst;
+ bomb_1.cnt = bomb_2.cnt = time + 10;
+
+ if(autocvar_g_vehicle_raptor_bomblet_alt)
+ bomb_1.nextthink = bomb_2.nextthink = time;
+ else
+ bomb_1.nextthink = bomb_2.nextthink = time + autocvar_g_vehicle_raptor_bomblet_time;
+
+ bomb_1.owner = bomb_2.owner = self;
+ bomb_1.realowner = bomb_2.realowner = self.owner;
+ bomb_1.solid = bomb_2.solid = SOLID_BBOX;
+ bomb_1.gravity = bomb_2.gravity = 1;
+
+ PROJECTILE_MAKETRIGGER(bomb_1);
+ PROJECTILE_MAKETRIGGER(bomb_2);
+
+ CSQCProjectile(bomb_1, true, PROJECTILE_RAPTORBOMB, true);
+ CSQCProjectile(bomb_2, true, PROJECTILE_RAPTORBOMB, true);
+}
+
+
+void raptor_fire_cannon(entity gun, string tagname)
+{
+ vehicles_projectile(EFFECT_RAPTOR_MUZZLEFLASH, W_Sound("lasergun_fire"),
+ gettaginfo(gun, gettagindex(gun, tagname)), normalize(v_forward + randomvec() * autocvar_g_vehicle_raptor_cannon_spread) * autocvar_g_vehicle_raptor_cannon_speed,
+ autocvar_g_vehicle_raptor_cannon_damage, autocvar_g_vehicle_raptor_cannon_radius, autocvar_g_vehicle_raptor_cannon_force, 0,
+ DEATH_VH_RAPT_CANNON, PROJECTILE_RAPTORCANNON, 0, true, true, self.owner);
+}
+
+void raptor_land()
+{
+ float hgt;
+
+ hgt = raptor_altitude(512);
+ self.velocity = (self.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * sys_frametime);
+ self.angles_x *= 0.95;
+ self.angles_z *= 0.95;
+
+ if(hgt < 128)
+ if(hgt > 0)
+ self.frame = (hgt / 128) * 25;
+
+ self.bomb1.gun1.avelocity_y = 90 + ((self.frame / 25) * 2000);
+ self.bomb1.gun2.avelocity_y = -self.bomb1.gun1.avelocity_y;
+
+ if(hgt < 16)
+ {
+ self.movetype = MOVETYPE_TOSS;
+ self.think = vehicles_think;
+ self.frame = 0;
+ }
+
+ self.nextthink = time;
+
+ CSQCMODEL_AUTOUPDATE();
+}
+
+void raptor_exit(float eject)
+{
+ vector spot;
+ self.tur_head.exteriormodeltoclient = world;
+
+ if(self.deadflag == DEAD_NO)
+ {
+ self.think = raptor_land;
+ self.nextthink = time;
+ }
+
+ if(!self.owner)
+ return;
+
+ makevectors(self.angles);
+ if(eject)
+ {
+ spot = self.origin + v_forward * 100 + '0 0 64';
+ spot = vehicles_findgoodexit(spot);
+ setorigin(self.owner , spot);
+ self.owner.velocity = (v_up + v_forward * 0.25) * 750;
+ self.owner.oldvelocity = self.owner.velocity;
+ }
+ else
+ {
+ if(vlen(self.velocity) > 2 * autocvar_sv_maxairspeed)
+ {
+ self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed * 2;
+ self.owner.velocity_z += 200;
+ spot = self.origin + v_forward * 32 + '0 0 64';
+ spot = vehicles_findgoodexit(spot);
+ }
+ else
+ {
+ self.owner.velocity = self.velocity * 0.5;
+ self.owner.velocity_z += 10;
+ spot = self.origin - v_forward * 200 + '0 0 64';
+ spot = vehicles_findgoodexit(spot);
+ }
+ self.owner.oldvelocity = self.owner.velocity;
+ setorigin(self.owner , spot);
+ }
+
+ antilag_clear(self.owner);
+ self.owner = world;
+}
+
+void raptor_flare_touch()
+{
+ remove(self);
+}
+
++void raptor_flare_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+ self.health -= damage;
+ if(self.health <= 0)
+ remove(self);
+}
+
+void raptor_flare_think()
+{
+ self.nextthink = time + 0.1;
+ entity _missile = findchainentity(enemy, self.owner);
+ while(_missile)
+ {
+ if(_missile.flags & FL_PROJECTILE)
+ if(vlen(self.origin - _missile.origin) < autocvar_g_vehicle_raptor_flare_range)
+ if(random() > autocvar_g_vehicle_raptor_flare_chase)
+ _missile.enemy = self;
+ _missile = _missile.chain;
+ }
+
+ if(self.tur_impacttime < time)
+ remove(self);
+}
+
+float raptor_frame()
+{
+ entity player, raptor;
+ float ftmp = 0;
+ vector df;
+
+ if(intermission_running)
+ {
+ self.vehicle.velocity = '0 0 0';
+ self.vehicle.avelocity = '0 0 0';
+ return 1;
+ }
+
+ player = self;
+ raptor = self.vehicle;
+ self = raptor;
+
+ vehicles_painframe();
+ /*
+ ftmp = vlen(self.velocity);
+ if(ftmp > autocvar_g_vehicle_raptor_speed_forward)
+ ftmp = 1;
+ else
+ ftmp = ftmp / autocvar_g_vehicle_raptor_speed_forward;
+ */
+
+ if(self.sound_nexttime < time)
+ {
+ self.sound_nexttime = time + 7.955812;
+ //sound (self.tur_head, CH_TRIGGER_SINGLE, "vehicles/raptor_fly.wav", 1 - ftmp, ATTEN_NORM );
+ sound (self, CH_TRIGGER_SINGLE, "vehicles/raptor_speed.wav", 1, ATTEN_NORM);
+ self.wait = ftmp;
+ }
+ /*
+ else if(fabs(ftmp - self.wait) > 0.2)
+ {
+ sound (self.tur_head, CH_TRIGGER_SINGLE, "", 1 - ftmp, ATTEN_NORM );
+ sound (self, CH_TRIGGER_SINGLE, "", ftmp, ATTEN_NORM);
+ self.wait = ftmp;
+ }
+ */
+
+ if(raptor.deadflag != DEAD_NO)
+ {
+ self = player;
+ player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
+ return 1;
+ }
+ crosshair_trace(player);
+
+ vector vang;
+ vang = raptor.angles;
+ df = vectoangles(normalize(trace_endpos - self.origin + '0 0 32'));
+ vang_x *= -1;
+ df_x *= -1;
+ if(df_x > 180) df_x -= 360;
+ if(df_x < -180) df_x += 360;
+ if(df_y > 180) df_y -= 360;
+ if(df_y < -180) df_y += 360;
+
+ ftmp = shortangle_f(player.v_angle_y - vang_y, vang_y);
+ if(ftmp > 180) ftmp -= 360; if(ftmp < -180) ftmp += 360;
+ raptor.avelocity_y = bound(-autocvar_g_vehicle_raptor_turnspeed, ftmp + raptor.avelocity_y * 0.9, autocvar_g_vehicle_raptor_turnspeed);
+
+ // Pitch
+ ftmp = 0;
+ if(player.movement_x > 0 && vang_x < autocvar_g_vehicle_raptor_pitchlimit) ftmp = 5;
+ else if(player.movement_x < 0 && vang_x > -autocvar_g_vehicle_raptor_pitchlimit) ftmp = -20;
+
+ df_x = bound(-autocvar_g_vehicle_raptor_pitchlimit, df_x , autocvar_g_vehicle_raptor_pitchlimit);
+ ftmp = vang_x - bound(-autocvar_g_vehicle_raptor_pitchlimit, df_x + ftmp, autocvar_g_vehicle_raptor_pitchlimit);
+ raptor.avelocity_x = bound(-autocvar_g_vehicle_raptor_pitchspeed, ftmp + raptor.avelocity_x * 0.9, autocvar_g_vehicle_raptor_pitchspeed);
+
+ raptor.angles_x = anglemods(raptor.angles_x);
+ raptor.angles_y = anglemods(raptor.angles_y);
+ raptor.angles_z = anglemods(raptor.angles_z);
+
+ if(autocvar_g_vehicle_raptor_movestyle == 1)
+ makevectors('0 1 0' * raptor.angles_y);
+ else
+ makevectors(player.v_angle);
+
+ df = raptor.velocity * -autocvar_g_vehicle_raptor_friction;
+
+ if(player.movement_x != 0)
+ {
+ if(player.movement_x > 0)
+ df += v_forward * autocvar_g_vehicle_raptor_speed_forward;
+ else if(player.movement_x < 0)
+ df -= v_forward * autocvar_g_vehicle_raptor_speed_forward;
+ }
+
+ if(player.movement_y != 0)
+ {
+ if(player.movement_y < 0)
+ df -= v_right * autocvar_g_vehicle_raptor_speed_strafe;
+ else if(player.movement_y > 0)
+ df += v_right * autocvar_g_vehicle_raptor_speed_strafe;
+
+ raptor.angles_z = bound(-30,raptor.angles_z + (player.movement_y / autocvar_g_vehicle_raptor_speed_strafe),30);
+ }
+ else
+ {
+ raptor.angles_z *= 0.95;
+ if(raptor.angles_z >= -1 && raptor.angles_z <= -1)
+ raptor.angles_z = 0;
+ }
+
+ if(player.BUTTON_CROUCH)
+ df -= v_up * autocvar_g_vehicle_raptor_speed_down;
+ else if (player.BUTTON_JUMP)
+ df += v_up * autocvar_g_vehicle_raptor_speed_up;
+
+ raptor.velocity += df * frametime;
+ player.velocity = player.movement = raptor.velocity;
+ setorigin(player, raptor.origin + '0 0 32');
+
+ player.vehicle_weapon2mode = raptor.vehicle_weapon2mode;
+
+ vector vf, ad;
+ // Target lock & predict
+ if(autocvar_g_vehicle_raptor_cannon_locktarget == 2)
+ {
+ if(raptor.gun1.lock_time < time || raptor.gun1.enemy.deadflag)
+ raptor.gun1.enemy = world;
+
+ if(trace_ent)
+ if(trace_ent.movetype)
+ if(trace_ent.takedamage)
+ if(!trace_ent.deadflag)
+ {
+ if(teamplay)
+ {
+ if(trace_ent.team != player.team)
+ {
+ raptor.gun1.enemy = trace_ent;
+ raptor.gun1.lock_time = time + 5;
+ }
+ }
+ else
+ {
+ raptor.gun1.enemy = trace_ent;
+ raptor.gun1.lock_time = time + 0.5;
+ }
+ }
+
+ if(raptor.gun1.enemy)
+ {
+ float distance, impact_time;
+
+ vf = real_origin(raptor.gun1.enemy);
+ UpdateAuxiliaryXhair(player, vf, '1 0 0', 1);
+ vector _vel = raptor.gun1.enemy.velocity;
+ if(raptor.gun1.enemy.movetype == MOVETYPE_WALK)
+ _vel_z *= 0.1;
+
+ if(autocvar_g_vehicle_raptor_cannon_predicttarget)
+ {
+ ad = vf;
+ distance = vlen(ad - player.origin);
+ impact_time = distance / autocvar_g_vehicle_raptor_cannon_speed;
+ ad = vf + _vel * impact_time;
+ trace_endpos = ad;
+ }
+ else
+ trace_endpos = vf;
+ }
+ }
+ else if(autocvar_g_vehicle_raptor_cannon_locktarget == 1)
+ {
+
+ vehicles_locktarget((1 / autocvar_g_vehicle_raptor_cannon_locking_time) * frametime,
+ (1 / autocvar_g_vehicle_raptor_cannon_locking_releasetime) * frametime,
+ autocvar_g_vehicle_raptor_cannon_locked_time);
+
+ if(self.lock_target != world)
+ if(autocvar_g_vehicle_raptor_cannon_predicttarget)
+ if(self.lock_strength == 1)
+ {
+ float i, distance, impact_time;
+
+ vf = real_origin(raptor.lock_target);
+ ad = vf;
+ for(i = 0; i < 4; ++i)
+ {
+ distance = vlen(ad - raptor.origin);
+ impact_time = distance / autocvar_g_vehicle_raptor_cannon_speed;
+ ad = vf + raptor.lock_target.velocity * impact_time;
+ }
+ trace_endpos = ad;
+ }
+
+ if(self.lock_target)
+ {
+ if(raptor.lock_strength == 1)
+ UpdateAuxiliaryXhair(player, real_origin(raptor.lock_target), '1 0 0', 1);
+ else if(self.lock_strength > 0.5)
+ UpdateAuxiliaryXhair(player, real_origin(raptor.lock_target), '0 1 0', 1);
+ else if(self.lock_strength < 0.5)
+ UpdateAuxiliaryXhair(player, real_origin(raptor.lock_target), '0 0 1', 1);
+ }
+ }
+
+
+ vehicle_aimturret(raptor, trace_endpos, raptor.gun1, "fire1",
+ autocvar_g_vehicle_raptor_cannon_pitchlimit_down * -1, autocvar_g_vehicle_raptor_cannon_pitchlimit_up,
+ autocvar_g_vehicle_raptor_cannon_turnlimit * -1, autocvar_g_vehicle_raptor_cannon_turnlimit, autocvar_g_vehicle_raptor_cannon_turnspeed);
+
+ vehicle_aimturret(raptor, trace_endpos, raptor.gun2, "fire1",
+ autocvar_g_vehicle_raptor_cannon_pitchlimit_down * -1, autocvar_g_vehicle_raptor_cannon_pitchlimit_up,
+ autocvar_g_vehicle_raptor_cannon_turnlimit * -1, autocvar_g_vehicle_raptor_cannon_turnlimit, autocvar_g_vehicle_raptor_cannon_turnspeed);
+
+ /*
+ ad = ad * 0.5;
+ v_forward = vf * 0.5;
+ traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, raptor);
+ UpdateAuxiliaryXhair(player, trace_endpos, '0 1 0', 0);
+ */
+
+ if(!forbidWeaponUse(player))
+ if(player.BUTTON_ATCK)
+ if(raptor.attack_finished_single <= time)
+ if(raptor.vehicle_energy > autocvar_g_vehicle_raptor_cannon_cost)
+ {
+ raptor.misc_bulletcounter += 1;
+ raptor.attack_finished_single = time + autocvar_g_vehicle_raptor_cannon_refire;
+ if(raptor.misc_bulletcounter <= 2)
+ raptor_fire_cannon(self.gun1, "fire1");
+ else if(raptor.misc_bulletcounter == 3)
+ raptor_fire_cannon(self.gun2, "fire1");
+ else
+ {
+ raptor.attack_finished_single = time + autocvar_g_vehicle_raptor_cannon_refire * 2;
+ raptor_fire_cannon(self.gun2, "fire1");
+ raptor.misc_bulletcounter = 0;
+ }
+ raptor.vehicle_energy -= autocvar_g_vehicle_raptor_cannon_cost;
+ self.cnt = time;
+ }
+
+ if(self.vehicle_flags & VHF_SHIELDREGEN)
+ vehicles_regen(raptor.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, true);
+
+ if(self.vehicle_flags & VHF_HEALTHREGEN)
+ vehicles_regen(raptor.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, false);
+
+ if(self.vehicle_flags & VHF_ENERGYREGEN)
+ vehicles_regen(raptor.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, false);
+
+ if(!forbidWeaponUse(player))
+ if(raptor.vehicle_weapon2mode == RSM_BOMB)
+ {
+ if(time > raptor.lip + autocvar_g_vehicle_raptor_bombs_refire)
+ if(player.BUTTON_ATCK2)
+ {
+ raptor_bombdrop();
+ raptor.delay = time + autocvar_g_vehicle_raptor_bombs_refire;
+ raptor.lip = time;
+ }
+ }
+ else
+ {
+ if(time > raptor.lip + autocvar_g_vehicle_raptor_flare_refire)
+ if(player.BUTTON_ATCK2)
+ {
+ float i;
+ entity _flare;
+
+ for(i = 0; i < 3; ++i)
+ {
+ _flare = spawn();
+ setmodel(_flare, "models/runematch/rune.mdl");
+ _flare.effects = EF_LOWPRECISION | EF_FLAME;
+ _flare.scale = 0.5;
+ setorigin(_flare, self.origin - '0 0 16');
+ _flare.movetype = MOVETYPE_TOSS;
+ _flare.gravity = 0.15;
+ _flare.velocity = 0.25 * raptor.velocity + (v_forward + randomvec() * 0.25)* -500;
+ _flare.think = raptor_flare_think;
+ _flare.nextthink = time;
+ _flare.owner = raptor;
+ _flare.solid = SOLID_CORPSE;
+ _flare.takedamage = DAMAGE_YES;
+ _flare.event_damage = raptor_flare_damage;
+ _flare.health = 20;
+ _flare.tur_impacttime = time + autocvar_g_vehicle_raptor_flare_lifetime;
+ _flare.touch = raptor_flare_touch;
+ }
+ raptor.delay = time + autocvar_g_vehicle_raptor_flare_refire;
+ raptor.lip = time;
+ }
+ }
+
+ raptor.bomb1.alpha = raptor.bomb2.alpha = (time - raptor.lip) / (raptor.delay - raptor.lip);
+ player.vehicle_reload2 = bound(0, raptor.bomb1.alpha * 100, 100);
+
+ if(self.bomb1.cnt < time)
+ {
+ entity _missile = findchainentity(enemy, raptor);
+ float _incomming = 0;
+ while(_missile)
+ {
+ if(_missile.flags & FL_PROJECTILE)
+ if(MISSILE_IS_TRACKING(_missile))
+ if(vlen(self.origin - _missile.origin) < 2 * autocvar_g_vehicle_raptor_flare_range)
+ ++_incomming;
+
+ _missile = _missile.chain;
+ }
+
+ if(_incomming)
+ sound(self, CH_PAIN_SINGLE, "vehicles/missile_alarm.wav", VOL_BASE, ATTEN_NONE);
+
+ self.bomb1.cnt = time + 1;
+ }
+
+
+ VEHICLE_UPDATE_PLAYER(player, health, raptor);
+ VEHICLE_UPDATE_PLAYER(player, energy, raptor);
+ if(self.vehicle_flags & VHF_HASSHIELD)
+ VEHICLE_UPDATE_PLAYER(player, shield, raptor);
+
+ player.BUTTON_ATCK = player.BUTTON_ATCK2 = player.BUTTON_CROUCH = 0;
+
+ self = player;
+ return 1;
+}
+
+float raptor_takeoff()
+{
+ entity player, raptor;
+
+ player = self;
+ raptor = self.vehicle;
+ self = raptor;
+
+ self.nextthink = time;
+ CSQCMODEL_AUTOUPDATE();
+ self.nextthink = 0; // will this work?
+
+ if(self.sound_nexttime < time)
+ {
+ self.sound_nexttime = time + 7.955812; //soundlength("vehicles/raptor_fly.wav");
+ sound (self, CH_TRIGGER_SINGLE, "vehicles/raptor_speed.wav", VOL_VEHICLEENGINE, ATTEN_NORM);
+ }
+
+ // Takeoff sequense
+ if(raptor.frame < 25)
+ {
+ raptor.frame += 25 / (autocvar_g_vehicle_raptor_takeofftime / sys_frametime);
+ raptor.velocity_z = min(raptor.velocity_z * 1.5, 256);
+ self.bomb1.gun1.avelocity_y = 90 + ((raptor.frame / 25) * 25000);
+ self.bomb1.gun2.avelocity_y = -self.bomb1.gun1.avelocity_y;
+ player.BUTTON_ATCK = player.BUTTON_ATCK2 = player.BUTTON_CROUCH = 0;
+
+ setorigin(player, raptor.origin + '0 0 32');
+ }
+ else
+ player.PlayerPhysplug = raptor_frame;
+
+ if(self.vehicle_flags & VHF_SHIELDREGEN)
+ vehicles_regen(raptor.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, true);
+
+ if(self.vehicle_flags & VHF_HEALTHREGEN)
+ vehicles_regen(raptor.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, false);
+
+ if(self.vehicle_flags & VHF_ENERGYREGEN)
+ vehicles_regen(raptor.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, false);
+
+
+ raptor.bomb1.alpha = raptor.bomb2.alpha = (time - raptor.lip) / (raptor.delay - raptor.lip);
+ player.vehicle_reload2 = bound(0, raptor.bomb1.alpha * 100, 100);
+
+ VEHICLE_UPDATE_PLAYER(player, health, raptor);
+ VEHICLE_UPDATE_PLAYER(player, energy, raptor);
+ if(self.vehicle_flags & VHF_HASSHIELD)
+ VEHICLE_UPDATE_PLAYER(player, shield, raptor);
+
+ player.BUTTON_ATCK = player.BUTTON_ATCK2 = player.BUTTON_CROUCH = 0;
+ self = player;
+ return 1;
+}
+
+void raptor_blowup()
+{
+ self.deadflag = DEAD_DEAD;
+ self.vehicle_exit(VHEF_NORMAL);
+ RadiusDamage (self, self.enemy, 250, 15, 250, world, world, 250, DEATH_VH_RAPT_DEATH, world);
+
+ self.alpha = -1;
+ self.movetype = MOVETYPE_NONE;
+ self.effects = EF_NODRAW;
+ self.colormod = '0 0 0';
+ self.avelocity = '0 0 0';
+ self.velocity = '0 0 0';
+
+ setorigin(self, self.pos1);
+ self.touch = func_null;
+ self.nextthink = 0;
+}
+
+void raptor_diethink()
+{
+ if(time >= self.wait)
+ self.think = raptor_blowup;
+
+ if(random() < 0.05)
+ {
+ sound (self, CH_SHOTS, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM);
+ Send_Effect(EFFECT_EXPLOSION_SMALL, randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
+ }
+ self.nextthink = time;
+
+ CSQCMODEL_AUTOUPDATE();
+}
+
+// If we dont do this ever now and then, the raptors rotors
+// stop working, presumably due to angle overflow. cute.
+void raptor_rotor_anglefix()
+{
+ self.gun1.angles_y = anglemods(self.gun1.angles_y);
+ self.gun2.angles_y = anglemods(self.gun2.angles_y);
+ self.nextthink = time + 15;
+}
+
+float raptor_impulse(float _imp)
+{
+ switch(_imp)
+ {
+ case 1:
+ case 230:
+ self.vehicle.vehicle_weapon2mode = RSM_BOMB;
+ CSQCVehicleSetup(self, 0);
+ return true;
+ case 2:
+ case 231:
+ self.vehicle.vehicle_weapon2mode = RSM_FLARE;
+ CSQCVehicleSetup(self, 0);
+ return true;
+
+ case 10:
+ case 15:
+ case 18:
+ self.vehicle.vehicle_weapon2mode += 1;
+ if(self.vehicle.vehicle_weapon2mode > RSM_LAST)
+ self.vehicle.vehicle_weapon2mode = RSM_FIRST;
+
+ CSQCVehicleSetup(self, 0);
+ return true;
+ case 11:
+ case 12:
+ case 16:
+ case 19:
+ self.vehicle.vehicle_weapon2mode -= 1;
+ if(self.vehicle.vehicle_weapon2mode < RSM_FIRST)
+ self.vehicle.vehicle_weapon2mode = RSM_LAST;
+
+ CSQCVehicleSetup(self, 0);
+ return true;
+
+ /*
+ case 17: // toss gun, could be used to exit?
+ break;
+ case 20: // Manual minigun reload?
+ break;
+ */
+ }
+ return false;
+}
+
+void spawnfunc_vehicle_raptor()
+{
+ if(!autocvar_g_vehicle_raptor) { remove(self); return; }
+ if(!vehicle_initialize(VEH_RAPTOR, false)) { remove(self); return; }
+}
+
+float v_raptor(float req)
+{
+ switch(req)
+ {
+ case VR_IMPACT:
+ {
+ if(autocvar_g_vehicle_raptor_bouncepain)
+ vehicles_impact(autocvar_g_vehicle_raptor_bouncepain_x, autocvar_g_vehicle_raptor_bouncepain_y, autocvar_g_vehicle_raptor_bouncepain_z);
+
+ return true;
+ }
+ case VR_ENTER:
+ {
+ self.vehicle_weapon2mode = RSM_BOMB;
+ self.owner.PlayerPhysplug = raptor_takeoff;
+ self.movetype = MOVETYPE_BOUNCEMISSILE;
+ self.solid = SOLID_SLIDEBOX;
+ self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_raptor_health) * 100;
+ self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_raptor_shield) * 100;
+ self.velocity_z = 1; // Nudge upwards to takeoff sequense can work.
+ self.tur_head.exteriormodeltoclient = self.owner;
+
+ self.delay = time + autocvar_g_vehicle_raptor_bombs_refire;
+ self.lip = time;
+
+ if(self.owner.flagcarried)
+ setorigin(self.owner.flagcarried, '-20 0 96');
+
+ CSQCVehicleSetup(self.owner, 0);
+ return true;
+ }
+ case VR_THINK:
+ {
+ return true;
+ }
+ case VR_DEATH:
+ {
+ self.health = 0;
+ self.event_damage = func_null;
+ self.solid = SOLID_CORPSE;
+ self.takedamage = DAMAGE_NO;
+ self.deadflag = DEAD_DYING;
+ self.movetype = MOVETYPE_BOUNCE;
+ self.think = raptor_diethink;
+ self.nextthink = time;
+ self.wait = time + 5 + (random() * 5);
+
+ Send_Effect(EFFECT_EXPLOSION_MEDIUM, findbetterlocation (self.origin, 16), '0 0 0', 1);
+
+ self.velocity_z += 600;
+
+ self.avelocity = '0 0.5 1' * (random() * 400);
+ self.avelocity -= '0 0.5 1' * (random() * 400);
+
+ self.colormod = '-0.5 -0.5 -0.5';
+ self.touch = raptor_blowup;
+ return true;
+ }
+ case VR_SPAWN:
+ {
+ if(!self.gun1)
+ {
+ entity spinner;
+ vector ofs;
+
+ //FIXME: Camera is in a bad place in HUD model.
+ //setorigin(self.vehicle_viewport, '25 0 5');
+
+ self.vehicles_impulse = raptor_impulse;
+
+ self.frame = 0;
+
+ self.bomb1 = spawn();
+ self.bomb2 = spawn();
+ self.gun1 = spawn();
+ self.gun2 = spawn();
+
+ setmodel(self.bomb1,"models/vehicles/clusterbomb_folded.md3");
+ setmodel(self.bomb2,"models/vehicles/clusterbomb_folded.md3");
+ setmodel(self.gun1, "models/vehicles/raptor_gun.dpm");
+ setmodel(self.gun2, "models/vehicles/raptor_gun.dpm");
+ setmodel(self.tur_head, "models/vehicles/raptor_body.dpm");
+
+ setattachment(self.bomb1, self, "bombmount_left");
+ setattachment(self.bomb2, self, "bombmount_right");
+ setattachment(self.tur_head, self,"root");
+
+ // FIXMODEL Guns mounts to angled bones
+ self.bomb1.angles = self.angles;
+ self.angles = '0 0 0';
+ // This messes up gun-aim, so work arround it.
+ //setattachment(self.gun1, self, "gunmount_left");
+ ofs = gettaginfo(self, gettagindex(self, "gunmount_left"));
+ ofs -= self.origin;
+ setattachment(self.gun1, self, "");
+ setorigin(self.gun1, ofs);
+
+ //setattachment(self.gun2, self, "gunmount_right");
+ ofs = gettaginfo(self, gettagindex(self, "gunmount_right"));
+ ofs -= self.origin;
+ setattachment(self.gun2, self, "");
+ setorigin(self.gun2, ofs);
+
+ self.angles = self.bomb1.angles;
+ self.bomb1.angles = '0 0 0';
+
+ spinner = spawn();
+ spinner.owner = self;
+ setmodel(spinner,"models/vehicles/spinner.dpm");
+ setattachment(spinner, self, "engine_left");
+ spinner.movetype = MOVETYPE_NOCLIP;
+ spinner.avelocity = '0 90 0';
+ self.bomb1.gun1 = spinner;
+
+ spinner = spawn();
+ spinner.owner = self;
+ setmodel(spinner,"models/vehicles/spinner.dpm");
+ setattachment(spinner, self, "engine_right");
+ spinner.movetype = MOVETYPE_NOCLIP;
+ spinner.avelocity = '0 -90 0';
+ self.bomb1.gun2 = spinner;
+
+ // Sigh.
+ self.bomb1.think = raptor_rotor_anglefix;
+ self.bomb1.nextthink = time;
+
+ self.mass = 1 ;
+ }
+
+ self.frame = 0;
+ self.vehicle_health = autocvar_g_vehicle_raptor_health;
+ self.vehicle_shield = autocvar_g_vehicle_raptor_shield;
+ self.movetype = MOVETYPE_TOSS;
+ self.solid = SOLID_SLIDEBOX;
+ self.vehicle_energy = 1;
+
+ self.PlayerPhysplug = raptor_frame;
+
+ self.bomb1.gun1.avelocity_y = 90;
+ self.bomb1.gun2.avelocity_y = -90;
+
+ self.delay = time;
+
+ self.bouncefactor = autocvar_g_vehicle_raptor_bouncefactor;
+ self.bouncestop = autocvar_g_vehicle_raptor_bouncestop;
+ self.damageforcescale = 0.25;
+ self.vehicle_health = autocvar_g_vehicle_raptor_health;
+ self.vehicle_shield = autocvar_g_vehicle_raptor_shield;
+ return true;
+ }
+ case VR_SETUP:
+ {
+ if(autocvar_g_vehicle_raptor_shield)
+ self.vehicle_flags |= VHF_HASSHIELD;
+
+ if(autocvar_g_vehicle_raptor_shield_regen)
+ self.vehicle_flags |= VHF_SHIELDREGEN;
+
+ if(autocvar_g_vehicle_raptor_health_regen)
+ self.vehicle_flags |= VHF_HEALTHREGEN;
+
+ if(autocvar_g_vehicle_raptor_energy_regen)
+ self.vehicle_flags |= VHF_ENERGYREGEN;
+
+ self.vehicle_exit = raptor_exit;
+ self.respawntime = autocvar_g_vehicle_raptor_respawntime;
+ self.vehicle_health = autocvar_g_vehicle_raptor_health;
+ self.vehicle_shield = autocvar_g_vehicle_raptor_shield;
+ self.max_health = self.vehicle_health;
+
+ return true;
+ }
+ case VR_PRECACHE:
+ {
+ precache_model ("models/vehicles/raptor.dpm");
+ precache_model ("models/vehicles/raptor_gun.dpm");
+ precache_model ("models/vehicles/spinner.dpm");
+ precache_model ("models/vehicles/raptor_cockpit.dpm");
+ precache_model ("models/vehicles/clusterbomb_folded.md3");
+ precache_model ("models/vehicles/raptor_body.dpm");
+
+ precache_sound ("vehicles/raptor_fly.wav");
+ precache_sound ("vehicles/raptor_speed.wav");
+ precache_sound ("vehicles/missile_alarm.wav");
+
+ return true;
+ }
+ }
+
+ return true;
+}
+
+#endif // SVQC
+#ifdef CSQC
+#define raptor_ico "gfx/vehicles/raptor.tga"
+#define raptor_gun "gfx/vehicles/raptor_guns.tga"
+#define raptor_bomb "gfx/vehicles/raptor_bombs.tga"
+#define raptor_drop "gfx/vehicles/axh-dropcross.tga"
+
+void RaptorCBShellfragDraw()
+{
+ if(wasfreed(self))
+ return;
+
+ Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy);
+ self.move_avelocity += randomvec() * 15;
+ self.renderflags = 0;
+
+ if(self.cnt < time)
+ self.alpha = bound(0, self.nextthink - time, 1);
+
+ if(self.alpha < ALPHA_MIN_VISIBLE)
+ remove(self);
+}
+
+void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang)
+{
+ entity sfrag;
+
+ sfrag = spawn();
+ setmodel(sfrag, "models/vehicles/clusterbomb_fragment.md3");
+ setorigin(sfrag, _org);
+
+ sfrag.move_movetype = MOVETYPE_BOUNCE;
+ sfrag.gravity = 0.15;
+ sfrag.solid = SOLID_CORPSE;
+
+ sfrag.draw = RaptorCBShellfragDraw;
+
+ sfrag.move_origin = sfrag.origin = _org;
+ sfrag.move_velocity = _vel;
+ sfrag.move_avelocity = prandomvec() * vlen(sfrag.move_velocity);
+ sfrag.angles = self.move_angles = _ang;
+
+ sfrag.move_time = time;
+ sfrag.damageforcescale = 4;
+
+ sfrag.nextthink = time + 3;
+ sfrag.cnt = time + 2;
+ sfrag.alpha = 1;
+ sfrag.drawmask = MASK_NORMAL;
+}
+
+float v_raptor(float req)
+{
+ switch(req)
+ {
+ case VR_HUD:
+ {
+ if(autocvar_r_letterbox)
+ return true;
+
+ vector picsize, hudloc = '0 0 0', pic2size, picloc;
+ string raptor_xhair;
+
+ // Fetch health & ammo stats
+ HUD_GETVEHICLESTATS
+
+ picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale;
+ hudloc_y = vid_conheight - picsize_y;
+ hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5;
+
+ drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL);
+
+ ammo1 *= 0.01;
+ ammo2 *= 0.01;
+ shield *= 0.01;
+ vh_health *= 0.01;
+ energy *= 0.01;
+ reload1 = reload2 * 0.01;
+ //reload2 *= 0.01;
+
+ pic2size = draw_getimagesize(raptor_ico) * (autocvar_cl_vehicles_hudscale * 0.8);
+ picloc = picsize * 0.5 - pic2size * 0.5;
+ if(vh_health < 0.25)
+ drawpic(hudloc + picloc, raptor_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
+ else
+ drawpic(hudloc + picloc, raptor_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL);
+ drawpic(hudloc + picloc, raptor_bomb, pic2size, '1 1 1' * reload1 + '1 0 0' * (1 - reload1), 1, DRAWFLAG_NORMAL);
+ drawpic(hudloc + picloc, raptor_gun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL);
+ drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL);
+
+ // Health bar
+ picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale;
+ picloc = '69 69 0' * autocvar_cl_vehicles_hudscale;
+ drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight);
+ drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL);
+ drawresetcliparea();
+ // .. and icon
+ picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale;
+ picloc = '37 65 0' * autocvar_cl_vehicles_hudscale;
+ if(vh_health < 0.25)
+ {
+ if(alarm1time < time)
+ {
+ alarm1time = time + 2;
+ vehicle_alarm(self, CH_PAIN_SINGLE, "vehicles/alarm.wav");
+ }
+
+ drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
+ }
+ else
+ {
+ drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+ if(alarm1time)
+ {
+ vehicle_alarm(self, CH_PAIN_SINGLE, "misc/null.wav");
+ alarm1time = 0;
+ }
+ }
+
+ // Shield bar
+ picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale;
+ picloc = '69 140 0' * autocvar_cl_vehicles_hudscale;
+ drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight);
+ drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+ drawresetcliparea();
+ // .. and icon
+ picloc = '40 136 0' * autocvar_cl_vehicles_hudscale;
+ picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale;
+ if(shield < 0.25)
+ {
+ if(alarm2time < time)
+ {
+ alarm2time = time + 1;
+ vehicle_alarm(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav");
+ }
+ drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
+ }
+ else
+ {
+ drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+ if(alarm2time)
+ {
+ vehicle_alarm(self, CH_TRIGGER_SINGLE, "misc/null.wav");
+ alarm2time = 0;
+ }
+ }
+
+ // Gun bar
+ picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale;
+ picloc = '450 69 0' * autocvar_cl_vehicles_hudscale;
+ drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * energy, vid_conheight);
+ drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+ drawresetcliparea();
+ // .. and icon
+ picsize = draw_getimagesize(hud_ammo1_ico) * autocvar_cl_vehicles_hudscale;
+ picloc = '664 60 0' * autocvar_cl_vehicles_hudscale;
+ if(energy < 0.2)
+ drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
+ else
+ drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+
+ // Bomb bar
+ picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale;
+ picloc = '450 140 0' * autocvar_cl_vehicles_hudscale;
+ drawsetcliparea(hudloc_x + picloc_x, hudloc_y + picloc_y, picsize_x * reload1, vid_conheight);
+ drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+ drawresetcliparea();
+ // .. and icon
+ pic2size = draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale;
+ picloc = '664 130 0' * autocvar_cl_vehicles_hudscale;
+ if(reload1 != 1)
+ drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
+ else
+ drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 1 1', 1, DRAWFLAG_NORMAL);
+
+ if(getstati(STAT_VEHICLESTAT_W2MODE) == RSM_FLARE)
+ {
+ raptor_xhair = "gfx/vehicles/axh-bracket.tga";
+ }
+ else
+ {
+ raptor_xhair = "gfx/vehicles/axh-ring.tga";
+
+ // Bombing crosshair
+ if(!dropmark)
+ {
+ dropmark = spawn();
+ dropmark.owner = self;
+ dropmark.gravity = 1;
+ }
+
+ if(reload2 == 100)
+ {
+ vector where;
+
+ setorigin(dropmark, pmove_org);
+ dropmark.velocity = pmove_vel;
+ tracetoss(dropmark, self);
+
+ where = project_3d_to_2d(trace_endpos);
+
+ setorigin(dropmark, trace_endpos);
+ picsize = draw_getimagesize(raptor_drop) * 0.2;
+
+ if(!(where_z < 0 || where_x < 0 || where_y < 0 || where_x > vid_conwidth || where_y > vid_conheight))
+ {
+ where_x -= picsize_x * 0.5;
+ where_y -= picsize_y * 0.5;
+ where_z = 0;
+ drawpic(where, raptor_drop, picsize, '0 2 0', 1, DRAWFLAG_ADDITIVE);
+ }
+ dropmark.cnt = time + 5;
+ }
+ else
+ {
+ vector where;
+ if(dropmark.cnt > time)
+ {
+ where = project_3d_to_2d(dropmark.origin);
+ picsize = draw_getimagesize(raptor_drop) * 0.25;
+
+ if(!(where_z < 0 || where_x < 0 || where_y < 0 || where_x > vid_conwidth || where_y > vid_conheight))
+ {
+ where_x -= picsize_x * 0.5;
+ where_y -= picsize_y * 0.5;
+ where_z = 0;
+ drawpic(where, raptor_drop, picsize, '2 0 0', 1, DRAWFLAG_ADDITIVE);
+ }
+ }
+ }
+ }
+
+ if (scoreboard_showscores)
+ HUD_DrawScoreboard();
+ else
+ {
+ picsize = draw_getimagesize(raptor_xhair);
+ picsize_x *= 0.5;
+ picsize_y *= 0.5;
+
+ drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), raptor_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+ }
+
+ return true;
+ }
+ case VR_SETUP:
+ {
+ AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-special2.tga";
+ AuxiliaryXhair[0].axh_scale = 0.5;
+
+ AuxiliaryXhair[1].axh_image = "gfx/vehicles/axh-bracket.tga";
+ AuxiliaryXhair[1].axh_scale = 0.25;
+ return true;
+ }
+ case VR_PRECACHE:
+ {
+ return true;
+ }
+ }
+
+ return true;
+}
+
+#endif // CSQC
+#endif // REGISTER_VEHICLE
#endif
#else
#ifdef SVQC
+#include "../effects.qh"
+
void spawnfunc_weapon_arc(void) { weapon_defaultspawnfunc(WEP_ARC); }
- float W_Arc_Beam_Send(entity to, float sf)
+ float W_Arc_Beam_Send(entity to, int sf)
{
WriteByte(MSG_ENTITY, ENT_CLIENT_ARC_BEAM);
--- /dev/null
- entity W_Flak_Projectile(entity o, bool isprimary, float damage, float grav, float lt, float spd, float upspd, float bnc, float deathtype)
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ FLAK,
+/* function */ W_Flak,
+/* ammotype */ ammo_shells,
+/* impulse */ 8,
+/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
+/* rating */ BOT_PICKUP_RATING_MID,
+/* color */ '1 1 0',
+/* modelname */ "flak",
+/* simplemdl */ "foobar",
+/* crosshair */ "gfx/crosshairflak 0.8",
+/* wepimg */ "weaponflak",
+/* refname */ "flak",
+/* wepname */ _("Flak Cannon")
+);
+
+#define FLAK_SETTINGS(w_cvar,w_prop) FLAK_SETTINGS_LIST(w_cvar, w_prop, FLAK, flak)
+#define FLAK_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
+ w_cvar(id, sn, BOTH, ammo) \
+ w_cvar(id, sn, BOTH, animtime) \
+ w_cvar(id, sn, BOTH, bouncefactor) \
+ w_cvar(id, sn, BOTH, count) \
+ w_cvar(id, sn, BOTH, damage) \
+ w_cvar(id, sn, BOTH, damage_bouncefactor) \
+ w_cvar(id, sn, BOTH, force) \
+ w_cvar(id, sn, BOTH, gravity) \
+ w_cvar(id, sn, BOTH, refire) \
+ w_cvar(id, sn, BOTH, speed) \
+ w_cvar(id, sn, BOTH, speed_up) \
+ w_cvar(id, sn, BOTH, spread_side) \
+ w_cvar(id, sn, BOTH, spread_up) \
+ w_cvar(id, sn, BOTH, passthrough) \
+ w_cvar(id, sn, BOTH, lifetime) \
+ w_cvar(id, sn, NONE, bomb_ammo) \
+ w_cvar(id, sn, NONE, bomb) \
+ w_cvar(id, sn, NONE, bomb_count) \
+ w_cvar(id, sn, NONE, bomb_damage) \
+ w_cvar(id, sn, NONE, bomb_damageforcescale) \
+ w_cvar(id, sn, NONE, bomb_edgedamage) \
+ w_cvar(id, sn, NONE, bomb_force) \
+ w_cvar(id, sn, NONE, bomb_health) \
+ w_cvar(id, sn, NONE, bomb_lifetime) \
+ w_cvar(id, sn, NONE, bomb_radius) \
+ w_cvar(id, sn, NONE, bomb_speed) \
+ w_cvar(id, sn, NONE, bomb_speed_up) \
+ w_cvar(id, sn, NONE, bomb_spread_side) \
+ w_cvar(id, sn, NONE, bomb_spread_up) \
+ 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)
+
+#ifdef SVQC
+FLAK_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
+#endif
+#else
+#ifdef SVQC
+#include "../effects.qh"
+
+void spawnfunc_weapon_flak(void) { weapon_defaultspawnfunc(WEP_FLAK); }
+
+void W_Flak_Projectile_Touch(void)
+{
+ PROJECTILE_TOUCH;
+
+ if(other.takedamage)
+ {
+ float damage, bnc, frc;
+ bool isprimary = !(self.projectiledeathtype & HITTYPE_SECONDARY);
+
+ if(other != self.enemy && (other != self.realowner || (self.projectiledeathtype & HITTYPE_BOUNCE)))
+ {
+ bnc = pow(WEP_CVAR_BOTH(flak, isprimary, damage_bouncefactor), self.cnt);
+ damage = WEP_CVAR_BOTH(flak, isprimary, damage) * bnc;
+ frc = WEP_CVAR_BOTH(flak, isprimary, force) * bnc;
+
+ vector force = normalize(NearestPointOnBox(other, self.origin) - self.origin + self.velocity) * frc;
+
+ self.owner = self.realowner;
+ Damage(other, self, self.realowner, damage, self.projectiledeathtype, self.origin, force);
+
+ if(accuracy_isgooddamage(self.realowner, other))
+ { accuracy_add(self.realowner, WEP_FLAK, 0, damage); }
+ }
+
+ //Send_Effect(EFFECT_FLAK_BOUNCE, self.origin, self.velocity, 1);
+
+ bool passThrough = WEP_CVAR_BOTH(flak, isprimary, passthrough);
+
+ if(passThrough <= 0)
+ {
+ remove(self);
+ return;
+ }
+
+ // semi-fix for "melee" hits
+ if(other == self.realowner && !(self.projectiledeathtype & HITTYPE_BOUNCE))
+ self.velocity = self.velocity * -1;
+
+ // pass through - we don't want to bounce here, overwrite velocity later
+ self.oldvelocity = self.velocity * passThrough;
+
+ // we don't want to hit the same entity again right away
+ self.owner = other;
+ if(other.iscreature)
+ self.enemy = other;
+ }
+ else if(other.solid == SOLID_BSP)
+ {
+ spamsound(self, CH_SHOTS, W_Sound(strcat("casings", ftos(floor(random() * 3) + 1))), VOL_BASE * 0.7, ATTN_NORM);
+ //pointparticles(particleeffectnum("flak_bounce"), self.origin, self.velocity, 1);
+ self.owner = world;
+ self.projectiledeathtype |= HITTYPE_BOUNCE;
+ self.cnt += 1;
+ }
+}
+
+void W_Flak_Projectile_Think(void)
+{
+ if(time > self.spawnshieldtime)
+ {
+ remove(self);
+ return;
+ }
+
+ if(self.oldvelocity)
+ {
+ self.velocity = self.oldvelocity;
+ self.oldvelocity = '0 0 0';
+ UpdateCSQCProjectile(self);
+ }
+
+ self.nextthink = time;
+}
+
- void W_Flak_Bomb_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
++entity W_Flak_Projectile(entity o, bool isprimary, float damage, float grav, float lt, float spd, float upspd, float bnc, int deathtype)
+{
+ entity e = spawn();
+ e.owner = e.realowner = o;
+ e.classname = "flak_proj";
+ e.bot_dodge = true;
+ e.bot_dodgerating = damage;
+ e.movetype = MOVETYPE_BOUNCE;
+ PROJECTILE_MAKETRIGGER(e);
+ e.projectiledeathtype = deathtype;
+ e.gravity = grav;
+ e.bouncefactor = bnc;
+ setorigin(e, w_shotorg);
+ setsize(e, '0 0 -3', '0 0 -3');
+
+ e.spawnshieldtime = time + lt;
+ e.nextthink = time;
+ e.think = W_Flak_Projectile_Think;
+ e.touch = W_Flak_Projectile_Touch;
+ W_SetupProjVelocity_Explicit(e, v_forward, v_up, spd, upspd, 0, 0, false);
+
+ e.angles = vectoangles(e.velocity);
+ e.flags = FL_PROJECTILE;
+ e.missile_flags = MIF_ARC;
+
+ CSQCProjectile(e, true, PROJECTILE_FLAK, true);
+ return e;
+}
+
+void W_Flak_Attack1(bool isprimary)
+{
+ float pcount = WEP_CVAR_BOTH(flak, isprimary, count), i;
+ int dtype = (isprimary) ? WEP_FLAK : (WEP_FLAK | HITTYPE_SECONDARY | HITTYPE_BOUNCE);
+
+ W_DecreaseAmmo(WEP_CVAR_BOTH(flak, isprimary, ammo));
+
+ W_SetupShot_ProjectileSize(self, '0 0 -3', '0 0 -3', false, 4,
+ W_Sound(((isprimary) ? "flak_fire2" : "flak_fire")), CH_WEAPON_A, WEP_CVAR_BOTH(flak, isprimary, damage) * pcount);
+ w_shotdir = v_forward;
+ vector a;
+
+ for(i = 0; i < pcount; ++i)
+ {
+ a = fixedvectoangles(w_shotdir);
+
+ fixedmakevectors(a + '1 0 0' * random() * WEP_CVAR_BOTH(flak, isprimary, spread_up) + '0 1 0' * crandom() * WEP_CVAR_BOTH(flak, isprimary, spread_side));
+
+ W_Flak_Projectile(self, isprimary,
+ WEP_CVAR_BOTH(flak, isprimary, damage),
+ WEP_CVAR_BOTH(flak, isprimary, gravity),
+ WEP_CVAR_BOTH(flak, isprimary, lifetime),
+ WEP_CVAR_BOTH(flak, isprimary, speed),
+ WEP_CVAR_BOTH(flak, isprimary, speed_up),
+ WEP_CVAR_BOTH(flak, isprimary, bouncefactor),
+ dtype);
+ }
+
+ entity flash = spawn();
+ setmodel(flash, "models/uziflash.md3");
+ flash.think = SUB_Remove;
+ flash.nextthink = time + 0.06;
+ flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
+ W_AttachToShotorg(flash, '5 0 0');
+}
+
+void W_Flak_Bomb_Explode(void)
+{
+ vector dir = normalize(self.velocity);
+ dir = dir - 2 * (dir * trace_plane_normal) * trace_plane_normal;
+
+ 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);
+
+ self.event_damage = func_null;
+ self.takedamage = DAMAGE_NO;
+ RadiusDamage(self, self.owner, WEP_CVAR(flak, bomb_damage), WEP_CVAR(flak, bomb_edgedamage), WEP_CVAR(flak, bomb_radius),
+ world, world, WEP_CVAR(flak, bomb_force), self.projectiledeathtype, other);
+
+ float i, c = WEP_CVAR(flak, bomb_count);
+ w_shotorg = self.origin;
+
+ float spread_up, spread_side;
+
+ if(other.takedamage == DAMAGE_AIM && other.iscreature)
+ {
+ spread_side = 360;
+ spread_up = 360;
+ }
+ else
+ {
+ spread_side = WEP_CVAR(flak, bomb_spread_side);
+ spread_up = WEP_CVAR(flak, bomb_spread_up);
+ }
+
+ for(i = 0; i < c; ++i)
+ {
+ vector a = fixedvectoangles(dir);
+
+ fixedmakevectors(a + '1 0 0' * crandom() * spread_up
+ + '0 1 0' * crandom() * spread_side);
+
+ entity p = W_Flak_Projectile(self.realowner, false,
+ WEP_CVAR_SEC(flak, damage),
+ WEP_CVAR_SEC(flak, gravity),
+ WEP_CVAR_SEC(flak, lifetime),
+ WEP_CVAR_SEC(flak, speed),
+ WEP_CVAR_SEC(flak, speed_up),
+ WEP_CVAR_SEC(flak, bouncefactor),
+ WEP_FLAK | HITTYPE_SECONDARY | HITTYPE_BOUNCE);
+
+ // do not hit the direct hit entity with shrapnel right away (causes very inconsistent behavior)
+ p.owner = other;
+ }
+
+ remove (self);
+}
+
+void W_Flak_Bomb_Touch(void)
+{
+ PROJECTILE_TOUCH;
+ self.use();
+}
+
++void W_Flak_Bomb_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+ if(self.health <= 0)
+ return;
+
+ if(!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
+ return; // g_projectiles_damage says to halt
+
+ self.health = self.health - damage;
+
+ if(self.health <= 0)
+ W_PrepareExplosionByDamage(attacker, self.use);
+}
+
+void W_Flak_Attack3(void)
+{
+ if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
+ self.ammo_rockets -= WEP_CVAR(flak, bomb_ammo);
+
+ W_DecreaseAmmo(WEP_CVAR_SEC(flak, ammo));
+
+ W_SetupShot_ProjectileSize (self, '0 0 -3', '0 0 -3', false, 4, W_Sound("flak_fire3"), CH_WEAPON_A, WEP_CVAR(flak, bomb_damage));
+ w_shotdir = v_forward;
+
+ entity e = spawn();
+ e.owner = e.realowner = self;
+ e.classname = "flak_bomb";
+ e.bot_dodge = true;
+ e.bot_dodgerating = WEP_CVAR(flak, bomb_damage);
+ e.movetype = MOVETYPE_BOUNCE;
+ PROJECTILE_MAKETRIGGER(e);
+ e.projectiledeathtype = WEP_FLAK | HITTYPE_SECONDARY;
+ setsize(e, '0 0 -3', '0 0 -3');
+ setorigin(e, w_shotorg);
+
+ e.nextthink = time + WEP_CVAR(flak, bomb_lifetime);
+ e.think = adaptor_think2use;
+ e.use = W_Flak_Bomb_Explode;
+ e.touch = W_Flak_Bomb_Touch;
+ e.takedamage = DAMAGE_YES;
+ e.health = WEP_CVAR(flak, bomb_health);
+ e.damageforcescale = WEP_CVAR(flak, bomb_damageforcescale);
+ e.event_damage = W_Flak_Bomb_Damage;
+ W_SetupProjVelocity_Explicit(e, w_shotdir, v_up, WEP_CVAR(flak, bomb_speed), WEP_CVAR(flak, bomb_speed_up), 0, 0, false);
+
+ e.angles = vectoangles(e.velocity);
+ e.flags = FL_PROJECTILE;
+ e.missile_flags = MIF_SPLASH | MIF_ARC | MIF_PROXY;
+
+ CSQCProjectile(e, true, PROJECTILE_FLAK_BOMB, true);
+}
+
+float W_Flak(float req)
+{
+ float ammo_amount;
+ switch(req)
+ {
+ case WR_AIM:
+ {
+ if(random() < 0.35 && WEP_CVAR(flak, bomb) && self.ammo_rockets >= WEP_CVAR(flak, bomb_ammo))
+ self.BUTTON_ATCK2 = bot_aim(WEP_CVAR(flak, bomb_speed), WEP_CVAR(flak, bomb_speed_up), WEP_CVAR(flak, bomb_lifetime), true);
+ else
+ self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(flak, speed), WEP_CVAR_PRI(flak, speed_up), WEP_CVAR_PRI(flak, lifetime), true);
+ return true;
+ }
+ case WR_THINK:
+ {
+ if(self.BUTTON_ATCK)
+ if(weapon_prepareattack(0, WEP_CVAR_PRI(flak, refire)))
+ {
+ W_Flak_Attack1(true);
+ weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(flak, animtime), w_ready);
+ }
+
+ if(self.BUTTON_ATCK2)
+ if(weapon_prepareattack(1, WEP_CVAR_SEC(flak, refire)))
+ {
+ if(WEP_CVAR(flak, bomb))
+ W_Flak_Attack3();
+ else
+ W_Flak_Attack1(false);
+ weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(flak, animtime), w_ready);
+ }
+
+ return true;
+ }
+ case WR_INIT:
+ {
+ precache_model(W_Model("g_flak.md3"));
+ precache_model(W_Model("v_flak.md3"));
+ precache_model(W_Model("h_flak.iqm"));
+ precache_sound(W_Sound("flak_fire"));
+ precache_sound(W_Sound("flak_fire2"));
+ precache_sound(W_Sound("flak_fire3"));
+ precache_sound(W_Sound("casings1"));
+ precache_sound(W_Sound("casings2"));
+ precache_sound(W_Sound("casings3"));
+ FLAK_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
+ return true;
+ }
+ case WR_CONFIG:
+ {
+ FLAK_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
+ return true;
+ }
+ case WR_CHECKAMMO1:
+ {
+ ammo_amount = self.WEP_AMMO(FLAK) >= WEP_CVAR_PRI(flak, ammo);
+ ammo_amount += self.(weapon_load[WEP_FLAK]) >= WEP_CVAR_PRI(flak, ammo);
+ return ammo_amount;
+ }
+ case WR_CHECKAMMO2:
+ {
+ // can't use above logic here, as we have 2 ammo types - WEAPONTODO
+ return self.ammo_shells >= WEP_CVAR_SEC(flak, ammo) && (!WEP_CVAR(flak, bomb) || self.ammo_rockets >= WEP_CVAR(flak, bomb_ammo));
+ }
+ case WR_RELOAD:
+ {
+ W_Reload(min(WEP_CVAR_PRI(flak, ammo), WEP_CVAR_SEC(flak, ammo), WEP_CVAR(flak, bomb_ammo)), W_Sound("reload"));
+ return true;
+ }
+ case WR_SUICIDEMESSAGE:
+ {
+ return WEAPON_FLAK_SUICIDE;
+ }
+ case WR_KILLMESSAGE:
+ {
+ if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH))
+ return WEAPON_FLAK_MURDER_SPLASH;
+ else
+ return WEAPON_FLAK_MURDER_SPRAY;
+ }
+ }
+
+ return true;
+}
+#endif
+#ifdef CSQC
+.float last_bounce;
+void Flak_Touch(void)
+{
+ if(time >= self.last_bounce)
+ {
+ self.last_bounce = time + 0.1; // spam
+ pointparticles(particleeffectnum("flak_bounce"), self.move_origin, self.move_velocity, 1);
+ }
+}
+
+float W_Flak(float req)
+{
+ switch(req)
+ {
+ 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 false;
+}
+#endif
+#endif
--- /dev/null
- float deathtype = WEP_LIGHTSABRE;
+#ifdef CHAOS
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ LIGHTSABRE,
+/* function */ W_Lightsabre,
+/* ammotype */ ammo_none,
+/* impulse */ 1,
+/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE,
+/* rating */ BOT_PICKUP_RATING_LOW,
+/* color */ '1 0.25 0.25',
+/* modelname */ "lightsabre",
+/* simplemdl */ "foobar",
+/* crosshair */ "gfx/crosshairlightsabre 0.35",
+/* wepimg */ "weaponlightsabre",
+/* refname */ "lightsabre",
+/* wepname */ _("Lightsabre")
+);
+
+#define LIGHTSABRE_SETTINGS(w_cvar,w_prop) LIGHTSABRE_SETTINGS_LIST(w_cvar, w_prop, LIGHTSABRE, lightsabre)
+#define LIGHTSABRE_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
+ w_cvar(id, sn, BOTH, animtime) \
+ w_cvar(id, sn, BOTH, refire) \
+ w_cvar(id, sn, BOTH, damage) \
+ w_cvar(id, sn, BOTH, force) \
+ w_cvar(id, sn, BOTH, melee_time) \
+ w_cvar(id, sn, BOTH, melee_no_doubleslap) \
+ w_cvar(id, sn, BOTH, melee_traces) \
+ w_cvar(id, sn, BOTH, melee_swing_up) \
+ w_cvar(id, sn, BOTH, melee_swing_side) \
+ w_cvar(id, sn, BOTH, melee_nonplayerdamage) \
+ w_cvar(id, sn, BOTH, melee_multihit) \
+ w_cvar(id, sn, BOTH, melee_delay) \
+ w_cvar(id, sn, BOTH, melee_range) \
+ 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)
+
+#ifdef SVQC
+LIGHTSABRE_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
+#endif
+#else
+#ifdef SVQC
+#include "../effects.qh"
+#include "../animdecide.qh"
+
+void spawnfunc_weapon_lightsabre(void) { weapon_defaultspawnfunc(WEP_LIGHTSABRE); }
+
+.float swing_prev;
+.entity swing_alreadyhit;
+
+float W_Lightsabre_Melee_Block(entity player)
+{
+ if(!IS_PLAYER(player)) { return false; }
+
+ entity player_melee = world, e;
+ entity myowner = self.realowner;
+
+ for(e = world; (e = find(e, classname, "melee_temp")); )
+ if(e.realowner == player)
+ {
+ player_melee = e;
+ break;
+ }
+
+ if(!player_melee) { return false; }
+
+ makevectors (player.v_angle);
+ float dot = normalize (myowner.origin - player.origin) * v_forward;
+
+ if(dot <= 0.3) { return false; }
+
+ //if((myowner.v_angle_x - player.v_angle_x < 70) && (myowner.v_angle_x - player.v_angle_x > -70)) //Look up and down
+ //if((myowner.v_angle_y - player.v_angle_y > 160) || (myowner.v_angle_y - player.v_angle_y < -160)) //Side to side Facing eachother
+ // fun stuff
+
+ animdecide_setaction(myowner, ANIMACTION_SHOOT, 1);
+ string thesound = strcat("lightsabre_hit", ftos(max(1, floor(random() * 4))));
+ sound(myowner, CH_WEAPON_A, W_Sound(thesound), VOL_BASE, ATTEN_NORM);
+
+ return true;
+}
+
+void W_Lightsabre_Melee_Think(void)
+{
+ // declarations
+ float i, f, swing, swing_factor, swing_damage, meleetime, is_player;
+ entity target_victim;
+ vector targpos;
+ float isprimary = !(self.realowner.BUTTON_ATCK2);
++ int deathtype = WEP_LIGHTSABRE;
+ if(!isprimary)
+ deathtype |= HITTYPE_SECONDARY;
+
+ if(!self.cnt) // set start time of melee
+ {
+ self.cnt = time;
+ }
+
+ makevectors(self.realowner.v_angle); // update values for v_* vectors
+
+ // calculate swing percentage based on time
+ meleetime = WEP_CVAR_BOTH(lightsabre, isprimary, melee_time) * W_WeaponRateFactor();
+ swing = bound(0, (self.cnt + meleetime - time) / meleetime, 10);
+ f = ((1 - swing) * WEP_CVAR_BOTH(lightsabre, isprimary, melee_traces));
+
+ // check to see if we can still continue, otherwise give up now
+ if((self.realowner.deadflag != DEAD_NO) && WEP_CVAR_BOTH(lightsabre, isprimary, melee_no_doubleslap))
+ {
+ remove(self);
+ return;
+ }
+
+ // if okay, perform the traces needed for this frame
+ for(i=self.swing_prev; i < f; ++i)
+ {
+ swing_factor = ((1 - (i / WEP_CVAR_BOTH(lightsabre, isprimary, melee_traces))) * 2 - 1);
+
+ targpos = (self.realowner.origin + self.realowner.view_ofs
+ + (v_forward * WEP_CVAR_BOTH(lightsabre, isprimary, melee_range))
+ + (v_up * swing_factor * WEP_CVAR_BOTH(lightsabre, isprimary, melee_swing_up))
+ + (v_right * swing_factor * WEP_CVAR_BOTH(lightsabre, isprimary, melee_swing_side)));
+
+ WarpZone_traceline_antilag(self, self.realowner.origin + self.realowner.view_ofs, targpos, false, self.realowner, ANTILAG_LATENCY(self.realowner));
+
+ // draw lightning beams for debugging
+ //te_lightning2(world, targpos, self.realowner.origin + self.realowner.view_ofs + v_forward * 5 - v_up * 5);
+ //te_customflash(targpos, 40, 2, '1 1 1');
+
+ is_player = (IS_PLAYER(trace_ent) || trace_ent.classname == "body" || (trace_ent.flags & FL_MONSTER));
+
+ if((trace_fraction < 1) // if trace is good, apply the damage and remove self
+ && (trace_ent.takedamage == DAMAGE_AIM)
+ && (trace_ent != self.swing_alreadyhit)
+ && (is_player || WEP_CVAR_BOTH(lightsabre, isprimary, melee_nonplayerdamage)))
+ {
+ target_victim = trace_ent; // so it persists through other calls
+
+ if(!W_Lightsabre_Melee_Block(trace_ent))
+ {
+ if(is_player) // this allows us to be able to nerf the non-player damage done in e.g. assault or onslaught.
+ swing_damage = (WEP_CVAR_BOTH(lightsabre, isprimary, damage) * min(1, swing_factor + 1));
+ else
+ swing_damage = (WEP_CVAR_BOTH(lightsabre, isprimary, melee_nonplayerdamage) * min(1, swing_factor + 1));
+
+ //print(strcat(self.realowner.netname, " hitting ", target_victim.netname, " with ", strcat(ftos(swing_damage), " damage (factor: ", ftos(swing_factor), ") at "), ftos(time), " seconds.\n"));
+
+ Damage(target_victim, self.realowner, self.realowner,
+ swing_damage, deathtype,
+ self.realowner.origin + self.realowner.view_ofs,
+ v_forward * WEP_CVAR_BOTH(lightsabre, isprimary, force));
+
+ string thesound = strcat("lightsabre_hit", ftos(max(1, floor(random() * 4))));
+
+ sound(self.realowner, CH_WEAPON_A, W_Sound(thesound), VOL_BASE, ATTEN_NORM);
+
+ if(accuracy_isgooddamage(self.realowner, target_victim)) { accuracy_add(self.realowner, WEP_LIGHTSABRE, 0, swing_damage); }
+ }
+
+ // draw large red flash for debugging
+ //te_customflash(targpos, 200, 2, '15 0 0');
+
+ if(WEP_CVAR_BOTH(lightsabre, isprimary, melee_multihit)) // allow multiple hits with one swing, but not against the same player twice.
+ {
+ self.swing_alreadyhit = target_victim;
+ continue; // move along to next trace
+ }
+ else
+ {
+ remove(self);
+ return;
+ }
+ }
+ }
+
+ if(time >= self.cnt + meleetime)
+ {
+ // melee is finished
+ remove(self);
+ return;
+ }
+ else
+ {
+ // set up next frame
+ self.swing_prev = i;
+ self.nextthink = time;
+ }
+}
+
+void W_Lightsabre_Attack(void)
+{
+ float isprimary = !(self.BUTTON_ATCK2);
+
+ sound(self, CH_WEAPON_A, ((isprimary) ? W_Sound("lightsabre_melee2") : W_Sound("lightsabre_melee1")), VOL_BASE, ATTEN_NORM);
+ weapon_thinkf(((isprimary) ? WFRAME_FIRE2 : WFRAME_FIRE1), WEP_CVAR_BOTH(lightsabre, isprimary, animtime), w_ready);
+
+ entity meleetemp;
+ meleetemp = spawn();
+ meleetemp.classname = "melee_temp";
+ meleetemp.realowner = self;
+ meleetemp.think = W_Lightsabre_Melee_Think;
+ meleetemp.nextthink = time + WEP_CVAR_BOTH(lightsabre, isprimary, melee_delay) * W_WeaponRateFactor();
+ W_SetupShot_Range(self, true, 0, "", 0, WEP_CVAR_BOTH(lightsabre, isprimary, damage), WEP_CVAR_BOTH(lightsabre, isprimary, melee_range));
+}
+
+.float lightsabre_active;
+
+void W_LightSabre_SetActive(float newactive, float dosound)
+{
+ if(newactive)
+ {
+ self.lightsabre_active = true;
+ self.weaponname = "lightsabre_active";
+ if(dosound)
+ sound(self, CH_WEAPON_A, W_Sound("lightsabre_activate"), VOL_BASE, ATTEN_NORM);
+ }
+ else
+ {
+ self.lightsabre_active = false;
+ self.weaponname = "lightsabre";
+ if(dosound)
+ sound(self, CH_WEAPON_A, W_Sound("lightsabre_deactivate"), VOL_BASE, ATTEN_NORM);
+ }
+}
+
+float W_Lightsabre(float req)
+{
+ switch(req)
+ {
+ case WR_AIM:
+ {
+ if(random() >= 0.5)
+ self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, false);
+ else
+ self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false);
+
+ return true;
+ }
+ case WR_THINK:
+ {
+ if(self.BUTTON_ATCK || self.BUTTON_ATCK2)
+ if(weapon_prepareattack(self.BUTTON_ATCK2, WEP_CVAR_BOTH(lightsabre, self.BUTTON_ATCK2, refire)))
+ {
+ if(!self.lightsabre_active)
+ {
+ W_LightSabre_SetActive(1, true);
+ weapon_thinkf(WFRAME_RELOAD, WEP_CVAR_BOTH(lightsabre, self.BUTTON_ATCK2, animtime), w_ready);
+ }
+ else
+ weapon_thinkf(WFRAME_FIRE1, 0, W_Lightsabre_Attack);
+ }
+
+ return true;
+ }
+ case WR_INIT:
+ {
+ precache_model("models/uziflash.md3");
+ precache_model(W_Model("g_lightsabre.md3"));
+ precache_model(W_Model("v_lightsabre.md3"));
+ precache_model(W_Model("h_lightsabre.iqm"));
+ precache_model(W_Model("v_lightsabre_active.md3"));
+ precache_model(W_Model("h_lightsabre_active.iqm"));
+ precache_sound("misc/itempickup.wav");
+ precache_sound(W_Sound("lightsabre_melee1"));
+ precache_sound(W_Sound("lightsabre_melee2"));
+ precache_sound(W_Sound("lightsabre_activate"));
+ precache_sound(W_Sound("lightsabre_deactivate"));
+ precache_sound(W_Sound("lightsabre_hit1"));
+ precache_sound(W_Sound("lightsabre_hit2"));
+ precache_sound(W_Sound("lightsabre_hit3"));
+ LIGHTSABRE_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP)
+ return true;
+ }
+ case WR_SETUP:
+ {
+ self.ammo_field = ammo_none;
+ W_LightSabre_SetActive(0, false);
+ return true;
+ }
+ case WR_CHECKAMMO1:
+ {
+ return true;
+ }
+ case WR_CHECKAMMO2:
+ {
+ return true;
+ }
+ case WR_CONFIG:
+ {
+ LIGHTSABRE_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS)
+ return true;
+ }
+ case WR_RELOAD:
+ {
+ W_LightSabre_SetActive(((self.lightsabre_active) ? 0 : 1), true);
+ return true;
+ }
+ case WR_SUICIDEMESSAGE:
+ {
+ return WEAPON_THINKING_WITH_PORTALS;
+ }
+ case WR_KILLMESSAGE:
+ {
+ return WEAPON_LIGHTSABRE_MURDER;
+ }
+ }
+ return false;
+}
+#endif
+#ifdef CSQC
+.float prevric;
+float W_Lightsabre(float req)
+{
+ switch(req)
+ {
+ case WR_IMPACTEFFECT:
+ {
+ return true;
+ }
+ case WR_INIT:
+ {
+ return true;
+ }
+ case WR_ZOOMRETICLE:
+ {
+ // no weapon specific image for this weapon
+ return false;
+ }
+ }
+ return false;
+}
+#endif
+#endif
+#endif
self.muzzle_flash.owner = self.muzzle_flash.realowner = self;
}
- void W_MachineGun_Attack(float deathtype)
+ void W_MachineGun_Attack(int deathtype)
{
- W_SetupShot(self, true, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, ((self.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage)));
+ W_SetupShot(self, true, 0, W_Sound("uzi_fire"), CH_WEAPON_A, ((self.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage)));
if(!autocvar_g_norecoil)
{
self.punchangle_x = random() - 0.5;
#endif
#else
#ifdef SVQC
+#include "../effects.qh"
+
void spawnfunc_weapon_tuba(void) { weapon_defaultspawnfunc(WEP_TUBA); }
- float W_Tuba_HasPlayed(entity pl, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo)
+ bool W_Tuba_HasPlayed(entity pl, string melody, int instrument, bool ignorepitch, float mintempo, float maxtempo)
{
float i, j, mmin, mmax, nolength;
float n = tokenize_console(melody);
W_DecreaseAmmo(((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)));
}
- void W_RocketMinsta_SuperRocket_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+void W_Vaporizer_SuperBlast()
+{
+ // declarations
+ float multiplier, multiplier_from_accuracy, multiplier_from_distance;
+ float final_damage; //, final_spread;
+ vector final_force, center, vel;
+ entity head, next;
+
+ float i, queue = 0;
+
+ // set up the shot direction
+ W_SetupShot(self, false, 3, W_Sound("minstanex_charge2"), CH_WEAPON_B, WEP_CVAR_PRI(vaporizer, damage));
+ vector attack_endpos = (w_shotorg + (w_shotdir * WEP_CVAR_PRI(vaporizer, charge_distance)));
+ WarpZone_TraceLine(w_shotorg, attack_endpos, MOVE_NOMONSTERS, self);
+ vector attack_hitpos = trace_endpos;
+ float distance_to_end = vlen(w_shotorg - attack_endpos);
+ float distance_to_hit = vlen(w_shotorg - attack_hitpos);
+ //entity transform = WarpZone_trace_transform;
+
+ // do the firing effect now
+ SendCSQCSuperBlastParticle(attack_endpos);
+ Damage_DamageInfo(attack_hitpos, WEP_CVAR_PRI(vaporizer, charge_splash_damage), WEP_CVAR_PRI(vaporizer, charge_splash_edgedamage), WEP_CVAR_PRI(vaporizer, charge_splash_radius), w_shotdir * WEP_CVAR_PRI(vaporizer, charge_splash_force), (WEP_VAPORIZER | HITTYPE_SPLASH), 0, self);
+
+ // splash damage/jumping trace
+ head = WarpZone_FindRadius(attack_hitpos, max(WEP_CVAR_PRI(vaporizer, charge_splash_radius), WEP_CVAR_PRI(vaporizer, charge_jump_radius)), false);
+ while(head)
+ {
+ next = head.chain;
+
+ if(head.takedamage)
+ {
+ // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
+ center = CENTER_OR_VIEWOFS(head);
+
+ float distance_to_head = vlen(attack_hitpos - head.WarpZone_findradius_nearest);
+
+ if((head == self) && (distance_to_head <= WEP_CVAR_PRI(vaporizer, charge_jump_radius)))
+ {
+ multiplier_from_accuracy = (1 - (distance_to_head ? min(1, (distance_to_head / WEP_CVAR_PRI(vaporizer, charge_jump_radius))) : 0));
+ multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_hit / distance_to_end)) : 0));
+ multiplier = max(WEP_CVAR_PRI(vaporizer, charge_jump_multiplier_min), ((multiplier_from_accuracy * WEP_CVAR_PRI(vaporizer, charge_jump_multiplier_accuracy)) + (multiplier_from_distance * WEP_CVAR_PRI(vaporizer, charge_jump_multiplier_distance))));
+
+ final_force = ((normalize(center - attack_hitpos) * WEP_CVAR_PRI(vaporizer, charge_jump_force)) * multiplier);
+ vel = head.velocity; vel_z = 0;
+ vel = normalize(vel) * bound(0, vlen(vel) / autocvar_sv_maxspeed, 1) * WEP_CVAR_PRI(vaporizer, charge_jump_force_velocitybias);
+ final_force = (vlen(final_force) * normalize(normalize(final_force) + vel));
+ final_force_z *= WEP_CVAR_PRI(vaporizer, charge_jump_force_zscale);
+ final_damage = (WEP_CVAR_PRI(vaporizer, charge_jump_damage) * multiplier + WEP_CVAR_PRI(vaporizer, charge_jump_edgedamage) * (1 - multiplier));
+
+ Damage(head, self, self, final_damage, (WEP_VAPORIZER | HITTYPE_SPLASH), head.origin, final_force);
+ //print("SELF HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n");
+ }
+ else if (distance_to_head <= WEP_CVAR_PRI(vaporizer, charge_splash_radius))
+ {
+ multiplier_from_accuracy = (1 - (distance_to_head ? min(1, (distance_to_head / WEP_CVAR_PRI(vaporizer, charge_splash_radius))) : 0));
+ multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_hit / distance_to_end)) : 0));
+ multiplier = max(WEP_CVAR_PRI(vaporizer, charge_splash_multiplier_min), ((multiplier_from_accuracy * WEP_CVAR_PRI(vaporizer, charge_splash_multiplier_accuracy)) + (multiplier_from_distance * WEP_CVAR_PRI(vaporizer, charge_splash_multiplier_distance))));
+
+ final_force = normalize(center - (attack_hitpos - (w_shotdir * WEP_CVAR_PRI(vaporizer, charge_splash_force_forwardbias))));
+ //te_lightning2(world, attack_hitpos, (attack_hitpos + (final_force * 200)));
+ final_force = ((final_force * WEP_CVAR_PRI(vaporizer, charge_splash_force)) * multiplier);
+ final_force_z *= WEP_CVAR_PRI(vaporizer, charge_splash_force_zscale);
+ final_damage = (WEP_CVAR_PRI(vaporizer, charge_splash_damage) * multiplier + WEP_CVAR_PRI(vaporizer, charge_splash_edgedamage) * (1 - multiplier));
+
+ if(W_Vaporizer_SuperBlast_CheckHit(queue, head, final_force, final_damage)) { ++queue; }
+ //print("SPLASH HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n");
+ }
+ }
+ head = next;
+ }
+
+ // cone damage trace
+ head = WarpZone_FindRadius(w_shotorg, WEP_CVAR_PRI(vaporizer, charge_distance), false);
+ while(head)
+ {
+ next = head.chain;
+
+ if((head != self) && head.takedamage)
+ {
+ // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
+ center = CENTER_OR_VIEWOFS(head);
+
+ // find the closest point on the enemy to the center of the attack
+ float h; // hypotenuse, which is the distance between attacker to head
+ float a; // adjacent side, which is the distance between attacker and the point on w_shotdir that is closest to head.origin
+
+ h = vlen(center - self.origin);
+ a = h * (normalize(center - self.origin) * w_shotdir);
+
+ vector nearest_on_line = (w_shotorg + a * w_shotdir);
+ vector nearest_to_attacker = WarpZoneLib_NearestPointOnBox(center + head.mins, center + head.maxs, nearest_on_line);
+ float distance_to_target = vlen(w_shotorg - nearest_to_attacker); // todo: use the findradius function for this
+
+ if((distance_to_target <= WEP_CVAR_PRI(vaporizer, charge_distance))
+ && (W_Vaporizer_SuperBlast_IsVisible(head, nearest_on_line, w_shotorg, attack_endpos)))
+ {
+ multiplier_from_accuracy = (1 - W_Vaporizer_SuperBlast_CheckSpread(nearest_to_attacker, nearest_on_line, w_shotorg, attack_endpos));
+ multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_target / distance_to_end)) : 0));
+ multiplier = max(WEP_CVAR_PRI(vaporizer, charge_multiplier_min), ((multiplier_from_accuracy * WEP_CVAR_PRI(vaporizer, charge_multiplier_accuracy)) + (multiplier_from_distance * WEP_CVAR_PRI(vaporizer, charge_multiplier_distance))));
+
+ final_force = normalize(center - (nearest_on_line - (w_shotdir * WEP_CVAR_PRI(vaporizer, charge_force_forwardbias))));
+ //te_lightning2(world, nearest_on_line, (attack_hitpos + (final_force * 200)));
+ final_force = ((final_force * WEP_CVAR_PRI(vaporizer, charge_force)) * multiplier);
+ final_force_z *= WEP_CVAR_PRI(vaporizer, charge_force_zscale);
+ final_damage = (WEP_CVAR_PRI(vaporizer, damage) * multiplier + WEP_CVAR_PRI(vaporizer, damage) * (1 - multiplier));
+
+ if(W_Vaporizer_SuperBlast_CheckHit(queue, head, final_force, final_damage)) { ++queue; }
+ //print("CONE HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n");
+ }
+ }
+ head = next;
+ }
+
+ for(i = 1; i <= queue; ++i)
+ {
+ head = superblast_hit[i];
+ final_force = superblast_hit_force[i];
+ final_damage = superblast_hit_damage[i];
+
+ Damage(head, self, self, final_damage, (WEP_VAPORIZER | HITTYPE_SPLASH), head.origin, final_force);
+
+ if(accuracy_isgooddamage(self, head))
+ accuracy_add(self, WEP_VAPORIZER, 1, final_damage);
+ //print("SHOCKWAVE by ", self.netname, ": damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force)), ".\n");
+
+ superblast_hit[i] = world;
+ superblast_hit_force[i] = '0 0 0';
+ superblast_hit_damage[i] = 0;
+ }
+ //print("queue was ", ftos(queue), ".\n\n");
+
+ self.ammo_supercells -= 1;
+}
+
+float W_Vaporizer_SuperBlast_CheckAmmo()
+{
+ if(WEP_CVAR_PRI(vaporizer, charge) == 3)
+ return true; // forced
+ float ammo_amount;
+ ammo_amount = self.ammo_supercells >= 1;
+ ammo_amount += self.(weapon_load[WEP_VAPORIZER]) >= 1;
+ return ammo_amount;
+}
+
+void W_RocketMinsta_SuperRocket_Explode()
+{
+ self.event_damage = func_null;
+ self.takedamage = DAMAGE_NO;
+
+ RadiusDamage (self, self.realowner, 250, 250, 250, world, world, 250, self.projectiledeathtype, other);
+
+ remove (self);
+}
+
+void W_RocketMinsta_SuperRocket_Think (void)
+{
+ self.nextthink = time;
+ makevectors(self.angles);
+ self.velocity = v_forward * autocvar_g_rm_superrocket_speed;
+ if (time > self.cnt)
+ {
+ other = world;
+ self.projectiledeathtype |= HITTYPE_BOUNCE;
+ W_RocketMinsta_SuperRocket_Explode ();
+ return;
+ }
+}
+
+void W_RocketMinsta_SuperRocket_Touch (void)
+{
+ if(other.takedamage == DAMAGE_AIM && other.classname != "object" && time > other.superrocket_lasthit)
+ {
+ float vaporizer_damage = ((WEP_CVAR_PRI(vaporizer, damage)) ? WEP_CVAR_PRI(vaporizer, damage) : 10000);
+ Damage(other, self, self.realowner, vaporizer_damage, WEP_DEVASTATOR, other.origin, '0 0 0');
+ other.superrocket_lasthit = time + 1;
+ return;
+ }
+ W_RocketMinsta_SuperRocket_Explode();
+}
+
++void W_RocketMinsta_SuperRocket_Damage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+ if (self.health <= 0)
+ return;
+
+ if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
+ return; // g_projectiles_damage says to halt
+
+ self.health = self.health - damage;
+ self.angles = vectoangles(self.velocity);
+
+ if (self.health <= 0)
+ W_PrepareExplosionByDamage(attacker, W_RocketMinsta_SuperRocket_Explode);
+}
+
+void W_RocketMinsta_SuperRocket()
+{
+ entity missile;
+ float vaporizer_damage = ((WEP_CVAR_PRI(vaporizer, damage)) ? WEP_CVAR_PRI(vaporizer, damage) : 10000);
+
+ makevectors(self.angles);
+
+ W_SetupShot_ProjectileSize (self, '-32 -32 -32', '32 32 32', false, 5, W_Sound("minstanex_charge2"), CH_WEAPON_A, vaporizer_damage);
+ Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
+
+ missile = WarpZone_RefSys_SpawnSameRefSys(self);
+ missile.owner = missile.realowner = self;
+ missile.classname = "rocket";
+ missile.bot_dodge = true;
+ missile.bot_dodgerating = vaporizer_damage;
+
+ missile.takedamage = DAMAGE_YES;
+ missile.damageforcescale = 0;
+ missile.health = 350;
+ missile.event_damage = W_RocketMinsta_SuperRocket_Damage;
+ missile.damagedbycontents = true;
+
+ missile.movetype = MOVETYPE_FLYMISSILE;
+ PROJECTILE_MAKETRIGGER(missile);
+ missile.projectiledeathtype = WEP_DEVASTATOR;
+ setsize (missile, '-32 -32 -32', '32 32 32'); // 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_SetupProjVelocity_Basic(missile, autocvar_g_rm_superrocket_speed, 0);
+ missile.angles = self.v_angle;
+
+ missile.touch = W_RocketMinsta_SuperRocket_Touch;
+ missile.think = W_RocketMinsta_SuperRocket_Think;
+ missile.nextthink = time;
+ missile.cnt = time + 10;
+ missile.flags = FL_PROJECTILE;
+ //missile.missile_flags = MIF_SPLASH;
+
+ CSQCProjectile(missile, true, PROJECTILE_SUPERROCKET, false); // because of fly sound
+
+ self.ammo_supercells -= 1;
+}
+
+void W_RocketMinsta_Laser_Explode (void)
+{
+ 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_ELECTROBITCH);
+
+ self.event_damage = func_null;
+ self.takedamage = DAMAGE_NO;
+ RadiusDamage (self, self.realowner, self.rm_damage, self.rm_edmg, autocvar_g_rm_laser_radius, world, world, self.rm_force, self.projectiledeathtype, other);
+ remove(self);
+}
+
+void W_RocketMinsta_Laser_Touch (void)
+{
+ PROJECTILE_TOUCH;
+ //W_RocketMinsta_Laser_Explode ();
+ RadiusDamage (self, self.realowner, self.rm_damage, self.rm_edmg, autocvar_g_rm_laser_radius, world, world, self.rm_force, self.projectiledeathtype, other);
+ remove(self);
+}
+
+void W_RocketMinsta_Attack2(void)
+{
+ makevectors(self.v_angle);
+
+ entity proj;
+ float counter = 0;
+ float total = autocvar_g_rm_laser_count;
+ float spread = autocvar_g_rm_laser_spread;
+ float rndspread = autocvar_g_rm_laser_spread_random;
+
+ float w = self.weapon;
+ self.weapon = WEP_ELECTRO;
+ W_SetupShot_ProjectileSize (self, '0 0 -3', '0 0 -3', false, 2, W_Sound("crylink_fire"), CH_WEAPON_A, autocvar_g_rm_laser_damage);
+ self.weapon = w;
+
+ Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
+
+ while(counter < total)
+ {
+ proj = spawn ();
+ proj.classname = "plasma_prim";
+ proj.owner = proj.realowner = self;
+ proj.bot_dodge = true;
+ proj.bot_dodgerating = autocvar_g_rm_laser_damage;
+ proj.use = W_RocketMinsta_Laser_Explode;
+ proj.think = adaptor_think2use_hittype_splash;
+ proj.nextthink = time + autocvar_g_rm_laser_lifetime;
+ PROJECTILE_MAKETRIGGER(proj);
+ proj.projectiledeathtype = WEP_ELECTRO;
+ setorigin(proj, w_shotorg);
+
+ proj.rm_force = autocvar_g_rm_laser_force / total;
+ proj.rm_damage = autocvar_g_rm_laser_damage / total;
+ proj.rm_edmg = proj.rm_damage;
+
+ //W_SetupProjectileVelocity(proj, autocvar_g_rm_laser_speed, spread * (rndspread ? random() : 1) * autocvar_g_rm_laser_speed);
+
+ proj.movetype = MOVETYPE_BOUNCEMISSILE;
+ //W_SETUPPROJECTILEVELOCITY(proj, g_balance_minstanex_laser);
+ proj.velocity = (w_shotdir + (((counter + 0.5) / total) * 2 - 1) * v_right * (spread * (rndspread ? random() : 1))) * cvar("g_rm_laser_speed");
+ proj.velocity_z = proj.velocity_z + cvar("g_rm_laser_zspread") * (random() - 0.5);
+ proj.velocity = W_CalculateProjectileVelocity(proj.realowner.velocity, proj.velocity, true);
+ proj.angles = vectoangles(proj.velocity);
+ proj.touch = W_RocketMinsta_Laser_Touch;
+ setsize(proj, '0 0 -3', '0 0 -3');
+ proj.flags = FL_PROJECTILE;
+ proj.missile_flags = MIF_SPLASH;
+
+ CSQCProjectile(proj, true, PROJECTILE_ROCKETMINSTA_LASER, true);
+
+ other = proj; MUTATOR_CALLHOOK(EditProjectile);
+ counter++;
+ }
+}
+
+void W_RocketMinsta_Attack3 (void)
+{
+ makevectors(self.v_angle);
+
+ entity proj;
+ float counter = 0;
+ float total = 1;
+
+ float w = self.weapon;
+ self.weapon = WEP_ELECTRO;
+ W_SetupShot_ProjectileSize (self, '0 0 -3', '0 0 -3', false, 2, W_Sound("electro_fire2"), CH_WEAPON_A, autocvar_g_rm_laser_damage);
+ self.weapon = w;
+
+ Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
+
+ while(counter < total)
+ {
+ proj = spawn ();
+ proj.classname = "plasma_prim";
+ proj.owner = proj.realowner = self;
+ proj.bot_dodge = true;
+ proj.bot_dodgerating = autocvar_g_rm_laser_damage;
+ proj.use = W_RocketMinsta_Laser_Explode;
+ proj.think = adaptor_think2use_hittype_splash;
+ proj.nextthink = time + autocvar_g_rm_laser_lifetime;
+ PROJECTILE_MAKETRIGGER(proj);
+ proj.projectiledeathtype = WEP_ELECTRO;
+ setorigin(proj, w_shotorg);
+
+ proj.rm_force = autocvar_g_rm_laser_force / total;
+ proj.rm_damage = autocvar_g_rm_laser_damage / total;
+ proj.rm_edmg = proj.rm_damage;
+
+ //W_SetupProjectileVelocity(proj, autocvar_g_rm_laser_speed, spread * (rndspread ? random() : 1) * autocvar_g_rm_laser_speed);
+
+ proj.movetype = MOVETYPE_BOUNCEMISSILE;
+ proj.velocity = w_shotdir * autocvar_g_rm_laser_speed;
+ proj.velocity = W_CalculateProjectileVelocity(proj.realowner.velocity, proj.velocity, true);
+ proj.angles = vectoangles(proj.velocity);
+ proj.touch = W_RocketMinsta_Laser_Touch;
+ setsize(proj, '0 0 -3', '0 0 -3');
+ proj.flags = FL_PROJECTILE;
+ proj.missile_flags = MIF_SPLASH;
+
+ CSQCProjectile(proj, true, PROJECTILE_ROCKETMINSTA_LASER, true);
+
+ other = proj; MUTATOR_CALLHOOK(EditProjectile);
+ counter++;
+ }
+}
+
float W_Vaporizer(float req)
{
float ammo_amount;
float autocvar_g_ban_sync_interval;
float autocvar_g_ban_sync_timeout;
string autocvar_g_ban_sync_trusted_servers;
- float autocvar_g_ban_sync_trusted_servers_verify;
+ bool autocvar_g_ban_sync_trusted_servers_verify;
string autocvar_g_ban_sync_uri;
string autocvar_g_banned_list;
- float autocvar_g_banned_list_idmode;
- float autocvar_g_botclip_collisions;
- float autocvar_g_bugrigs;
+ bool autocvar_g_banned_list_idmode;
-bool autocvar_g_bastet;
+ bool autocvar_g_botclip_collisions;
+ bool autocvar_g_bugrigs;
float autocvar_g_ca_damage2score_multiplier;
- float autocvar_g_ca_point_leadlimit;
- float autocvar_g_ca_point_limit;
+ int autocvar_g_ca_point_leadlimit;
+ int autocvar_g_ca_point_limit;
float autocvar_g_ca_round_timelimit;
- float autocvar_g_ca_spectate_enemies;
- float autocvar_g_ca_teams;
- float autocvar_g_ca_teams_override;
- float autocvar_g_ca_team_spawns;
+ bool autocvar_g_ca_spectate_enemies;
+ int autocvar_g_ca_teams;
+ int autocvar_g_ca_teams_override;
+ bool autocvar_g_ca_team_spawns;
float autocvar_g_ca_warmup;
- float autocvar_g_campaign;
+ bool autocvar_g_campaign;
#define autocvar_g_campaign_forceteam cvar("g_campaign_forceteam")
- float autocvar_g_campaign_skill;
- float autocvar_g_casings;
- float autocvar_g_changeteam_banned;
+ int autocvar_g_campaign_skill;
+ int autocvar_g_casings;
+ bool autocvar_g_changeteam_banned;
float autocvar_g_chat_flood_burst;
float autocvar_g_chat_flood_burst_team;
float autocvar_g_chat_flood_burst_tell;
float autocvar_g_ctf_throw_velocity_up;
float autocvar_g_ctf_drop_velocity_up;
float autocvar_g_ctf_drop_velocity_side;
- float autocvar_g_ctf_oneflag_reverse;
- float autocvar_g_ctf_portalteleport;
- float autocvar_g_ctf_pass;
++bool autocvar_g_ctf_oneflag_reverse;
+ bool autocvar_g_ctf_portalteleport;
+ bool autocvar_g_ctf_pass;
float autocvar_g_ctf_pass_arc;
float autocvar_g_ctf_pass_arc_max;
float autocvar_g_ctf_pass_directional_max;
float autocvar_g_ctf_pass_turnrate;
float autocvar_g_ctf_pass_timelimit;
float autocvar_g_ctf_pass_velocity;
- float autocvar_g_ctf_dynamiclights;
+ bool autocvar_g_ctf_dynamiclights;
string autocvar_g_ctf_flag_blue_model;
- float autocvar_g_ctf_flag_blue_skin;
+ int autocvar_g_ctf_flag_blue_skin;
float autocvar_g_ctf_flag_collect_delay;
float autocvar_g_ctf_flag_damageforcescale;
- float autocvar_g_ctf_flag_dropped_waypoint;
+ int autocvar_g_ctf_flag_dropped_waypoint;
float autocvar_g_ctf_flag_dropped_floatinwater;
- float autocvar_g_ctf_flag_glowtrails;
+ bool autocvar_g_ctf_flag_glowtrails;
float autocvar_g_ctf_flag_health;
+string autocvar_g_ctf_flag_neutral_model;
+float autocvar_g_ctf_flag_neutral_skin;
+string autocvar_g_ctf_flag_pink_model;
+float autocvar_g_ctf_flag_pink_skin;
string autocvar_g_ctf_flag_red_model;
- float autocvar_g_ctf_flag_red_skin;
- float autocvar_g_ctf_flag_return;
+ int autocvar_g_ctf_flag_red_skin;
++bool autocvar_g_ctf_flag_return;
+float autocvar_g_ctf_flag_return_carried;
float autocvar_g_ctf_flag_return_time;
- float autocvar_g_ctf_flag_return_when_unreachable;
+ bool autocvar_g_ctf_flag_return_when_unreachable;
float autocvar_g_ctf_flag_return_damage;
+float autocvar_g_ctf_flag_return_damage_delay;
float autocvar_g_ctf_flag_return_dropped;
+string autocvar_g_ctf_flag_yellow_model;
+float autocvar_g_ctf_flag_yellow_skin;
float autocvar_g_ctf_flagcarrier_auto_helpme_damage;
float autocvar_g_ctf_flagcarrier_auto_helpme_time;
float autocvar_g_ctf_flagcarrier_selfdamagefactor;
float autocvar_g_domination_warmup;
#define autocvar_g_domination_point_limit cvar("g_domination_point_limit")
float autocvar_g_domination_point_rate;
- float autocvar_g_domination_teams_override;
+ int autocvar_g_domination_teams_override;
+float autocvar_g_domination_controlpoint_unlock_speed;
+float autocvar_g_domination_controlpoint_unlock_damage_pushback;
+float autocvar_g_domination_controlpoint_idletime_neutral_initial;
+float autocvar_g_domination_controlpoint_idletime_initial;
+float autocvar_g_domination_controlpoint_idletime_neutral;
+float autocvar_g_domination_controlpoint_idletime_neutral_power;
+float autocvar_g_domination_controlpoint_idletime_neutral_factor;
+float autocvar_g_domination_controlpoint_idletime;
+float autocvar_g_domination_controlpoint_idletime_power;
+float autocvar_g_domination_controlpoint_idletime_factor;
- float autocvar_g_forced_respawn;
+ bool autocvar_g_forced_respawn;
string autocvar_g_forced_team_blue;
string autocvar_g_forced_team_otherwise;
string autocvar_g_forced_team_pink;
string autocvar_g_forced_team_red;
string autocvar_g_forced_team_yellow;
-bool autocvar_g_freezetag_frozen_damage_trigger;
-float autocvar_g_freezetag_frozen_force;
float autocvar_g_freezetag_frozen_maxtime;
- float autocvar_g_freezetag_point_leadlimit;
- float autocvar_g_freezetag_point_limit;
-float autocvar_g_freezetag_revive_falldamage;
-float autocvar_g_freezetag_revive_falldamage_health;
-bool autocvar_g_freezetag_revive_nade;
-float autocvar_g_freezetag_revive_nade_health;
+ int autocvar_g_freezetag_point_leadlimit;
+ int autocvar_g_freezetag_point_limit;
float autocvar_g_freezetag_revive_extra_size;
+var float autocvar_g_freezetag_revive_health = 100;
float autocvar_g_freezetag_revive_speed;
float autocvar_g_freezetag_revive_clearspeed;
float autocvar_g_freezetag_round_timelimit;
#define autocvar_g_friendlyfire cvar("g_friendlyfire")
#define autocvar_g_friendlyfire_virtual cvar("g_friendlyfire_virtual")
#define autocvar_g_friendlyfire_virtual_force cvar("g_friendlyfire_virtual_force")
- float autocvar_g_full_getstatus_responses;
- float autocvar_g_fullbrightitems;
- float autocvar_g_fullbrightplayers;
+ bool autocvar_g_full_getstatus_responses;
+ bool autocvar_g_fullbrightitems;
+ bool autocvar_g_fullbrightplayers;
#define autocvar_g_grappling_hook cvar("g_grappling_hook")
- float autocvar_g_grappling_hook_tarzan;
- float autocvar_g_hitplots;
+ int autocvar_g_grappling_hook_tarzan;
+ bool autocvar_g_hitplots;
string autocvar_g_hitplots_individuals;
+float autocvar_g_jetpack_reverse_thrust;
float autocvar_g_jetpack_acceleration_side;
float autocvar_g_jetpack_acceleration_up;
float autocvar_g_jetpack_antigravity;
- float autocvar_g_jetpack_fuel;
+ int autocvar_g_jetpack_fuel;
float autocvar_g_jetpack_maxspeed_side;
float autocvar_g_jetpack_maxspeed_up;
- float autocvar_g_keepaway_point_limit;
- float autocvar_g_keepaway_ballcarrier_effects;
++int autocvar_g_keepaway_point_limit;
+ int autocvar_g_keepaway_ballcarrier_effects;
float autocvar_g_keepaway_ballcarrier_damage;
float autocvar_g_keepaway_ballcarrier_force;
float autocvar_g_keepaway_ballcarrier_highspeed;
float autocvar_g_keepaway_noncarrier_force;
float autocvar_g_keepaway_noncarrier_selfdamage;
float autocvar_g_keepaway_noncarrier_selfforce;
- float autocvar_g_keepaway_noncarrier_warn;
- float autocvar_g_keepaway_score_bckill;
- float autocvar_g_keepaway_score_killac;
- float autocvar_g_keepaway_score_timepoints;
+ bool autocvar_g_keepaway_noncarrier_warn;
+ int autocvar_g_keepaway_score_bckill;
+ int autocvar_g_keepaway_score_killac;
+ int autocvar_g_keepaway_score_timepoints;
float autocvar_g_keepaway_score_timeinterval;
float autocvar_g_keepawayball_damageforcescale;
- float autocvar_g_keepawayball_effects;
+ int autocvar_g_keepawayball_effects;
float autocvar_g_keepawayball_respawntime;
- float autocvar_g_keepawayball_trail_color;
- float autocvar_g_keyhunt_allow_vehicle_carry;
- float autocvar_g_keyhunt_allow_vehicle_touch;
+ int autocvar_g_keepawayball_trail_color;
++bool autocvar_g_keyhunt_allow_vehicle_carry;
++bool autocvar_g_keyhunt_allow_vehicle_touch;
+float autocvar_g_keyhunt_capture_radius;
- float autocvar_g_keyhunt_throw;
++bool autocvar_g_keyhunt_throw;
+float autocvar_g_keyhunt_throw_angle_max;
+float autocvar_g_keyhunt_throw_angle_min;
+float autocvar_g_keyhunt_throw_punish_count;
+float autocvar_g_keyhunt_throw_punish_delay;
+float autocvar_g_keyhunt_throw_punish_time;
+float autocvar_g_keyhunt_throw_velocity_forward;
+float autocvar_g_keyhunt_throw_velocity_up;
+float autocvar_g_keyhunt_drop_velocity_up;
+float autocvar_g_keyhunt_drop_velocity_side;
- float autocvar_g_keyhunt_portalteleport;
- float autocvar_g_keyhunt_pass;
++bool autocvar_g_keyhunt_portalteleport;
++bool autocvar_g_keyhunt_pass;
+float autocvar_g_keyhunt_pass_arc;
+float autocvar_g_keyhunt_pass_arc_max;
+float autocvar_g_keyhunt_pass_directional_max;
+float autocvar_g_keyhunt_pass_directional_min;
+float autocvar_g_keyhunt_pass_radius;
+float autocvar_g_keyhunt_pass_wait;
+float autocvar_g_keyhunt_pass_request;
+float autocvar_g_keyhunt_pass_turnrate;
+float autocvar_g_keyhunt_pass_timelimit;
+float autocvar_g_keyhunt_pass_velocity;
- float autocvar_g_keyhunt_dynamiclights;
++bool autocvar_g_keyhunt_dynamiclights;
+float autocvar_g_keyhunt_key_collect_delay;
+float autocvar_g_keyhunt_key_damageforcescale;
- float autocvar_g_keyhunt_key_dropped_waypoint;
++int autocvar_g_keyhunt_key_dropped_waypoint;
+float autocvar_g_keyhunt_key_dropped_floatinwater;
- float autocvar_g_keyhunt_key_glowtrails;
++bool autocvar_g_keyhunt_key_glowtrails;
+float autocvar_g_keyhunt_key_health;
+float autocvar_g_keyhunt_key_return_time;
- float autocvar_g_keyhunt_key_return_tokiller;
- float autocvar_g_keyhunt_key_return_toenemy;
- float autocvar_g_keyhunt_key_return_when_unreachable;
- float autocvar_g_keyhunt_key_return_damage;
++bool autocvar_g_keyhunt_key_return_tokiller;
++bool autocvar_g_keyhunt_key_return_toenemy;
++bool autocvar_g_keyhunt_key_return_when_unreachable;
++bool autocvar_g_keyhunt_key_return_damage;
+float autocvar_g_keyhunt_keycarrier_auto_helpme_damage;
+float autocvar_g_keyhunt_keycarrier_auto_helpme_time;
+float autocvar_g_keyhunt_keycarrier_selfdamagefactor;
+float autocvar_g_keyhunt_keycarrier_selfforcefactor;
+float autocvar_g_keyhunt_keycarrier_damagefactor;
+float autocvar_g_keyhunt_keycarrier_forcefactor;
- float autocvar_g_keyhunt_fullbrightkeys;
- float autocvar_g_keyhunt_ignore_frags;
- float autocvar_g_keyhunt_score_capture;
- float autocvar_g_keyhunt_score_capture_assist;
- float autocvar_g_keyhunt_score_kill;
- float autocvar_g_keyhunt_score_penalty_drop;
- float autocvar_g_keyhunt_score_pickup_dropped_early;
- float autocvar_g_keyhunt_score_pickup_dropped_late;
- float autocvar_g_keyhunt_team_spawns;
- float autocvar_g_keyhunt_point_leadlimit;
++bool autocvar_g_keyhunt_fullbrightkeys;
++bool autocvar_g_keyhunt_ignore_frags;
++int autocvar_g_keyhunt_score_capture;
++int autocvar_g_keyhunt_score_capture_assist;
++int autocvar_g_keyhunt_score_kill;
++int autocvar_g_keyhunt_score_penalty_drop;
++int autocvar_g_keyhunt_score_pickup_dropped_early;
++int autocvar_g_keyhunt_score_pickup_dropped_late;
++bool autocvar_g_keyhunt_team_spawns;
+ int autocvar_g_keyhunt_point_leadlimit;
#define autocvar_g_keyhunt_point_limit cvar("g_keyhunt_point_limit")
- float autocvar_g_keyhunt_teams;
- float autocvar_g_keyhunt_teams_override;
+ int autocvar_g_keyhunt_teams;
+ int autocvar_g_keyhunt_teams_override;
+float autocvar_g_keyhunt_warmup;
+float autocvar_g_keyhunt_round_timelimit;
- float autocvar_g_lms_extra_lives;
- float autocvar_g_lms_join_anytime;
- float autocvar_g_lms_last_join;
+ int autocvar_g_lms_extra_lives;
+ bool autocvar_g_lms_join_anytime;
+ int autocvar_g_lms_last_join;
#define autocvar_g_lms_lives_override cvar("g_lms_lives_override")
- float autocvar_g_lms_regenerate;
+ bool autocvar_g_lms_regenerate;
#define autocvar_g_maplist cvar_string("g_maplist")
- float autocvar_g_maplist_check_waypoints;
- float autocvar_g_maplist_index;
+ bool autocvar_g_maplist_check_waypoints;
+ int autocvar_g_maplist_index;
string autocvar_g_maplist_mostrecent;
- float autocvar_g_maplist_mostrecent_count;
- float autocvar_g_maplist_selectrandom;
+ int autocvar_g_maplist_mostrecent_count;
+ bool autocvar_g_maplist_selectrandom;
float autocvar_g_maplist_shuffle;
#define autocvar_g_maplist_votable cvar("g_maplist_votable")
- float autocvar_g_maplist_votable_abstain;
+ bool autocvar_g_maplist_votable_abstain;
float autocvar_g_maplist_votable_keeptwotime;
- float autocvar_g_maplist_votable_nodetail;
+ bool autocvar_g_maplist_votable_nodetail;
string autocvar_g_maplist_votable_screenshot_dir;
- float autocvar_g_maplist_votable_suggestions;
- float autocvar_g_maplist_votable_suggestions_override_mostrecent;
+ bool autocvar_g_maplist_votable_suggestions;
+ bool autocvar_g_maplist_votable_suggestions_override_mostrecent;
float autocvar_g_maplist_votable_timeout;
- float autocvar_g_maxplayers;
+ int autocvar_g_maxplayers;
float autocvar_g_maxplayers_spectator_blocktime;
float autocvar_g_maxpushtime;
float autocvar_g_maxspeed;
float autocvar_g_midair_shieldtime;
#define autocvar_g_instagib cvar("g_instagib")
- float autocvar_g_instagib_use_normal_ammo;
- float autocvar_g_instagib_ammo_drop;
- float autocvar_g_instagib_ammo_rockets;
- float autocvar_g_instagib_extralives;
++bool autocvar_g_instagib_use_normal_ammo;
+ int autocvar_g_instagib_ammo_drop;
++int autocvar_g_instagib_ammo_rockets;
+ int autocvar_g_instagib_extralives;
float autocvar_g_instagib_speed_highspeed;
float autocvar_g_instagib_invis_alpha;
+float autocvar_g_instagib_damagedbycontents;
+float autocvar_g_instagib_blaster_keepdamage;
+float autocvar_g_instagib_blaster_keepforce;
#define autocvar_g_mirrordamage cvar("g_mirrordamage")
#define autocvar_g_mirrordamage_virtual cvar("g_mirrordamage_virtual")
float autocvar_g_movement_highspeed = 1;
- float autocvar_g_multijump;
+ int autocvar_g_multijump;
float autocvar_g_multijump_add;
float autocvar_g_multijump_speed;
+float autocvar_g_multijump_maxspeed;
string autocvar_g_mutatormsg;
float autocvar_g_nexball_basketball_bouncefactor;
float autocvar_g_nexball_basketball_bouncestop;
float autocvar_g_nexball_delay_start;
float autocvar_g_nexball_football_bouncefactor;
float autocvar_g_nexball_football_bouncestop;
- float autocvar_g_nexball_goalleadlimit;
+ int autocvar_g_nexball_goalleadlimit;
#define autocvar_g_nexball_goallimit cvar("g_nexball_goallimit")
- float autocvar_g_nexball_radar_showallplayers;
- float autocvar_g_nexball_sound_bounce;
- float autocvar_g_nexball_trail_color;
+ bool autocvar_g_nexball_radar_showallplayers;
+ bool autocvar_g_nexball_sound_bounce;
+ int autocvar_g_nexball_trail_color;
//float autocvar_g_nick_flood_penalty;
- float autocvar_g_nick_flood_penalty_red;
- float autocvar_g_nick_flood_penalty_yellow;
+ int autocvar_g_nick_flood_penalty_red;
+ int autocvar_g_nick_flood_penalty_yellow;
//float autocvar_g_nick_flood_timeout;
- float autocvar_g_nix_with_healtharmor;
- float autocvar_g_nix_with_blaster;
- float autocvar_g_nix_with_powerups;
- float autocvar_g_nodepthtestitems;
- float autocvar_g_nodepthtestplayers;
- float autocvar_g_norecoil;
+ bool autocvar_g_nix_with_healtharmor;
+ bool autocvar_g_nix_with_blaster;
+ bool autocvar_g_nix_with_powerups;
+ bool autocvar_g_nodepthtestitems;
+ bool autocvar_g_nodepthtestplayers;
-bool autocvar_g_norecoil;
-float autocvar_g_onslaught_cp_buildhealth;
-float autocvar_g_onslaught_cp_buildtime;
-float autocvar_g_onslaught_cp_health;
-float autocvar_g_onslaught_cp_regen;
-float autocvar_g_onslaught_gen_health;
++int autocvar_g_norecoil;
+float autocvar_g_items_mindist;
+float autocvar_g_items_maxdist;
- float autocvar_g_pickup_cells_max;
- float autocvar_g_pickup_plasma_max;
- float autocvar_g_pickup_fuel_max;
- float autocvar_g_pickup_items;
- float autocvar_g_pickup_nails_max;
- float autocvar_g_pickup_rockets_max;
- float autocvar_g_pickup_shells_max;
+ int autocvar_g_pickup_cells_max;
+ int autocvar_g_pickup_plasma_max;
+ int autocvar_g_pickup_fuel_max;
+ int autocvar_g_pickup_items;
+ int autocvar_g_pickup_nails_max;
+ int autocvar_g_pickup_rockets_max;
+ int autocvar_g_pickup_shells_max;
float autocvar_g_player_alpha;
float autocvar_g_player_brightness;
- float autocvar_g_playerclip_collisions;
- float autocvar_g_powerups;
- float autocvar_g_projectiles_damage;
- float autocvar_g_projectiles_keep_owner;
- float autocvar_g_projectiles_newton_style;
+ bool autocvar_g_playerclip_collisions;
+ int autocvar_g_powerups;
+ int autocvar_g_projectiles_damage;
+ bool autocvar_g_projectiles_keep_owner;
+ int autocvar_g_projectiles_newton_style;
float autocvar_g_projectiles_newton_style_2_maxfactor;
float autocvar_g_projectiles_newton_style_2_minfactor;
- float autocvar_g_projectiles_spread_style;
+ int autocvar_g_projectiles_spread_style;
#define autocvar_g_race_laps_limit cvar("g_race_laps_limit")
float autocvar_g_race_qualifying_timelimit;
float autocvar_g_race_qualifying_timelimit_override;
- float autocvar_g_race_teams;
- var float autocvar_g_race_team_spawns = 1;
- var float autocvar_g_respawn_delay_small = 2;
- float autocvar_g_respawn_delay_small_count;
- var float autocvar_g_respawn_delay_large = 2;
- float autocvar_g_respawn_delay_large_count;
+ int autocvar_g_race_teams;
-float autocvar_g_respawn_delay_small;
++bool autocvar_g_race_team_spawns = true;
++float autocvar_g_respawn_delay_small = 2;
+ int autocvar_g_respawn_delay_small_count;
-float autocvar_g_respawn_delay_large;
++float autocvar_g_respawn_delay_large = 2;
+ int autocvar_g_respawn_delay_large_count;
float autocvar_g_respawn_delay_max;
- float autocvar_g_respawn_delay_forced;
- float autocvar_g_respawn_ghosts;
++bool autocvar_g_respawn_delay_forced;
+ bool autocvar_g_respawn_ghosts;
float autocvar_g_respawn_ghosts_maxtime;
float autocvar_g_respawn_ghosts_speed;
- float autocvar_g_respawn_waves;
- float autocvar_g_running_guns;
- float autocvar_g_shootfromcenter;
- float autocvar_g_shootfromclient;
- float autocvar_g_shootfromeye;
+ int autocvar_g_respawn_waves;
+ bool autocvar_g_running_guns;
+ bool autocvar_g_shootfromcenter;
+ int autocvar_g_shootfromclient;
+ bool autocvar_g_shootfromeye;
string autocvar_g_shootfromfixedorigin;
- float autocvar_g_showweaponspawns;
- float autocvar_g_spawn_alloweffects;
+ int autocvar_g_showweaponspawns;
+ bool autocvar_g_spawn_alloweffects;
float autocvar_g_spawn_furthest;
- float autocvar_g_spawn_useallspawns;
- float autocvar_g_spawnpoints_auto_move_out_of_solid;
+ bool autocvar_g_spawn_useallspawns;
+ bool autocvar_g_spawnpoints_auto_move_out_of_solid;
#define autocvar_g_spawnshieldtime cvar("g_spawnshieldtime")
- float autocvar_g_spawnshield_nodamage;
- float autocvar_g_tdm_team_spawns;
- float autocvar_g_tdm_point_limit;
- float autocvar_g_tdm_point_leadlimit;
- float autocvar_g_tdm_teams;
- float autocvar_g_tdm_teams_override;
++bool autocvar_g_spawnshield_nodamage;
+ bool autocvar_g_tdm_team_spawns;
+ int autocvar_g_tdm_point_limit;
+ int autocvar_g_tdm_point_leadlimit;
+ int autocvar_g_tdm_teams;
+ int autocvar_g_tdm_teams_override;
float autocvar_g_teamdamage_resetspeed;
float autocvar_g_teamdamage_threshold;
- float autocvar_g_telefrags;
- float autocvar_g_telefrags_avoid;
- float autocvar_g_telefrags_teamplay;
+ bool autocvar_g_telefrags;
+ bool autocvar_g_telefrags_avoid;
+ bool autocvar_g_telefrags_teamplay;
float autocvar_g_teleport_maxspeed;
- float autocvar_g_throughfloor_debug;
+ bool autocvar_g_throughfloor_debug;
float autocvar_g_throughfloor_damage;
float autocvar_g_throughfloor_force;
float autocvar_g_throughfloor_damage_max_stddev;
float autocvar_g_triggerimpulse_accel_power;
float autocvar_g_triggerimpulse_directional_multiplier;
float autocvar_g_triggerimpulse_radial_multiplier;
- float autocvar_g_turrets;
+ bool autocvar_g_turrets;
float autocvar_g_turrets_aimidle_delay;
- float autocvar_g_turrets_nofire;
- float autocvar_g_turrets_reloadcvars;
+ bool autocvar_g_turrets_nofire;
+ bool autocvar_g_turrets_reloadcvars;
float autocvar_g_turrets_targetscan_maxdelay;
float autocvar_g_turrets_targetscan_mindelay;
-float autocvar_g_turrets_unit_ewheel_speed_fast;
-float autocvar_g_turrets_unit_ewheel_speed_slow;
-float autocvar_g_turrets_unit_ewheel_speed_slower;
-float autocvar_g_turrets_unit_ewheel_speed_stop;
-float autocvar_g_turrets_unit_ewheel_turnrate;
-float autocvar_g_turrets_unit_hellion_std_shot_speed_gain;
-float autocvar_g_turrets_unit_hellion_std_shot_speed_max;
-float autocvar_g_turrets_unit_hk_std_shot_speed;
-float autocvar_g_turrets_unit_hk_std_shot_speed_accel;
-float autocvar_g_turrets_unit_hk_std_shot_speed_accel2;
-float autocvar_g_turrets_unit_hk_std_shot_speed_decel;
-float autocvar_g_turrets_unit_hk_std_shot_speed_max;
-float autocvar_g_turrets_unit_hk_std_shot_speed_turnrate;
-float autocvar_g_turrets_unit_walker_speed_jump;
-float autocvar_g_turrets_unit_walker_speed_roam;
-float autocvar_g_turrets_unit_walker_speed_run;
-float autocvar_g_turrets_unit_walker_speed_stop;
-float autocvar_g_turrets_unit_walker_speed_swim;
-float autocvar_g_turrets_unit_walker_speed_walk;
-float autocvar_g_turrets_unit_walker_std_meele_dmg;
-float autocvar_g_turrets_unit_walker_std_meele_force;
-float autocvar_g_turrets_unit_walker_std_meele_range;
-float autocvar_g_turrets_unit_walker_std_rocket_dmg;
-float autocvar_g_turrets_unit_walker_std_rocket_force;
-float autocvar_g_turrets_unit_walker_std_rocket_radius;
-float autocvar_g_turrets_unit_walker_std_rocket_refire;
-float autocvar_g_turrets_unit_walker_std_rocket_speed;
-float autocvar_g_turrets_unit_walker_std_rocket_turnrate;
-float autocvar_g_turrets_unit_walker_std_rockets_range;
-float autocvar_g_turrets_unit_walker_std_rockets_range_min;
-float autocvar_g_turrets_unit_walker_turn;
-float autocvar_g_turrets_unit_walker_turn_walk;
-float autocvar_g_turrets_unit_walker_turn_run;
-float autocvar_g_turrets_unit_walker_turn_strafe;
-float autocvar_g_turrets_unit_walker_turn_swim;
+float autocvar_g_turrets_weapon_damagerate = 1;
+float autocvar_g_turrets_player_damagerate = 1; // incorrectly named, but there is no g_player yet
- float autocvar_g_use_ammunition;
- float autocvar_g_waypointeditor;
- float autocvar_g_waypointeditor_auto;
- float autocvar_g_waypoints_for_items;
+ bool autocvar_g_use_ammunition;
+ bool autocvar_g_waypointeditor;
+ int autocvar_g_waypointeditor_auto;
-int autocvar_g_waypoints_for_items;
++bool autocvar_g_waypoints_for_items;
float autocvar_g_weapon_charge_colormod_blue_full;
float autocvar_g_weapon_charge_colormod_blue_half;
float autocvar_g_weapon_charge_colormod_green_full;
float autocvar_skill_auto;
#define autocvar_slowmo cvar("slowmo")
float autocvar_snd_soundradius;
- float autocvar_spawn_debug;
- float autocvar_speedmeter;
+ int autocvar_spawn_debug;
+ bool autocvar_speedmeter;
-float autocvar_sv_accelerate;
float autocvar_sv_accuracy_data_share = 1;
string autocvar_sv_adminnick;
- float autocvar_sv_autoscreenshot;
- float autocvar_sv_cheats;
-float autocvar_sv_airaccel_qw;
-float autocvar_sv_airaccel_qw_stretchfactor;
-float autocvar_sv_airaccel_sideways_friction;
-float autocvar_sv_airaccelerate;
-float autocvar_sv_aircontrol;
-float autocvar_sv_aircontrol_penalty;
-float autocvar_sv_aircontrol_power;
-float autocvar_sv_airspeedlimit_nonqw;
-float autocvar_sv_airstopaccelerate;
-float autocvar_sv_airstrafeaccel_qw;
-float autocvar_sv_airstrafeaccelerate;
+ bool autocvar_sv_autoscreenshot;
+ int autocvar_sv_cheats;
float autocvar_sv_clientcommand_antispam_time;
- float autocvar_sv_clientcommand_antispam_count;
- float autocvar_sv_curl_serverpackages_auto;
- float autocvar_sv_db_saveasdump;
- float autocvar_sv_defaultcharacter;
+ int autocvar_sv_clientcommand_antispam_count;
+ bool autocvar_sv_curl_serverpackages_auto;
+ bool autocvar_sv_db_saveasdump;
+ bool autocvar_sv_defaultcharacter;
string autocvar_sv_defaultplayercolors;
string autocvar_sv_defaultplayermodel;
string autocvar_sv_defaultplayermodel_blue;
float autocvar_sv_dodging_horiz_speed;
float autocvar_sv_dodging_horiz_speed_frozen;
float autocvar_sv_dodging_ramp_time;
- float autocvar_sv_dodging_sound;
+ bool autocvar_sv_dodging_sound;
float autocvar_sv_dodging_up_speed;
float autocvar_sv_dodging_wall_distance_threshold;
- float autocvar_sv_dodging_wall_dodging;
- float autocvar_sv_dodging_frozen;
- float autocvar_sv_dodging_frozen_doubletap;
- float autocvar_sv_doublejump;
- float autocvar_sv_eventlog;
- float autocvar_sv_eventlog_console;
- float autocvar_sv_eventlog_files;
- float autocvar_sv_eventlog_files_counter;
+ bool autocvar_sv_dodging_wall_dodging;
+ bool autocvar_sv_dodging_frozen;
+ bool autocvar_sv_dodging_frozen_doubletap;
+ bool autocvar_sv_doublejump;
+ bool autocvar_sv_eventlog;
+ bool autocvar_sv_eventlog_console;
+ bool autocvar_sv_eventlog_files;
+ int autocvar_sv_eventlog_files_counter;
string autocvar_sv_eventlog_files_nameprefix;
string autocvar_sv_eventlog_files_namesuffix;
- float autocvar_sv_eventlog_files_timestamps;
+ bool autocvar_sv_eventlog_files_timestamps;
-float autocvar_sv_friction;
float autocvar_sv_friction_on_land;
+var float autocvar_sv_friction_slick = 0.5;
float autocvar_sv_gameplayfix_q2airaccelerate;
- float autocvar_sv_gentle;
+ int autocvar_sv_gentle;
#define autocvar_sv_gravity cvar("sv_gravity")
string autocvar_sv_intermission_cdtrack;
-string autocvar_sv_jumpspeedcap_max;
+float autocvar_sv_jumpspeedcap_max;
float autocvar_sv_jumpspeedcap_max_disable_on_ramps;
-string autocvar_sv_jumpspeedcap_min;
+float autocvar_sv_jumpspeedcap_min;
float autocvar_sv_jumpvelocity;
- float autocvar_sv_logscores_bots;
- float autocvar_sv_logscores_console;
- float autocvar_sv_logscores_file;
+ bool autocvar_sv_logscores_bots;
+ bool autocvar_sv_logscores_console;
+ bool autocvar_sv_logscores_file;
string autocvar_sv_logscores_filename;
float autocvar_sv_mapchange_delay;
float autocvar_sv_maxairspeed;
-float autocvar_sv_maxairstrafespeed;
float autocvar_sv_maxspeed;
string autocvar_sv_motd;
- float autocvar_sv_precacheplayermodels;
+ bool autocvar_sv_precacheplayermodels;
//float autocvar_sv_precacheweapons; // WEAPONTODO?
- float autocvar_sv_q3acompat_machineshotgunswap;
- float autocvar_sv_ready_restart;
- float autocvar_sv_ready_restart_after_countdown;
- float autocvar_sv_ready_restart_repeatable;
- float autocvar_sv_servermodelsonly;
- float autocvar_sv_spectate;
+ bool autocvar_sv_q3acompat_machineshotgunswap;
+ bool autocvar_sv_ready_restart;
+ bool autocvar_sv_ready_restart_after_countdown;
+ bool autocvar_sv_ready_restart_repeatable;
+ bool autocvar_sv_servermodelsonly;
+ int autocvar_sv_spectate;
float autocvar_sv_spectator_speed_multiplier;
- float autocvar_sv_status_privacy;
+ bool autocvar_sv_status_privacy;
float autocvar_sv_stepheight;
-float autocvar_sv_stopspeed;
float autocvar_sv_strengthsound_antispam_refire_threshold;
float autocvar_sv_strengthsound_antispam_time;
- float autocvar_sv_teamnagger;
- float autocvar_sv_timeout;
+ bool autocvar_sv_teamnagger;
+ bool autocvar_sv_timeout;
float autocvar_sv_timeout_leadtime;
float autocvar_sv_timeout_length;
- float autocvar_sv_timeout_number;
+ int autocvar_sv_timeout_number;
float autocvar_sv_timeout_resumetime;
- float autocvar_sv_vote_call;
- float autocvar_sv_vote_auto;
- float autocvar_sv_vote_change;
+ bool autocvar_sv_vote_call;
++bool autocvar_sv_vote_auto;
+ bool autocvar_sv_vote_change;
string autocvar_sv_vote_commands;
- float autocvar_sv_vote_gametype;
+ bool autocvar_sv_vote_gametype;
float autocvar_sv_vote_gametype_timeout;
string autocvar_sv_vote_gametype_options;
float autocvar_sv_vote_gametype_keeptwotime;
float autocvar_sv_vote_stop;
float autocvar_sv_vote_timeout;
float autocvar_sv_vote_wait;
- float autocvar_sv_vote_gamestart;
+ bool autocvar_sv_vote_gamestart;
-float autocvar_sv_warsowbunny_accel;
-float autocvar_sv_warsowbunny_airforwardaccel;
-float autocvar_sv_warsowbunny_backtosideratio;
-float autocvar_sv_warsowbunny_topspeed;
-float autocvar_sv_warsowbunny_turnaccel;
float autocvar_sv_waypointsprite_deadlifetime;
float autocvar_sv_waypointsprite_deployed_lifetime;
float autocvar_sv_waypointsprite_limitedrange;
float autocvar_timelimit_min;
float autocvar_timelimit_max;
float autocvar_timelimit_overtime;
- float autocvar_timelimit_overtimes;
+ int autocvar_timelimit_overtimes;
float autocvar_timelimit_suddendeath;
#define autocvar_utf8_enable cvar("utf8_enable")
- float autocvar_waypoint_benchmark;
- float autocvar_sv_gameplayfix_gravityunaffectedbyticrate;
- float autocvar_sv_gameplayfix_upwardvelocityclearsongroundflag;
+ bool autocvar_waypoint_benchmark;
+ bool autocvar_sv_gameplayfix_gravityunaffectedbyticrate;
++bool autocvar_sv_gameplayfix_upwardvelocityclearsongroundflag;
float autocvar_g_trueaim_minrange;
- float autocvar_g_debug_defaultsounds;
+ bool autocvar_g_debug_defaultsounds;
float autocvar_g_grab_range;
- float autocvar_g_sandbox_info;
+ int autocvar_g_sandbox_info;
+float autocvar_g_sandbox_snaptogrid;
- float autocvar_g_sandbox_readonly;
+ bool autocvar_g_sandbox_readonly;
string autocvar_g_sandbox_storage_name;
float autocvar_g_sandbox_storage_autosave;
- float autocvar_g_sandbox_storage_autoload;
+ bool autocvar_g_sandbox_storage_autoload;
float autocvar_g_sandbox_editor_flood;
- float autocvar_g_sandbox_editor_maxobjects;
- float autocvar_g_sandbox_editor_free;
+ int autocvar_g_sandbox_editor_maxobjects;
+ int autocvar_g_sandbox_editor_free;
float autocvar_g_sandbox_editor_distance_spawn;
float autocvar_g_sandbox_editor_distance_edit;
float autocvar_g_sandbox_object_scale_min;
float autocvar_g_sandbox_object_scale_max;
float autocvar_g_sandbox_object_material_velocity_min;
float autocvar_g_sandbox_object_material_velocity_factor;
- float autocvar_g_sandbox_allow_bspsolid;
- float autocvar_g_max_info_autoscreenshot;
- float autocvar_physics_ode;
- float autocvar_g_physical_items;
++bool autocvar_g_sandbox_allow_bspsolid;
+ int autocvar_g_max_info_autoscreenshot;
+ bool autocvar_physics_ode;
+ int autocvar_g_physical_items;
float autocvar_g_physical_items_damageforcescale;
float autocvar_g_physical_items_reset;
+float autocvar_g_rm;
+float autocvar_g_rm_damage;
+float autocvar_g_rm_edgedamage;
+float autocvar_g_rm_damage_multiplier_accuracy;
+float autocvar_g_rm_damage_multiplier_min;
+float autocvar_g_rm_force;
+float autocvar_g_rm_radius;
+float autocvar_g_rm_laser;
+float autocvar_g_rm_laser_count;
+float autocvar_g_rm_laser_speed;
+float autocvar_g_rm_laser_spread;
+float autocvar_g_rm_laser_spread_random;
+float autocvar_g_rm_laser_lifetime;
+float autocvar_g_rm_laser_damage;
+float autocvar_g_rm_laser_refire;
+float autocvar_g_rm_laser_rapid;
+float autocvar_g_rm_laser_rapid_refire;
+float autocvar_g_rm_laser_rapid_delay;
+float autocvar_g_rm_laser_radius;
+float autocvar_g_rm_laser_force;
+float autocvar_g_rm_superrocket;
+float autocvar_g_rm_superrocket_speed;
+float autocvar_g_rm_hook_damage;
+float autocvar_g_rm_hook_damage_always;
+float autocvar_g_rm_hook_team;
+float autocvar_g_rm_hook_damagefactor;
+float autocvar_g_rm_hook_breakable;
+float autocvar_g_rm_hook_breakable_owner;
+float autocvar_g_rm_hook_damage_health;
float autocvar_g_monsters;
- float autocvar_g_monsters_edit;
- float autocvar_g_monsters_sounds;
+ bool autocvar_g_monsters_edit;
+ bool autocvar_g_monsters_sounds;
float autocvar_g_monsters_think_delay;
- float autocvar_g_monsters_max;
- float autocvar_g_monsters_max_perplayer;
- var float autocvar_g_monsters_damageforcescale = 0.8;
- float autocvar_g_player_gib_always;
- float autocvar_g_player_crush;
- var float autocvar_g_player_crush_simple = 1;
- var float autocvar_g_player_crush_damage = 200;
- var float autocvar_g_player_crush_bounce = 300;
- var float autocvar_g_player_crush_bounce_jump = 600;
+ int autocvar_g_monsters_max;
+ int autocvar_g_monsters_max_perplayer;
++float autocvar_g_monsters_damageforcescale = 0.8;
++bool autocvar_g_player_gib_always;
++bool autocvar_g_player_crush;
++bool autocvar_g_player_crush_simple = true;
++float autocvar_g_player_crush_damage = 200;
++float autocvar_g_player_crush_bounce = 300;
++float autocvar_g_player_crush_bounce_jump = 600;
+float autocvar_g_player_crush_headheight;
+float autocvar_g_freeze_revive_speed;
+float autocvar_g_freeze_revive_speed_random;
- float autocvar_g_freeze_noauto;
- float autocvar_g_freeze_norespawn;
++bool autocvar_g_freeze_noauto;
++bool autocvar_g_freeze_norespawn;
+float autocvar_g_freeze_revive_minhealth;
+float autocvar_g_freeze_revive_falldamage;
+float autocvar_g_freeze_revive_falldamage_health;
- float autocvar_g_freeze_revive_nade;
++bool autocvar_g_freeze_revive_nade;
+float autocvar_g_freeze_revive_nade_health;
+float autocvar_g_freeze_frozen_force;
- float autocvar_g_freeze_frozen_damage_trigger;
++bool autocvar_g_freeze_frozen_damage_trigger;
+float autocvar_g_freeze_frozen_maxtime;
+float autocvar_g_freeze_respawn_time;
float autocvar_g_monsters_target_range;
- float autocvar_g_monsters_target_infront;
+ bool autocvar_g_monsters_target_infront;
float autocvar_g_monsters_attack_range;
- float autocvar_g_monsters_score_kill;
- float autocvar_g_monsters_score_spawned;
- float autocvar_g_monsters_typefrag;
- float autocvar_g_monsters_owners;
+ int autocvar_g_monsters_score_kill;
+ int autocvar_g_monsters_score_spawned;
+ bool autocvar_g_monsters_typefrag;
+ bool autocvar_g_monsters_owners;
float autocvar_g_monsters_miniboss_chance;
float autocvar_g_monsters_miniboss_healthboost;
float autocvar_g_monsters_drop_time;
float autocvar_g_monsters_spawnshieldtime;
- float autocvar_g_monsters_teams;
+ bool autocvar_g_monsters_teams;
float autocvar_g_monsters_respawn_delay;
- float autocvar_g_monsters_respawn;
+ bool autocvar_g_monsters_respawn;
float autocvar_g_monsters_armor_blockpercent;
+float autocvar_g_monsters_healthbars;
+float autocvar_g_monsters_lineofsight;
float autocvar_g_touchexplode_radius;
float autocvar_g_touchexplode_damage;
float autocvar_g_touchexplode_edgedamage;
float autocvar_g_touchexplode_force;
+float autocvar_g_vip_teams;
+float autocvar_g_vip_warmup;
+float autocvar_g_vip_round_timelimit;
+float autocvar_g_vip_point_limit;
+float autocvar_g_vip_point_leadlimit;
+float autocvar_g_vip_drop;
+float autocvar_g_vip_drop_punish_delay;
+float autocvar_g_vip_drop_punish_count;
+float autocvar_g_vip_drop_punish_time;
+float autocvar_g_vip_pickup_wait;
+float autocvar_sv_allow_customplayermodels;
+string autocvar_sv_allow_customplayermodels_idlist;
float autocvar_g_invasion_round_timelimit;
- float autocvar_g_invasion_teams;
- float autocvar_g_invasion_team_spawns;
+ int autocvar_g_invasion_teams;
+ bool autocvar_g_invasion_team_spawns;
float autocvar_g_invasion_spawnpoint_spawn_delay;
#define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit")
float autocvar_g_invasion_warmup;
float autocvar_g_nades_napalm_fountain_damage;
float autocvar_g_nades_napalm_fountain_edgedamage;
float autocvar_g_nades_napalm_burntime;
- float autocvar_g_nades_napalm_selfdamage;
- float autocvar_g_nades_nade_type;
- float autocvar_g_nades_bonus_type;
- float autocvar_g_nades_bonus;
- float autocvar_g_nades_bonus_only;
- float autocvar_g_nades_bonus_client_select;
- float autocvar_g_nades_bonus_max;
- float autocvar_g_nades_bonus_score_max;
- float autocvar_g_nades_bonus_score_time;
- float autocvar_g_nades_bonus_score_time_flagcarrier;
- float autocvar_g_nades_bonus_score_minor;
- float autocvar_g_nades_bonus_score_low;
- float autocvar_g_nades_bonus_score_high;
- float autocvar_g_nades_bonus_score_medium;
- float autocvar_g_nades_bonus_score_spree;
+ bool autocvar_g_nades_napalm_selfdamage;
+ int autocvar_g_nades_nade_type;
+ int autocvar_g_nades_bonus_type;
+ bool autocvar_g_nades_bonus;
-bool autocvar_g_nades_bonus_onstrength;
++bool autocvar_g_nades_bonus_only;
+ bool autocvar_g_nades_bonus_client_select;
+ int autocvar_g_nades_bonus_max;
+ int autocvar_g_nades_bonus_score_max;
+ int autocvar_g_nades_bonus_score_time;
+ int autocvar_g_nades_bonus_score_time_flagcarrier;
+ int autocvar_g_nades_bonus_score_minor;
+ int autocvar_g_nades_bonus_score_low;
+ int autocvar_g_nades_bonus_score_high;
+ int autocvar_g_nades_bonus_score_medium;
+ int autocvar_g_nades_bonus_score_spree;
float autocvar_g_nades_ice_freeze_time;
float autocvar_g_nades_ice_health;
- float autocvar_g_nades_ice_explode;
- float autocvar_g_nades_ice_teamcheck;
+ bool autocvar_g_nades_ice_explode;
+ bool autocvar_g_nades_ice_teamcheck;
float autocvar_g_nades_heal_time;
float autocvar_g_nades_heal_rate;
float autocvar_g_nades_heal_friend;
float autocvar_g_campcheck_damage;
float autocvar_g_campcheck_distance;
float autocvar_g_campcheck_interval;
- float autocvar_g_jump_grunt;
- float autocvar_g_overkill_powerups_replace;
- float autocvar_g_za;
- float autocvar_g_za_max_monsters;
+ bool autocvar_g_jump_grunt;
+ bool autocvar_g_overkill_powerups_replace;
++bool autocvar_g_za;
++int autocvar_g_za_max_monsters;
+string autocvar_g_za_spawnmonster;
+float autocvar_g_za_spawn_delay;
float autocvar_g_overkill_superguns_respawn_time;
- float autocvar_g_overkill_100h_anyway;
- float autocvar_g_overkill_100a_anyway;
- float autocvar_g_overkill_ammo_charge;
+ bool autocvar_g_overkill_100h_anyway;
+ bool autocvar_g_overkill_100a_anyway;
+ bool autocvar_g_overkill_ammo_charge;
float autocvar_g_overkill_ammo_charge_notice;
float autocvar_g_overkill_ammo_charge_limit;
+float autocvar_g_overkill_ammo_charge_attack;
float autocvar_g_spawn_near_teammate_distance;
- float autocvar_g_spawn_near_teammate_ignore_spawnpoint;
+ bool autocvar_g_spawn_near_teammate_ignore_spawnpoint;
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
- float autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health;
- float autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath;
- float autocvar_g_conquest_spawn_close_to_death;
-int autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health;
++bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health;
+ bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath;
++bool autocvar_g_conquest_spawn_close_to_death;
+float autocvar_g_conquest_capture_distance_default;
+float autocvar_g_conquest_capture_sps;
+float autocvar_g_conquest_controlpoint_health_default;
- float autocvar_g_conquest_spawn_choose;
++bool autocvar_g_conquest_spawn_choose;
+float autocvar_g_conquest_teleport_radius;
+float autocvar_g_conquest_teleport_wait;
+float autocvar_g_conquest_click_radius;
- float autocvar_g_conquest_teams;
- float autocvar_g_conquest_teams_override;
++int autocvar_g_conquest_teams;
++int autocvar_g_conquest_teams_override;
+float autocvar_g_conquest_warmup;
+float autocvar_g_conquest_round_timelimit;
- float autocvar_g_conquest_point_limit;
- float autocvar_g_walljump;
++int autocvar_g_conquest_point_limit;
++bool autocvar_g_walljump;
+float autocvar_g_walljump_delay;
+float autocvar_g_walljump_force;
+float autocvar_g_walljump_velocity_xy_factor;
+float autocvar_g_walljump_velocity_z_factor;
- float autocvar_g_infection_teams;
++int autocvar_g_infection_teams;
+float autocvar_g_infection_warmup;
+float autocvar_g_infection_round_timelimit;
- float autocvar_g_infection_point_limit;
- float autocvar_g_infection_point_leadlimit;
++int autocvar_g_infection_point_limit;
++int autocvar_g_infection_point_leadlimit;
+float autocvar_g_jailbreak_warmup;
+float autocvar_g_jailbreak_round_timelimit;
- float autocvar_g_jailbreak_point_limit;
- float autocvar_g_jailbreak_point_leadlimit;
++int autocvar_g_jailbreak_point_limit;
++int autocvar_g_jailbreak_point_leadlimit;
+float autocvar_g_jailbreak_controlpoint_unlock_damage_pushback;
- float autocvar_g_jailbreak_jail_deathmatch;
++bool autocvar_g_jailbreak_jail_deathmatch;
+float autocvar_g_jailbreak_score_jbreak_neutralmultiplier;
+float autocvar_g_jailbreak_score_jbreak_perplayer;
+float autocvar_g_jailbreak_score_jbreak;
+float autocvar_g_jailbreak_controlpoint_claim_noneutral;
+float autocvar_g_jailbreak_controlpoint_unlock_speed;
+float autocvar_g_jailbreak_controlpoint_idletime_global_own;
+float autocvar_g_jailbreak_controlpoint_idletime_global;
+float autocvar_g_jailbreak_controlpoint_claim_allneutral;
- float autocvar_g_jailbreak_penalty_death;
- float autocvar_g_jailbreak_score_imprison;
- float autocvar_g_jailbreak_score_defense;
- float autocvar_g_jailbreak_penalty_teamkill;
- float autocvar_g_jailbreak_controlpoint_claim;
- float autocvar_g_jailbreak_nonjb_openjails;
++int autocvar_g_jailbreak_penalty_death;
++int autocvar_g_jailbreak_score_imprison;
++int autocvar_g_jailbreak_score_defense;
++int autocvar_g_jailbreak_penalty_teamkill;
++bool autocvar_g_jailbreak_controlpoint_claim;
++int autocvar_g_jailbreak_nonjb_openjails;
+float autocvar_g_jailbreak_prisoner_health;
+float autocvar_g_jailbreak_prisoner_armor;
+float autocvar_g_jailbreak_controlpoint_idletime_neutral_initial;
+float autocvar_g_jailbreak_controlpoint_idletime_initial;
+float autocvar_g_jailbreak_controlpoint_idletime_neutral;
+float autocvar_g_jailbreak_controlpoint_idletime_neutral_power;
+float autocvar_g_jailbreak_controlpoint_idletime_neutral_factor;
+float autocvar_g_jailbreak_controlpoint_idletime;
+float autocvar_g_jailbreak_controlpoint_idletime_power;
+float autocvar_g_jailbreak_controlpoint_idletime_factor;
+float autocvar_g_jailbreak_defense_range;
- float autocvar_g_jailbreak_debug;
- float autocvar_g_jailbreak_teams;
- float autocvar_g_jailbreak_teams_override;
- float autocvar_sv_headshot;
- var float autocvar_sv_headshot_damage = 1.5;
- float autocvar_g_piggyback_ride_enemies;
- var float autocvar_g_assault_round_limit = 1;
- var float autocvar_g_assault_repair_amount = 40;
- float autocvar_g_onslaught_debug;
++int autocvar_g_jailbreak_debug;
++int autocvar_g_jailbreak_teams;
++int autocvar_g_jailbreak_teams_override;
++bool autocvar_sv_headshot;
++float autocvar_sv_headshot_damage = 1.5;
++bool autocvar_g_piggyback_ride_enemies;
++float autocvar_g_assault_round_limit = 1;
++float autocvar_g_assault_repair_amount = 40;
++int autocvar_g_onslaught_debug;
+float autocvar_g_onslaught_teleport_wait;
- float autocvar_g_onslaught_spawn_at_controlpoints;
- var float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5;
- float autocvar_g_onslaught_spawn_at_controlpoints_random;
- float autocvar_g_onslaught_spawn_at_generator;
++bool autocvar_g_onslaught_spawn_at_controlpoints;
++float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5;
++bool autocvar_g_onslaught_spawn_at_controlpoints_random;
++bool autocvar_g_onslaught_spawn_at_generator;
+float autocvar_g_onslaught_spawn_at_generator_chance;
+float autocvar_g_onslaught_spawn_at_generator_random;
+float autocvar_g_onslaught_cp_proxydecap;
- var float autocvar_g_onslaught_cp_proxydecap_distance = 512;
- var float autocvar_g_onslaught_cp_proxydecap_dps = 100;
++float autocvar_g_onslaught_cp_proxydecap_distance = 512;
++float autocvar_g_onslaught_cp_proxydecap_dps = 100;
+float autocvar_g_onslaught_cp_buildhealth;
+float autocvar_g_onslaught_cp_buildtime;
+float autocvar_g_onslaught_cp_health;
+float autocvar_g_onslaught_cp_regen;
+float autocvar_g_onslaught_gen_health;
- var float autocvar_g_onslaught_shield_force = 100;
- float autocvar_g_onslaught_allow_vehicle_touch;
++float autocvar_g_onslaught_shield_force = 100;
++bool autocvar_g_onslaught_allow_vehicle_touch;
+float autocvar_g_onslaught_round_timelimit;
- float autocvar_g_onslaught_point_limit;
++int autocvar_g_onslaught_point_limit;
+float autocvar_g_onslaught_warmup;
+float autocvar_g_onslaught_teleport_radius;
- float autocvar_g_onslaught_spawn_choose;
++bool autocvar_g_onslaught_spawn_choose;
+float autocvar_g_onslaught_click_radius;
- float autocvar_g_observer_glowtrails;
- float autocvar_g_riflearena_withlaser;
++bool autocvar_g_observer_glowtrails;
++bool autocvar_g_riflearena_withlaser;
float autocvar_g_buffs_waypoint_distance;
- float autocvar_g_buffs_randomize;
+bool autocvar_g_buffs_effects;
+ bool autocvar_g_buffs_randomize;
float autocvar_g_buffs_random_lifetime;
- float autocvar_g_buffs_random_location;
- float autocvar_g_buffs_random_location_attempts;
- float autocvar_g_buffs_spawn_count;
- float autocvar_g_buffs_replace_powerups;
+ bool autocvar_g_buffs_random_location;
+ int autocvar_g_buffs_random_location_attempts;
+ int autocvar_g_buffs_spawn_count;
+ bool autocvar_g_buffs_replace_powerups;
float autocvar_g_buffs_cooldown_activate;
float autocvar_g_buffs_cooldown_respawn;
float autocvar_g_buffs_resistance_blockpercent;
#include "g_hook.qh"
#include "scores.qh"
#include "spawnpoints.qh"
+ #include "../common/movetypes/movetypes.qh"
#endif
- float Damage_DamageInfo_SendEntity(entity to, float sf)
+ float Damage_DamageInfo_SendEntity(entity to, int sf)
{
WriteByte(MSG_ENTITY, ENT_CLIENT_DAMAGEINFO);
WriteShort(MSG_ENTITY, self.projectiledeathtype);
--- /dev/null
- void jeff_Announcer_PlayerDies(entity attacker, float deathtype, entity targ, entity inflictor)
+// Modded announcer and random functions for the Jeff Resurrection servers
+#ifdef JEFF
+#include "../common/buffs.qh"
+#include "miscfunctions.qh"
+
+float autocvar_jeff_announce_eagleeye_distance = 32768;
+int autocvar_jeff_announce_rocketscientist_count = 5;
+int autocvar_jeff_announce_shaftmaster_count = 5;
+int autocvar_jeff_announce_flakmaster_count = 5;
+int autocvar_jeff_announce_jackhammer_count = 5;
+int autocvar_jeff_announce_nodebuster_count = 5;
+float autocvar_jeff_announce_mutdestruct_time = 1.5;
+float autocvar_jeff_announce_multikill_time = 0.6;
+int autocvar_jeff_announce_roadrage_count = 6;
+int autocvar_jeff_announce_roadrampage_count = 12;
+int autocvar_jeff_announce_manslaughter_count = 20;
+int autocvar_jeff_announce_hattrick_count = 50;
+float autocvar_jeff_announce_denied_radius = 60;
+int autocvar_jeff_announce_bottomfeeder_count = 20;
+int autocvar_jeff_announce_wickedsick_count = 3;
+float autocvar_jeff_announce_bluestreak_distance = 400;
+float autocvar_jeff_announce_holy_distance = 800;
+float autocvar_jeff_announce_hitandrun_speed = 1000;
+float autocvar_jeff_announce_juggernaut_armor = 180;
+float autocvar_jeff_announce_juggernaut_health = 170;
+float autocvar_jeff_announce_slacker_time = 30;
+int autocvar_jeff_announce_comboking_count = 30;
+int autocvar_jeff_announce_wrecker_count = 30;
+
+.int rocketkill_count; // counter for rocket scientist
+.int arckill_count; // counter for shaft master
+.int hagarkill_count; // counter for flak master
+.int meleekill_count; // counter for flak master
+.int electrokill_count; // counter for flak master
+.int vkill_count; // counter for vehicle kills
+.int minipickup_count; // counter for bottom feeder
+.int nadekill_count; // counter for wrecker
+
+.int lastkiller_weapon;
+.float lastkiller_time;
+.float lastkilled_time;
+.float lastkilled_flying; // actually a counter
+
+.int jeff_weaponswitch_count;
+
+.int annce_count;
+.float last_announcer;
+
+bool Fire_IsBurning(entity e); // defined later
+
+void jeff_Announcer_Send(entity player, bool toall, int announce)
+{
+ if(player == world || !IS_PLAYER(player)) { return; }
+ if(player.last_announcer > time) { return; }
+
+ player.last_announcer = time + 0.3; // avoid spam
+ player.annce_count += 1;
+
+ //dprint("Sending an announcement to player ", player.netname, "\n");
+
+ if(player.annce_count == autocvar_jeff_announce_hattrick_count)
+ {
+ Send_Notification(NOTIF_ONE, player, MSG_ANNCE, ANNCE_JEFF_HATTRICK);
+ return;
+ }
+
+ Send_Notification(((toall) ? NOTIF_ALL : NOTIF_ONE), ((toall) ? world : player), MSG_ANNCE, announce);
+}
+
- void jeff_Announcer_PlayerDies(entity attacker, float deathtype, entity targ, entity inflictor) { }
++void jeff_Announcer_PlayerDies(entity attacker, int deathtype, entity targ, entity inflictor)
+{
+ if(attacker == targ) { return; } // don't play announcements for self kills (yet)
+ if(!IS_PLAYER(targ) || !targ) { return; } // don't play announcements for non-player kills
+
+ // set required values
+ float death_weapon = DEATH_WEAPONOF(deathtype);
+ float targ_flying = !(targ.flags & FL_ONGROUND);
+ float attacker_flying = !(attacker.flags & FL_ONGROUND);
+ float denied = false; entity head;
+ float bluestreak = false;
+ float killed = 0;
+ float player_count = 0, best_killcount = 0;
+ float wepcount = 0, mywepcount = 0;
+ float i;
+
+ for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+ {
+ if(weaponsInMap & WepSet_FromWeapon(i))
+ {
+ if(attacker.weapons & WepSet_FromWeapon(i))
+ ++mywepcount;
+ ++wepcount;
+ }
+ }
+
+ FOR_EACH_PLAYER(head)
+ {
+ if(head != attacker)
+ if(head.lastkiller == attacker)
+ if(head.lastkiller_time - time <= 0.1) // killed within 0.1 seconds by this attacker
+ ++killed;
+
+ ++player_count;
+ }
+
+ if(player_count > 5)
+ FOR_EACH_PLAYER(head)
+ {
+ if(head != attacker)
+ if(head.killcount >= 1)
+ if(!best_killcount || head.killcount >= best_killcount)
+ best_killcount = head.killcount;
+ }
+
+ if(!IS_PLAYER(attacker))
+ FOR_EACH_PLAYER(head)
+ if(vlen(head.origin - targ.origin) <= autocvar_jeff_announce_bluestreak_distance)
+ {
+ bluestreak = true;
+ break;
+ }
+
+ if(deathtype == DEATH_NADE) { attacker.nadekill_count += 1; }
+ else { attacker.nadekill_count = 0; }
+
+ if(DEATH_ISWEAPON(deathtype, WEP_DEVASTATOR)) { attacker.rocketkill_count += 1; }
+ else { attacker.rocketkill_count = 0; }
+
+ if(DEATH_ISWEAPON(deathtype, WEP_HAGAR)) { attacker.hagarkill_count += 1; }
+ else { attacker.hagarkill_count = 0; }
+
+ if(DEATH_ISWEAPON(deathtype, WEP_ARC)) { attacker.arckill_count += 1; }
+ else { attacker.arckill_count = 0; }
+
+ if((DEATH_ISWEAPON(deathtype, WEP_SHOTGUN) || DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE)) && (deathtype & HITTYPE_SECONDARY)) { attacker.meleekill_count += 1; }
+ else { attacker.meleekill_count = 0; }
+
+ if(DEATH_ISWEAPON(deathtype, WEP_ELECTRO)) { attacker.electrokill_count += 1; }
+ else { attacker.electrokill_count = 0; }
+
+ if(attacker.vehicle) { attacker.vkill_count += 1; }
+ else { attacker.vkill_count = 0; }
+
+ if(attacker_flying) { attacker.lastkilled_flying += 1; }
+ else { attacker.lastkilled_flying = 0; }
+
+ targ.lastkiller_weapon = death_weapon;
+ targ.lastkiller_time = time;
+
+ for(head = findradius(targ.origin, autocvar_jeff_announce_denied_radius); head; head = head.chain)
+ {
+ float avail = (head.ItemStatus & ITS_AVAILABLE);
+ if( (head.classname == "item_health_mega" && avail)
+ || (head.classname == "item_armor_large" && avail)
+ || (head.classname == "item_flag_team" && (CTF_DIFFTEAM(head, targ) || targ.flagcarried))
+ || (avail && (head.weapon == WEP_VORTEX || head.weapon == WEP_DEVASTATOR || (head.weapon == WEP_VAPORIZER && !g_instagib)))
+ )
+ {
+ denied = true;
+ break;
+ }
+ }
+
+ // play the announcements
+ if(attacker.meleekill_count == autocvar_jeff_announce_jackhammer_count)
+ if((DEATH_ISWEAPON(deathtype, WEP_SHOTGUN) || DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE)) && (deathtype & HITTYPE_SECONDARY))
+ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_JACKHAMMER);
+
+ if(DEATH_ISWEAPON(deathtype, WEP_BLASTER) || ((DEATH_ISWEAPON(deathtype, WEP_SHOTGUN) || DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE)) && (deathtype & HITTYPE_SECONDARY)))
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_HUMILIATION);
+
+ if(deathtype == DEATH_FALL && IS_PLAYER(attacker))
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_PANCAKE);
+
+ if(SAME_TEAM(attacker, targ))
+ if(attacker.lastkiller == targ)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_RETRIBUTION);
+ else
+ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_TEAMKILLER);
+
+ if(vlen(attacker.origin - targ.origin) > autocvar_jeff_announce_eagleeye_distance)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_EAGLEEYE);
+
+ if(attacker.lastkiller == targ)
+ if(attacker.lastkiller_weapon == death_weapon)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_PAYBACK);
+ else if(player_count > 2)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_VENGEANCE);
+
+ if(attacker.rocketkill_count == autocvar_jeff_announce_rocketscientist_count && DEATH_ISWEAPON(deathtype, WEP_DEVASTATOR))
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_ROCKETSCIENTIST);
+
+ if(attacker.arckill_count == autocvar_jeff_announce_shaftmaster_count && DEATH_ISWEAPON(deathtype, WEP_ARC))
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_SHAFTMASTER);
+
+ if(time - attacker.lastkiller_time <= autocvar_jeff_announce_mutdestruct_time)
+ if(time - targ.lastkiller_time <= autocvar_jeff_announce_mutdestruct_time)
+ if(attacker.lastkiller == targ && targ.lastkiller == attacker)
+ {
+ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_MUTDESTRUCT);
+ //jeff_Announcer_Send(targ, false, ANNCE_JEFF_MUTDESTRUCT);
+ }
+
+ if(time - attacker.lastkilled_time <= autocvar_jeff_announce_multikill_time && time - attacker.lastkilled_time > 0.2) // don't do this for real multikills
+ if(attacker.lastkilled_flying == autocvar_jeff_announce_wickedsick_count)
+ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_NUKEMHOLY);
+ else
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_MULTIKILL);
+
+ if(attacker.vehicle && deathtype == DEATH_VH_CRUSH && attacker.vkill_count && !!(attacker.vkill_count % 2))
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_ROADKILL);
+
+ if(attacker.vehicle && attacker.vkill_count == autocvar_jeff_announce_roadrage_count)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_ROADRAGE);
+
+ if(attacker.vehicle && attacker.vkill_count == autocvar_jeff_announce_roadrampage_count)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_ROADRAMPAGE);
+
+ if(attacker.vehicle && attacker.vkill_count == autocvar_jeff_announce_manslaughter_count)
+ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_MANSLAUGHTER);
+
+ if(attacker_flying && targ_flying)
+ if(DEATH_ISWEAPON(deathtype, WEP_DEVASTATOR) && !(deathtype & HITTYPE_SPLASH))
+ if(vlen(targ.origin - attacker.origin) >= autocvar_jeff_announce_holy_distance)
+ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_HOLY);
+
+ if(denied)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_DENIED);
+
+ if(inflictor.realowner == attacker && inflictor.jeff_projowner == targ) // killed by their own projectile
+ jeff_Announcer_Send(targ, false, ANNCE_JEFF_REJECTED);
+
+ if(attacker.minipickup_count >= autocvar_jeff_announce_bottomfeeder_count)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_BOTTOMFEEDER);
+
+ if(attacker.lastkilled_flying == autocvar_jeff_announce_wickedsick_count)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_WICKEDSICK);
+
+ if(deathtype == DEATH_SLIME && IS_PLAYER(attacker))
+ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_BIOHAZARD);
+
+ if(Fire_IsBurning(attacker) && deathtype == DEATH_FIRE)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_BLAZEOFGLORY);
+
+ if(bluestreak)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_BLUESTREAK);
+
+ if(vlen(attacker.velocity) >= autocvar_jeff_announce_hitandrun_speed)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_HITANDRUN);
+
+ if(attacker.armorvalue >= autocvar_jeff_announce_juggernaut_armor && attacker.health >= autocvar_jeff_announce_juggernaut_health)
+ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_JUGGERNAUT);
+
+ switch(killed)
+ {
+ case 2: jeff_Announcer_Send(attacker, false, ANNCE_JEFF_DOUBLEKILL); break;
+ case 3: jeff_Announcer_Send(attacker, false, ANNCE_JEFF_MULTIKILL); break;
+ case 4: jeff_Announcer_Send(attacker, true, ANNCE_JEFF_MEGAKILL); break;
+ case 5: jeff_Announcer_Send(attacker, true, ANNCE_JEFF_ULTRAKILL); break;
+ case 6: jeff_Announcer_Send(attacker, true, ANNCE_JEFF_MONSTERKILL); break;
+ case 7: jeff_Announcer_Send(attacker, true, ANNCE_JEFF_LUDICROUSKILL); break;
+ case 10: jeff_Announcer_Send(attacker, true, ANNCE_JEFF_GODLIKE); break;
+ }
+
+ if(attacker.hagarkill_count == autocvar_jeff_announce_flakmaster_count && DEATH_ISWEAPON(deathtype, WEP_HAGAR))
+ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_FLAKMASTER);
+
+ if(attacker.electrokill_count == autocvar_jeff_announce_nodebuster_count && DEATH_ISWEAPON(deathtype, WEP_ELECTRO))
+ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_NODEBUSTER);
+
+ if(attacker.lastkilled_time - time >= autocvar_jeff_announce_slacker_time)
+ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_SLACKER);
+
+ if(attacker.killcount >= 1)
+ if(best_killcount == attacker.killcount - 1) // attacker just took the lead (TODO: make sure this isn't spammed)
+ jeff_Announcer_Send(attacker, true, ANNCE_JEFF_TOPGUN);
+
+ if(attacker.jeff_weaponswitch_count >= autocvar_jeff_announce_comboking_count)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_COMBOKING);
+
+ if(mywepcount >= wepcount)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_GUNSLINGER);
+
+ if(targ_flying && attacker_flying && (DEATH_ISWEAPON(deathtype, WEP_HAGAR) || DEATH_ISWEAPON(deathtype, WEP_SEEKER) || DEATH_ISWEAPON(deathtype, WEP_CRYLINK)))
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_OUTSTANDING);
+
+ if(time - targ.spawnshieldtime <= 5)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_INSTAGIB);
+
+ if(attacker.nadekill_count == autocvar_jeff_announce_wrecker_count && deathtype == DEATH_NADE)
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_WRECKER);
+
+ if(targ_flying && attacker_flying && DEATH_ISWEAPON(deathtype, WEP_FLAK))
+ jeff_Announcer_Send(attacker, false, ANNCE_JEFF_EXCELLENT);
+
+
+ attacker.jeff_weaponswitch_count = 0;
+ attacker.lastkilled_time = time;
+}
+
+void jeff_Announcer_MatchEnd()
+{
+ Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_JEFF_GAMEOVER);
+}
+
+void jeff_Announcer_VehicleEnter(entity player, entity veh)
+{
+ if(DIFF_TEAM(player, veh))
+ jeff_Announcer_Send(player, true, ANNCE_JEFF_HIJACKED);
+}
+
+void jeff_Announcer_ItemTouch(entity player, entity item)
+{
+ if(item.classname == "item_health_small" || item.classname == "item_armor_small")
+ player.minipickup_count += 1;
+ else
+ player.minipickup_count -= 1;
+}
+
+void jeff_Announcer_FireBullet(entity targ, entity ent, vector hitloc, vector start, vector end)
+{
+ if(ent.weapon != WEP_RIFLE && ent.weapon != WEP_VAPORIZER && ent.weapon != WEP_VORTEX)
+ return;
+ if(!IS_PLAYER(targ))
+ return;
+ if(!Player_Trapped(targ) || !targ.takedamage)
+ return;
+ vector headmins, headmaxs, org;
+ org = antilag_takebackorigin(targ, time - ANTILAG_LATENCY(ent));
+ headmins = org - '5 5 10';
+ headmaxs = org + '5 5 10';
+ if(trace_hits_box(start, end, headmins, headmaxs))
+ jeff_Announcer_Send(ent, false, ANNCE_JEFF_BULLSEYE);
+}
+
+void jeff_Annoncer_BuffPickup(entity player, entity item)
+{
+ if(item.buffs & BUFF_INVISIBLE)
+ jeff_Announcer_Send(player, true, ANNCE_JEFF_CAMOUFLAGED);
+
+ if(item.buffs & BUFF_VAMPIRE)
+ jeff_Announcer_Send(player, true, ANNCE_JEFF_VAMPIRE);
+
+ if(item.buffs & BUFF_BASH)
+ jeff_Announcer_Send(player, true, ANNCE_JEFF_BOOSTER);
+
+ if(item.buffs & BUFF_SPEED)
+ jeff_Announcer_Send(player, true, ANNCE_JEFF_SPEED);
+}
+
+#else
+
++void jeff_Announcer_PlayerDies(entity attacker, int deathtype, entity targ, entity inflictor) { }
+void jeff_Announcer_MatchEnd() { }
+void jeff_Announcer_VehicleEnter(entity player, entity veh) { }
+void jeff_Announcer_ItemTouch(entity player, entity item) { }
+void jeff_Announcer_FireBullet(entity targ, entity ent, vector hitloc, vector start, vector end) { }
+void jeff_Annoncer_BuffPickup(entity player, entity item) { }
+
+#endif
--- /dev/null
- void jeff_Announcer_PlayerDies(entity attacker, float deathtype, entity targ, entity inflictor);
+#ifndef SV_JEFF_H
+#define SV_JEFF_H
+
+void jeff_Announcer_MatchEnd();
+
++void jeff_Announcer_PlayerDies(entity attacker, int deathtype, entity targ, entity inflictor);
+
+void jeff_Announcer_VehicleEnter(entity player, entity veh);
+
+void jeff_Announcer_ItemTouch(entity player, entity item);
+
+void jeff_Announcer_FireBullet(entity targ, entity ent, vector hitloc, vector start, vector end);
+
+void jeff_Annoncer_BuffPickup(entity player, entity item);
+
+#endif
entno = num_for_edict(e);
idx = precache_sound_index(samp);
- float sflags;
+ int sflags;
sflags = 0;
- _atten = floor(_atten * 64);
+ attenu = floor(attenu * 64);
vol = floor(vol * 255);
if (vol != 255)
}
else
{
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_WAIT, p1, p2, p3, p4);
- kh_Controller_SetThink(1, kh_WaitForPlayers);
+ setattachment(key, player, "");
+ //setorigin(key, KEY_CARRY_OFFSET);
+ setorigin(key, '0 0 1' * KH_KEY_ZSHIFT); // fixing x, y in think
+ key.angles_y -= player.angles_y;
}
-}
-
-void kh_EnableTrackingDevice() // runs after each round
-{
- Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_KEYHUNT);
- Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_KEYHUNT_OTHER);
- kh_tracking_enabled = true;
-}
-
-void kh_StartRound() // runs at the start of each round
-{
- float i, players, teem;
- entity player;
-
- if(time < game_starttime)
+ // key setup
+ key.movetype = MOVETYPE_NONE;
+ key.takedamage = DAMAGE_NO;
+ key.solid = SOLID_NOT;
+ key.angles = '0 0 0';
+ key.kh_status = KEY_CARRY;
+
+ //if(pickuptype != PICKUP_KILLED)
+ if(key.kh_dropper)
{
- kh_Controller_SetThink(game_starttime - time + 0.1, kh_WaitForPlayers);
- return;
+ entity tmp_entity;
+ float key_count = 0;
+ KH_FOR_EACH_KEY(tmp_entity) if(tmp_entity.kh_dropper == key.kh_dropper && tmp_entity != key) { ++key_count; }
+ if(key_count < 1) // player dropped no other keys
+ key.kh_dropper.kh_lastkiller = world; // reset when picked up
}
- float p1 = kh_CheckPlayers(0), p2 = kh_CheckPlayers(1), p3 = kh_CheckPlayers(2), p4 = kh_CheckPlayers(3);
- if(p1 || p2 || p3 || p4)
+ switch(pickuptype)
{
- kh_Controller_SetThink(1, kh_WaitForPlayers);
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_WAIT, p1, p2, p3, p4);
- return;
+ case PICKUP_KILLED:
+ case PICKUP_DROPPED: key.health = key.max_key_health; break; // reset health/return timelimit
+ default: break;
}
- Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_KEYHUNT);
- Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_KEYHUNT_OTHER);
-
- for(i = 0; i < kh_teams; ++i)
+ // messages and sounds
+ if(pickuptype == PICKUP_START)
{
- teem = kh_Team_ByID(i);
- players = 0;
- entity my_player = world;
- FOR_EACH_PLAYER(player)
- if(player.deadflag == DEAD_NO)
- if(!player.BUTTON_CHAT)
- if(player.team == teem)
- {
- ++players;
- if(random() * players <= 1)
- my_player = player;
- }
- kh_Key_Spawn(my_player, 360 * i / kh_teams, i);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_START_), player.netname);
+ Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_4(key, CENTER_KEYHUNT_START_));
+ Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, APP_TEAM_ENT_4(key, CHOICE_KEYHUNT_START_TEAM_), Team_ColorCode(player.team), player.netname);
}
+ else
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_PICKUP_), player.netname);
+ Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_4(key, CENTER_KEYHUNT_PICKUP_));
+ Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, APP_TEAM_ENT_4(key, CHOICE_KEYHUNT_PICKUP_TEAM_), Team_ColorCode(player.team), player.netname);
- kh_tracking_enabled = false;
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_SCAN, autocvar_g_balance_keyhunt_delay_tracking);
- kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_tracking, kh_EnableTrackingDevice);
-}
-
-float kh_HandleFrags(entity attacker, entity targ, float f) // adds to the player score
-{
- if(attacker == targ)
- return f;
+ sound(player, CH_TRIGGER, key.snd_key_taken, VOL_BASE, ATTEN_NONE);
+ }
- if(targ.kh_next)
+ // scoring
+ PlayerScore_Add(player, SP_KH_PICKUPS, 1);
+ nades_GiveBonus(player, autocvar_g_nades_bonus_score_minor);
+ switch(pickuptype)
{
- if(attacker.team == targ.team)
- {
- entity k;
- float nk;
- nk = 0;
- for(k = targ.kh_next; k != world; k = k.kh_next)
- ++nk;
- kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", -nk * autocvar_g_balance_keyhunt_score_collect, 0);
- }
- else
+ case PICKUP_KILLED:
+ case PICKUP_DROPPED:
{
- kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", autocvar_g_balance_keyhunt_score_carrierfrag-1, 0);
- PlayerScore_Add(attacker, SP_KH_KCKILLS, 1);
- // the frag gets added later
+ pickup_dropped_score = (autocvar_g_keyhunt_key_return_time ? bound(0, ((key.kh_droptime + autocvar_g_keyhunt_key_return_time) - time) / autocvar_g_keyhunt_key_return_time, 1) : 1);
+ pickup_dropped_score = floor((autocvar_g_keyhunt_score_pickup_dropped_late * (1 - pickup_dropped_score) + autocvar_g_keyhunt_score_pickup_dropped_early * pickup_dropped_score) + 0.5);
+ dprint("pickup_dropped_score is ", ftos(pickup_dropped_score), "\n");
+ PlayerTeamScore_AddScore(player, pickup_dropped_score);
+ kh_EventLog("pickup", key.team, player);
+ break;
}
+
+ default: break;
}
- return f;
+ // effects
+ Send_Effect(key.toucheffectnum, player.origin, '0 0 0', 1);
+
+ // waypoints
+ if(pickuptype == PICKUP_DROPPED || pickuptype == PICKUP_KILLED) { WaypointSprite_Kill(key.wps_keydropped); }
+ kh_KeycarrierWaypoints(player);
+ WaypointSprite_Ping(player.wps_keycarrier);
}
-void kh_Initialize() // sets up th KH environment
+
+// ===================
+// Main Key Functions
+// ===================
+
+void kh_CheckKeyReturn(entity key, float returntype)
{
- precache_sound(kh_sound_capture);
- precache_sound(kh_sound_destroy);
- precache_sound(kh_sound_drop);
- precache_sound(kh_sound_collect);
- precache_sound(kh_sound_alarm); // the new siren
-
-#ifdef KH_PLAYER_USE_CARRIEDMODEL
- precache_model("models/keyhunt/key-carried.md3");
-#endif
- precache_model("models/keyhunt/key.md3");
+ if((key.kh_status == KEY_DROPPED) || (key.kh_status == KEY_PASSING))
+ {
+ if(key.wps_keydropped) { WaypointSprite_UpdateHealth(key.wps_keydropped, key.health); }
- // setup variables
- kh_teams = autocvar_g_keyhunt_teams_override;
- if(kh_teams < 2)
- kh_teams = autocvar_g_keyhunt_teams;
- kh_teams = bound(2, kh_teams, 4);
+ if((key.health <= 0) || (time >= key.kh_droptime + autocvar_g_keyhunt_key_return_time))
+ {
+ switch(returntype)
+ {
+ case RETURN_DAMAGE: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_KEYRETURN_DAMAGED_)); break;
+ case RETURN_NEEDKILL:
+ {
+ entity killer = key.kh_dropper.kh_lastkiller;
+ if(autocvar_g_keyhunt_key_return_tokiller && IS_PLAYER(killer) && killer.health > 0 && !killer.frozen)
+ {
+ kh_Handle_Pickup(key, killer, PICKUP_KILLED);
+ return;
+ }
+ else
+ {
+ entity newcarrier = kh_ChooseNewCarrier(key);
+ if(autocvar_g_keyhunt_key_return_toenemy && newcarrier)
+ {
+ kh_Handle_Pickup(key, newcarrier, PICKUP_KILLED);
+ return;
+ }
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_KEYRETURN_NEEDKILL_));
+ break;
+ }
+ }
- // make a KH entity for controlling the game
- kh_controller = spawn();
- kh_controller.think = kh_Controller_Think;
- kh_Controller_SetThink(0, kh_WaitForPlayers);
-
- setmodel(kh_controller, "models/keyhunt/key.md3");
- kh_key_dropped = kh_controller.modelindex;
- /*
- dprint(vtos(kh_controller.mins));
- dprint(vtos(kh_controller.maxs));
- dprint("\n");
- */
-#ifdef KH_PLAYER_USE_CARRIEDMODEL
- setmodel(kh_controller, "models/keyhunt/key-carried.md3");
- kh_key_carried = kh_controller.modelindex;
-#else
- kh_key_carried = kh_key_dropped;
-#endif
-
- kh_controller.model = "";
- kh_controller.modelindex = 0;
-
- addstat(STAT_KH_KEYS, AS_INT, kh_state);
+ default:
+ case RETURN_TIMEOUT:
+ { Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(key, INFO_KEYHUNT_KEYRETURN_TIMEOUT_)); break; }
+ }
+ sound(key, CH_TRIGGER, key.snd_key_respawn, VOL_BASE, ATTEN_NONE);
+ kh_EventLog("returned", key.team, world);
+ kh_RemoveKey(key);
+ }
+ }
+}
- void kh_KeyDamage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
- kh_ScoreRules(kh_teams);
++void kh_KeyDamage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+ if(ITEM_DAMAGE_NEEDKILL(deathtype))
+ {
+ self.health = 0;
+ kh_CheckKeyReturn(self, RETURN_NEEDKILL);
+ return;
+ }
+ if(autocvar_g_keyhunt_key_return_damage)
+ {
+ // reduce health and check if it should be returned
+ self.health = self.health - damage;
+ kh_CheckKeyReturn(self, RETURN_DAMAGE);
+ return;
+ }
}
-void kh_finalize()
+void kh_KeyThink()
{
- // to be called before intermission
- kh_FinishRound();
- remove(kh_controller);
- kh_controller = world;
+ self.nextthink = time + KEY_THINKRATE; // only 5 fps, more is unnecessary.
+
+ // sanity checks
+ if(self.mins != KEY_MIN || self.maxs != KEY_MAX) { // reset the key boundaries in case it got squished
+ dprint("wtf the key got squashed?\n");
+ tracebox(self.origin, KEY_MIN, KEY_MAX, self.origin, MOVE_NOMONSTERS, self);
+ if(!trace_startsolid) // can we resize it without getting stuck?
+ setsize(self, KEY_MIN, KEY_MAX); }
+
+ switch(self.kh_status) // reset key angles in case warpzones adjust it
+ {
+ case KEY_DROPPED:
+ {
+ self.angles = '0 0 0';
+ break;
+ }
+
+ default: break;
+ }
+
+ // main think method
+ switch(self.kh_status)
+ {
+ case KEY_DROPPED:
+ {
+ if(autocvar_g_keyhunt_key_dropped_floatinwater)
+ {
+ vector midpoint = ((self.absmin + self.absmax) * 0.5);
+ if(pointcontents(midpoint) == CONTENT_WATER)
+ {
+ self.velocity = self.velocity * 0.5;
+
+ if(pointcontents(midpoint + KEY_FLOAT_OFFSET) == CONTENT_WATER)
+ { self.velocity_z = autocvar_g_keyhunt_key_dropped_floatinwater; }
+ else
+ { self.movetype = MOVETYPE_FLY; }
+ }
+ else if(self.movetype == MOVETYPE_FLY) { self.movetype = MOVETYPE_TOSS; }
+ }
+ if(autocvar_g_keyhunt_key_return_time)
+ {
+ self.health -= ((self.max_key_health / autocvar_g_keyhunt_key_return_time) * KEY_THINKRATE);
+ kh_CheckKeyReturn(self, RETURN_TIMEOUT);
+ return;
+ }
+ return;
+ }
+
+ case KEY_CARRY:
+ {
+ makevectors('0 1 0' * (self.cnt + (time % 360) * KH_KEY_XYSPEED));
+ setorigin(self, v_forward * ((self.owner.vehicle) ? KH_VEHICLE_KEY_XYDIST : KH_KEY_XYDIST) + '0 0 1' * self.origin_z);
+ return;
+ }
+
+ case KEY_PASSING:
+ {
+ vector targ_origin = ((self.pass_target.absmin + self.pass_target.absmax) * 0.5);
+ targ_origin = WarpZone_RefSys_TransformOrigin(self.pass_target, self, targ_origin); // origin of target as seen by the key (us)
+ WarpZone_TraceLine(self.origin, targ_origin, MOVE_NOMONSTERS, self);
+
+ if((self.pass_target == world)
+ || (self.pass_target.deadflag != DEAD_NO)
+ || (vlen(self.origin - targ_origin) > autocvar_g_keyhunt_pass_radius)
+ || ((trace_fraction < 1) && (trace_ent != self.pass_target))
+ || (time > self.kh_droptime + autocvar_g_keyhunt_pass_timelimit))
+ {
+ // give up, pass failed
+ kh_Handle_Drop(self, world, DROP_PASS);
+ }
+ else
+ {
+ // still a viable target, go for it
+ kh_CalculatePassVelocity(self, targ_origin, self.origin, true);
+ }
+ return;
+ }
+
+ default: // this should never happen
+ {
+ dprint("kh_KeyThink(): Key exists with no status?\n");
+ return;
+ }
+ }
}
-// register this as a mutator
+float kh_Customize()
+{
+ entity e = WaypointSprite_getviewentity(other);
-MUTATOR_HOOKFUNCTION(kh_Key_DropAll)
+ if(self.owner == e)
+ self.glow_trail = 0;
+ else if(autocvar_g_keyhunt_key_glowtrails)
+ self.glow_trail = 1;
+
+ return true;
+}
+
+void kh_KeyTouch()
{
- kh_Key_DropAll(self, true);
- return 0;
+ if(gameover) { return; }
+
+ entity toucher = other;
+
+ // automatically kill the key and return it if it touched lava/slime/nodrop surfaces
+ if(ITEM_TOUCH_NEEDKILL())
+ {
+ self.health = 0;
+ kh_CheckKeyReturn(self, RETURN_NEEDKILL);
+ return;
+ }
+
+ // special touch behaviors
+ if(toucher.frozen) { return; }
+ else if(IS_VEHICLE(toucher))
+ {
+ if(autocvar_g_keyhunt_allow_vehicle_touch && toucher.owner)
+ toucher = toucher.owner; // the player is actually the vehicle owner, not other
+ else
+ return; // do nothing
+ }
+ else if (!IS_PLAYER(toucher)) // The key just touched an object, most likely the world
+ {
+ if(time > self.wait) // if we haven't in a while, play a sound/effect
+ {
+ Send_Effect(self.toucheffectnum, self.origin, '0 0 0', 1);
+ sound(self, CH_TRIGGER, self.snd_key_touch, VOL_BASE, ATTEN_NORM);
+ self.wait = time + KEY_TOUCHRATE;
+ }
+ return;
+ }
+ else if(toucher.deadflag != DEAD_NO) { return; }
+
+ switch(self.kh_status)
+ {
+ case KEY_DROPPED:
+ {
+ if(((toucher != self.kh_dropper) || (time > self.kh_droptime + autocvar_g_keyhunt_key_collect_delay)))
+ kh_Handle_Pickup(self, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy key
+ break;
+ }
+
+ case KEY_CARRY:
+ {
+ dprint("Someone touched a key even though it was being carried?\n");
+ break;
+ }
+
+ case KEY_PASSING:
+ {
+ if((IS_PLAYER(toucher)) && (toucher.deadflag == DEAD_NO) && (toucher != self.pass_sender))
+ kh_Handle_Retrieve(self, toucher);
+ break;
+ }
+ }
+}
+
+void kh_RemoveKey(entity key)
+{
+ // kill old waypointsprite
+ WaypointSprite_Kill(key.owner.wps_keycarrier);
+ WaypointSprite_Kill(key.owner.wps_enemykeycarrier);
+
+ if(key.kh_status == KEY_DROPPED)
+ { WaypointSprite_Kill(key.wps_keydropped); }
+
+ // reset the key
+ setattachment(key, world, "");
+
+ remove(key);
+}
+
+void kh_Reset()
+{
+ if(self.owner)
+ if(IS_PLAYER(self.owner))
+ kh_Handle_Throw(self.owner, world, self, DROP_RESET);
+
+ kh_RemoveKey(self);
+}
+
+void kh_KeySetup(float teamnumber, entity key, entity player) // called when spawning a key entity
+{
+ // declarations
+ self = key; // for later usage with droptofloor()
+
+ // main setup
+ key.kh_worldkeynext = kh_worldkeylist; // link key into kh_worldkeylist
+ kh_worldkeylist = key;
+
+ setattachment(key, world, "");
+
+ key.netname = sprintf("%s%s^7 key", Team_ColorCode(teamnumber), Team_ColorName_Upper(teamnumber));
+ key.team = teamnumber;
+ key.classname = "item_key_team";
+ key.target = "###item###"; // wut?
+ key.flags = FL_ITEM | FL_NOTARGET;
+ key.solid = SOLID_TRIGGER;
+ key.takedamage = DAMAGE_NO;
+ key.customizeentityforclient = kh_Customize;
+ key.damageforcescale = autocvar_g_keyhunt_key_damageforcescale;
+ key.max_key_health = ((autocvar_g_keyhunt_key_return_damage && autocvar_g_keyhunt_key_health) ? autocvar_g_keyhunt_key_health : 100);
+ key.health = key.max_key_health;
+ key.event_damage = kh_KeyDamage;
+ key.pushable = true;
+ key.teleportable = TELEPORT_NORMAL;
+ key.damagedbytriggers = autocvar_g_keyhunt_key_return_when_unreachable;
+ key.damagedbycontents = autocvar_g_keyhunt_key_return_when_unreachable;
+ key.velocity = '0 0 0';
+ key.mangle = key.angles;
+ key.cnt = 360 * Team_TeamToNumber(teamnumber) / kh_teams;
+ key.reset = kh_Reset;
+ key.touch = kh_KeyTouch;
+ key.think = kh_KeyThink;
+ key.nextthink = time + KEY_THINKRATE;
+ key.kh_status = KEY_CARRY;
+ key.colormod = Team_ColorRGB(teamnumber) * KEY_BRIGHTNESS;
+
+ // appearence
+ key.model = "models/keyhunt/key.md3";
+ key.scale = KEY_SCALE;
+ key.toucheffectnum = ((teamnumber == NUM_TEAM_1) ? EFFECT_FLAG_RED_TOUCH : ((teamnumber == NUM_TEAM_2) ? EFFECT_FLAG_BLUE_TOUCH : ((teamnumber == NUM_TEAM_3) ? EFFECT_FLAG_YELLOW_TOUCH : EFFECT_FLAG_PINK_TOUCH)));
+ key.passeffectnum = ((teamnumber == NUM_TEAM_1) ? EFFECT_RED_PASS : ((teamnumber == NUM_TEAM_2) ? EFFECT_BLUE_PASS : ((teamnumber == NUM_TEAM_3) ? EFFECT_YELLOW_PASS : EFFECT_PINK_PASS)));
+ key.capeffectnum = ((teamnumber == NUM_TEAM_1) ? EFFECT_RED_CAP : ((teamnumber == NUM_TEAM_2) ? EFFECT_BLUE_CAP : ((teamnumber == NUM_TEAM_3) ? EFFECT_YELLOW_CAP : EFFECT_PINK_CAP)));
+
+ // sound
+ key.snd_key_taken = "kh/collect.wav";
+ key.snd_key_capture = "kh/capture.wav";
+ key.snd_key_dropped = "kh/drop.wav";
+ key.snd_key_touch = "ctf/touch.wav";
+ key.snd_key_pass = "ctf/pass.wav";
+
+ // appearence
+ setmodel(key, key.model); // precision set below
+ setsize(key, KEY_MIN, KEY_MAX);
+ setorigin(key, (key.origin + KEY_SPAWN_OFFSET));
+
+ if(autocvar_g_keyhunt_key_glowtrails)
+ {
+ key.glow_color = ((teamnumber == NUM_TEAM_1) ? 251 : ((teamnumber == NUM_TEAM_2) ? 210 : ((teamnumber == NUM_TEAM_3) ? 110 : 145)));
+ key.glow_size = 25;
+ key.glow_trail = 1;
+ }
+
+ key.effects |= EF_LOWPRECISION;
+ if(autocvar_g_keyhunt_fullbrightkeys) { key.effects |= EF_FULLBRIGHT; }
+ if(autocvar_g_keyhunt_dynamiclights)
+ {
+ switch(teamnumber)
+ {
+ case NUM_TEAM_1: key.effects |= EF_RED; break;
+ case NUM_TEAM_2: key.effects |= EF_BLUE; break;
+ case NUM_TEAM_3: key.effects |= EF_DIMLIGHT; break;
+ case NUM_TEAM_4: key.effects |= EF_RED; break;
+ }
+ }
+
+ kh_Handle_Pickup(key, player, PICKUP_START);
+}
+
+
+// ==================
+// Legacy Bot Logic
+// ==================
+
+void() havocbot_role_kh_carrier;
+void() havocbot_role_kh_defense;
+void() havocbot_role_kh_offense;
+void() havocbot_role_kh_freelancer;
+void havocbot_goalrating_kh(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy)
+{
+ entity head;
+ for (head = kh_worldkeylist; head && !wasfreed(head); head = head.kh_worldkeynext)
+ {
+ if(head.owner == self)
+ continue;
+ if(!head.owner)
+ navigation_routerating(head, ratingscale_dropped * BOT_PICKUP_RATING_HIGH, 100000);
+ else if(head.team == self.team)
+ navigation_routerating(head.owner, ratingscale_team * BOT_PICKUP_RATING_HIGH, 100000);
+ else
+ navigation_routerating(head.owner, ratingscale_enemy * BOT_PICKUP_RATING_HIGH, 100000);
+ }
+
+ havocbot_goalrating_items(1, self.origin, 10000);
+}
+
+void havocbot_role_kh_carrier()
+{
+ if(self.deadflag != DEAD_NO)
+ return;
+
+ entity key;
+ float is_carrier = false;
+ KH_FOR_EACH_KEY(key)
+ if(key.owner == self)
+ {
+ is_carrier = true;
+ break;
+ }
+
+ if (!is_carrier)
+ {
+ dprint("changing role to freelancer\n");
+ self.havocbot_role = havocbot_role_kh_freelancer;
+ self.havocbot_role_timeout = 0;
+ return;
+ }
+
+ if (self.bot_strategytime < time)
+ {
+ self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+ navigation_goalrating_start();
+
+ float key_count = 0;
+ KH_FOR_EACH_KEY(key) { if(!wasfreed(key) && SAME_TEAM(key, self)) ++key_count; }
+
+ if(key_count >= kh_teams)
+ havocbot_goalrating_kh(10, 0.1, 0.1); // bring home
+ else
+ havocbot_goalrating_kh(4, 4, 1); // play defensively
+
+ navigation_goalrating_end();
+ }
+}
+
+void havocbot_role_kh_defense()
+{
+ if(self.deadflag != DEAD_NO)
+ return;
+
+ entity key;
+ float is_carrier = false;
+ KH_FOR_EACH_KEY(key)
+ if(key.owner == self)
+ {
+ is_carrier = true;
+ break;
+ }
+
+ if (is_carrier)
+ {
+ dprint("changing role to carrier\n");
+ self.havocbot_role = havocbot_role_kh_carrier;
+ self.havocbot_role_timeout = 0;
+ return;
+ }
+
+ if (!self.havocbot_role_timeout)
+ self.havocbot_role_timeout = time + random() * 10 + 20;
+ if (time > self.havocbot_role_timeout)
+ {
+ dprint("changing role to freelancer\n");
+ self.havocbot_role = havocbot_role_kh_freelancer;
+ self.havocbot_role_timeout = 0;
+ return;
+ }
+
+ if (self.bot_strategytime < time)
+ {
+ self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+ navigation_goalrating_start();
+
+ float key_count = 0;
+ KH_FOR_EACH_KEY(key) { if(!wasfreed(key) && SAME_TEAM(key, self)) ++key_count; }
+
+ if(key_count >= kh_teams)
+ havocbot_goalrating_kh(10, 0.1, 0.1); // defend key carriers
+ else if(key_count == 0)
+ havocbot_goalrating_kh(4, 1, 0.1); // play defensively
+ else
+ havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK ANYWAY
+
+ navigation_goalrating_end();
+ }
+}
+
+void havocbot_role_kh_offense()
+{
+ if(self.deadflag != DEAD_NO)
+ return;
+
+ entity key;
+ float is_carrier = false;
+ KH_FOR_EACH_KEY(key)
+ if(key.owner == self)
+ {
+ is_carrier = true;
+ break;
+ }
+
+ if (is_carrier)
+ {
+ dprint("changing role to carrier\n");
+ self.havocbot_role = havocbot_role_kh_carrier;
+ self.havocbot_role_timeout = 0;
+ return;
+ }
+
+ if (!self.havocbot_role_timeout)
+ self.havocbot_role_timeout = time + random() * 10 + 20;
+ if (time > self.havocbot_role_timeout)
+ {
+ dprint("changing role to freelancer\n");
+ self.havocbot_role = havocbot_role_kh_freelancer;
+ self.havocbot_role_timeout = 0;
+ return;
+ }
+
+ if (self.bot_strategytime < time)
+ {
+ self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+ navigation_goalrating_start();
+
+ float key_count = 0;
+ KH_FOR_EACH_KEY(key) { if(!wasfreed(key) && SAME_TEAM(key, self)) ++key_count; }
+
+ if(key_count >= kh_teams)
+ havocbot_goalrating_kh(10, 0.1, 0.1); // defend key carriers
+ else if(key_count == 0)
+ havocbot_goalrating_kh(4, 1, 0.1); // play defensively
+ else
+ havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK ANYWAY
+
+ navigation_goalrating_end();
+ }
+}
+
+void havocbot_role_kh_freelancer()
+{
+ if(self.deadflag != DEAD_NO)
+ return;
+
+ entity key;
+ float is_carrier = false;
+ KH_FOR_EACH_KEY(key)
+ if(key.owner == self)
+ {
+ is_carrier = true;
+ break;
+ }
+
+ if (is_carrier)
+ {
+ dprint("changing role to carrier\n");
+ self.havocbot_role = havocbot_role_kh_carrier;
+ self.havocbot_role_timeout = 0;
+ return;
+ }
+
+ if (!self.havocbot_role_timeout)
+ self.havocbot_role_timeout = time + random() * 10 + 10;
+ if (time > self.havocbot_role_timeout)
+ {
+ if (random() < 0.5)
+ {
+ dprint("changing role to offense\n");
+ self.havocbot_role = havocbot_role_kh_offense;
+ }
+ else
+ {
+ dprint("changing role to defense\n");
+ self.havocbot_role = havocbot_role_kh_defense;
+ }
+ self.havocbot_role_timeout = 0;
+ return;
+ }
+
+ if (self.bot_strategytime < time)
+ {
+ self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+ navigation_goalrating_start();
+
+ float key_count = 0;
+ KH_FOR_EACH_KEY(key) { if(!wasfreed(key) && SAME_TEAM(key, self)) ++key_count; }
+
+ if(key_count >= kh_teams)
+ havocbot_goalrating_kh(10, 0.1, 0.1); // defend key carriers
+ else if(key_count == 0)
+ havocbot_goalrating_kh(4, 1, 0.1); // play defensively
+ else
+ havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK ANYWAY
+
+ navigation_goalrating_end();
+ }
+}
+
+
+// ==============
+// Hook Functions
+// ==============
+
+MUTATOR_HOOKFUNCTION(kh_PlayerPreThink)
+{
+ entity key;
+ float s;
+ float f;
+
+ s = 0;
+ KH_FOR_EACH_KEY(key)
+ {
+ if(key.owner)
+ f = key.team;
+ else
+ f = 30;
+ s |= pow(32, Team_TeamToNumber(key.team) - 1) * f;
+ }
+
+ self.kh_keystatus = s;
+
+ KH_FOR_EACH_KEY(key)
+ {
+ if(key.owner == self)
+ key.owner.kh_keystatus |= pow(32, Team_TeamToNumber(key.team) - 1) * 31;
+ }
+
+ // update the health of the key carrier waypointsprite
+ if(self.wps_keycarrier)
+ WaypointSprite_UpdateHealth(self.wps_keycarrier, '1 0 0' * healtharmor_maxdamage(self.health, self.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON));
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(kh_PlayerDamage) // for changing damage and force values that are applied to players in g_damage.qc
+{
+ entity key;
+ float targ_iscarrier = false, attacker_iscarrier = false;
+ KH_FOR_EACH_KEY(key)
+ {
+ if(key.owner == frag_attacker) { attacker_iscarrier = true; }
+ if(key.owner == frag_target) { targ_iscarrier = true; }
+ }
+
+ if(attacker_iscarrier) // if the attacker is a keycarrier
+ {
+ if(frag_target == frag_attacker) // damage done to yourself
+ {
+ frag_damage *= autocvar_g_keyhunt_keycarrier_selfdamagefactor;
+ frag_force *= autocvar_g_keyhunt_keycarrier_selfforcefactor;
+ }
+ else // damage done to everyone else
+ {
+ frag_damage *= autocvar_g_keyhunt_keycarrier_damagefactor;
+ frag_force *= autocvar_g_keyhunt_keycarrier_forcefactor;
+ }
+ }
+ else if(targ_iscarrier && (frag_target.deadflag == DEAD_NO) && DIFF_TEAM(frag_target, frag_attacker)) // if the target is a keycarrier
+ {
+ if(autocvar_g_keyhunt_keycarrier_auto_helpme_damage > ('1 0 0' * healtharmor_maxdamage(frag_target.health, frag_target.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON)))
+ if(time > frag_target.wps_helpme_time + autocvar_g_keyhunt_keycarrier_auto_helpme_time)
+ {
+ frag_target.wps_helpme_time = time;
+ WaypointSprite_HelpMePing(frag_target.wps_keycarrier);
+ }
+ // todo: add notification for when key carrier needs help?
+ }
+ return false;
}
MUTATOR_HOOKFUNCTION(kh_PlayerDies)
-float autocvar_g_onslaught_spawn_at_controlpoints;
-float autocvar_g_onslaught_spawn_at_generator;
-float autocvar_g_onslaught_cp_proxydecap;
-float autocvar_g_onslaught_cp_proxydecap_distance = 512;
-float autocvar_g_onslaught_cp_proxydecap_dps = 100;
+#include "../../common/effects.qh"
+#include "../round_handler.qh"
+#include "../controlpoint.qh"
+#include "../generator.qh"
+#include "../../common/triggers/subs.qh"
- float ons_CaptureShield_Customize()
-void onslaught_generator_updatesprite(entity e);
-void onslaught_controlpoint_updatesprite(entity e);
-void onslaught_link_checkupdate();
-
-.entity sprite;
-.string target2;
-.float iscaptured;
-.float islinked;
-.float isgenneighbor_red;
-.float isgenneighbor_blue;
-.float iscpneighbor_red;
-.float iscpneighbor_blue;
-.float isshielded;
-.float lasthealth;
-.float lastteam;
-.float lastshielded;
-.float lastcaptured;
++bool ons_CaptureShield_Customize()
+{
+ entity e = WaypointSprite_getviewentity(other);
-entity ons_red_generator;
-entity ons_blue_generator;
+ if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, e.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return false; }
+ if(SAME_TEAM(self, e)) { return false; }
-void ons_gib_damage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector vforce)
-{
- self.velocity = self.velocity + vforce;
+ return true;
}
-.float giblifetime;
-void ons_throwgib_think()
+void ons_CaptureShield_Touch()
{
- float d;
+ if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, other.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return; }
+ if(!IS_PLAYER(other)) { return; }
+ if(SAME_TEAM(other, self)) { return; }
- self.nextthink = time + 0.05;
+ vector mymid = (self.absmin + self.absmax) * 0.5;
+ vector othermid = (other.absmin + other.absmax) * 0.5;
- d = self.giblifetime - time;
+ Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * ons_captureshield_force);
- if(d<0)
+ if(IS_REAL_CLIENT(other))
{
- self.think = SUB_Remove;
- return;
+ play2(other, "onslaught/damageblockedbyshield.wav");
+
+ if(self.enemy.classname == "onslaught_generator")
+ Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_GENERATOR_SHIELDED);
+ else
+ Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_CONTROLPOINT_SHIELDED);
}
- if(d<1)
- self.alpha = d;
+}
- if(d>2)
- if(random()<0.6)
- pointparticles(particleeffectnum("onslaught_generator_gib_flame"), self.origin, '0 0 0', 1);
+void ons_CaptureShield_Reset()
+{
+ self.colormap = self.enemy.colormap;
+ self.team = self.enemy.team;
}
-void ons_throwgib(vector v_from, vector v_to, string smodel, float f_lifetime, float b_burn)
+void ons_CaptureShield_Spawn(entity generator, float is_generator)
{
- entity gib;
+ entity shield = spawn();
+
+ shield.enemy = generator;
+ shield.team = generator.team;
+ shield.colormap = generator.colormap;
+ shield.reset = ons_CaptureShield_Reset;
+ shield.touch = ons_CaptureShield_Touch;
+ shield.customizeentityforclient = ons_CaptureShield_Customize;
+ shield.classname = "ons_captureshield";
+ shield.effects = EF_ADDITIVE;
+ shield.movetype = MOVETYPE_NOCLIP;
+ shield.solid = SOLID_TRIGGER;
+ shield.avelocity = '7 0 11';
+ shield.scale = 1;
+ shield.model = ((is_generator) ? "models/onslaught/generator_shield.md3" : "models/onslaught/controlpoint_shield.md3");
+
+ precache_model(shield.model);
+ setorigin(shield, generator.origin);
+ setmodel(shield, shield.model);
+ setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs);
+}
- gib = spawn();
- setmodel(gib, smodel);
- setorigin(gib, v_from);
- gib.solid = SOLID_BBOX;
- gib.movetype = MOVETYPE_BOUNCE;
- gib.takedamage = DAMAGE_YES;
- gib.event_damage = ons_gib_damage;
- gib.health = -1;
- gib.effects = EF_LOWPRECISION;
- gib.flags = FL_NOTARGET;
- gib.velocity = v_to;
- gib.giblifetime = time + f_lifetime;
+// ==========
+// Junk Pile
+// ==========
- if (b_burn)
+void ons_debug(string input)
+{
+ switch(autocvar_g_onslaught_debug)
{
- gib.think = ons_throwgib_think;
- gib.nextthink = time + 0.05;
+ case 1: dprint(input); break;
+ case 2: print(input); break;
}
- else
- SUB_SetFade(gib, gib.giblifetime, 2);
}
void onslaught_updatelinks()
return 0;
}
- void ons_ControlPoint_Icon_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-float overtime_msg_time;
-void onslaught_generator_think()
++void ons_ControlPoint_Icon_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
{
- float d;
- entity e;
- self.nextthink = ceil(time + 1);
- if (!gameover)
+ entity oself;
+
+ if(damage <= 0) { return; }
+
+ if (self.owner.isshielded)
{
- if (autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60)
- {
- if (!overtime_msg_time)
- {
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT);
- overtime_msg_time = time;
- }
- // self.max_health / 300 gives 5 minutes of overtime.
- // control points reduce the overtime duration.
- sound(self, CH_TRIGGER, "onslaught/generator_decay.wav", VOL_BASE, ATTEN_NORM);
- d = 1;
- e = findchain(classname, "onslaught_controlpoint");
- while (e)
+ // this is protected by a shield, so ignore the damage
+ if (time > self.pain_finished)
+ if (IS_PLAYER(attacker))
{
- if (e.team != self.team)
- if (e.islinked)
- d = d + 1;
- e = e.chain;
+ play2(attacker, "onslaught/damageblockedbyshield.wav");
+ self.pain_finished = time + 1;
+ attacker.typehitsound += 1; // play both sounds (shield is way too quiet)
}
- if(autocvar_g_campaign && autocvar__campaign_testrun)
- d = d * self.max_health;
- else
- d = d * self.max_health / max(30, 60 * autocvar_timelimit_suddendeath);
-
- Damage(self, self, self, d, DEATH_HURTTRIGGER, self.origin, '0 0 0');
- }
- else if (overtime_msg_time)
- overtime_msg_time = 0;
-
- if(!self.isshielded && self.wait < time)
- {
- self.wait = time + 5;
- FOR_EACH_REALPLAYER(e)
- {
- if(SAME_TEAM(e, self))
- {
- Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_ONS_NOTSHIELDED);
- soundto(MSG_ONE, e, CHAN_AUTO, "kh/alarm.wav", VOL_BASE, ATTEN_NONE); // FIXME: Uniqe sound?
- }
- }
- }
+ return;
}
-}
-
-void onslaught_generator_ring_spawn(vector org)
-{
- modeleffect_spawn("models/onslaught/shockwavetransring.md3", 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, -16, 0.1, 1.25, 0.25);
-}
-void onslaught_generator_ray_think()
-{
- self.nextthink = time + 0.05;
- if(self.count > 10)
+ if(IS_PLAYER(attacker))
+ if(time - ons_notification_time[self.team] > 10)
{
- self.think = SUB_Remove;
- return;
+ play2team(self.team, "onslaught/controlpoint_underattack.wav");
+ ons_notification_time[self.team] = time;
}
- if(self.count > 5)
- self.alpha -= 0.1;
+ self.health = self.health - damage;
+ if(self.owner.iscaptured)
+ WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
+ else
+ WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / ONS_CP_THINKRATE));
+ self.pain_finished = time + 1;
+ // particles on every hit
+ Send_Effect(EFFECT_SPARKS, hitloc, force * -1, 1);
+ //sound on every hit
+ if (random() < 0.5)
+ sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTEN_NORM);
else
- self.alpha += 0.1;
+ sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTEN_NORM);
- self.scale += 0.2;
- self.count +=1;
-}
+ if (self.health < 0)
+ {
+ sound(self, CH_TRIGGER, W_Sound("grenade_impact"), VOL_BASE, ATTEN_NORM);
+ Send_Effect(EFFECT_ROCKET_EXPLODE, self.origin, '0 0 0', 1);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_CPDESTROYED_), self.owner.message, attacker.netname);
+
+ PlayerScore_Add(attacker, SP_ONS_TAKES, 1);
+ PlayerScore_Add(attacker, SP_SCORE, 10);
+
+ self.owner.goalentity = world;
+ self.owner.islinked = false;
+ self.owner.iscaptured = false;
+ self.owner.team = 0;
+ self.owner.colormap = 1024;
-void onslaught_generator_ray_spawn(vector org)
-{
- entity e;
- e = spawn();
- setmodel(e, "models/onslaught/ons_ray.md3");
- setorigin(e, org);
- e.angles = randomvec() * 360;
- e.alpha = 0;
- e.scale = random() * 5 + 8;
- e.think = onslaught_generator_ray_think;
- e.nextthink = time + 0.05;
-}
+ WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0);
-void onslaught_generator_shockwave_spawn(vector org)
-{
- shockwave_spawn("models/onslaught/shockwave.md3", org, -64, 0.75, 0.5);
-}
+ onslaught_updatelinks();
-void onslaught_generator_damage_think()
-{
- if(self.owner.health < 0)
- {
- self.think = SUB_Remove;
- return;
- }
- self.nextthink = time+0.1;
+ // Use targets now (somebody make sure this is in the right place..)
+ oself = self;
+ self = self.owner;
+ activator = self;
+ SUB_UseTargets ();
+ self = oself;
- // damaged fx (less probable the more damaged is the generator)
- if(random() < 0.9 - self.owner.health / self.owner.max_health)
- if(random() < 0.01)
- {
- pointparticles(particleeffectnum("electro_ballexplode"), self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1);
- sound(self, CH_TRIGGER, "onslaught/electricity_explode.wav", VOL_BASE, ATTEN_NORM);
- }
- else
- pointparticles(particleeffectnum("torch_small"), self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1);
-}
+ self.owner.waslinked = self.owner.islinked;
+ if(self.owner.model != "models/onslaught/controlpoint_pad.md3")
+ setmodel_fixsize(self.owner, "models/onslaught/controlpoint_pad.md3");
+ //setsize(self, '-32 -32 0', '32 32 8');
-void onslaught_generator_damage_spawn(entity gd_owner)
-{
- entity e;
- e = spawn();
- e.owner = gd_owner;
- e.health = self.owner.health;
- setorigin(e, gd_owner.origin);
- e.think = onslaught_generator_damage_think;
- e.nextthink = time+1;
+ remove(self);
+ }
+
+ self.SendFlags |= CPSF_STATUS;
}
-void onslaught_generator_deaththink()
+void ons_ControlPoint_Icon_Think()
{
- vector org;
- float i;
-
- if (!self.count)
- self.count = 40;
+ entity oself;
+ self.nextthink = time + ONS_CP_THINKRATE;
- // White shockwave
- if(self.count==40||self.count==20)
+ if(autocvar_g_onslaught_cp_proxydecap)
{
- onslaught_generator_ring_spawn(self.origin);
- sound(self, CH_TRIGGER, "onslaught/shockwave.wav", VOL_BASE, ATTEN_NORM);
- }
+ float _enemy_count = 0;
+ float _friendly_count = 0;
+ float _dist;
+ entity _player;
- // Throw some gibs
- if(random() < 0.3)
- {
- i = random();
- if(i < 0.3)
- ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 11 + '0 0 20', "models/onslaught/gen_gib1.md3", 6, true);
- else if(i > 0.7)
- ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 12 + '0 0 20', "models/onslaught/gen_gib2.md3", 6, true);
- else
- ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 13 + '0 0 20', "models/onslaught/gen_gib3.md3", 6, true);
- }
+ FOR_EACH_PLAYER(_player)
+ {
+ if(!_player.deadflag)
+ {
+ _dist = vlen(_player.origin - self.origin);
+ if(_dist < autocvar_g_onslaught_cp_proxydecap_distance)
+ {
+ if(SAME_TEAM(_player, self))
+ ++_friendly_count;
+ else
+ ++_enemy_count;
+ }
+ }
+ }
+
+ _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE);
+ _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE);
- // Spawn fire balls
- for(i=0;i < 10;++i)
+ self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health);
+ self.SendFlags |= CPSF_STATUS;
+ if(self.health <= 0)
+ {
+ ons_ControlPoint_Icon_Damage(self, self, 1, 0, self.origin, '0 0 0');
+ return;
+ }
+ }
+
+ if (time > self.pain_finished + 5)
{
- org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20');
- pointparticles(particleeffectnum("onslaught_generator_gib_explode"), org, '0 0 0', 1);
+ if(self.health < self.max_health)
+ {
+ self.health = self.health + self.count;
+ if (self.health >= self.max_health)
+ self.health = self.max_health;
+ WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
+ }
}
- // Short explosion sound + small explosion
- if(random() < 0.25)
+ if(self.owner.islinked != self.owner.waslinked)
{
- te_explosion(self.origin);
- sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM);
- }
+ // unteam the spawnpoint if needed
+ float t;
+ t = self.owner.team;
+ if(!self.owner.islinked)
+ self.owner.team = 0;
+
+ oself = self;
+ self = self.owner;
+ activator = self;
+ SUB_UseTargets ();
+ self = oself;
- // Particles
- org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8');
- pointparticles(particleeffectnum("onslaught_generator_smallexplosion"), org, '0 0 0', 1);
+ self.owner.team = t;
- // rays
- if(random() > 0.25 )
- {
- onslaught_generator_ray_spawn(self.origin);
+ self.owner.waslinked = self.owner.islinked;
}
- // Final explosion
- if(self.count==1)
+ // damaged fx
+ if(random() < 0.6 - self.health / self.max_health)
{
- org = self.origin;
- te_explosion(org);
- onslaught_generator_shockwave_spawn(org);
- pointparticles(particleeffectnum("onslaught_generator_finalexplosion"), org, '0 0 0', 1);
- sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
- }
- else
- self.nextthink = time + 0.05;
+ Send_Effect(EFFECT_ELECTRIC_SPARKS, self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
- self.count = self.count - 1;
+ if(random() > 0.8)
+ sound(self, CH_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTEN_NORM);
+ else if (random() > 0.5)
+ sound(self, CH_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTEN_NORM);
+ }
}
-void onslaught_generator_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+void ons_ControlPoint_Icon_BuildThink()
{
- float i;
- if (damage <= 0)
- return;
- if(warmup_stage)
+ entity oself;
+ float a;
+
+ self.nextthink = time + ONS_CP_THINKRATE;
+
+ // only do this if there is power
+ a = ons_ControlPoint_CanBeLinked(self.owner, self.owner.team);
+ if(!a)
return;
- if (attacker != self)
+
+ self.health = self.health + self.count;
+
+ self.SendFlags |= CPSF_STATUS;
+
+ if (self.health >= self.max_health)
{
- if (self.isshielded)
+ self.health = self.max_health;
+ self.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on
+ self.think = ons_ControlPoint_Icon_Think;
+ sound(self, CH_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTEN_NORM);
+ self.owner.iscaptured = true;
+ self.solid = SOLID_BBOX;
+
+ float eff_team;
+ switch(self.owner.team)
{
- // this is protected by a shield, so ignore the damage
- if (time > self.pain_finished)
- if (IS_PLAYER(attacker))
- {
- play2(attacker, "onslaught/damageblockedbyshield.wav");
- self.pain_finished = time + 1;
- }
- return;
- }
- if (time > self.pain_finished)
- {
- self.pain_finished = time + 10;
- bprint(Team_ColoredFullName(self.team), " generator under attack!\n");
- play2team(self.team, "onslaught/generator_underattack.wav");
- }
- }
- self.health = self.health - damage;
- WaypointSprite_UpdateHealth(self.sprite, self.health);
- // choose an animation frame based on health
- self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1);
- // see if the generator is still functional, or dying
- if (self.health > 0)
- {
-#ifdef ONSLAUGHT_SPAM
- float h, lh;
- lh = ceil(self.lasthealth / 100) * 100;
- h = ceil(self.health / 100) * 100;
- if(lh != h)
- bprint(Team_ColoredFullName(self.team), " generator has less than ", ftos(h), " health remaining\n");
-#endif
- self.lasthealth = self.health;
- }
- else if (!warmup_stage)
- {
- if (attacker == self)
- bprint(Team_ColoredFullName(self.team), " generator spontaneously exploded due to overtime!\n");
- else
- {
- string t;
- t = Team_ColoredFullName(attacker.team);
- bprint(Team_ColoredFullName(self.team), " generator destroyed by ", t, "!\n");
+ case NUM_TEAM_1: eff_team = EFFECT_RED_CAP; break;
+ case NUM_TEAM_2: eff_team = EFFECT_BLUE_CAP; break;
+ case NUM_TEAM_3: eff_team = EFFECT_YELLOW_CAP; break;
+ case NUM_TEAM_4: eff_team = EFFECT_PINK_CAP; break;
+ default: eff_team = EFFECT_SPAWN_NEUTRAL; break;
}
- self.iscaptured = false;
- self.islinked = false;
- self.isshielded = false;
- self.takedamage = DAMAGE_NO; // can't be hurt anymore
- self.event_damage = func_null; // won't do anything if hurt
- self.count = 0; // reset counter
- self.think = onslaught_generator_deaththink; // explosion sequence
- self.nextthink = time; // start exploding immediately
- self.think(); // do the first explosion now
- WaypointSprite_UpdateMaxHealth(self.sprite, 0);
+ Send_Effect(eff_team, self.owner.origin, '0 0 0', 1);
- onslaught_updatelinks();
- }
+ WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);
+ WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
- if(self.health <= 0)
- setmodel(self, "models/onslaught/generator_dead.md3");
- else if(self.health < self.max_health * 0.10)
- setmodel(self, "models/onslaught/generator_dmg9.md3");
- else if(self.health < self.max_health * 0.20)
- setmodel(self, "models/onslaught/generator_dmg8.md3");
- else if(self.health < self.max_health * 0.30)
- setmodel(self, "models/onslaught/generator_dmg7.md3");
- else if(self.health < self.max_health * 0.40)
- setmodel(self, "models/onslaught/generator_dmg6.md3");
- else if(self.health < self.max_health * 0.50)
- setmodel(self, "models/onslaught/generator_dmg5.md3");
- else if(self.health < self.max_health * 0.60)
- setmodel(self, "models/onslaught/generator_dmg4.md3");
- else if(self.health < self.max_health * 0.70)
- setmodel(self, "models/onslaught/generator_dmg3.md3");
- else if(self.health < self.max_health * 0.80)
- setmodel(self, "models/onslaught/generator_dmg2.md3");
- else if(self.health < self.max_health * 0.90)
- setmodel(self, "models/onslaught/generator_dmg1.md3");
- setsize(self, '-52 -52 -14', '52 52 75');
+ if(IS_PLAYER(self.owner.ons_toucher))
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, self.owner.ons_toucher.netname, self.owner.message);
+ Send_Notification(NOTIF_ALL_EXCEPT, self.owner.ons_toucher, MSG_CENTER, APP_TEAM_ENT_4(self.owner.ons_toucher, CENTER_ONS_CAPTURE_), self.owner.message);
+ Send_Notification(NOTIF_ONE, self.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, self.owner.message);
+ PlayerScore_Add(self.owner.ons_toucher, SP_ONS_CAPS, 1);
+ PlayerTeamScore_AddScore(self.owner.ons_toucher, 10);
+ }
+
+ self.owner.ons_toucher = world;
- // Throw some flaming gibs on damage, more damage = more chance for gib
- if(random() < damage/220)
- {
- sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
- i = random();
- if(i < 0.3)
- ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib1.md3", 5, true);
- else if(i > 0.7)
- ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib2.md3", 5, true);
- else
- ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib3.md3", 5, true);
- }
- else
- {
- // particles on every hit
- pointparticles(particleeffectnum("sparks"), hitloc, force * -1, 1);
+ onslaught_updatelinks();
- //sound on every hit
- if (random() < 0.5)
- sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTEN_NORM);
- else
- sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM);
+ // Use targets now (somebody make sure this is in the right place..)
+ oself = self;
+ self = self.owner;
+ activator = self;
+ SUB_UseTargets ();
+ self = oself;
+
+ self.SendFlags |= CPSF_SETUP;
}
- //throw some gibs on damage
- if(random() < damage/200+0.2)
- if(random() < 0.5)
- ons_throwgib(hitloc + '0 0 20', randomvec()*360, "models/onslaught/gen_gib1.md3", 5, false);
+ if(self.owner.model != "models/onslaught/controlpoint_pad2.md3")
+ setmodel_fixsize(self.owner, "models/onslaught/controlpoint_pad2.md3");
+
+ if(random() < 0.9 - self.health / self.max_health)
+ Send_Effect(EFFECT_RAGE, self.origin + 10 * randomvec(), '0 0 -1', 1);
}
-// update links after a delay
-void onslaught_generator_delayed()
+void ons_ControlPoint_Icon_Spawn(entity cp, entity player)
{
- onslaught_updatelinks();
- // now begin normal thinking
- self.think = onslaught_generator_think;
- self.nextthink = time;
-}
+ entity e = spawn();
+
+ setsize(e, CPICON_MIN, CPICON_MAX);
+ setorigin(e, cp.origin + CPICON_OFFSET);
+
+ e.classname = "onslaught_controlpoint_icon";
+ e.owner = cp;
+ e.max_health = autocvar_g_onslaught_cp_health;
+ e.health = autocvar_g_onslaught_cp_buildhealth;
+ e.solid = SOLID_NOT;
+ e.takedamage = DAMAGE_AIM;
+ e.bot_attack = true;
+ e.event_damage = ons_ControlPoint_Icon_Damage;
+ e.team = player.team;
+ e.colormap = 1024 + (e.team - 1) * 17;
+ e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
+
+ sound(e, CH_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTEN_NORM);
+
+ cp.goalentity = e;
+ cp.team = e.team;
+ cp.colormap = e.colormap;
-string onslaught_generator_waypointsprite_for_team(entity e, float t)
-{
- if(t == e.team)
+ float eff_team;
+ switch(player.team)
{
- if(e.team == NUM_TEAM_1)
- return "ons-gen-red";
- else if(e.team == NUM_TEAM_2)
- return "ons-gen-blue";
+ case NUM_TEAM_1: eff_team = EFFECT_FLAG_RED_TOUCH; break;
+ case NUM_TEAM_2: eff_team = EFFECT_FLAG_BLUE_TOUCH; break;
+ case NUM_TEAM_3: eff_team = EFFECT_FLAG_YELLOW_TOUCH; break;
+ case NUM_TEAM_4: eff_team = EFFECT_FLAG_PINK_TOUCH; break;
+ default: eff_team = EFFECT_FLAG_NEUTRAL_TOUCH; break;
}
- if(e.isshielded)
- return "ons-gen-shielded";
- if(e.team == NUM_TEAM_1)
- return "ons-gen-red";
- else if(e.team == NUM_TEAM_2)
- return "ons-gen-blue";
- return "";
-}
-void onslaught_generator_updatesprite(entity e)
-{
- string s1, s2, s3;
- s1 = onslaught_generator_waypointsprite_for_team(e, NUM_TEAM_1);
- s2 = onslaught_generator_waypointsprite_for_team(e, NUM_TEAM_2);
- s3 = onslaught_generator_waypointsprite_for_team(e, -1);
- WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);
+ Send_Effect(eff_team, self.owner.origin, '0 0 0', 1);
- if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)
- {
- e.lastteam = e.team + 2;
- e.lastshielded = e.isshielded;
- if(e.lastshielded)
- {
- if(e.team == NUM_TEAM_1 || e.team == NUM_TEAM_2)
- WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false));
- else
- WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');
- }
- else
- {
- if(e.team == NUM_TEAM_1 || e.team == NUM_TEAM_2)
- WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false));
- else
- WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');
- }
- WaypointSprite_Ping(e.sprite);
- }
+ WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE));
+ WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY);
+ cp.sprite.SendFlags |= 16;
+
+ onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink);
}
-string onslaught_controlpoint_waypointsprite_for_team(entity e, float t)
+string ons_ControlPoint_Waypoint(entity e)
{
float a;
- if(t != -1)
+ if(e.team)
{
- a = onslaught_controlpoint_attackable(e, t);
- if(a == 3 || a == 4) // ATTACK/TOUCH THIS ONE NOW
- {
- if(e.team == NUM_TEAM_1)
- return "ons-cp-atck-red";
- else if(e.team == NUM_TEAM_2)
- return "ons-cp-atck-blue";
- else
- return "ons-cp-atck-neut";
- }
- else if(a == -2) // DEFEND THIS ONE NOW
- {
- if(e.team == NUM_TEAM_1)
- return "ons-cp-dfnd-red";
- else if(e.team == NUM_TEAM_2)
- return "ons-cp-dfnd-blue";
- }
- else if(e.team == t || a == -1 || a == 1) // own point, or fire at it
- {
- if(e.team == NUM_TEAM_1)
- return "ons-cp-red";
- else if(e.team == NUM_TEAM_2)
- return "ons-cp-blue";
- }
- else if(a == 2) // touch it
- return "ons-cp-neut";
+ a = ons_ControlPoint_Attackable(e, e.team);
+
+ if(a == -2) { return "ons-cp-dfnd"; } // defend now
+ if(a == -1 || a == 1 || a == 2) { return "ons-cp"; } // touch
+ if(a == 3 || a == 4) { return "ons-cp-atck"; } // attack
}
else
- {
- if(e.team == NUM_TEAM_1)
- return "ons-cp-red";
- else if(e.team == NUM_TEAM_2)
- return "ons-cp-blue";
- else
- return "ons-cp-neut";
- }
+ return "ons-cp";
+
return "";
}
}
}
-void onslaught_generator_reset()
+void ons_ControlPoint_Touch()
{
- self.team = self.team_saved;
- self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
- self.takedamage = DAMAGE_AIM;
- self.bot_attack = true;
- self.iscaptured = true;
- self.islinked = true;
- self.isshielded = true;
- self.enemy.solid = SOLID_NOT;
- self.think = onslaught_generator_delayed;
- self.nextthink = time + 0.2;
- setmodel(self, "models/onslaught/generator.md3");
- setsize(self, '-52 -52 -14', '52 52 75');
-
- if(!self.noalign)
- {
- setorigin(self, self.origin + '0 0 20');
- droptofloor();
+ entity toucher = other;
+ float attackable;
+
+ if(IS_VEHICLE(toucher) && toucher.owner)
+ if(autocvar_g_onslaught_allow_vehicle_touch)
+ toucher = toucher.owner;
+ else
+ return;
+
+ if(!IS_PLAYER(toucher)) { return; }
+ if(toucher.frozen) { return; }
+ if(toucher.deadflag != DEAD_NO) { return; }
+
+ if ( SAME_TEAM(self,toucher) )
+ if ( self.iscaptured )
+ {
+ if(time <= toucher.teleport_antispam)
+ Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT_ANTISPAM, rint(toucher.teleport_antispam - time));
+ else
+ Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT);
}
+
+ attackable = ons_ControlPoint_Attackable(self, toucher.team);
+ if(attackable != 2 && attackable != 4)
+ return;
+ // we've verified that this player has a legitimate claim to this point,
+ // so start building the captured point icon (which only captures this
+ // point if it successfully builds without being destroyed first)
+ ons_ControlPoint_Icon_Spawn(self, toucher);
+
+ self.ons_toucher = toucher;
- WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
- WaypointSprite_UpdateHealth(self.sprite, self.health);
+ onslaught_updatelinks();
}
-/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)
- Base generator.
-
- spawnfunc_onslaught_link entities can target this.
+void ons_ControlPoint_Think()
+{
+ self.nextthink = time + ONS_CP_THINKRATE;
+ CSQCMODEL_AUTOUPDATE();
+}
-keys:
-"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.
-"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
- */
-void spawnfunc_onslaught_generator()
+void ons_ControlPoint_Reset()
{
- if (!g_onslaught)
- {
- remove(self);
- return;
- }
+ if(self.goalentity)
+ remove(self.goalentity);
- //entity e;
- precache_model("models/onslaught/generator.md3");
- precache_model("models/onslaught/generator_shield.md3");
- precache_model("models/onslaught/generator_dmg1.md3");
- precache_model("models/onslaught/generator_dmg2.md3");
- precache_model("models/onslaught/generator_dmg3.md3");
- precache_model("models/onslaught/generator_dmg4.md3");
- precache_model("models/onslaught/generator_dmg5.md3");
- precache_model("models/onslaught/generator_dmg6.md3");
- precache_model("models/onslaught/generator_dmg7.md3");
- precache_model("models/onslaught/generator_dmg8.md3");
- precache_model("models/onslaught/generator_dmg9.md3");
- precache_model("models/onslaught/generator_dead.md3");
- precache_model("models/onslaught/shockwave.md3");
- precache_model("models/onslaught/shockwavetransring.md3");
- precache_model("models/onslaught/gen_gib1.md3");
- precache_model("models/onslaught/gen_gib2.md3");
- precache_model("models/onslaught/gen_gib3.md3");
- precache_model("models/onslaught/ons_ray.md3");
- precache_sound("onslaught/generator_decay.wav");
- precache_sound("weapons/grenade_impact.wav");
- precache_sound("weapons/rocket_impact.wav");
- precache_sound("onslaught/generator_underattack.wav");
- precache_sound("onslaught/shockwave.wav");
- precache_sound("onslaught/ons_hit1.wav");
- precache_sound("onslaught/ons_hit2.wav");
- precache_sound("onslaught/electricity_explode.wav");
- if (!self.team)
- objerror("team must be set");
+ self.goalentity = world;
+ self.team = 0;
+ self.colormap = 1024;
+ self.iscaptured = false;
+ self.islinked = false;
+ self.isshielded = true;
+ self.think = ons_ControlPoint_Think;
+ self.ons_toucher = world;
+ self.nextthink = time + ONS_CP_THINKRATE;
+ setmodel_fixsize(self, "models/onslaught/controlpoint_pad.md3");
- if(self.team == NUM_TEAM_1)
- ons_red_generator = self;
+ WaypointSprite_UpdateMaxHealth(self.sprite, 0);
+ WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY);
- if(self.team == NUM_TEAM_2)
- ons_blue_generator = self;
+ onslaught_updatelinks();
- self.team_saved = self.team;
- self.colormap = 1024 + (self.team - 1) * 17;
- self.solid = SOLID_BBOX;
- self.movetype = MOVETYPE_NONE;
- self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
- setmodel(self, "models/onslaught/generator.md3");
- setsize(self, '-52 -52 -14', '52 52 75');
- setorigin(self, self.origin);
- self.takedamage = DAMAGE_AIM;
- self.bot_attack = true;
- self.event_damage = onslaught_generator_damage;
- self.iscaptured = true;
- self.islinked = true;
- self.isshielded = true;
- // helper entity that create fx when generator is damaged
- onslaught_generator_damage_spawn(self);
- // spawn shield model which indicates whether this can be damaged
- self.enemy = spawn();
- setattachment(self.enemy , self, "");
- self.enemy.classname = "onslaught_generator_shield";
- self.enemy.solid = SOLID_NOT;
- self.enemy.movetype = MOVETYPE_NONE;
- self.enemy.effects = EF_ADDITIVE;
- setmodel(self.enemy, "models/onslaught/generator_shield.md3");
- //setorigin(e, self.origin);
- self.enemy.colormap = self.colormap;
- self.enemy.team = self.team;
- //self.think = onslaught_generator_delayed;
- //self.nextthink = time + 0.2;
- InitializeEntity(self, onslaught_generator_delayed, INITPRIO_LAST);
-
- WaypointSprite_SpawnFixed(string_null, self.origin + '0 0 128', self, sprite, RADARICON_NONE, '0 0 0');
- WaypointSprite_UpdateRule(self.sprite, NUM_TEAM_2, SPRITERULE_TEAMPLAY);
- WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
- WaypointSprite_UpdateHealth(self.sprite, self.health);
+ activator = self;
+ SUB_UseTargets(); // to reset the structures, playerspawns etc.
- waypoint_spawnforitem(self);
+ CSQCMODEL_AUTOUPDATE();
+}
+void ons_DelayedControlPoint_Setup(void)
+{
onslaught_updatelinks();
+
+ // captureshield setup
+ ons_CaptureShield_Spawn(self, false);
+
+ CSQCMODEL_AUTOINIT();
+}
- self.reset = onslaught_generator_reset;
+void ons_ControlPoint_Setup(entity cp)
+{
+ // declarations
+ self = cp; // for later usage with droptofloor()
+
+ // main setup
+ cp.ons_worldcpnext = ons_worldcplist; // link control point into ons_worldcplist
+ ons_worldcplist = cp;
+
+ cp.netname = "Control point";
+ cp.team = 0;
+ cp.solid = SOLID_BBOX;
+ cp.movetype = MOVETYPE_NONE;
+ cp.touch = ons_ControlPoint_Touch;
+ cp.think = ons_ControlPoint_Think;
+ cp.nextthink = time + ONS_CP_THINKRATE;
+ cp.reset = ons_ControlPoint_Reset;
+ cp.colormap = 1024;
+ cp.iscaptured = false;
+ cp.islinked = false;
+ cp.isshielded = true;
+
+ if(cp.message == "") { cp.message = "a"; }
+
+ // precache - TODO: clean up!
+ precache_model("models/onslaught/controlpoint_pad.md3");
+ precache_model("models/onslaught/controlpoint_pad2.md3");
+ precache_model("models/onslaught/controlpoint_shield.md3");
+ precache_model("models/onslaught/controlpoint_icon.md3");
+ precache_model("models/onslaught/controlpoint_icon_dmg1.md3");
+ precache_model("models/onslaught/controlpoint_icon_dmg2.md3");
+ precache_model("models/onslaught/controlpoint_icon_dmg3.md3");
+ precache_model("models/onslaught/controlpoint_icon_gib1.md3");
+ precache_model("models/onslaught/controlpoint_icon_gib2.md3");
+ precache_model("models/onslaught/controlpoint_icon_gib4.md3");
+ precache_sound("onslaught/controlpoint_build.wav");
+ precache_sound("onslaught/controlpoint_built.wav");
+ precache_sound(W_Sound("grenade_impact"));
+ precache_sound("onslaught/damageblockedbyshield.wav");
+ precache_sound("onslaught/controlpoint_underattack.wav");
+ precache_sound("onslaught/ons_spark1.wav");
+ precache_sound("onslaught/ons_spark2.wav");
+
+ // appearence
+ setmodel_fixsize(cp, "models/onslaught/controlpoint_pad.md3");
+
+ // control point placement
+ if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location
+ {
+ cp.noalign = true;
+ cp.movetype = MOVETYPE_NONE;
+ }
+ else // drop to floor, automatically find a platform and set that as spawn origin
+ {
+ setorigin(cp, cp.origin + '0 0 20');
+ cp.noalign = false;
+ self = cp;
+ droptofloor();
+ cp.movetype = MOVETYPE_TOSS;
+ }
+
+ // waypointsprites
+ WaypointSprite_SpawnFixed(string_null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE, '0 0 0');
+ WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY);
+
+ InitializeEntity(cp, ons_DelayedControlPoint_Setup, INITPRIO_SETLOCATION);
}
-.float waslinked;
-.float cp_bob_spd;
-.vector cp_origin, cp_bob_origin, cp_bob_dmg;
-float ons_notification_time_team1;
-float ons_notification_time_team2;
+// =========================
+// Main Generator Functions
+// =========================
-void onslaught_controlpoint_icon_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+string ons_Generator_Waypoint(entity e)
{
- entity oself;
- float nag;
+ if(e.isshielded)
+ return "ons-gen-shielded";
+ return "ons-gen";
+}
- if (damage <= 0)
- return;
- if (self.owner.isshielded)
- {
- // this is protected by a shield, so ignore the damage
- if (time > self.pain_finished)
- if (IS_PLAYER(attacker))
- {
- play2(attacker, "onslaught/damageblockedbyshield.wav");
- self.pain_finished = time + 1;
- }
- return;
- }
+void ons_Generator_UpdateSprite(entity e)
+{
+ string s1;
+ s1 = ons_Generator_Waypoint(e);
+ WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1);
- if (IS_PLAYER(attacker))
+ if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)
{
- nag = false;
- if(self.team == NUM_TEAM_1)
+ e.lastteam = e.team + 2;
+ e.lastshielded = e.isshielded;
+ if(e.lastshielded)
{
- if(time - ons_notification_time_team1 > 10)
- {
- nag = true;
- ons_notification_time_team1 = time;
- }
+ if(e.team)
+ WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false));
+ else
+ WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');
}
- else if(self.team == NUM_TEAM_2)
+ else
{
- if(time - ons_notification_time_team2 > 10)
- {
- nag = true;
- ons_notification_time_team2 = time;
- }
+ if(e.team)
+ WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false));
+ else
+ WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');
}
- else
- nag = true;
-
- if(nag)
- play2team(self.team, "onslaught/controlpoint_underattack.wav");
+ WaypointSprite_Ping(e.sprite);
}
+}
- void ons_GeneratorDamage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
- self.health = self.health - damage;
- if(self.owner.iscaptured)
- WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
- else
- WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / sys_frametime));
- self.pain_finished = time + 1;
- self.punchangle = (2 * randomvec() - '1 1 1') * 45;
- self.cp_bob_dmg_z = (2 * random() - 1) * 15;
- // colormod flash when shot
- self.colormod = '2 2 2';
- // particles on every hit
- pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1);
- //sound on every hit
- if (random() < 0.5)
- sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTEN_NORM);
- else
- sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTEN_NORM);
++void ons_GeneratorDamage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+ if(damage <= 0) { return; }
+ if(warmup_stage || gameover) { return; }
+ if(!round_handler_IsRoundStarted()) { return; }
- if (self.health < 0)
+ if (attacker != self)
{
- sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
+ if (self.isshielded)
{
- string t;
- t = Team_ColoredFullName(attacker.team);
- bprint(Team_ColoredFullName(self.team), " ", self.message, " control point destroyed by ", t, "\n");
- ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 25, "models/onslaught/controlpoint_icon_gib1.md3", 3, false);
- ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, false);
- ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, false);
- ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, false);
- ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, false);
- ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, false);
- ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, false);
+ // this is protected by a shield, so ignore the damage
+ if (time > self.pain_finished)
+ if (IS_PLAYER(attacker))
+ {
+ play2(attacker, "onslaught/damageblockedbyshield.wav");
+ attacker.typehitsound += 1;
+ self.pain_finished = time + 1;
+ }
+ return;
}
- self.owner.goalentity = world;
- self.owner.islinked = false;
- self.owner.iscaptured = false;
- self.owner.team = 0;
- self.owner.colormap = 1024;
+ if (time > self.pain_finished)
+ {
+ self.pain_finished = time + 10;
+ entity head;
+ FOR_EACH_REALPLAYER(head) if(SAME_TEAM(head, self)) { Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_GENERATOR_UNDERATTACK); }
+ play2team(self.team, "onslaught/generator_underattack.wav");
+ }
+ }
+ self.health = self.health - damage;
+ WaypointSprite_UpdateHealth(self.sprite, self.health);
+ // choose an animation frame based on health
+ self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1);
+ // see if the generator is still functional, or dying
+ if (self.health > 0)
+ {
+ self.lasthealth = self.health;
+ }
+ else
+ {
+ if (attacker == self)
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME_));
+ else
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_));
+ PlayerScore_Add(attacker, SP_SCORE, 100);
+ }
+ self.iscaptured = false;
+ self.islinked = false;
+ self.isshielded = false;
+ self.takedamage = DAMAGE_NO; // can't be hurt anymore
+ self.event_damage = func_null; // won't do anything if hurt
+ self.count = 0; // reset counter
+ self.think = func_null;
+ self.nextthink = 0;
+ //self.think(); // do the first explosion now
- WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0);
+ WaypointSprite_UpdateMaxHealth(self.sprite, 0);
+ WaypointSprite_Ping(self.sprite);
+ //WaypointSprite_Kill(self.sprite); // can't do this yet, code too poor
onslaught_updatelinks();
+ }
- // Use targets now (somebody make sure this is in the right place..)
- oself = self;
- self = self.owner;
- activator = self;
- SUB_UseTargets ();
- self = oself;
-
-
- self.owner.waslinked = self.owner.islinked;
- if(self.owner.model != "models/onslaught/controlpoint_pad.md3")
- setmodel(self.owner, "models/onslaught/controlpoint_pad.md3");
- //setsize(self, '-32 -32 0', '32 32 8');
+ // Throw some flaming gibs on damage, more damage = more chance for gib
+ if(random() < damage/220)
+ {
+ sound(self, CH_TRIGGER, W_Sound("rocket_impact"), VOL_BASE, ATTEN_NORM);
+ }
+ else
+ {
+ // particles on every hit
+ Send_Effect(EFFECT_SPARKS, hitloc, force * -1, 1);
- remove(self);
+ //sound on every hit
+ if (random() < 0.5)
+ sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTEN_NORM);
+ else
+ sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM);
}
+
+ self.SendFlags |= GSF_STATUS;
}
-void onslaught_controlpoint_icon_think()
+void ons_GeneratorThink()
{
- entity oself;
- self.nextthink = time + sys_frametime;
-
- if(autocvar_g_onslaught_cp_proxydecap)
+ entity e;
+ self.nextthink = time + GEN_THINKRATE;
+ if (!gameover)
{
- float _enemy_count = 0;
- float _friendly_count = 0;
- float _dist;
- entity _player;
-
- FOR_EACH_PLAYER(_player)
+ if(!self.isshielded && self.wait < time)
{
- if(!_player.deadflag)
+ self.wait = time + 5;
+ FOR_EACH_REALPLAYER(e)
{
- _dist = vlen(_player.origin - self.origin);
- if(_dist < autocvar_g_onslaught_cp_proxydecap_distance)
- {
- if(_player.team == self.team)
- ++_friendly_count;
- else
- ++_enemy_count;
+ if(SAME_TEAM(e, self))
+ {
+ Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM);
+ soundto(MSG_ONE, e, CHAN_AUTO, "kh/alarm.wav", VOL_BASE, ATTEN_NONE); // FIXME: unique sound?
}
+ else
+ Send_Notification(NOTIF_ONE, e, MSG_CENTER, APP_TEAM_NUM_4(self.team, CENTER_ONS_NOTSHIELDED_));
}
}
+ }
+}
- _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * sys_frametime);
- _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * sys_frametime);
+void ons_GeneratorReset()
+{
+ self.team = self.team_saved;
+ self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
+ self.takedamage = DAMAGE_AIM;
+ self.bot_attack = true;
+ self.iscaptured = true;
+ self.islinked = true;
+ self.isshielded = true;
+ self.event_damage = ons_GeneratorDamage;
+ self.think = ons_GeneratorThink;
+ self.nextthink = time + GEN_THINKRATE;
+
+ Net_LinkEntity(self, false, 0, generator_send);
+
+ self.SendFlags = GSF_SETUP; // just incase
+ self.SendFlags |= GSF_STATUS;
- self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health);
- if(self.health <= 0)
- {
- onslaught_controlpoint_icon_damage(self, self, 1, 0, self.origin, '0 0 0');
- return;
- }
- }
+ WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
+ WaypointSprite_UpdateHealth(self.sprite, self.health);
+ WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY);
+
+ onslaught_updatelinks();
+}
- if (time > self.pain_finished + 5)
+void ons_DelayedGeneratorSetup()
+{
+ // bot waypoints
+ waypoint_spawnforitem_force(self, self.origin);
+ self.nearestwaypointtimeout = 0; // activate waypointing again
+ self.bot_basewaypoint = self.nearestwaypoint;
+
+ // captureshield setup
+ ons_CaptureShield_Spawn(self, true);
+
+ onslaught_updatelinks();
+
+ Net_LinkEntity(self, false, 0, generator_send);
+}
+
+
+void onslaught_generator_touch()
+{
+ if ( IS_PLAYER(other) )
+ if ( SAME_TEAM(self,other) )
+ if ( self.iscaptured )
{
- if(self.health < self.max_health)
- {
- self.health = self.health + self.count;
- if (self.health >= self.max_health)
- self.health = self.max_health;
- WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
- }
+ Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_TELEPORT);
}
- if (self.health < self.max_health * 0.25)
- setmodel(self, "models/onslaught/controlpoint_icon_dmg3.md3");
- else if (self.health < self.max_health * 0.50)
- setmodel(self, "models/onslaught/controlpoint_icon_dmg2.md3");
- else if (self.health < self.max_health * 0.75)
- setmodel(self, "models/onslaught/controlpoint_icon_dmg1.md3");
- else if (self.health < self.max_health * 0.90)
- setmodel(self, "models/onslaught/controlpoint_icon.md3");
- // colormod flash when shot
- self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));
+}
- if(self.owner.islinked != self.owner.waslinked)
- {
- // unteam the spawnpoint if needed
- float t;
- t = self.owner.team;
- if(!self.owner.islinked)
- self.owner.team = 0;
+void ons_GeneratorSetup(entity gen) // called when spawning a generator entity on the map as a spawnfunc
+{
+ // declarations
+ float teamnumber = gen.team;
+ self = gen; // for later usage with droptofloor()
+
+ // main setup
+ gen.ons_worldgeneratornext = ons_worldgeneratorlist; // link generator into ons_worldgeneratorlist
+ ons_worldgeneratorlist = gen;
+
+ gen.netname = sprintf("%s generator", Team_ColoredFullName(teamnumber));
+ gen.classname = "onslaught_generator";
+ gen.solid = SOLID_BBOX;
+ gen.team_saved = teamnumber;
+ gen.movetype = MOVETYPE_NONE;
+ gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health;
+ gen.takedamage = DAMAGE_AIM;
+ gen.bot_attack = true;
+ gen.event_damage = ons_GeneratorDamage;
+ gen.reset = ons_GeneratorReset;
+ gen.think = ons_GeneratorThink;
+ gen.nextthink = time + GEN_THINKRATE;
+ gen.iscaptured = true;
+ gen.islinked = true;
+ gen.isshielded = true;
+ gen.touch = onslaught_generator_touch;
+
+ // precache - TODO: clean up!
+ precache_model("models/onslaught/generator_shield.md3");
+ precache_model("models/onslaught/gen_gib1.md3");
+ precache_model("models/onslaught/gen_gib2.md3");
+ precache_model("models/onslaught/gen_gib3.md3");
+ precache_sound("onslaught/generator_decay.wav");
+ precache_sound(W_Sound("grenade_impact"));
+ precache_sound(W_Sound("rocket_impact"));
+ precache_sound("onslaught/generator_underattack.wav");
+ precache_sound("onslaught/shockwave.wav");
+ precache_sound("onslaught/ons_hit1.wav");
+ precache_sound("onslaught/ons_hit2.wav");
+ precache_sound("onslaught/generator_underattack.wav");
+
+ // appearence
+ // model handled by CSQC
+ setsize(gen, GENERATOR_MIN, GENERATOR_MAX);
+ setorigin(gen, (gen.origin + CPGEN_SPAWN_OFFSET));
+ gen.colormap = 1024 + (teamnumber - 1) * 17;
+
+ // generator placement
+ self = gen;
+ droptofloor();
+
+ // waypointsprites
+ WaypointSprite_SpawnFixed(string_null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE, '0 0 0');
+ WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY);
+ WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
+ WaypointSprite_UpdateHealth(self.sprite, self.health);
+
+ InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION);
+}
- oself = self;
- self = self.owner;
- activator = self;
- SUB_UseTargets ();
- self = oself;
- self.owner.team = t;
+// ===============
+// Round Handler
+// ===============
- self.owner.waslinked = self.owner.islinked;
+float total_generators;
+void Onslaught_count_generators()
+{
+ entity e;
+ total_generators = redowned = blueowned = yellowowned = pinkowned = 0;
+ for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext)
+ {
+ ++total_generators;
+ redowned += (e.team == NUM_TEAM_1 && e.health > 0);
+ blueowned += (e.team == NUM_TEAM_2 && e.health > 0);
+ yellowowned += (e.team == NUM_TEAM_3 && e.health > 0);
+ pinkowned += (e.team == NUM_TEAM_4 && e.health > 0);
}
+}
- if (self.punchangle.x > 0)
+int Onslaught_GetWinnerTeam()
+{
+ float winner_team = 0;
+ if(redowned > 0)
+ winner_team = NUM_TEAM_1;
+ if(blueowned > 0)
{
- self.punchangle_x = self.punchangle.x - 60 * sys_frametime;
- if (self.punchangle.x < 0)
- self.punchangle_x = 0;
+ if(winner_team) return 0;
+ winner_team = NUM_TEAM_2;
}
- else if (self.punchangle.x < 0)
+ if(yellowowned > 0)
{
- self.punchangle_x = self.punchangle.x + 60 * sys_frametime;
- if (self.punchangle.x > 0)
- self.punchangle_x = 0;
+ if(winner_team) return 0;
+ winner_team = NUM_TEAM_3;
}
-
- if (self.punchangle.y > 0)
+ if(pinkowned > 0)
{
- self.punchangle_y = self.punchangle.y - 60 * sys_frametime;
- if (self.punchangle.y < 0)
- self.punchangle_y = 0;
+ if(winner_team) return 0;
+ winner_team = NUM_TEAM_4;
}
- else if (self.punchangle.y < 0)
+ if(winner_team)
+ return winner_team;
+ return -1; // no generators left?
+}
+
+#define ONS_OWNED_GENERATORS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0))
+#define ONS_OWNED_GENERATORS_OK() (ONS_OWNED_GENERATORS() > 1)
+float Onslaught_CheckWinner()
+{
+ entity e;
+
+ if ((autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) || (round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0))
{
- self.punchangle_y = self.punchangle.y + 60 * sys_frametime;
- if (self.punchangle.y > 0)
- self.punchangle_y = 0;
+ ons_stalemate = true;
+
+ if (!wpforenemy_announced)
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT);
+ sound(world, CH_INFO, "onslaught/generator_decay.wav", VOL_BASE, ATTEN_NONE);
+
+ wpforenemy_announced = true;
+ }
+
+ entity tmp_entity; // temporary entity
+ float d;
+ for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) if(time >= tmp_entity.ons_overtime_damagedelay)
+ {
+ // tmp_entity.max_health / 300 gives 5 minutes of overtime.
+ // control points reduce the overtime duration.
+ d = 1;
+ for(e = ons_worldcplist; e; e = e.ons_worldcpnext)
+ {
+ if(DIFF_TEAM(e, tmp_entity))
+ if(e.islinked)
+ d = d + 1;
+ }
+
+ if(autocvar_g_campaign && autocvar__campaign_testrun)
+ d = d * tmp_entity.max_health;
+ else
+ d = d * tmp_entity.max_health / max(30, 60 * autocvar_timelimit_suddendeath);
+
+ Damage(tmp_entity, tmp_entity, tmp_entity, d, DEATH_HURTTRIGGER, tmp_entity.origin, '0 0 0');
+
+ tmp_entity.sprite.SendFlags |= 16;
+
+ tmp_entity.ons_overtime_damagedelay = time + 1;
+ }
}
+ else { wpforenemy_announced = false; ons_stalemate = false; }
+
+ Onslaught_count_generators();
+
+ if(ONS_OWNED_GENERATORS_OK())
+ return 0;
+
+ int winner_team = Onslaught_GetWinnerTeam();
- if (self.punchangle.z > 0)
+ if(winner_team > 0)
{
- self.punchangle_z = self.punchangle.z - 60 * sys_frametime;
- if (self.punchangle.z < 0)
- self.punchangle_z = 0;
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_));
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_));
+ TeamScore_AddToTeam(winner_team, ST_ONS_CAPS, +1);
}
- else if (self.punchangle.z < 0)
+ else if(winner_team == -1)
{
- self.punchangle_z = self.punchangle.z + 60 * sys_frametime;
- if (self.punchangle.z > 0)
- self.punchangle_z = 0;
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
}
+
+ ons_stalemate = false;
- self.angles_x = self.punchangle.x;
- self.angles_y = self.punchangle.y + self.mangle.y;
- self.angles_z = self.punchangle.z;
- self.mangle_y = self.mangle.y + 45 * sys_frametime;
-
- self.cp_bob_origin_z = 4 * PI * (1 - cos(self.cp_bob_spd));
- self.cp_bob_spd = self.cp_bob_spd + 1.875 * sys_frametime;
- if(self.cp_bob_dmg.z > 0)
- self.cp_bob_dmg_z = self.cp_bob_dmg.z - 3 * sys_frametime;
- else
- self.cp_bob_dmg_z = 0;
- setorigin(self,self.cp_origin + self.cp_bob_origin + self.cp_bob_dmg);
-
- // damaged fx
- if(random() < 0.6 - self.health / self.max_health)
+ play2all(sprintf("ctf/%s_capture.wav", Static_Team_ColorName_Lower(winner_team)));
+
+ round_handler_Init(7, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit);
+
+ FOR_EACH_PLAYER(e)
{
- pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
-
- if(random() > 0.8)
- sound(self, CH_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTEN_NORM);
- else if (random() > 0.5)
- sound(self, CH_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTEN_NORM);
+ e.ons_roundlost = true;
+ e.player_blocked = true;
}
+
+ nades_Clear(world, true);
+
+ return 1;
}
-void onslaught_controlpoint_icon_buildthink()
+bool Onslaught_CheckPlayers()
{
- entity oself;
- float a;
+ return true;
+}
- self.nextthink = time + sys_frametime;
+void Onslaught_RoundStart()
+{
+ entity tmp_entity;
+ FOR_EACH_PLAYER(tmp_entity) { tmp_entity.player_blocked = false; }
- // only do this if there is power
- a = onslaught_controlpoint_can_be_linked(self.owner, self.owner.team);
- if(!a)
- return;
+ for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext)
+ tmp_entity.sprite.SendFlags |= 16;
- self.health = self.health + self.count;
+ for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
+ tmp_entity.sprite.SendFlags |= 16;
+}
- if (self.health >= self.max_health)
- {
- self.health = self.max_health;
- self.count = autocvar_g_onslaught_cp_regen * sys_frametime; // slow repair rate from now on
- self.think = onslaught_controlpoint_icon_think;
- sound(self, CH_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTEN_NORM);
- bprint(Team_ColoredFullName(self.team), " captured ", self.owner.message, " control point\n");
- self.owner.iscaptured = true;
- WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);
- WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
+// ================
+// Bot player logic
+// ================
- onslaught_updatelinks();
+// NOTE: LEGACY CODE, needs to be re-written!
- // Use targets now (somebody make sure this is in the right place..)
- oself = self;
- self = self.owner;
- activator = self;
- SUB_UseTargets ();
- self = oself;
- self.cp_origin = self.origin;
- self.cp_bob_origin = '0 0 0.1';
- self.cp_bob_spd = 0;
+void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float sradius)
+{
+ entity head;
+ float t, i, c, needarmor = false, needweapons = false;
+
+ // Needs armor/health?
+ if(self.health<100)
+ needarmor = true;
+
+ // Needs weapons?
+ c = 0;
+ for(i = WEP_FIRST; i <= WEP_LAST ; ++i)
+ {
+ // Find weapon
+ if(self.weapons & WepSet_FromWeapon(i))
+ if(++c>=4)
+ break;
}
- self.alpha = self.health / self.max_health;
- // colormod flash when shot
- self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));
- if(self.owner.model != "models/onslaught/controlpoint_pad2.md3")
- setmodel(self.owner, "models/onslaught/controlpoint_pad2.md3");
- //setsize(self, '-32 -32 0', '32 32 8');
- if(random() < 0.9 - self.health / self.max_health)
- pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1);
-}
+ if(c<4)
+ needweapons = true;
+ if(!needweapons && !needarmor)
+ return;
+ ons_debug(strcat(self.netname, " needs weapons ", ftos(needweapons) , "\n"));
+ ons_debug(strcat(self.netname, " needs armor ", ftos(needarmor) , "\n"));
+ // See what is around
+ head = findchainfloat(bot_pickup, true);
+ while (head)
+ {
+ // gather health and armor only
+ if (head.solid)
+ if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) )
+ if (vlen(head.origin - org) < sradius)
+ {
+ t = head.bot_pickupevalfunc(self, head);
+ if (t > 0)
+ navigation_routerating(head, t * ratingscale, 500);
+ }
+ head = head.chain;
+ }
+}
-void onslaught_controlpoint_touch()
+void havocbot_role_ons_setrole(entity bot, float role)
{
- entity e;
- float a;
- if (!IS_PLAYER(other))
- return;
- a = onslaught_controlpoint_attackable(self, other.team);
- if(a != 2 && a != 4)
- return;
- // we've verified that this player has a legitimate claim to this point,
- // so start building the captured point icon (which only captures this
- // point if it successfully builds without being destroyed first)
- self.goalentity = e = spawn();
- e.classname = "onslaught_controlpoint_icon";
- e.owner = self;
- e.max_health = autocvar_g_onslaught_cp_health;
- e.health = autocvar_g_onslaught_cp_buildhealth;
- e.solid = SOLID_BBOX;
- e.movetype = MOVETYPE_NONE;
- setmodel(e, "models/onslaught/controlpoint_icon.md3");
- setsize(e, '-32 -32 -32', '32 32 32');
- setorigin(e, self.origin + '0 0 96');
- e.takedamage = DAMAGE_AIM;
- e.bot_attack = true;
- e.event_damage = onslaught_controlpoint_icon_damage;
- e.team = other.team;
- e.colormap = 1024 + (e.team - 1) * 17;
- e.think = onslaught_controlpoint_icon_buildthink;
- e.nextthink = time + sys_frametime;
- e.count = (e.max_health - e.health) * sys_frametime / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
- sound(e, CH_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTEN_NORM);
- self.team = e.team;
- self.colormap = e.colormap;
- WaypointSprite_UpdateBuildFinished(self.sprite, time + (e.max_health - e.health) / (e.count / sys_frametime));
- onslaught_updatelinks();
+ ons_debug(strcat(bot.netname," switched to "));
+ switch(role)
+ {
+ case HAVOCBOT_ONS_ROLE_DEFENSE:
+ ons_debug("defense");
+ bot.havocbot_role = havocbot_role_ons_defense;
+ bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_DEFENSE;
+ bot.havocbot_role_timeout = 0;
+ break;
+ case HAVOCBOT_ONS_ROLE_ASSISTANT:
+ ons_debug("assistant");
+ bot.havocbot_role = havocbot_role_ons_assistant;
+ bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_ASSISTANT;
+ bot.havocbot_role_timeout = 0;
+ break;
+ case HAVOCBOT_ONS_ROLE_OFFENSE:
+ ons_debug("offense");
+ bot.havocbot_role = havocbot_role_ons_offense;
+ bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE;
+ bot.havocbot_role_timeout = 0;
+ break;
+ }
+ ons_debug("\n");
}
-void onslaught_controlpoint_think()
+float havocbot_ons_teamcount(entity bot, float role)
{
- self.nextthink = time;
- CSQCMODEL_AUTOUPDATE();
+ float c = 0;
+ entity head;
+
+ FOR_EACH_PLAYER(head)
+ if(SAME_TEAM(head, self))
+ if(head.havocbot_role_flags & role)
+ ++c;
+
+ return c;
}
-void onslaught_controlpoint_reset()
+void havocbot_goalrating_ons_controlpoints_attack(float ratingscale)
{
- if(self.goalentity && self.goalentity != world)
- remove(self.goalentity);
- self.goalentity = world;
- self.team = 0;
- self.colormap = 1024;
- self.iscaptured = false;
- self.islinked = false;
- self.isshielded = true;
- self.enemy.solid = SOLID_NOT;
- self.enemy.colormap = self.colormap;
- self.think = onslaught_controlpoint_think;
- self.enemy.think = func_null;
- self.nextthink = time; // don't like func_null :P
- setmodel(self, "models/onslaught/controlpoint_pad.md3");
- //setsize(self, '-32 -32 0', '32 32 8');
+ entity cp, cp1, cp2, best, pl, wp;
+ float radius, found, bestvalue, c;
- WaypointSprite_UpdateMaxHealth(self.sprite, 0);
+ // Filter control points
+ for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext)
+ {
+ cp2.wpcost = c = 0;
+ cp2.wpconsidered = false;
- onslaught_updatelinks();
+ if(cp2.isshielded)
+ continue;
- activator = self;
- SUB_UseTargets(); // to reset the structures, playerspawns etc.
+ // Ignore owned controlpoints
+ if(!(cp2.isgenneighbor[self.team] || cp2.iscpneighbor[self.team]))
+ continue;
- CSQCMODEL_AUTOUPDATE();
+ // Count team mates interested in this control point
+ // (easier and cleaner than keeping counters per cp and teams)
+ FOR_EACH_PLAYER(pl)
+ if(SAME_TEAM(pl, self))
+ if(pl.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE)
+ if(pl.havocbot_ons_target==cp2)
+ ++c;
+
+ // NOTE: probably decrease the cost of attackable control points
+ cp2.wpcost = c;
+ cp2.wpconsidered = true;
+ }
+
+ // We'll consider only the best case
+ bestvalue = 99999999999;
+ cp = world;
+ for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext)
+ {
+ if (!cp1.wpconsidered)
+ continue;
+
+ if(cp1.wpcost<bestvalue)
+ {
+ bestvalue = cp1.wpcost;
+ cp = cp1;
+ self.havocbot_ons_target = cp1;
+ }
+ }
+
+ if (!cp)
+ return;
+
+ ons_debug(strcat(self.netname, " chose cp ranked ", ftos(bestvalue), "\n"));
+
+ if(cp.goalentity)
+ {
+ // Should be attacked
+ // Rate waypoints near it
+ found = false;
+ best = world;
+ bestvalue = 99999999999;
+ for(radius=0; radius<1000 && !found; radius+=500)
+ {
+ for(wp=findradius(cp.origin,radius); wp; wp=wp.chain)
+ {
+ if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
+ if(wp.classname=="waypoint")
+ if(checkpvs(wp.origin,cp))
+ {
+ found = true;
+ if(wp.cnt<bestvalue)
+ {
+ best = wp;
+ bestvalue = wp.cnt;
+ }
+ }
+ }
+ }
+
+ if(best)
+ {
+ navigation_routerating(best, ratingscale, 10000);
+ best.cnt += 1;
+
+ self.havocbot_attack_time = 0;
+ if(checkpvs(self.view_ofs,cp))
+ if(checkpvs(self.view_ofs,best))
+ self.havocbot_attack_time = time + 2;
+ }
+ else
+ {
+ navigation_routerating(cp, ratingscale, 10000);
+ }
+ ons_debug(strcat(self.netname, " found an attackable controlpoint at ", vtos(cp.origin) ,"\n"));
+ }
+ else
+ {
+ // Should be touched
+ ons_debug(strcat(self.netname, " found a touchable controlpoint at ", vtos(cp.origin) ,"\n"));
+ found = false;
+
+ // Look for auto generated waypoint
+ if (!bot_waypoints_for_items)
+ for (wp = findradius(cp.origin,100); wp; wp = wp.chain)
+ {
+ if(wp.classname=="waypoint")
+ {
+ navigation_routerating(wp, ratingscale, 10000);
+ found = true;
+ }
+ }
+
+ // Nothing found, rate the controlpoint itself
+ if (!found)
+ navigation_routerating(cp, ratingscale, 10000);
+ }
}
-/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)
- Control point. Be sure to give this enough clearance so that the shootable part has room to exist
+float havocbot_goalrating_ons_generator_attack(float ratingscale)
+{
+ entity g, wp, bestwp;
+ float found, best;
- This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
+ for(g = ons_worldgeneratorlist; g; g = g.ons_worldgeneratornext)
+ {
+ if(SAME_TEAM(g, self) || g.isshielded)
+ continue;
-keys:
-"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
-"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.
-"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)
- */
+ // Should be attacked
+ // Rate waypoints near it
+ found = false;
+ bestwp = world;
+ best = 99999999999;
-void spawnfunc_onslaught_controlpoint()
+ for(wp=findradius(g.origin,400); wp; wp=wp.chain)
+ {
+ if(wp.classname=="waypoint")
+ if(checkpvs(wp.origin,g))
+ {
+ found = true;
+ if(wp.cnt<best)
+ {
+ bestwp = wp;
+ best = wp.cnt;
+ }
+ }
+ }
+
+ if(bestwp)
+ {
+ ons_debug("waypoints found around generator\n");
+ navigation_routerating(bestwp, ratingscale, 10000);
+ bestwp.cnt += 1;
+
+ self.havocbot_attack_time = 0;
+ if(checkpvs(self.view_ofs,g))
+ if(checkpvs(self.view_ofs,bestwp))
+ self.havocbot_attack_time = time + 5;
+
+ return true;
+ }
+ else
+ {
+ ons_debug("generator found without waypoints around\n");
+ // if there aren't waypoints near the generator go straight to it
+ navigation_routerating(g, ratingscale, 10000);
+ self.havocbot_attack_time = 0;
+ return true;
+ }
+ }
+ return false;
+}
+
+void havocbot_role_ons_offense()
{
- //entity e;
- if (!g_onslaught)
+ if(self.deadflag != DEAD_NO)
{
- remove(self);
+ self.havocbot_attack_time = 0;
+ havocbot_ons_reset_role(self);
return;
}
- precache_model("models/onslaught/controlpoint_pad.md3");
- precache_model("models/onslaught/controlpoint_pad2.md3");
- precache_model("models/onslaught/controlpoint_shield.md3");
- precache_model("models/onslaught/controlpoint_icon.md3");
- precache_model("models/onslaught/controlpoint_icon_dmg1.md3");
- precache_model("models/onslaught/controlpoint_icon_dmg2.md3");
- precache_model("models/onslaught/controlpoint_icon_dmg3.md3");
- precache_model("models/onslaught/controlpoint_icon_gib1.md3");
- precache_model("models/onslaught/controlpoint_icon_gib2.md3");
- precache_model("models/onslaught/controlpoint_icon_gib4.md3");
- precache_sound("onslaught/controlpoint_build.wav");
- precache_sound("onslaught/controlpoint_built.wav");
- precache_sound("weapons/grenade_impact.wav");
- precache_sound("onslaught/damageblockedbyshield.wav");
- precache_sound("onslaught/controlpoint_underattack.wav");
- precache_sound("onslaught/ons_spark1.wav");
- precache_sound("onslaught/ons_spark2.wav");
- self.solid = SOLID_BBOX;
- self.movetype = MOVETYPE_NONE;
- setmodel(self, "models/onslaught/controlpoint_pad.md3");
- //setsize(self, '-32 -32 0', '32 32 8');
- if(!self.noalign)
+ // Set the role timeout if necessary
+ if (!self.havocbot_role_timeout)
+ self.havocbot_role_timeout = time + 120;
+
+ if (time > self.havocbot_role_timeout)
{
- setorigin(self, self.origin + '0 0 20');
- droptofloor();
+ havocbot_ons_reset_role(self);
+ return;
}
- self.touch = onslaught_controlpoint_touch;
- self.team = 0;
- self.colormap = 1024;
- self.iscaptured = false;
- self.islinked = false;
- self.isshielded = true;
- // spawn shield model which indicates whether this can be damaged
- self.enemy = spawn();
- self.enemy.classname = "onslaught_controlpoint_shield";
- self.enemy.solid = SOLID_NOT;
- self.enemy.movetype = MOVETYPE_NONE;
- self.enemy.effects = EF_ADDITIVE;
- setmodel(self.enemy , "models/onslaught/controlpoint_shield.md3");
+ if(self.havocbot_attack_time>time)
+ return;
+
+ if (self.bot_strategytime < time)
+ {
+ navigation_goalrating_start();
+ havocbot_goalrating_enemyplayers(20000, self.origin, 650);
+ if(!havocbot_goalrating_ons_generator_attack(20000))
+ havocbot_goalrating_ons_controlpoints_attack(20000);
+ havocbot_goalrating_ons_offenseitems(10000, self.origin, 10000);
+ navigation_goalrating_end();
+
+ self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+ }
+}
+
+void havocbot_role_ons_assistant()
+{
+ havocbot_ons_reset_role(self);
+}
+
+void havocbot_role_ons_defense()
+{
+ havocbot_ons_reset_role(self);
+}
- setattachment(self.enemy , self, "");
- //setsize(e, '-32 -32 0', '32 32 128');
+void havocbot_ons_reset_role(entity bot)
+{
+ entity head;
+ float c;
- //setorigin(e, self.origin);
- self.enemy.colormap = self.colormap;
+ if(self.deadflag != DEAD_NO)
+ return;
- waypoint_spawnforitem(self);
+ bot.havocbot_ons_target = world;
- self.think = onslaught_controlpoint_think;
- self.nextthink = time;
+ // TODO: Defend control points or generator if necessary
- WaypointSprite_SpawnFixed(string_null, self.origin + '0 0 128', self, sprite, RADARICON_NONE, '0 0 0');
- WaypointSprite_UpdateRule(self.sprite, NUM_TEAM_2, SPRITERULE_TEAMPLAY);
+ // if there is only me on the team switch to offense
+ c = 0;
+ FOR_EACH_PLAYER(head)
+ if(SAME_TEAM(head, self))
+ ++c;
- onslaught_updatelinks();
+ if(c==1)
+ {
+ havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE);
+ return;
+ }
+
+ havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE);
+}
- self.reset = onslaught_controlpoint_reset;
- CSQCMODEL_AUTOINIT();
+/*
+ * Find control point or generator owned by the same team self which is nearest to pos
+ * if max_dist is positive, only control points within this range will be considered
+ */
+entity ons_Nearest_ControlPoint(vector pos, float max_dist)
+{
+ entity tmp_entity, closest_target = world;
+ tmp_entity = findchain(classname, "onslaught_controlpoint");
+ while(tmp_entity)
+ {
+ if(SAME_TEAM(tmp_entity, self))
+ if(tmp_entity.iscaptured)
+ if(max_dist <= 0 || vlen(tmp_entity.origin - pos) <= max_dist)
+ if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world)
+ closest_target = tmp_entity;
+ tmp_entity = tmp_entity.chain;
+ }
+ tmp_entity = findchain(classname, "onslaught_generator");
+ while(tmp_entity)
+ {
+ if(SAME_TEAM(tmp_entity, self))
+ if(max_dist <= 0 || vlen(tmp_entity.origin - pos) < max_dist)
+ if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world)
+ closest_target = tmp_entity;
+ tmp_entity = tmp_entity.chain;
+ }
+
+ return closest_target;
}
-float onslaught_link_send(entity to, float sendflags)
+/*
+ * Find control point or generator owned by the same team self which is nearest to pos
+ * if max_dist is positive, only control points within this range will be considered
+ * This function only check distances on the XY plane, disregarding Z
+ */
+entity ons_Nearest_ControlPoint_2D(vector pos, float max_dist)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK);
- WriteByte(MSG_ENTITY, sendflags);
- if(sendflags & 1)
+ entity tmp_entity, closest_target = world;
+ vector delta;
+ float smallest_distance = 0, distance;
+
+ tmp_entity = findchain(classname, "onslaught_controlpoint");
+ while(tmp_entity)
+ {
+ delta = tmp_entity.origin - pos;
+ delta_z = 0;
+ distance = vlen(delta);
+
+ if(SAME_TEAM(tmp_entity, self))
+ if(tmp_entity.iscaptured)
+ if(max_dist <= 0 || distance <= max_dist)
+ if(closest_target == world || distance <= smallest_distance )
+ {
+ closest_target = tmp_entity;
+ smallest_distance = distance;
+ }
+
+ tmp_entity = tmp_entity.chain;
+ }
+ tmp_entity = findchain(classname, "onslaught_generator");
+ while(tmp_entity)
+ {
+ delta = tmp_entity.origin - pos;
+ delta_z = 0;
+ distance = vlen(delta);
+
+ if(SAME_TEAM(tmp_entity, self))
+ if(max_dist <= 0 || distance <= max_dist)
+ if(closest_target == world || distance <= smallest_distance )
+ {
+ closest_target = tmp_entity;
+ smallest_distance = distance;
+ }
+
+ tmp_entity = tmp_entity.chain;
+ }
+
+ return closest_target;
+}
+/**
+ * find the number of control points and generators in the same team as self
+ */
+float ons_Count_SelfControlPoints()
+{
+ entity tmp_entity;
+ tmp_entity = findchain(classname, "onslaught_controlpoint");
+ float n = 0;
+ while(tmp_entity)
{
- WriteCoord(MSG_ENTITY, self.goalentity.origin.x);
- WriteCoord(MSG_ENTITY, self.goalentity.origin.y);
- WriteCoord(MSG_ENTITY, self.goalentity.origin.z);
+ if(SAME_TEAM(tmp_entity, self))
+ if(tmp_entity.iscaptured)
+ n++;
+ tmp_entity = tmp_entity.chain;
}
- if(sendflags & 2)
+ tmp_entity = findchain(classname, "onslaught_generator");
+ while(tmp_entity)
{
- WriteCoord(MSG_ENTITY, self.enemy.origin.x);
- WriteCoord(MSG_ENTITY, self.enemy.origin.y);
- WriteCoord(MSG_ENTITY, self.enemy.origin.z);
+ if(SAME_TEAM(tmp_entity, self))
+ n++;
+ tmp_entity = tmp_entity.chain;
}
- if(sendflags & 4)
+ return n;
+}
+
+/**
+ * Teleport player to a random position near tele_target
+ * if tele_effects is true, teleport sound+particles are created
+ * return false on failure
+ */
+float ons_Teleport(entity player, entity tele_target, float range, float tele_effects)
+{
+ if ( !tele_target )
+ return false;
+
+ float i;
+ vector loc;
+ float theta;
+ for(i = 0; i < 16; ++i)
+ {
+ theta = random() * 2 * M_PI;
+ loc_y = sin(theta);
+ loc_x = cos(theta);
+ loc_z = 0;
+ loc *= random() * range;
+
+ loc += tele_target.origin + '0 0 128';
+
+ tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, player);
+ if(trace_fraction == 1.0 && !trace_startsolid)
+ {
+ traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the world
+ if(trace_fraction == 1.0 && !trace_startsolid)
+ {
+ if ( tele_effects )
+ {
+ Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1);
+ sound (player, CH_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTEN_NORM);
+ }
+ setorigin(player, loc);
+ player.angles = '0 1 0' * ( theta * RAD2DEG + 180 );
+ makevectors(player.angles);
+ player.fixangle = true;
+ player.teleport_antispam = time + autocvar_g_onslaught_teleport_wait;
+
+ if ( tele_effects )
+ Send_Effect(EFFECT_TELEPORT, player.origin + v_forward * 32, '0 0 0', 1);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+// ==============
+// Hook Functions
+// ==============
+
+MUTATOR_HOOKFUNCTION(ons_ResetMap)
+{
+ FOR_EACH_PLAYER(self)
{
- WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16
+ self.ons_roundlost = false;
+ self.ons_deathloc = '0 0 0';
+ PutClientInServer();
}
- return true;
+ return false;
}
-void onslaught_link_checkupdate()
+MUTATOR_HOOKFUNCTION(ons_RemovePlayer)
{
- // TODO check if the two sides have moved (currently they won't move anyway)
- float redpower, bluepower;
+ self.ons_deathloc = '0 0 0';
+ return false;
+}
- redpower = bluepower = 0;
- if(self.goalentity.islinked)
+MUTATOR_HOOKFUNCTION(ons_PlayerSpawn)
+{
+ if(!round_handler_IsRoundStarted())
{
- if(self.goalentity.team == NUM_TEAM_1)
- redpower = 1;
- else if(self.goalentity.team == NUM_TEAM_2)
- bluepower = 1;
+ self.player_blocked = true;
+ return false;
}
- if(self.enemy.islinked)
+
+ entity l;
+ for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
{
- if(self.enemy.team == NUM_TEAM_1)
- redpower = 2;
- else if(self.enemy.team == NUM_TEAM_2)
- bluepower = 2;
+ l.sprite.SendFlags |= 16;
+ }
+ for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
+ {
+ l.sprite.SendFlags |= 16;
}
- float cc;
- if(redpower == 1 && bluepower == 2)
- cc = (NUM_TEAM_1 - 1) * 0x01 + (NUM_TEAM_2 - 1) * 0x10;
- else if(redpower == 2 && bluepower == 1)
- cc = (NUM_TEAM_1 - 1) * 0x10 + (NUM_TEAM_2 - 1) * 0x01;
- else if(redpower)
- cc = (NUM_TEAM_1 - 1) * 0x11;
- else if(bluepower)
- cc = (NUM_TEAM_2 - 1) * 0x11;
- else
- cc = 0;
+ if(ons_stalemate) { Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); }
+
+ if ( autocvar_g_onslaught_spawn_choose )
+ if ( self.ons_spawn_by )
+ if ( ons_Teleport(self,self.ons_spawn_by,autocvar_g_onslaught_teleport_radius,false) )
+ {
+ self.ons_spawn_by = world;
+ return false;
+ }
+
+ if(autocvar_g_onslaught_spawn_at_controlpoints)
+ if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance)
+ {
+ float random_target = autocvar_g_onslaught_spawn_at_controlpoints_random;
+ entity tmp_entity, closest_target = world;
+ vector spawn_loc = self.ons_deathloc;
+
+ // new joining player or round reset, don't bother checking
+ if(spawn_loc == '0 0 0') { return false; }
- //print(etos(self), " rp=", ftos(redpower), " bp=", ftos(bluepower), " ");
- //print("cc=", ftos(cc), "\n");
+ if(random_target) { RandomSelection_Init(); }
- if(cc != self.clientcolors)
+ for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext)
+ {
+ if(SAME_TEAM(tmp_entity, self))
+ if(random_target)
+ RandomSelection_Add(tmp_entity, 0, string_null, 1, 1);
+ else if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world)
+ closest_target = tmp_entity;
+ }
+
+ if(random_target) { closest_target = RandomSelection_chosen_ent; }
+
+ if(closest_target)
+ {
+ float i;
+ vector loc;
+ for(i = 0; i < 10; ++i)
+ {
+ loc = closest_target.origin + '0 0 96';
+ loc += ('0 1 0' * random()) * 128;
+ tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self);
+ if(trace_fraction == 1.0 && !trace_startsolid)
+ {
+ traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world
+ if(trace_fraction == 1.0 && !trace_startsolid)
+ {
+ setorigin(self, loc);
+ self.angles = normalize(loc - closest_target.origin) * RAD2DEG;
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ if(autocvar_g_onslaught_spawn_at_generator)
+ if(random() <= autocvar_g_onslaught_spawn_at_generator_chance)
{
- self.clientcolors = cc;
- self.SendFlags |= 4;
+ float random_target = autocvar_g_onslaught_spawn_at_generator_random;
+ entity tmp_entity, closest_target = world;
+ vector spawn_loc = self.ons_deathloc;
+
+ // new joining player or round reset, don't bother checking
+ if(spawn_loc == '0 0 0') { return false; }
+
+ if(random_target) { RandomSelection_Init(); }
+
+ for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
+ {
+ if(random_target)
+ RandomSelection_Add(tmp_entity, 0, string_null, 1, 1);
+ else
+ {
+ if(SAME_TEAM(tmp_entity, self))
+ if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world)
+ closest_target = tmp_entity;
+ }
+ }
+
+ if(random_target) { closest_target = RandomSelection_chosen_ent; }
+
+ if(closest_target)
+ {
+ float i;
+ vector loc;
+ for(i = 0; i < 10; ++i)
+ {
+ loc = closest_target.origin + '0 0 128';
+ loc += ('0 1 0' * random()) * 256;
+ tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self);
+ if(trace_fraction == 1.0 && !trace_startsolid)
+ {
+ traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world
+ if(trace_fraction == 1.0 && !trace_startsolid)
+ {
+ setorigin(self, loc);
+ self.angles = normalize(loc - closest_target.origin) * RAD2DEG;
+ return false;
+ }
+ }
+ }
+ }
}
- self.nextthink = time;
+ return false;
}
-void onslaught_link_delayed()
+MUTATOR_HOOKFUNCTION(ons_PlayerDies)
{
- self.goalentity = find(world, targetname, self.target);
- self.enemy = find(world, targetname, self.target2);
- if (!self.goalentity)
- objerror("can not find target\n");
- if (!self.enemy)
- objerror("can not find target2\n");
- dprint(etos(self.goalentity), " linked with ", etos(self.enemy), "\n");
- self.SendFlags |= 3;
- self.think = onslaught_link_checkupdate;
- self.nextthink = time;
+ frag_target.ons_deathloc = frag_target.origin;
+ entity l;
+ for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
+ {
+ l.sprite.SendFlags |= 16;
+ }
+ for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
+ {
+ l.sprite.SendFlags |= 16;
+ }
+
+ if ( autocvar_g_onslaught_spawn_choose )
+ if ( ons_Count_SelfControlPoints() > 1 )
+ stuffcmd(self, "qc_cmd_cl hud clickradar\n");
+
+ return false;
}
-/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)
- Link between control points.
+MUTATOR_HOOKFUNCTION(ons_MonsterThink)
+{
+ entity e = find(world, targetname, self.target);
+ if (e != world)
+ self.team = e.team;
- This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams.
+ return false;
+}
-keys:
-"target" - first control point.
-"target2" - second control point.
- */
-void spawnfunc_onslaught_link()
+void ons_MonsterSpawn_Delayed()
{
- if (!g_onslaught)
+ entity e, own = self.owner;
+
+ if(!own) { remove(self); return; }
+
+ if(own.targetname)
{
- remove(self);
- return;
+ e = find(world, target, own.targetname);
+ if(e != world)
+ {
+ own.team = e.team;
+
+ activator = e;
+ own.use();
+ }
}
- if (self.target == "" || self.target2 == "")
- objerror("target and target2 must be set\n");
- InitializeEntity(self, onslaught_link_delayed, INITPRIO_FINDTARGET);
- Net_LinkEntity(self, false, 0, onslaught_link_send);
+
+ remove(self);
}
-MUTATOR_HOOKFUNCTION(ons_BuildMutatorsString)
+MUTATOR_HOOKFUNCTION(ons_MonsterSpawn)
{
- ret_string = strcat(ret_string, ":ONS");
- return 0;
-}
+ entity e = spawn();
+ e.owner = self;
+ InitializeEntity(e, ons_MonsterSpawn_Delayed, INITPRIO_FINDTARGET);
-MUTATOR_HOOKFUNCTION(ons_BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Onslaught");
- return 0;
+ return false;
}
-MUTATOR_HOOKFUNCTION(ons_Spawn_Score)
+void ons_TurretSpawn_Delayed()
{
+ entity e, own = self.owner;
- /*
- float _neer_home = (random() > 0.5 ? true : false);
+ if(!own) { remove(self); return; }
- RandomSelection_Init();
+ if(own.targetname)
+ {
+ e = find(world, target, own.targetname);
+ if(e != world)
+ {
+ own.team = e.team;
+ own.active = ACTIVE_NOT;
+
+ activator = e;
+ own.use();
+ }
+ }
- if(self.team == NUM_TEAM_1)
- RandomSelection_Add(ons_red_generator, 0, string_null, 1, 1);
+ remove(self);
+}
- if(self.team == NUM_TEAM_2)
- RandomSelection_Add(ons_blue_generator, 0, string_null, 1, 1);
+MUTATOR_HOOKFUNCTION(ons_TurretSpawn)
+{
+ entity e = spawn();
+ e.owner = self;
+ InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET);
- entity _cp = findchain(classname, "onslaught_controlpoint"):
- while _cp;
- {
- if(_cp.team == self.team)
- RandomSelection_Add(_cp, 0, string_null, 1, 1);
+ return false;
+}
- _cp = _cp.chain;
- }
+MUTATOR_HOOKFUNCTION(ons_BotRoles)
+{
+ havocbot_ons_reset_role(self);
+ return true;
+}
- if(RandomSelection_chosen_ent)
+MUTATOR_HOOKFUNCTION(ons_GetTeamCount)
+{
+ // onslaught is special
+ entity tmp_entity;
+ for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
{
- self.tur_head = RandomSelection_chosen_ent;
- spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND;
+ switch(tmp_entity.team)
+ {
+ case NUM_TEAM_1: c1 = 0; break;
+ case NUM_TEAM_2: c2 = 0; break;
+ case NUM_TEAM_3: c3 = 0; break;
+ case NUM_TEAM_4: c4 = 0; break;
+ }
}
- else if(self.team == spawn_spot.team)
- spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate
- */
+ return true;
+}
+
+MUTATOR_HOOKFUNCTION(ons_SpectateCopy)
+{
+ self.ons_roundlost = other.ons_roundlost; // make spectators see it too
+ return false;
+}
+MUTATOR_HOOKFUNCTION(ons_SV_ParseClientCommand)
+{
+ if(MUTATOR_RETURNVALUE) // command was already handled?
+ return false;
+
+ if ( cmd_name == "ons_spawn" )
+ {
+ vector pos = self.origin;
+ if(cmd_argc > 1)
+ pos_x = stof(argv(1));
+ if(cmd_argc > 2)
+ pos_y = stof(argv(2));
+ if(cmd_argc > 3)
+ pos_z = stof(argv(3));
+
+ if ( IS_PLAYER(self) )
+ {
+ if ( !self.frozen )
+ {
+ entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius);
+
+ if ( !source_point && self.health > 0 )
+ {
+ sprint(self, "\nYou need to be next to a control point\n");
+ return 1;
+ }
+
+
+ entity closest_target = ons_Nearest_ControlPoint_2D(pos, autocvar_g_onslaught_click_radius);
+
+ if ( closest_target == world )
+ {
+ sprint(self, "\nNo control point found\n");
+ return 1;
+ }
+
+ if ( self.health <= 0 )
+ {
+ self.ons_spawn_by = closest_target;
+ self.respawn_flags = self.respawn_flags | RESPAWN_FORCE;
+ }
+ else
+ {
+ if ( source_point == closest_target )
+ {
+ sprint(self, "\nTeleporting to the same point\n");
+ return 1;
+ }
+
+ if ( !ons_Teleport(self,closest_target,autocvar_g_onslaught_teleport_radius,true) )
+ sprint(self, "\nUnable to teleport there\n");
+ }
+
+ return 1;
+ }
+
+ sprint(self, "\nNo teleportation for you\n");
+ }
+
+ return 1;
+ }
return 0;
}
// until it's 0.
.float dodging_velocity_gain;
-MUTATOR_HOOKFUNCTION(dodging_GetCvars) {
- GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
- return 0;
+#ifdef CSQC
- .float pressedkeys;
++.int pressedkeys;
+
+#elif defined(SVQC)
+
+void dodging_UpdateStats()
+{
+ self.stat_dodging = PHYS_DODGING;
+ self.stat_dodging_delay = PHYS_DODGING_DELAY;
+ self.stat_dodging_horiz_speed_frozen = PHYS_DODGING_HORIZ_SPEED_FROZEN;
+ self.stat_dodging_frozen = PHYS_DODGING_FROZEN;
+ self.stat_dodging_frozen_nodoubletap = PHYS_DODGING_FROZEN_NODOUBLETAP;
+ self.stat_dodging_height_threshold = PHYS_DODGING_HEIGHT_THRESHOLD;
+ self.stat_dodging_distance_threshold = PHYS_DODGING_DISTANCE_THRESHOLD;
+ self.stat_dodging_ramp_time = PHYS_DODGING_RAMP_TIME;
+ self.stat_dodging_up_speed = PHYS_DODGING_UP_SPEED;
+ self.stat_dodging_wall = PHYS_DODGING_WALL;
}
-MUTATOR_HOOKFUNCTION(dodging_PlayerPhysics) {
- // print("dodging_PlayerPhysics\n");
+void dodging_Initialize()
+{
+ addstat(STAT_DODGING, AS_INT, stat_dodging);
+ addstat(STAT_DODGING_DELAY, AS_FLOAT, stat_dodging_delay);
+ addstat(STAT_DODGING_TIMEOUT, AS_FLOAT, cvar_cl_dodging_timeout); // we stat this, so it is updated on the client when updated on server (otherwise, chaos)
+ addstat(STAT_DODGING_FROZEN_NO_DOUBLETAP, AS_INT, stat_dodging_frozen_nodoubletap);
+ addstat(STAT_DODGING_HORIZ_SPEED_FROZEN, AS_FLOAT, stat_dodging_horiz_speed_frozen);
+ addstat(STAT_DODGING_FROZEN, AS_INT, stat_dodging_frozen);
+ addstat(STAT_DODGING_HORIZ_SPEED, AS_FLOAT, stat_dodging_horiz_speed);
+ addstat(STAT_DODGING_HEIGHT_THRESHOLD, AS_FLOAT, stat_dodging_height_threshold);
+ addstat(STAT_DODGING_DISTANCE_THRESHOLD, AS_FLOAT, stat_dodging_distance_threshold);
+ addstat(STAT_DODGING_RAMP_TIME, AS_FLOAT, stat_dodging_ramp_time);
+ addstat(STAT_DODGING_UP_SPEED, AS_FLOAT, stat_dodging_up_speed);
+ addstat(STAT_DODGING_WALL, AS_FLOAT, stat_dodging_wall);
+}
- float common_factor;
- float new_velocity_gain;
- float velocity_difference;
- float clean_up_and_do_nothing;
- float horiz_speed = autocvar_sv_dodging_horiz_speed;
+#endif
- if(self.frozen)
- horiz_speed = autocvar_sv_dodging_horiz_speed_frozen;
+// returns 1 if the player is close to a wall
- float check_close_to_wall(float threshold)
++bool check_close_to_wall(float threshold)
+{
+ if (PHYS_DODGING_WALL == 0) { return false; }
- if (self.deadflag != DEAD_NO)
- return 0;
+ #define X(OFFSET) \
+ tracebox(self.origin, self.mins, self.maxs, self.origin + OFFSET, true, self); \
+ if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold) \
+ return true;
+ X(1000*v_right);
+ X(-1000*v_right);
+ X(1000*v_forward);
+ X(-1000*v_forward);
+ #undef X
- new_velocity_gain = 0;
- clean_up_and_do_nothing = 0;
+ return false;
+}
- float check_close_to_ground(float threshold)
- if (g_dodging == 0)
- clean_up_and_do_nothing = 1;
++bool check_close_to_ground(float threshold)
+{
+ return IS_ONGROUND(self) ? true : false;
+}
- // when swimming, no dodging allowed..
- if (self.waterlevel >= WATERLEVEL_SWIMMING)
- clean_up_and_do_nothing = 1;
+float PM_dodging_checkpressedkeys()
+{
+ if(!PHYS_DODGING)
+ return false;
+
+ float frozen_dodging = (PHYS_FROZEN(self) && PHYS_DODGING_FROZEN);
+ float frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP);
+
+ // first check if the last dodge is far enough back in time so we can dodge again
+ if ((time - self.last_dodging_time) < PHYS_DODGING_DELAY)
+ return false;
+
+ makevectors(self.angles);
+
+ if (check_close_to_ground(PHYS_DODGING_HEIGHT_THRESHOLD) != 1
+ && check_close_to_wall(PHYS_DODGING_DISTANCE_THRESHOLD) != 1)
+ return true;
+
+ float tap_direction_x = 0;
+ float tap_direction_y = 0;
+ float dodge_detected = 0;
+
+ #define X(COND,BTN,RESULT) \
+ if (self.movement_##COND) \
+ /* is this a state change? */ \
+ if(!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_##BTN) || frozen_no_doubletap) { \
+ tap_direction_##RESULT; \
+ if ((time - self.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(self)) \
+ dodge_detected = 1; \
+ self.last_##BTN##_KEY_time = time; \
+ }
+ X(x < 0, BACKWARD, x--);
+ X(x > 0, FORWARD, x++);
+ X(y < 0, LEFT, y--);
+ X(y > 0, RIGHT, y++);
+ #undef X
+
+ if (dodge_detected == 1)
+ {
+ self.last_dodging_time = time;
+
+ self.dodging_action = 1;
+ self.dodging_single_action = 1;
+
+ self.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED;
+
+ self.dodging_direction_x = tap_direction_x;
+ self.dodging_direction_y = tap_direction_y;
+
+ // normalize the dodging_direction vector.. (unlike UT99) XD
+ float length = self.dodging_direction_x * self.dodging_direction_x
+ + self.dodging_direction_y * self.dodging_direction_y;
+ length = sqrt(length);
+
+ self.dodging_direction_x = self.dodging_direction_x * 1.0 / length;
+ self.dodging_direction_y = self.dodging_direction_y * 1.0 / length;
+ return true;
+ }
+ return false;
+}
+
+void PM_dodging()
+{
+ if (!PHYS_DODGING)
+ return;
+
+#ifdef SVQC
+ dodging_UpdateStats();
+#endif
+
+ if (PHYS_DEAD(self))
+ return;
+
+ float frozen_dodging;
+ bool do_nothing = 0;
+ frozen_dodging = (PHYS_FROZEN(self) && PHYS_DODGING_FROZEN);
+
+ if(!self.cvar_cl_dodging)
+ do_nothing = 1;
+
+ if(cvar("g_overkill"))
+ do_nothing = 0; // always enabled in overkill
+
+ if(g_dodging == 2)
+ do_nothing = 0; // forced enabled
+
+ if(frozen_dodging)
+ do_nothing = 0;
- if (clean_up_and_do_nothing != 0) {
+ if(self.waterlevel >= WATERLEVEL_SWIMMING)
+ do_nothing = 1;
+
+ // when swimming, no dodging allowed..
+ if(do_nothing != 0)
+ {
self.dodging_action = 0;
self.dodging_direction_x = 0;
self.dodging_direction_y = 0;
}
// the up part of the dodge is a single shot action
- if (self.dodging_single_action == 1) {
- self.flags &= ~FL_ONGROUND;
+ if (self.dodging_single_action == 1)
+ {
+ UNSET_ONGROUND(self);
- self.velocity =
- self.velocity
- + (autocvar_sv_dodging_up_speed * v_up);
+ self.velocity += PHYS_DODGING_UP_SPEED * v_up;
- if (autocvar_sv_dodging_sound)
+#ifdef SVQC
- if (autocvar_sv_dodging_sound == 1)
++ if(autocvar_sv_dodging_sound)
PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
animdecide_setaction(self, ANIMACTION_JUMP, true);
void nade_boom()
{
- float expef;
- float nade_blast = 1;
- string expef;
++ int expef;
+ bool nade_blast = true;
switch ( self.nade_type )
{
case NADE_TYPE_NAPALM:
nade_blast = autocvar_g_nades_napalm_blast;
- expef = "explosion_medium";
+ expef = EFFECT_EXPLOSION_MEDIUM;
break;
case NADE_TYPE_ICE:
- nade_blast = 0;
+ nade_blast = false;
- expef = "electro_combo"; // hookbomb_explode electro_combo bigplasma_impact
+ expef = EFFECT_ELECTRO_COMBO; // hookbomb_explode electro_combo bigplasma_impact
break;
case NADE_TYPE_TRANSLOCATE:
- nade_blast = 0;
+ nade_blast = false;
- expef = "";
+ expef = 0;
break;
case NADE_TYPE_MONSTER:
case NADE_TYPE_SPAWN:
- nade_blast = 0;
+ nade_blast = false;
switch(self.realowner.team)
{
- case NUM_TEAM_1: expef = "spawn_event_red"; break;
- case NUM_TEAM_2: expef = "spawn_event_blue"; break;
- case NUM_TEAM_3: expef = "spawn_event_yellow"; break;
- case NUM_TEAM_4: expef = "spawn_event_pink"; break;
- default: expef = "spawn_event_neutral"; break;
+ case NUM_TEAM_1: expef = EFFECT_SPAWN_RED; break;
+ case NUM_TEAM_2: expef = EFFECT_SPAWN_BLUE; break;
+ case NUM_TEAM_3: expef = EFFECT_SPAWN_YELLOW; break;
+ case NUM_TEAM_4: expef = EFFECT_SPAWN_PINK; break;
+ default: expef = EFFECT_SPAWN_NEUTRAL; break;
}
break;
case NADE_TYPE_HEAL:
- nade_blast = 0;
+ nade_blast = false;
- expef = "spawn_event_red";
+ expef = EFFECT_SPAWN_RED;
break;
default:
.vector spawnpoint_score;
float spawnpoint_nag;
- float SpawnEvent_Send(entity to, float sf);
+ float SpawnEvent_Send(entity to, int sf);
entity Spawn_FilterOutBadSpots(entity firstspot, float mindist, float teamcheck);
+void spawnfunc_info_player_deathmatch();
+void spawnpoint_use();
entity SelectSpawnPoint (float anypoint);
#endif
#endif
#ifdef SVQC
- float ItemSend(entity to, float sf)
+ float ItemSend(entity to, int sf)
{
- if(self.gravity)
- sf |= ISF_DROP;
- else
- sf &= ~ISF_DROP;
+ if(self.gravity)
+ sf |= ISF_DROP;
+ else
+ sf &= ~ISF_DROP;
WriteByte(MSG_ENTITY, ENT_CLIENT_ITEM);
WriteByte(MSG_ENTITY, sf);
return item.bot_pickupbasevalue * c;
}
- void Item_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+ void Item_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
{
if(ITEM_DAMAGE_NEEDKILL(deathtype))
- RemoveItem();
+ RemoveItem(self);
}
void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue)
.float prevstrengthsound;
.float prevstrengthsoundattempt;
void W_PlayStrengthSound(entity player);
- float W_CheckProjectileDamage(entity inflictor, entity projowner, float deathtype, float exception);
+ float W_CheckProjectileDamage(entity inflictor, entity projowner, int deathtype, float exception);
void W_PrepareExplosionByDamage(entity attacker, void() explode);
+
+// jeff
+.entity jeff_projowner;
+
#endif