djcev.com

//

Git Repos / fte_dogmode / qc / func / breakable.qc

Last update to this file was on 2025-08-13 at 05:20.

Show breakable.qc

//==============================================================================
// func_breakable -- AD breakable code modified by Qmaster, iw, and dumptruck_ds
//==============================================================================

//======================================================================
// constants
//======================================================================

#ifdef SSQC
//----------------------------------------------------------------------
// base_breakable spawnflags -- CEV
//----------------------------------------------------------------------
typedef enumflags
{
SPAWNFLAG_BASE_BREAKABLE_NO_MONSTERS = 1, // used in base_entities.qc
SPAWNFLAG_BASE_BREAKABLE_EXPLODE = 2, // explosion effect and sound
// SPAWNFLAG_NOT_ON_EASY = 256, // see base_entities.qc -- CEV
// SPAWNFLAG_NOT_ON_NORMAL = 512,
// SPAWNFLAG_NOT_ON_HARD_OR_NIGHTMARE = 1024,
// SPAWNFLAG_NOT_IN_DEATHMATCH = 2048,
// SPAWNFLAG_NOT_IN_COOP = 4096,
// SPAWNFLAG_NOT_IN_SP = 8192,
// SPAWNFLAG_NOT_ON_SKILL2 = 32768, // see base_entities.qc -- CEV
// SPAWNFLAG_NOT_ON_SKILL3 = 65536, // see base_entities.qc -- CEV
// SPAWNFLAG_CENTERPRINTALL = 131072 // see base_entities.qc -- CEV
} base_breakable_spawnflags;
#endif

#ifdef SSQC
const float BASE_BREAKABLE_DEFAULTDEBRIS = 5;
const float BASE_BREAKABLE_MAXDEBRIS = 8;
#endif

//======================================================================
// forward declarations
//======================================================================

// base_breakable
#ifdef SSQC
void() sub_dislodge_resting_entities;
void(vector dir) base_breakable_die;
void(vector dir) base_breakable_destroy;
void() base_breakable_use;
void(string key, string value) base_breakable_init_field;
void(entity e) base_breakable_init;
strip void() base_breakable;
#endif

// func_breakable
#ifdef SSQC
void(entity e) func_breakable_init;
void() func_breakable;
#endif

//------------------------------------------------------------------------------

//----------------------------------------------------------------------
// base breakable class; used by func_breakable (below), func_fall2
//----------------------------------------------------------------------
// class base_breakable: base_func
// {
#ifdef SSQC
//--------------------------------------------------------------
// SUB_DislodgeRestingEntities
// This clears the FL_ONGROUND flag from any entities that are on
// top of self. The engine does not update the FL_ONGROUND flag
// automatically in some cases, with the result that certain types
// of entities can be left floating in mid-air if the entity they
// are resting on is removed from under them. This function is
// intended to be called in the case where self is going to be
// removed, to ensure that other entities are not left floating. -- iw
//--------------------------------------------------------------
void() sub_dislodge_resting_entities =
{
local entity e = nextent (world);

while (e && e != world)
{
if (e.groundentity == self && e.flags & FL_ONGROUND)
{
e.flags &= ~FL_ONGROUND;

// TODO CEV auto-set NETFLAGS from here
// to keep CSQC updated on origin as entity
// e falls to the ground
}

e = nextent (e);
}
};

//==============================================================
// Below this is from Rubicon2 -- dumptruck_ds
//==============================================================

//--------------------------------------------------------------
// dumptruck_ds -- set the spawnflag for cosmetic explosion
// effect; use "dmg" value to hurt the player
//--------------------------------------------------------------
void(vector dir) base_breakable_die =
{
var vector org, vel;
var entity d;
var float item_index;

self.deadflag |= DF_DEAD | DF_GIBBED;

if (self.noise1 != __NULL__ && self.noise1 != "")
{
// thanks to Qmaster!!! He helped me sort out
// noise1 playing from 0 0 0 with this temp
// entity - dumptruck_ds
var entity s = spawn ();

s.origin = ((self.absmin + self.absmax) * 0.5);
setsize (s, '0 0 0', '0 0 0');
s.solid = SOLID_NOT;
s.think = sub_remove;
s.nextthink = time + 60;

sound (s, CHAN_AUTO, self.noise1, VOL_HIGH, ATTN_NORM);
}

// this is to ensure that any debris from another
// func_breakable which is resting on top of this
// func_breakable is not left floating in mid-air
// after this entity is removed -- iw
sub_dislodge_resting_entities ();

self.origin = ((self.absmin + self.absmax) * 0.5);
setorigin (self, self.origin);

if (self.switchshadstyle)
lightstyle (self.switchshadstyle, "m");

// increase the distance debris will fly -- CEV
self.health *= 2;

// a more complicated base_monster_destroy_items () that
// randomizes origin a bit & sets debris frames -- CEV
for (float i = 0; i < BASE_ENTITY_ITEMLEN; i++)
{
item_index = self.item[i];

if (item_index == 0 || item_index == __NULL__)
continue;

org_x = (self.maxs_x - self.mins_x) * random() +
self.mins_x;
org_y = (self.maxs_y - self.mins_y) * random() +
self.mins_y;
org_z = (self.maxs_z - self.mins_z) * random() +
self.mins_z;
vel = velocity_for_damage (dir, self.health);

d = spawn_base_item_throwable (item_index,
org, vel, SPAWNFLAG_ITEM_THROWN);

// randomly choose size -- this won't persist once
// a player picks up a piece of debris but that's
// fine -- CEV
if (item_index >= ITEM_SEQ_BREAKABLE_START &&
item_index <= ITEM_SEQ_BREAKABLE_END)
{
if (random() > 0.333)
// larger
d.frame = 1;
else
// smaller
d.frame = 2;
}
}

if (self.spawnflags & SPAWNFLAG_BASE_BREAKABLE_EXPLODE)
{
self.takedamage = DAMAGE_NO;
t_radiusdamage2 (self, self, self.dmg, world);
write_explosion (self.origin);
spawn_base_explosion (self.origin);
}

base_entity_remove (self);
};

//--------------------------------------------------------------
void(vector dir) base_breakable_destroy =
{
activator = damage_attacker;
sub_usetargets ();
base_breakable_die (dir);
};

//--------------------------------------------------------------
void() base_breakable_use =
{
if (self.targetname == __NULL__ || self.targetname == "")
return;

activator = other;
sub_usetargets ();
base_breakable_die ('0 0 0');
};

//--------------------------------------------------------------
void(string key, string value) base_breakable_init_field =
{
switch (key)
{
case "drop_item":
base_entity_init_drop_item (stof(value));
break;
}
};

//--------------------------------------------------------------
// need to precache destruction sounds here; debris models
// will be precached in items/throwables.qc -- CEV
//--------------------------------------------------------------
void(entity e) base_breakable_init =
{
base_func_init (e);

if (e.noise != __NULL__ && e.noise1 != "")
{
precache_sound (e.noise1);
}
else
{
// adding new default sounds for "simple" breakables
// in 1.2.0 -- dumptruck_ds
switch (e.style)
{
// here's generic metal breaking
case 0: case 11: case 12: case 17:
case 18: case 19: case 24: case 31:
precache_sound ("break/metal2.wav");
e.noise1 = "break/metal2.wav";
break;
// wood sounds -- CEV
case 3: case 4: case 5:
precache_sound ("break/wood1.wav");
precache_sound ("break/wood2.wav");
// wood only randomized
if (random() > 0.6)
e.noise1 = "break/wood1.wav";
else
e.noise1 = "break/wood2.wav";
break;
// glass sounds -- more of a shattering sound
case 6: case 7: case 8: case 9: case 10:
precache_sound ("break/metal1.wav");
e.noise1 = "break/metal1.wav";
break;
// bricks -- CEV
case 1: case 2: case 13: case 14: case 15:
case 16: case 20: case 21: case 22: case 23:
precache_sound ("break/stones1.wav");
precache_sound ("break/bricks1.wav");
if (random() > 0.6)
e.noise1 = "break/bricks1.wav";
else
e.noise1 = "break/stones1.wav";
break;
// stones -- CEV
case 25: case 26: case 27: case 28:
case 29: case 30:
precache_sound ("break/stones1.wav");
precache_sound ("break/bricks1.wav");
if (random() > 0.6)
e.noise1 = "break/stones1.wav";
else
e.noise1 = "break/bricks1.wav";
break;
}
}
};

//--------------------------------------------------------------
strip void() base_breakable =
{
base_breakable_init (self);
};
#endif
// };

/*QUAKED func_breakable (0 .5 .8) ? NO_MONSTERS X X X X X X X NOT_ON_EASY NOT_ON_NORMAL NOT_ON_HARD_OR_NIGHTMARE NOT_IN_DEATHMATCH NOT_IN_COOP NOT_IN_SINGLEPLAYER X NOT_ON_HARD_ONLY NOT_ON_NIGHTMARE_ONLY

"Breakable - See manual for full details

Defaults to built-in .mdl file with 32 styles, cnt is number of pieces of debris to spawn (built-in only)
If noise1 is not set it will default to various sounds in sounds/break folder
Use spawnflag 2 for an explosion, dmg is amount of damage inflicted"

spawnflags(flags)
1 : "No Monster Damage" : 0 : "Only the player can break"
2 : "Explosion" : 0 : "Produces explosion effect and sound"

style(choices) : "Built-in debris style" : 0
0 : "Green Metal (default)"
1 : "Red Metal"
2 : "Concrete"
3 : "Pine wood"
4 : "Brown wood"
5 : "Red wood"
6 : "Stained Glass Yellow Flames"
7 : "Stained Glass Red Rays"
8 : "Stained Glass Yellow Dragon"
9 : "Stained Glass Blue Dragon"
10 : "Stained Glass Red Dragon"
11 : "Light Copper"
12 : "Dark Copper"
13 : "Tan Bricks Large"
14 : "Brown Bricks Large"
15 : "Green Bricks Large"
16 : "Generic Light Brown"
17 : "Red Brown Computer"
18 : "Grey Black Computer"
19 : "Blue Green Metal"
20 : "Blue Green Runic Wall"
21 : "Brown Metal"
22 : "Dark Brown Metal"
23 : "Medium Brown Metal"
24 : "Blue Metal"
25 : "Green Stonework"
26 : "Blue Stonework"
27 : "Brown Bricks"
28 : "Tan Blue Bricks"
29 : "Red Bricks"
30 : "Blue Bricks"
31 : "Metal Rivets"

noise1(string) : "Break noise (overrides default sounds)"
cnt(integer) : "Number of pieces of debris to spawn" : 4
health(integer) : "Health of breakable" : 20
dmg(integer) : "Amount of Explosive Damage" : 20

*/
//----------------------------------------------------------------------
// class func_breakable: base_breakable
// {
#ifdef SSQC
//--------------------------------------------------------------
void(entity e) func_breakable_init =
{
var float iaidx = 0;

e.classname = "func_breakable";
e.classtype = CT_FUNC_BREAKABLE;

base_breakable_init (e);

e.solid = SOLID_BSP;
e.movetype = MOVETYPE_PUSH;
setmodel (e, e.model);

if (!e.health)
e.health = 20;

// restrict cnt to a sane range w/a default -- CEV
if (!e.cnt)
e.cnt = BASE_BREAKABLE_DEFAULTDEBRIS;
else
e.cnt = bound (1, e.cnt, BASE_BREAKABLE_MAXDEBRIS);

// set up debris item index array -- CEV
for (float i = 0; i < e.cnt; i++)
{
iaidx = BASE_ENTITY_ITEMLEN - (i + 1);

// skip if this item index is occupied -- CEV
if (e.item[iaidx])
continue;

// .style maps directly to item_info starting at
// the ITEM_SEQ_BREAKABLE_START offset (that is,
// item_info is in the same order as listed in the
// QUAKEED comments on func_breakable -- CEV
e.item[iaidx] = ITEM_SEQ_BREAKABLE_START + e.style;
}

if (e.targetname != "" && e.targetname != __NULL__)
{
e.use = base_breakable_use;
}
else
{
e.takedamage = DAMAGE_YES;
e.th_destroy = base_breakable_destroy;
}

if (e.switchshadstyle)
lightstyle (e.switchshadstyle, "a");
};

//--------------------------------------------------------------
void() func_breakable =
{
BASE_FUNC_PREINIT (base_breakable_init_field)
func_breakable_init (self);
};
#endif
// };

Return to the top of this page or return to the overview of this repo.

Log breakable.qc

Return to the top of this page or return to the overview of this repo.