]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/func_breakable.qc
func_breakable: more cleanups, assault merge, entities.def docs
[divverent/nexuiz.git] / data / qcsrc / server / func_breakable.qc
1 // TODO add assault's colormodding to this, make assault redirect to this one
2 // TODO add the fields this uses to the entities.def when done
3
4 .string mdl_dead; // or "" to hide when broken
5 .string debris; // space separated list of debris models
6 // other fields:
7 //   mdl = particle effect name
8 //   count = particle effect multiplier
9 //   targetname = target to trigger to unbreak the model
10 //   target = targets to trigger when broken
11 //   health = amount of damage it can take
12 //   spawnflags:
13 //     1 = start disabled (needs to be triggered to activate)
14 //     2 = indicate damage
15 // notes:
16 //   for mdl_dead to work, origin must be set (using a common/origin brush).
17 //   Otherwise mdl_dead will be displayed at the map origin, and nobody would
18 //   want that!
19
20 .vector mins_save, maxs_save;
21
22 void func_breakable_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force);
23
24 //
25 // func_breakable
26 // - basically func_assault_destructible for general gameplay use
27 //
28 void LaunchDebris (string debrisname) =
29 {
30         local   entity dbr;
31
32         dbr = spawn();
33         dbr.origin = self.origin + self.absmin
34                    + '1 0 0' * random() * (self.absmax_x - self.absmin_x)
35                    + '0 1 0' * random() * (self.absmax_y - self.absmin_y)
36                    + '0 0 1' * random() * (self.absmax_z - self.absmin_z);
37         setmodel (dbr, debrisname );
38         dbr.movetype = MOVETYPE_BOUNCE;
39         dbr.solid = SOLID_NOT;
40         // TODO parametrize this
41         dbr.velocity_x = 70 * crandom();
42         dbr.velocity_y = 70 * crandom();
43         dbr.velocity_z = 140 + 70 * random();
44         dbr.avelocity_x = random()*600;
45         dbr.avelocity_y = random()*600;
46         dbr.avelocity_z = random()*600;
47         SUB_SetFade(dbr, time + 1 + random() * 5, 1);
48 };
49
50 void func_breakable_colormod()
51 {
52         float h;
53         if not(self.spawnflags & 2)
54                 return;
55         h = self.health / self.max_health;
56         if(h < 0.25)
57                 self.colormod = '1 0 0';
58         else if(h <= 0.75)
59                 self.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5);
60         else
61                 self.colormod = '1 1 1';
62 }
63
64 void func_breakable_look_destroyed()
65 {
66         if(self.mdl_dead == "")
67         {
68                 self.model = "";
69                 self.solid = SOLID_NOT;
70         }
71         else
72         {
73                 setmodel(self, self.mdl_dead);
74                 self.solid = SOLID_BSP;
75         }
76 }
77
78 void func_breakable_look_restore()
79 {
80         setmodel(self, self.mdl);
81         self.solid = SOLID_BSP;
82 }
83
84 void func_breakable_behave_destroyed()
85 {
86         self.health = self.max_health;
87         self.takedamage = DAMAGE_NO;
88         self.event_damage = SUB_Null;
89         self.state = 1;
90         setsize(self, '0 0 0', '0 0 0');
91         func_breakable_colormod();
92 }
93
94 void func_breakable_behave_restore()
95 {
96         self.health = self.max_health;
97         self.takedamage = DAMAGE_AIM;
98         self.event_damage = func_breakable_damage;
99         self.state = 0;
100         setsize(self, self.mins_save, self.maxs_save);
101         func_breakable_colormod();
102 }
103
104 void func_breakable_destroyed()
105 {
106         func_breakable_look_destroyed();
107         func_breakable_behave_destroyed();
108 }
109
110 void func_breakable_restore()
111 {
112         func_breakable_look_restore();
113         func_breakable_behave_restore();
114 }
115
116 void func_breakable_destroy() {
117         float n, i;
118
119         func_breakable_destroyed();
120
121         // now throw around the debris
122         n = tokenize_sane(self.debris);
123         for(i = 0; i < n; ++i)
124                 LaunchDebris(argv(i));
125
126         if(self.cnt)
127                 pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count);
128
129         SUB_UseTargets();
130 }
131
132 void func_breakable_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
133 {
134         if(self.state == 1)
135                 return;
136         self.health = self.health - damage;
137         if(self.health < 0)
138         {
139                 activator = attacker;
140                 func_breakable_destroy();
141         }
142         func_breakable_colormod();
143 }
144
145 void func_breakable_reset()
146 {
147         func_breakable_look_restore();
148         if(self.spawnflags & 1)
149                 func_breakable_behave_destroyed();
150         else
151                 func_breakable_behave_restore();
152 }
153
154 // destructible walls that can be used to trigger target_objective_decrease
155 void spawnfunc_func_breakable() {
156         float n, i;
157         if(!self.health)
158                 self.health = 100;
159         self.max_health = self.health;
160
161         if(self.mdl != "")
162                 self.cnt = particleeffectnum(self.mdl);
163         if(self.count == 0)
164                 self.count = 1;
165
166         self.mdl = self.model;
167         SetBrushEntityModel();
168         self.mins_save = self.mins;
169         self.maxs_save = self.mins;
170
171         self.use = func_breakable_restore;
172
173         // precache all the models
174         if (self.mdl_dead)
175                 precache_model(self.mdl_dead);
176         n = tokenize_sane(self.debris);
177         for(i = 0; i < n; ++i)
178                 precache_model(argv(i));
179
180         func_breakable_reset();
181 }