mirror of
https://github.com/nzp-team/quakec.git
synced 2024-11-25 13:21:32 +00:00
SERVER: Bouncing Betty Revamp
* Fix timing of priming sound. * Sped up animation playback rate. * Fix not playing "take out" animation after placing Betty. * Decreases raise time from 0.35 seconds to 0.28 seconds. * Betties rise with more force. * Increase explosion effect dramatics. * Betties no longer inflict self-damage. * Betties can no longer be re-bought from wall chalk. * Fixed betties using cost2 for their entity instead of cost. * Move bouncing betty logic to separate source file.
This commit is contained in:
parent
973d60eb9b
commit
bf4afd4ae3
8 changed files with 223 additions and 137 deletions
|
@ -37,6 +37,7 @@
|
|||
../source/server/weapons/tesla.qc
|
||||
../source/server/weapons/flamethrower.qc
|
||||
../source/server/weapons/grenade_launcher.qc
|
||||
../source/server/weapons/bouncing_betty.qc
|
||||
../source/server/weapons/weapon_core.qc
|
||||
|
||||
../source/server/entities/powerups.qc
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
../source/server/weapons/tesla.qc
|
||||
../source/server/weapons/flamethrower.qc
|
||||
../source/server/weapons/grenade_launcher.qc
|
||||
../source/server/weapons/bouncing_betty.qc
|
||||
../source/server/weapons/weapon_core.qc
|
||||
|
||||
../source/server/entities/powerups.qc
|
||||
|
|
|
@ -594,7 +594,9 @@ void(entity inflictor, entity attacker, float damage2, float mindamage, float ra
|
|||
{
|
||||
if(ent.classname == "player" && ent == attacker) // we don't want OUR explosives to harm other players..
|
||||
{
|
||||
if (ent.perks & P_FLOP)
|
||||
if (ent.perks & P_FLOP) // PhD Flopper makes us immune to any explosive damage
|
||||
final_damage = 0;
|
||||
else if (inflictor.classname == "betty") // Self-inflicted betties don't do damage either.
|
||||
final_damage = 0;
|
||||
else
|
||||
{
|
||||
|
@ -623,23 +625,22 @@ void(entity inflictor, entity attacker, float damage2, float mindamage, float ra
|
|||
final_damage *= 60;
|
||||
}
|
||||
DamageHandler (ent, ent, final_damage, S_EXPLOSIVE);
|
||||
|
||||
// shake the camera on impact
|
||||
vector distance;
|
||||
distance = inflictor.angles - ent.angles;
|
||||
|
||||
// just to prevent radical punchangles
|
||||
while(distance_x > 10 || distance_x < -10) {
|
||||
distance_x /= 2;
|
||||
}
|
||||
while(distance_y > 10 || distance_y < -10) {
|
||||
distance_y /= 2;
|
||||
}
|
||||
|
||||
// apply
|
||||
ent.punchangle_x = distance_x;
|
||||
ent.punchangle_y = distance_y;
|
||||
}
|
||||
// shake the camera on impact
|
||||
vector distance;
|
||||
distance = inflictor.angles - ent.angles;
|
||||
|
||||
// just to prevent radical punchangles
|
||||
while(distance_x > 10 || distance_x < -10) {
|
||||
distance_x /= 2;
|
||||
}
|
||||
while(distance_y > 10 || distance_y < -10) {
|
||||
distance_y /= 2;
|
||||
}
|
||||
|
||||
// apply
|
||||
ent.punchangle_x = distance_x;
|
||||
ent.punchangle_y = distance_y;
|
||||
}
|
||||
else if (ent.classname == "explosive_barrel")
|
||||
{
|
||||
|
|
|
@ -108,6 +108,11 @@ void() weapon_wall =
|
|||
makevectors(self.angles);
|
||||
setorigin(self.enemy, self.origin + v_right*-4);
|
||||
|
||||
// Backwards compatibility for betties brokenly using
|
||||
// cost2.
|
||||
if (self.cost == 0 && self.weapon == W_BETTY)
|
||||
self.cost = self.cost2;
|
||||
|
||||
// Prep it for usage.
|
||||
self.skin = 0;
|
||||
self.frame = self.sequence;
|
||||
|
@ -248,10 +253,12 @@ void () WallWeapon_TouchTrigger =
|
|||
}
|
||||
if (self.weapon == W_BETTY)
|
||||
{
|
||||
if (other.secondary_grenades < 2)
|
||||
useprint (other, 3, self.cost2, self.weapon);
|
||||
else
|
||||
// Betties are a one-time purchase.
|
||||
if (other.grenades & 2)
|
||||
return;
|
||||
|
||||
useprint (other, 4, self.cost, self.weapon);
|
||||
|
||||
if (!other.button7 || other.semiuse)
|
||||
return;
|
||||
if (other.points < self.cost2)
|
||||
|
|
173
source/server/weapons/bouncing_betty.qc
Normal file
173
source/server/weapons/bouncing_betty.qc
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
server/weapons/bouncing_betty.qc
|
||||
|
||||
Core logic for the "Bouncing Betty" grenade.
|
||||
|
||||
Copyright (C) 2021-2023 NZ:P Team
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
*/
|
||||
void() W_PlayTakeOut;
|
||||
|
||||
//
|
||||
// Betty_Rise()
|
||||
// Rise from the ground and explode.
|
||||
//
|
||||
void() Betty_Rise =
|
||||
{
|
||||
if (self.ltime < time) {
|
||||
self.velocity = '0 0 0';
|
||||
self.movetype = MOVETYPE_NONE;
|
||||
self.think = SUB_Null;
|
||||
GrenadeExplode();
|
||||
}
|
||||
|
||||
self.nextthink = time + 0.05;
|
||||
makevectors(self.angles);
|
||||
self.velocity = v_up*100;
|
||||
}
|
||||
|
||||
//
|
||||
// Betty_Touch()
|
||||
// Check if contact is with an enemy,
|
||||
// trigger if so.
|
||||
//
|
||||
void() Betty_Touch =
|
||||
{
|
||||
// Only trigger if an enemy is making contact.
|
||||
if (other.classname != "ai_zombie" && other.classname != "ai_dog")
|
||||
return;
|
||||
// Additional sanity check -- don't activate because of us.
|
||||
if (other == self.owner || other.solid == SOLID_TRIGGER)
|
||||
return;
|
||||
|
||||
// Next step is to rise!
|
||||
self.think = Betty_Rise;
|
||||
self.touch = SUB_Null;
|
||||
self.nextthink = time + 0.1;
|
||||
|
||||
// Set a timer for when to explode.
|
||||
self.ltime = time + 0.28;
|
||||
}
|
||||
|
||||
//
|
||||
// Betty_Drop()
|
||||
// Spawns the Betty grenade and puts it
|
||||
// on the ground.
|
||||
//
|
||||
void() Betty_Drop =
|
||||
{
|
||||
// Spawn it and set its attributes.
|
||||
entity betty;
|
||||
betty = spawn ();
|
||||
betty.owner = self;
|
||||
betty.grenade_delay = betty.owner.grenade_delay;
|
||||
betty.owner.grenade_delay = 0;
|
||||
betty.movetype = MOVETYPE_NOCLIP;
|
||||
betty.solid = SOLID_TRIGGER;
|
||||
betty.classname = "betty";
|
||||
|
||||
betty.velocity = v_forward*0;
|
||||
betty.touch = Betty_Touch;
|
||||
|
||||
setmodel (betty, "models/weapons/grenade/g_betty.mdl");
|
||||
setsize (betty, '-64 -64 -4', '64 64 32');
|
||||
|
||||
betty.origin = betty.owner.origin - self.view_ofs + '0 0 1';
|
||||
|
||||
// If we're in the air, just drop the betty to the ground
|
||||
// cypress -- This felt a little whack so I checked World at War..
|
||||
// ..and it does this too, so..
|
||||
if (!(self.flags & FL_ONGROUND)) {
|
||||
entity oldself;
|
||||
oldself = self;
|
||||
self = betty;
|
||||
|
||||
#ifdef FTE
|
||||
|
||||
droptofloor();
|
||||
|
||||
#else
|
||||
|
||||
droptofloor(0, 0);
|
||||
|
||||
#endif // FTE
|
||||
|
||||
self = oldself;
|
||||
}
|
||||
|
||||
// Sink into the ground a bit
|
||||
betty.origin += v_forward * 0;
|
||||
betty.origin += v_up * -1;
|
||||
setorigin (betty, betty.origin);
|
||||
|
||||
// Return the player's weapon to them
|
||||
self.animend = W_PlayTakeOut;
|
||||
self.callfuncat = 0;
|
||||
self.isBuying = false;
|
||||
}
|
||||
|
||||
//
|
||||
// Betty_CheckForRelease()
|
||||
// Holds the Bouncing Betty until the
|
||||
// action button is released, then kicks
|
||||
// off spawning the entity.
|
||||
//
|
||||
void() Betty_CheckForRelease =
|
||||
{
|
||||
// Release the Betty
|
||||
if (!self.button3) {
|
||||
if(self.grenade_delay < time)
|
||||
self.grenade_delay = time + 0.05;
|
||||
self.isBuying = true;
|
||||
Set_W_Frame (18, 23, 0.3, 5, GRENADE, Betty_Drop, "models/weapons/grenade/v_betty.mdl", true, S_RIGHT);
|
||||
sound (self, CHAN_WEAPON, "sounds/weapons/grenade/throw.wav", 1, ATTN_NORM);
|
||||
self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = time + 0.4;
|
||||
self.throw_delay = time + 0.9;
|
||||
}
|
||||
// Keep holding it
|
||||
else {
|
||||
self.isBuying = true;
|
||||
Set_W_Frame (18, 18, 0, 0, GRENADE, Betty_CheckForRelease, "models/weapons/grenade/v_betty.mdl", true, S_RIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// W_PrimeBetty()
|
||||
// Priming/Init state for the Bouncing Betty,
|
||||
// called inside of weapon_core.
|
||||
//
|
||||
void() W_PrimeBetty =
|
||||
{
|
||||
if (self.throw_delay > time || self.zoom || self.downed || self.secondary_grenades < 1 || self.isBuying)
|
||||
return;
|
||||
|
||||
// Prevent the Player from Sprinting and also avoid issues with
|
||||
// the equipment being completely cancelled..
|
||||
if (self.sprinting)
|
||||
W_SprintStop();
|
||||
|
||||
// Play the "prime" animation for the betty viewmodel.
|
||||
Set_W_Frame (0, 18, 1, 0, GRENADE, Betty_CheckForRelease, "models/weapons/grenade/v_betty.mdl", true, S_RIGHT);
|
||||
|
||||
// Subtract a betty from our inventory, set up delays, prevent use spam.
|
||||
self.secondary_grenades -= 1;
|
||||
self.reload_delay2 = self.fire_delay2 = self.throw_delay = self.reload_delay = self.fire_delay = time + 6;
|
||||
self.seminade = true;
|
||||
}
|
|
@ -149,6 +149,10 @@ void () W_Frame_Update =
|
|||
}
|
||||
}
|
||||
PlayWeaponSound(self.weapon, self.weapon_anim_type, FALSE, self.weaponframe);
|
||||
|
||||
// FIXME: We need a way to play sounds at specific frames for any viewmodel, not just weapons
|
||||
if (self.weaponmodel == "models/weapons/grenade/v_betty.mdl" && self.weaponframe == 12)
|
||||
sound (self, CHAN_WEAPON, "sounds/weapons/grenade/prime.wav", 1, ATTN_NORM);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
Core logic for the Wunderwaffe special weapon.
|
||||
|
||||
Copyright (C) 2021-2022 NZ:P Team
|
||||
Copyright (C) 2021-2023 NZ:P Team
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
|
|
|
@ -1403,14 +1403,23 @@ void() GrenadeExplode =
|
|||
DamgageExplode (self, self.owner, 12000, 11000, 128);
|
||||
else
|
||||
DamgageExplode (self, self.owner, 1200, 1000, 75);
|
||||
|
||||
|
||||
#ifdef FTE
|
||||
|
||||
te_customflash(self.origin, 200, 300, '1 1 1');
|
||||
|
||||
#endif // FTE
|
||||
|
||||
CallExplosion(self.origin);
|
||||
// Really stupid hack: For betties, let's dramatically increase
|
||||
// the explosion effect.
|
||||
if (self.classname == "betty") {
|
||||
CallExplosion(self.origin);
|
||||
CallExplosion(self.origin);
|
||||
CallExplosion(self.origin);
|
||||
CallExplosion(self.origin);
|
||||
} else {
|
||||
CallExplosion(self.origin);
|
||||
}
|
||||
|
||||
SUB_Remove ();
|
||||
};
|
||||
|
@ -1433,79 +1442,6 @@ void() Velocity_reduce =
|
|||
NadeStraighten();
|
||||
};
|
||||
|
||||
void() betty_do_rise =
|
||||
{
|
||||
if (self.ltime < time) {
|
||||
self.velocity = '0 0 0';
|
||||
self.movetype = MOVETYPE_NONE;
|
||||
self.think = SUB_Null;
|
||||
GrenadeExplode();
|
||||
}
|
||||
|
||||
self.nextthink = time + 0.05;
|
||||
makevectors(self.angles);
|
||||
self.velocity = v_up*70;
|
||||
}
|
||||
|
||||
void() betty_touch =
|
||||
{
|
||||
// Only trigger if an enemy is making contact.
|
||||
if (other.classname != "ai_zombie" && other.classname != "ai_dog")
|
||||
return;
|
||||
|
||||
if (other == self.owner || other.solid == SOLID_TRIGGER)
|
||||
return;
|
||||
|
||||
self.think = betty_do_rise;
|
||||
self.touch = SUB_Null;
|
||||
self.nextthink = time + 0.1;
|
||||
self.ltime = time + 0.35;
|
||||
}
|
||||
|
||||
void() W_ThrowBetty =
|
||||
{
|
||||
entity betty;
|
||||
betty = spawn ();
|
||||
betty.owner = self;
|
||||
betty.grenade_delay = betty.owner.grenade_delay;
|
||||
betty.owner.grenade_delay = 0;
|
||||
betty.movetype = MOVETYPE_NOCLIP;
|
||||
betty.solid = SOLID_TRIGGER;
|
||||
betty.classname = "betty";
|
||||
|
||||
betty.velocity = v_forward*0;
|
||||
betty.touch = betty_touch;
|
||||
|
||||
setmodel (betty, "models/weapons/grenade/g_betty.mdl");
|
||||
setsize (betty, '-64 -64 -4', '64 64 32');
|
||||
|
||||
betty.origin = betty.owner.origin - self.view_ofs + '0 0 1';
|
||||
//if player isn't on ground, drop the betty to the floor.
|
||||
if (!(self.flags & FL_ONGROUND)) {
|
||||
local entity oldself;
|
||||
oldself = self;
|
||||
self = betty;
|
||||
|
||||
#ifdef FTE
|
||||
|
||||
droptofloor();
|
||||
|
||||
#else
|
||||
|
||||
droptofloor(0, 0);
|
||||
|
||||
#endif // FTE
|
||||
|
||||
self = oldself;
|
||||
}
|
||||
betty.origin += v_forward * 0;
|
||||
setorigin (betty, betty.origin);
|
||||
|
||||
self.animend = ReturnWeaponModel;
|
||||
self.callfuncat = 0;
|
||||
self.isBuying = false;
|
||||
}
|
||||
|
||||
void() W_ThrowGrenade =
|
||||
{
|
||||
string modelname;
|
||||
|
@ -1549,7 +1485,7 @@ void() W_ThrowGrenade =
|
|||
self.callfuncat = 0;
|
||||
self.isBuying = false;
|
||||
} else if (self.pri_grenade_state == 1) {
|
||||
W_ThrowBetty();
|
||||
Betty_Drop();
|
||||
} else {
|
||||
centerprint (other, "No grenadetype defined...\n");
|
||||
}
|
||||
|
@ -1566,30 +1502,10 @@ void() W_ThrowGrenade =
|
|||
SetUpdate(self, UT_HUD, 6, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
void() checkHoldB =
|
||||
{
|
||||
if (!self.button3)
|
||||
{
|
||||
if(self.grenade_delay < time)
|
||||
self.grenade_delay = time + 0.05;
|
||||
self.isBuying = true;
|
||||
Set_W_Frame (18, 23, 0, 5, GRENADE, W_ThrowBetty, "models/weapons/grenade/v_betty.mdl", true, S_RIGHT);
|
||||
sound (self, CHAN_WEAPON, "sounds/weapons/grenade/throw.wav", 1, ATTN_NORM);
|
||||
self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = time + 0.4;
|
||||
self.throw_delay = time + 0.9;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.isBuying = true;
|
||||
Set_W_Frame (18, 18, 0, 0, GRENADE, checkHoldB, "models/weapons/grenade/v_betty.mdl", true, S_RIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
void() checkHold =
|
||||
{
|
||||
if (self.pri_grenade_state == 1) {
|
||||
checkHoldB();
|
||||
Betty_CheckForRelease();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1611,23 +1527,6 @@ void() checkHold =
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void() W_Betty = {
|
||||
if (self.throw_delay > time || self.zoom || self.downed || self.secondary_grenades < 1 || self.isBuying)
|
||||
return;
|
||||
|
||||
// Prevent the Player from Sprinting and also avoid issues with
|
||||
// the equipment being completely cancelled..
|
||||
if (self.sprinting)
|
||||
W_SprintStop();
|
||||
|
||||
Set_W_Frame (0, 18, 0, 0, GRENADE, checkHoldB, "models/weapons/grenade/v_betty.mdl", true, S_RIGHT);
|
||||
sound (self, CHAN_WEAPON, "sounds/weapons/grenade/prime.wav", 1, ATTN_NORM);
|
||||
self.secondary_grenades -= 1;
|
||||
self.reload_delay2 = self.fire_delay2 = self.throw_delay = self.reload_delay = self.fire_delay = time + 6;
|
||||
self.seminade = true;
|
||||
}
|
||||
|
||||
void() W_Grenade =
|
||||
{
|
||||
if (self.throw_delay > time || self.zoom || self.downed || self.primary_grenades < 1 || self.isBuying)
|
||||
|
@ -1777,7 +1676,7 @@ void () Impulse_Functions =
|
|||
switch_nade();
|
||||
break;
|
||||
case 33:
|
||||
W_Betty();
|
||||
W_PrimeBetty();
|
||||
break;
|
||||
case 26:
|
||||
case 30:
|
||||
|
@ -2301,7 +2200,7 @@ void () Weapon_Logic =
|
|||
if (self.pri_grenade_state == 0)
|
||||
W_Grenade();
|
||||
else
|
||||
W_Betty();
|
||||
W_PrimeBetty();
|
||||
self.seminade = true;
|
||||
} else if (!self.button3 && self.seminade) {
|
||||
self.seminade = false;
|
||||
|
|
Loading…
Reference in a new issue