323 lines
7.3 KiB
Plaintext
323 lines
7.3 KiB
Plaintext
static enumflags { EXPLODE, NO_EXPLOSION_SPRITE, NO_EXPLOSION_SOUND, SPAWN_SMOKE };
|
|
|
|
/*==========
|
|
breakable_pain
|
|
==========*/
|
|
static void(entity attacker, float damage) breakable_pain =
|
|
{
|
|
// play impact sound if set
|
|
if (self.noise2 != "")
|
|
sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
|
|
};
|
|
|
|
/*==========
|
|
breakable_think
|
|
==========*/
|
|
static void() breakable_think =
|
|
{
|
|
// hack to drop to ground if something below us was removed
|
|
if (self.flags & FL_ONGROUND)
|
|
self.flags = self.flags - FL_ONGROUND;
|
|
|
|
// fade out when self.attack_finished is up
|
|
if (time > self.attack_finished)
|
|
{
|
|
if (self.alpha == 0)
|
|
self.alpha = 1;
|
|
|
|
self.alpha = self.alpha - 0.1;
|
|
if (self.alpha <= 0)
|
|
{
|
|
remove(self);
|
|
return;
|
|
}
|
|
}
|
|
|
|
self.think = breakable_think;
|
|
self.nextthink = time + 0.1;
|
|
};
|
|
|
|
/*==========
|
|
breakable_touch
|
|
==========*/
|
|
static void() breakable_touch =
|
|
{
|
|
if (other != world)
|
|
return;
|
|
if (self.count)
|
|
return;
|
|
if (random() < 0.5)
|
|
return;
|
|
self.count = 1;
|
|
if (self.noise2 != "")
|
|
sound(self, CHAN_VOICE, self.noise2, 0.5, ATTN_IDLE);
|
|
};
|
|
|
|
/*==========
|
|
breakable_smoke
|
|
==========*/
|
|
static void(vector org) breakable_smoke =
|
|
{
|
|
entity smoke = spawn();
|
|
smoke.classname = "smoke";
|
|
smoke.solid = SOLID_NOT;
|
|
smoke.movetype = MOVETYPE_TOSS;
|
|
setmodel(smoke, "progs/smoke.mdl");
|
|
setorigin(smoke, org);
|
|
setsize(smoke, '0 0 0', '0 0 0');
|
|
|
|
smoke.velocity = [crandom()*200, crandom()*200, random()*100 + 100];
|
|
smoke.think = SUB_Remove;
|
|
smoke.nextthink = time + 1;
|
|
};
|
|
|
|
/*==========
|
|
breakable_spawn_debris
|
|
==========*/
|
|
static void(vector org, vector sz, vector dir) breakable_spawn_debris =
|
|
{
|
|
entity debris = spawn();
|
|
debris.classname = "debris";
|
|
debris.solid = SOLID_TRIGGER;
|
|
debris.movetype = MOVETYPE_BOUNCE;
|
|
|
|
setorigin(debris, [org.x + random() * sz.x, org.y + random() * sz.y, org.z + random() * sz.z]);
|
|
|
|
float r = random();
|
|
if (r < 0.33)
|
|
setmodel(debris, self.mdl1);
|
|
else if (r < 0.66)
|
|
setmodel(debris, self.mdl2);
|
|
else
|
|
setmodel(debris, self.mdl3);
|
|
|
|
// spawn some smoke particles
|
|
if (self.spawnflags & SPAWN_SMOKE)
|
|
if (random() < 0.5)
|
|
breakable_smoke(debris.origin);
|
|
|
|
debris.angles_y = random() * 360;
|
|
debris.velocity = dir * (50 + random() * 50);
|
|
debris.velocity_z = 50 + random() * 50;
|
|
if (self.health < -40)
|
|
debris.velocity = debris.velocity * 2;
|
|
debris.avelocity = randomvec() * 100;
|
|
|
|
debris.noise2 = self.noise2;
|
|
debris.touch = breakable_touch;
|
|
|
|
debris.think = breakable_think;
|
|
debris.nextthink = time + 0.1;
|
|
debris.attack_finished = time + 10 + random() * 10;
|
|
};
|
|
|
|
/*==========
|
|
breakable_explode
|
|
==========*/
|
|
static void() breakable_explode =
|
|
{
|
|
setorigin(self, realorigin(self)); // fix bmodel origin
|
|
|
|
if (self.dmg > 0)
|
|
RadiusDamage (self, self, self.dmg, world);
|
|
|
|
if (self.spawnflags & NO_EXPLOSION_SOUND)
|
|
{
|
|
// vanilla hack to get explosion particles with no sound/light
|
|
particle (self.origin, '0 0 0', 0, 255);
|
|
}
|
|
else
|
|
{
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_EXPLOSION);
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
}
|
|
|
|
if (self.spawnflags & NO_EXPLOSION_SPRITE)
|
|
remove(self);
|
|
else
|
|
BecomeExplosion ();
|
|
};
|
|
|
|
/*==========
|
|
breakable_break
|
|
==========*/
|
|
static void(entity inflictor) breakable_break =
|
|
{
|
|
self.takedamage = DAMAGE_NO;
|
|
self.use = SUB_Null;
|
|
|
|
// play sound, if set
|
|
if (self.noise1 != "")
|
|
sound(self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
|
|
|
|
// turn shadow off, supported by ericw-tools
|
|
if (self.switchshadstyle >= 32)
|
|
lightstyle(self.switchshadstyle, "m");
|
|
|
|
// the debris moves away from the direction of attack
|
|
// if movedir is set, this always overrides the direction
|
|
while (self.count > 0)
|
|
{
|
|
vector dir;
|
|
if (self.movedir)
|
|
dir = self.movedir;
|
|
else
|
|
{
|
|
if (inflictor == world)
|
|
dir = randomvec();
|
|
else
|
|
dir = normalize((self.absmin + self.absmax) * 0.5 - inflictor.origin);
|
|
}
|
|
breakable_spawn_debris(self.absmin, self.size, dir);
|
|
self.count = self.count - 1;
|
|
}
|
|
|
|
if (self.spawnflags & EXPLODE)
|
|
self.think = breakable_explode;
|
|
else
|
|
self.think = SUB_Remove;
|
|
self.nextthink = self.ltime + 0.01; // use ltime because it's MOVETYPE_PUSH
|
|
|
|
// fire targets
|
|
activator = self.enemy;
|
|
UseTargets();
|
|
};
|
|
|
|
/*==========
|
|
breakable_die
|
|
==========*/
|
|
static void(entity inflictor, entity attacker) breakable_die =
|
|
{
|
|
self.enemy = attacker;
|
|
breakable_break(inflictor);
|
|
};
|
|
|
|
/*==========
|
|
breakable_use
|
|
==========*/
|
|
static void() breakable_use =
|
|
{
|
|
self.enemy = activator;
|
|
breakable_break(world);
|
|
};
|
|
|
|
/*QUAKED func_breakable (0 .5 .8) ? EXPLODE NO_EXPLOSION_SPRITE NO_EXPLOSION_SOUND SPAWN_SMOKE X X X X NOT_IN_EASY NOT_IN_NORMAL NOT_IN_HARD NOT_IN_DM
|
|
Breakable brush model
|
|
|
|
Spawnflags:
|
|
EXPLODE - will explode upon destruction. Set the dmg key to the force of the explosion.
|
|
You can set dmg to 0 for just the explosion effects.
|
|
NO_EXPLOSION_SPRITE - don't show explosion sprite.
|
|
NO_EXPLOSION_SOUND - don't make explosion sound or light.
|
|
SPAWN_SMOKE - will spawn some additional smoke upon destuction
|
|
|
|
Keys:
|
|
"style" "n"
|
|
Preset style, 1=base crates, 2=stone/brick
|
|
Sets debris model, particle color and sounds automatically
|
|
If mdl1-3, noise or colour are set they will override the preset
|
|
|
|
"mdl1", "mdl2", "mdl3" "name"
|
|
custom models for debris chunks
|
|
|
|
"color" "n"
|
|
particle color for damage, range 0-254 (from palette)
|
|
|
|
"noise1", "noise2" "name"
|
|
custom sound for break / damage
|
|
|
|
"health" "n"
|
|
damage required to destroy. default 30.
|
|
|
|
"targetname" "name"
|
|
if set, the breakable must be triggered to destroy it
|
|
|
|
"count" "n"
|
|
number of pieces of debris to spawn. default 8.
|
|
|
|
"angle" "n"
|
|
If set, overrides the direction debris moves in. Use -1 and -2 for up/down. Use 360 for a yaw of 0.
|
|
|
|
"dmg" "n"
|
|
Amount of explosive damage to deal. Default 60.
|
|
*/
|
|
void() func_breakable =
|
|
{
|
|
// turn shadow on, supported by ericw-tools
|
|
if (self.switchshadstyle >= 32)
|
|
lightstyle(self.switchshadstyle, "a");
|
|
|
|
// pre-set styles
|
|
// 1 - base crate
|
|
if (self.style == 1)
|
|
{
|
|
if (!self.mdl1)
|
|
self.mdl1 = "maps/crate1.bsp";
|
|
if (!self.mdl2)
|
|
self.mdl2 = "maps/crate2.bsp";
|
|
if (!self.mdl3)
|
|
self.mdl3 = "maps/crate3.bsp";
|
|
if (!self.noise1)
|
|
self.noise1 = "break/metbrk.wav";
|
|
if (!self.noise2)
|
|
self.noise2 = "break/methit.wav";
|
|
if (!self.colour)
|
|
self.colour = 24;
|
|
}
|
|
// 2 - stone/brick
|
|
else if (self.style == 2)
|
|
{
|
|
if (!self.mdl1)
|
|
self.mdl1 = "maps/brick1.bsp";
|
|
if (!self.mdl2)
|
|
self.mdl2 = "maps/brick2.bsp";
|
|
if (!self.mdl3)
|
|
self.mdl3 = "maps/brick3.bsp";
|
|
if (!self.noise1)
|
|
self.noise1 = "break/stonebrk.wav";
|
|
if (!self.noise2)
|
|
self.noise2 = "break/stonehit.wav";
|
|
if (!self.colour)
|
|
self.colour = 16;
|
|
}
|
|
|
|
if (!self.mdl1 || !self.mdl2 || !self.mdl3)
|
|
{
|
|
objerror("mdl1, mdl2 or mdl3 not set\n");
|
|
remove(self);
|
|
return;
|
|
}
|
|
precache_model(self.mdl1);
|
|
precache_model(self.mdl2);
|
|
precache_model(self.mdl3);
|
|
|
|
// destruction and impact noises - optional
|
|
if (self.noise1 != "")
|
|
precache_sound(self.noise1);
|
|
if (self.noise2 != "")
|
|
precache_sound(self.noise2);
|
|
|
|
SetPositiveDefault(count, 8);
|
|
SetPositiveDefault(dmg, 8);
|
|
if (self.angles)
|
|
SetMovedir();
|
|
|
|
self.movetype = MOVETYPE_PUSH;
|
|
self.solid = SOLID_BSP;
|
|
setmodel (self, self.model);
|
|
|
|
// shootable, not triggered
|
|
if (!self.targetname)
|
|
{
|
|
SetPositiveDefault(health, 30);
|
|
self.takedamage = DAMAGE_YES;
|
|
self.th_pain = breakable_pain;
|
|
self.th_die = breakable_die;
|
|
}
|
|
else
|
|
self.use = breakable_use;
|
|
};
|