game-source/ParoxysmII/source/wall.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;
}
};