mirror of
https://git.code.sf.net/p/quake/game-source
synced 2024-12-01 08:01:19 +00:00
329 lines
8 KiB
R
329 lines
8 KiB
R
#include "config.rh"
|
|
|
|
#include "paroxysm.rh"
|
|
|
|
/*
|
|
Breakable object code
|
|
|
|
These are really cool entities, but try not to overuse them.
|
|
*/
|
|
#define SPAWN_GLASS 2
|
|
#define SPAWN_METAL 4
|
|
#define SPAWN_WOOD 8
|
|
|
|
.integer gib_caught; // if a gib bounces more than 4 times it's removed
|
|
|
|
vector (float dm, vector dir) VelocityForRubble =
|
|
{
|
|
local vector v, gib_dir;
|
|
|
|
gib_dir = normalize (dir);
|
|
|
|
v_x = 90 * gib_dir_x + (random () * 70 - 35);
|
|
v_y = 90 * gib_dir_y + (random () * 70 - 35);
|
|
v_z = 200 + 100 * random ();
|
|
|
|
if (dm > -50) {
|
|
v *= 0.9;
|
|
} else if (dm > -200) {
|
|
v *= 2;
|
|
} else {
|
|
v *= 10;
|
|
}
|
|
|
|
return v;
|
|
};
|
|
|
|
void () brik_touch =
|
|
{
|
|
local float vol; // randomize the volume so it don't sound like shit
|
|
|
|
vol = random ();
|
|
|
|
if (@self.velocity == '0 0 0') {
|
|
@self.avelocity = '0 0 0';
|
|
@self.solid = SOLID_NOT;
|
|
@self.touch = NIL;
|
|
@self.think = SUB_Remove;
|
|
@self.nextthink = time + (2 * random());
|
|
return;
|
|
}
|
|
|
|
if (@self.gib_caught > 4) {
|
|
remove (@self);
|
|
return;
|
|
}
|
|
|
|
// Gib already bounced twice, so remove damage (too easy to get killed)
|
|
if (@self.gib_caught > 1)
|
|
@self.dmg = 0;
|
|
|
|
if (@self.dmg) { // do damage if set
|
|
if (other.takedamage) {
|
|
T_Damage (other, @self, @self.owner, @self.dmg);
|
|
remove (@self);
|
|
}
|
|
}
|
|
|
|
@self.gib_caught++;
|
|
|
|
if (!(@self.cnt))
|
|
return;
|
|
|
|
if (vol < 0.3) // mute low volume
|
|
return;
|
|
|
|
if (pointcontents(@self.origin) == CONTENT_SOLID) { // bounce sound
|
|
switch (@self.cnt) {
|
|
case 1:
|
|
sound (@self, CHAN_AUTO, "ambience/brik_hit.wav", vol, ATTN_NORM);
|
|
break;
|
|
case 2:
|
|
sound (@self, CHAN_AUTO, "ambience/brikhit2.wav", vol, ATTN_NORM);
|
|
break;
|
|
case 3:
|
|
sound (@self, CHAN_AUTO, "ambience/methit1.wav", vol, ATTN_NORM);
|
|
break;
|
|
case 4:
|
|
sound (@self, CHAN_AUTO, "ambience/woodhit1.wav", vol, ATTN_NORM);
|
|
break;
|
|
case 6:
|
|
sound (@self, CHAN_AUTO, "ambience/methit2.wav", vol, ATTN_NORM);
|
|
break;
|
|
case 8:
|
|
sound (@self, CHAN_AUTO, "ambience/woodhit2.wav", vol, ATTN_NORM);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
void (string gibname, float dm, vector ddir) ThrowRubble =
|
|
{
|
|
local entity new;
|
|
local float sndrnd;
|
|
|
|
new = spawn ();
|
|
sndrnd = random ();
|
|
|
|
// new.origin = @self.origin doesnt work because the origin
|
|
// is at world (0,0,0).
|
|
new.origin_x = @self.absmin_x + (random() * @self.size_x);
|
|
new.origin_y = @self.absmin_y + (random() * @self.size_y);
|
|
new.origin_z = @self.absmin_z + (random() * @self.size_z);
|
|
|
|
setmodel (new, gibname);
|
|
setsize (new, '0 0 0', '0 0 0');
|
|
|
|
if (sndrnd < 0.25)
|
|
new.cnt = 1;
|
|
else if (sndrnd < 0.5)
|
|
new.cnt = 2;
|
|
|
|
// No bounce sound for glass since initial sound drags on for a bit
|
|
if (@self.spawnflags & SPAWN_GLASS)
|
|
new.cnt = 0;
|
|
|
|
if (@self.spawnflags & SPAWN_METAL)
|
|
new.cnt *= 3; // Play metal bounce on 3 & 6
|
|
|
|
if (@self.spawnflags & SPAWN_WOOD) {
|
|
new.cnt *= 4; // Play wood bounce on 4 & 8
|
|
new.skin = 1; // Change skin to wood if wood gib
|
|
}
|
|
|
|
new.velocity = VelocityForRubble (dm, ddir);
|
|
new.movetype = MOVETYPE_BOUNCE;
|
|
new.classname = "rubble";
|
|
new.solid = SOLID_BBOX;
|
|
new.touch = brik_touch;
|
|
new.avelocity_x = random () * 600;
|
|
new.avelocity_y = random () * 600;
|
|
new.avelocity_z = random () * 600;
|
|
new.think = SUB_Remove;
|
|
new.ltime = time;
|
|
new.nextthink = time + 10 + random () * 10;
|
|
new.dmg = @self.dmg;
|
|
new.frame = 0;
|
|
new.flags = 0;
|
|
};
|
|
|
|
/*
|
|
wall_killed
|
|
|
|
Called when a wall is destroyed. Throws "gibs" (rubble).
|
|
*/
|
|
void () wall_killed =
|
|
{
|
|
local entity sndspot;
|
|
local float rubble_count = 0;
|
|
local string md1, md2, md3, md4;
|
|
|
|
sndspot = spawn();
|
|
sndspot.origin = @self.absmin;
|
|
setorigin (sndspot, sndspot.origin);
|
|
|
|
if (@self.spawnflags & SPAWN_GLASS)
|
|
sound (sndspot, CHAN_AUTO, "ambience/glassbrk.wav", 1, ATTN_NORM);
|
|
else if (@self.spawnflags & SPAWN_METAL)
|
|
sound (sndspot, CHAN_AUTO, "ambience/metbrk.wav", 1, ATTN_NORM);
|
|
else if (@self.spawnflags & SPAWN_WOOD)
|
|
sound (sndspot, CHAN_AUTO, "ambience/woodbrk.wav", 1, ATTN_NORM);
|
|
else // New rubble sound
|
|
sound (sndspot, CHAN_AUTO, "ambience/wall01.wav", 1, ATTN_NORM);
|
|
|
|
remove (sndspot);
|
|
|
|
// determine volume of destroyed wall and throw rubble accordingly
|
|
rubble_count = (@self.size_x * @self.size_y * @self.size_z) / 64000;
|
|
|
|
if (rubble_count > 5)
|
|
rubble_count = 6;
|
|
|
|
if (@self.spawnflags & SPAWN_GLASS) {
|
|
md4 = md3 = md2 = md1 = "progs/glassrub.md";
|
|
|
|
while (rubble_count > -1) {
|
|
@self.dest_x = (random () * 100) - 50;
|
|
@self.dest_y = (random () * 100) - 50;
|
|
@self.dest_z = (random () * 100);
|
|
|
|
// This was cut down by 1/5 to deal with packet overflow errors
|
|
ThrowRubble (md1, -100, @self.dest);
|
|
ThrowRubble (md2, -100, @self.dest);
|
|
ThrowRubble (md3, -100, @self.dest);
|
|
ThrowRubble (md4, -100, @self.dest);
|
|
rubble_count--;
|
|
}
|
|
} else {
|
|
if (@self.spawnflags & SPAWN_METAL) {
|
|
md1 = "progs/mwrub1.mdl";
|
|
md2 = "progs/mwrub2.mdl";
|
|
md4 = md3 = "progs/mwrub3.mdl";
|
|
} else if (@self.spawnflags & SPAWN_WOOD) {
|
|
md1 = "progs/mwrub1.mdl";
|
|
md4 = md2 = "progs/mwrub2.mdl";
|
|
md3 = "progs/mwrub3.mdl";
|
|
} else {
|
|
md1 = "progs/rubble1.mdl";
|
|
md4 = md2 = "progs/rubble2.mdl";
|
|
md3 = "progs/rubble3.mdl";
|
|
}
|
|
|
|
while (rubble_count > -1) {
|
|
@self.dest_x = (random () * 100) - 50;
|
|
@self.dest_y = (random () * 100) - 50;
|
|
@self.dest_z = (random () * 100);
|
|
|
|
ThrowRubble (md1, @self.health, @self.dest);
|
|
ThrowRubble (md2, @self.health, @self.dest);
|
|
ThrowRubble (md3, @self.health, @self.dest);
|
|
ThrowRubble (md4, @self.health, @self.dest);
|
|
rubble_count--;
|
|
}
|
|
}
|
|
activator = @self;
|
|
|
|
SUB_UseTargets ();
|
|
|
|
@self.no_obj = TRUE; // mine fix - mines will detonate if spawnmaster.no_obj = TRUE (blown up)
|
|
remove (@self);
|
|
};
|
|
|
|
void() wall_pain =
|
|
{
|
|
if (@self.health > 0)
|
|
@self.health = @self.max_health;
|
|
};
|
|
|
|
void() wall_use =
|
|
{
|
|
@self.health = -100;
|
|
@self.dest_x = (random () * 10) - 5;
|
|
@self.dest_y = (random () * 10) - 5;
|
|
@self.dest_z = (random () * 10);
|
|
wall_killed ();
|
|
};
|
|
|
|
/*QUAKED exploding_wall
|
|
When the exploding wall is shot, it "gibs" into rubble.
|
|
Can also be triggered to explode.
|
|
|
|
"target" all entities with a matching targetname will be used when killed
|
|
"health" the amount of damage needed to destroy the wall instead of touched
|
|
"dmg" damage caused when hit by a gib
|
|
|
|
New Spawnflags added for PAROXYSM:
|
|
|
|
SPAWN_GLASS - glass explosion
|
|
|
|
SPAWN_METAL - metal shrapnel
|
|
|
|
SPAWN_WOOD - wood splintering
|
|
|
|
- no spawnflags is concrete rubble
|
|
|
|
Although it is possible to combine different types of explosions on one object,
|
|
it is not recommended. You can easily get overflow errors on large objects AND
|
|
since wood and metal share a gib model, no metal skins will be used if the
|
|
wood spawnflag is set.
|
|
*/
|
|
void() exploding_wall =
|
|
{
|
|
setmodel (@self, @self.model);
|
|
|
|
// New precache routine
|
|
|
|
precache_sound("zombie/z_hit.wav"); // for damage
|
|
|
|
if (@self.spawnflags & SPAWN_GLASS) {
|
|
precache_model("progs/glassrub.mdl");
|
|
precache_sound("ambience/glassbrk.wav");
|
|
} else if (@self.spawnflags & SPAWN_METAL) {
|
|
precache_model("progs/mwrub1.mdl");
|
|
precache_model("progs/mwrub2.mdl");
|
|
precache_model("progs/mwrub3.mdl");
|
|
precache_sound("ambience/metbrk.wav");
|
|
precache_sound("ambience/methit1.wav");
|
|
precache_sound("ambience/methit2.wav");
|
|
} else if (@self.spawnflags & SPAWN_WOOD) {
|
|
precache_model("progs/mwrub1.mdl");
|
|
precache_model("progs/mwrub2.mdl");
|
|
precache_model("progs/mwrub3.mdl");
|
|
precache_sound("ambience/woodbrk.wav");
|
|
precache_sound("ambience/woodhit1.wav");
|
|
precache_sound("ambience/woodhit2.wav");
|
|
} else { // precache concrete
|
|
precache_model("progs/rubble1.mdl");
|
|
precache_model("progs/rubble2.mdl");
|
|
precache_model("progs/rubble3.mdl");
|
|
precache_sound("ambience/wall01.wav");
|
|
precache_sound("ambience/brik_hit.wav");
|
|
precache_sound("ambience/brikhit2.wav");
|
|
}
|
|
|
|
@self.solid = SOLID_BBOX;
|
|
@self.movetype = MOVETYPE_NONE;
|
|
|
|
// POX v1.2 - default gib damage to 1
|
|
if (!@self.dmg)
|
|
@self.dmg = 1;
|
|
|
|
@self.nobleed = TRUE;
|
|
|
|
if (@self.health) {
|
|
@self.max_health = @self.health;
|
|
@self.th_die = wall_killed;
|
|
@self.takedamage = DAMAGE_YES;
|
|
} else {
|
|
@self.max_health = 100;
|
|
@self.th_die = wall_killed;
|
|
@self.takedamage = DAMAGE_YES;
|
|
}
|
|
|
|
@self.th_pain = wall_pain;
|
|
|
|
if (@self.targetname) {
|
|
@self.use = wall_use;
|
|
@self.max_health = 10000;
|
|
}
|
|
};
|