/*============================================ Wazat's Voretournament Grabber Contact: Wazat1@gmail.com Installation instructions: -------------------------- 1. Place grabber.c in your gamec source directory with the other source files. 2. Add this line to the bottom of progs.src: gamec/grabber.c 3. Open defs.h and add these lines to the very bottom: // Wazat's grabber .entity grabber; voidGrabberFrame(); void RemoveGrabber(entity pl); void SetGrabberBindings(); // grabber impulses float GRABBER_FIRE = 20; float GRABBER_RELEASE = 21; // (note: you can change the grabber impulse #'s to whatever you please) 4. Open client.c and add this to the top of PutClientInServer(): RemoveGrabber(self); // Wazat's Grabber 5. Find ClientConnect() (in client.c) and add these lines to the bottom: // Wazat's grabber SetGrabberBindings(); 6. Still in client.c, find PlayerPreThink and add this line just above the call to W_WeaponFrame: GrabberFrame(); 7. Build and test the mod. You'll want to bind a key to "+grabber" like this: bind ctrl "+grabber" And you should be done! ============================================*/ .string aiment_classname; .float aiment_deadflag; void SetMovetypeFollow(entity ent, entity e) { // FIXME this may not be warpzone aware ent.movetype = MOVETYPE_FOLLOW; // make the hole follow ent.solid = SOLID_NOT; // MOVETYPE_FOLLOW is always non-solid - this means this cannot be teleported by warpzones any more! Instead, we must notice when our owner gets teleported. ent.aiment = e; // make the hole follow bmodel ent.punchangle = e.angles; // the original angles of bmodel ent.view_ofs = ent.origin - e.origin; // relative origin ent.v_angle = ent.angles - e.angles; // relative angles ent.aiment_classname = strzone(e.classname); ent.aiment_deadflag = e.deadflag; } void UnsetMovetypeFollow(entity ent) { ent.movetype = MOVETYPE_FLY; PROJECTILE_MAKETRIGGER(ent); ent.aiment = world; } float LostMovetypeFollow(entity ent) { /* if(ent.movetype != MOVETYPE_FOLLOW) if(ent.aiment) error("???"); */ if(ent.aiment) { if(ent.aiment.classname != ent.aiment_classname) return 1; if(ent.aiment.deadflag != ent.aiment_deadflag) return 1; } return 0; } .float grabber_length; void RemoveGrabber(entity pl) { if(pl.grabber == world) return; remove(pl.grabber); pl.grabber = world; if(pl.movetype == MOVETYPE_FLY) pl.movetype = MOVETYPE_WALK; //pl.disableclientprediction = FALSE; } void GrabberThink(); void Grabber_Stop() { pointparticles(particleeffectnum("grabber_impact"), self.origin, '0 0 0', 1); sound (self, CHAN_PROJECTILE, "weapons/grabber_impact.wav", VOL_BASE, ATTN_NORM); self.state = 1; self.think =GrabberThink; self.nextthink = time; self.touch = SUB_Null; self.velocity = '0 0 0'; self.movetype = MOVETYPE_NONE; self.grabber_length = -1; } void GrabberThink() { float spd, dist, minlength, pullspeed, ropestretch, ropeairfriction, rubberforce, newlength, rubberforce_overstretch; vector dir, org, end, v0, dv, v, myorg; if(self.owner.health <= 0 || self.owner.grabber != self) // how did that happen? { // well, better fix it anyway remove(self); return; } if(LostMovetypeFollow(self)) { RemoveGrabber(self.owner); return; } if(self.aiment) WarpZone_RefSys_AddIncrementally(self, self.aiment); // prevent the grabber from sticking to a player that has been swallowed if(self.aiment.predator.classname == "player") { // if the grabber is linked to a player that we swallowed, disconnect it (or the grabber will stick to us / the player inside us) // otherwise, link the grabber to the player who ate our linked player if(self.aiment.predator != self.owner) SetMovetypeFollow(self, self.aiment.predator); else RemoveGrabber(self.owner); } self.nextthink = time; makevectors(self.owner.v_angle); org = self.owner.origin + self.owner.view_ofs + v_forward * grabber_shotorigin_x + v_right * grabber_shotorigin_y + v_up * grabber_shotorigin_z; myorg = WarpZone_RefSys_TransformOrigin(self.owner, self, org); if(self.grabber_length < 0) self.grabber_length = vlen(myorg - self.origin); if(self.state == 1) { pullspeed = cvar("g_balance_grabber_speed_pull");//2000; // speed the rope is pulled with rubberforce = cvar("g_balance_grabber_force_rubber");//2000; // force the rope will use if it is stretched rubberforce_overstretch = cvar("g_balance_grabber_force_rubber_overstretch");//1000; // force the rope will use if it is stretched minlength = cvar("g_balance_grabber_length_min");//100; // minimal rope length // if the rope goes below this length, it isn't pulled any more ropestretch = cvar("g_balance_grabber_stretch");//400; // if the rope is stretched by more than this amount, more rope is // given to you again ropeairfriction = cvar("g_balance_grabber_airfriction");//0.2 // while hanging on the rope, this friction component will help you a // bit to control the rope dir = self.origin - myorg; dist = vlen(dir); dir = normalize(dir); if(cvar("g_grabber_tarzan")) { v = v0 = WarpZone_RefSys_TransformVelocity(self.owner, self, self.owner.velocity); // first pull the rope... if(self.owner.grabber_state & GRABBER_PULLING) { newlength = self.grabber_length; newlength = max(newlength - pullspeed * frametime, minlength); if(newlength < dist - ropestretch) // overstretched? { newlength = dist - ropestretch; if(v * dir < 0) // only if not already moving in grabber direction v = v + frametime * dir * rubberforce_overstretch; } self.grabber_length = newlength; } if(self.owner.grabber_state & GRABBER_RELEASING) { newlength = dist; self.grabber_length = newlength; } else { // then pull the player spd = bound(0, (dist - self.grabber_length) / ropestretch, 1); v = v * (1 - frametime * ropeairfriction); v = v + frametime * dir * spd * rubberforce; dv = ((v - v0) * dir) * dir; if(cvar("g_grabber_tarzan") >= 2) { if(self.aiment.movetype == MOVETYPE_WALK) { v = v - dv * 0.5; self.aiment.velocity = self.aiment.velocity - dv * 0.5; self.aiment.flags &~= FL_ONGROUND; self.aiment.pusher = self.owner; self.aiment.pushltime = time + cvar("g_maxpushtime"); } } self.owner.flags &~= FL_ONGROUND; } self.owner.velocity = WarpZone_RefSys_TransformVelocity(self, self.owner, v); } else { end = self.origin - dir*50; dist = vlen(end - myorg); if(dist < 200) spd = dist * (pullspeed / 200); else spd = pullspeed; if(spd < 50) spd = 0; self.owner.velocity = dir*spd; self.owner.movetype = MOVETYPE_FLY; self.owner.flags &~= FL_ONGROUND; } } makevectors(self.angles_x * '-1 0 0' + self.angles_y * '0 1 0'); te_beam(self.owner, WarpZone_RefSys_TransformOrigin(self, self.owner, self.origin) + v_forward * (-9), org); } void GrabberTouch (void) { if(SUB_OwnerCheck()) return; if(SUB_NoImpactCheck()) { RemoveGrabber(self.owner); return; } PROJECTILE_TOUCH; Grabber_Stop(); if(other) if(other.movetype != MOVETYPE_NONE) { if(other.classname == "player") { if(other.BUTTON_CHAT) self.owner.typehitsound += 1; else self.owner.hitsound += 1; // play this for team mates too, as we could be grabbing them to heal them W_Grabber_UpdateStats(self.owner, FALSE, TRUE); // count this as a hit } SetMovetypeFollow(self, other); WarpZone_RefSys_BeginAddingIncrementally(self, self.aiment); } //self.owner.disableclientprediction = TRUE; } void Grabber_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) { if(self.health > 0) { self.health = self.health - damage; if (self.health <= 0) { if(attacker != self.owner) { self.owner.pusher = attacker; self.owner.pushltime = time + cvar("g_maxpushtime"); } RemoveGrabber(self.owner); } } } void FireGrabber (void) { local entity missile; local vector org; W_Grabber_UpdateStats(self, TRUE, FALSE); if((arena_roundbased && time < warmup) || (time < game_starttime)) return; makevectors(self.v_angle); // UGLY WORKAROUND: play this on CHAN_WEAPON2 so it can't cut off fire sounds sound (self, CHAN_WEAPON2, "weapons/grabber_fire.wav", VOL_BASE, ATTN_NORM); org = self.origin + self.view_ofs + v_forward * grabber_shotorigin_x + v_right * grabber_shotorigin_y + v_up * grabber_shotorigin_z; pointparticles(particleeffectnum("grapple_muzzleflash"), org, '0 0 0', 1); missile = WarpZone_RefSys_SpawnSameRefSys(self); missile.owner = self; self.grabber = missile; missile.classname = "grabber"; missile.movetype = MOVETYPE_FLY; PROJECTILE_MAKETRIGGER(missile); setmodel (missile, "models/grabber.md3"); // precision set below setsize (missile, '-3 -3 -3', '3 3 3'); setorigin (missile, org); missile.state = 0; // not latched onto anything W_SetupProjectileVelocityEx(missile, v_forward, v_up, cvar("g_balance_grabber_speed_fly"), 0, 0, 0); missile.angles = vectoangles (missile.velocity); //missile.glow_color = 250; // 244, 250 //missile.glow_size = 120; missile.touch =GrabberTouch; missile.think =GrabberThink; missile.nextthink = time + 0.1; missile.effects = /*EF_FULLBRIGHT | EF_ADDITIVE |*/ EF_LOWPRECISION; missile.health = cvar("g_balance_grabber_health");//120 missile.event_damage =Grabber_Damage; missile.takedamage = DAMAGE_AIM; missile.damageforcescale = 0; } void GrabberFrame() { if(timeoutStatus != 2 && self.weapon != WEP_GRABBER) { self.grabber_state |= GRABBER_REMOVING; self.grabber_state &~= GRABBER_WAITING_FOR_RELEASE; self.grabber_state &~= GRABBER_RELEASING; if(self.BUTTON_CROUCH) { self.grabber_state &~= GRABBER_PULLING; //self.grabber_state |= GRABBER_RELEASING; } else { self.grabber_state |= GRABBER_PULLING; //self.grabber_state &~= GRABBER_RELEASING; } } if(self.weapon != WEP_GRABBER) { self.grabber_state &~= GRABBER_FIRING; self.grabber_state |= GRABBER_REMOVING; } if (self.grabber_state & GRABBER_FIRING) { if (self.grabber) RemoveGrabber(self); FireGrabber(); self.grabber_state &~= GRABBER_FIRING; } else if(self.grabber_state & GRABBER_REMOVING) { if (self.grabber) RemoveGrabber(self); self.grabber_state &~= GRABBER_REMOVING; } /* // if I have no grabber or it's not pulling yet, make sure I'm not flying! if((self.grabber == world || !self.grabber.state) && self.movetype == MOVETYPE_FLY) { self.movetype = MOVETYPE_WALK; } if(self.impulse == GRABBER_FIRE && self.grabber_time <= time) { // fire grabber FireGrabber(); return; } else if(self.grabberimpulse == GRABBER_RELEASE) { // remove grabber, reset movement type RemoveGrabber(self); return; } */ /*else // make sure the player's movetype is correct { //if(self.grabber == world && self.movetype == MOVETYPE_FLY) if((self.grabber == world || !self.grabber.state) && self.movetype == MOVETYPE_FLY) { self.movetype = MOVETYPE_WALK; } }*/ // note: The grabber entity does the actual pulling } void GrabberInit() { grabber_shotorigin = shotorg_adjust('26.2148 9.2059 -15.9772', TRUE, FALSE); } void SetGrabberBindings() { // this function has been modified for Voretournament // don't remove these lines! old server or demos coud overwrite the new aliases stuffcmd(self, "alias +grabber +button6\n"); stuffcmd(self, "alias -grabber -button6\n"); }