]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/func_breakable.qc
- Allow func_breakable to use .anglesjitter (and anglejitter) so that we can have...
[divverent/nexuiz.git] / data / qcsrc / server / func_breakable.qc
1 .entity sprite;
2
3 .float dmg;
4 .float dmg_edge;
5 .float dmg_radius;
6 .float dmg_force;
7
8 .string mdl_dead; // or "" to hide when broken
9 .string debris; // space separated list of debris models
10 // other fields:
11 //   mdl = particle effect name
12 //   count = particle effect multiplier
13 //   targetname = target to trigger to unbreak the model
14 //   target = targets to trigger when broken
15 //   health = amount of damage it can take
16 //   spawnflags:
17 //     1 = start disabled (needs to be triggered to activate)
18 //     2 = indicate damage
19 // notes:
20 //   for mdl_dead to work, origin must be set (using a common/origin brush).
21 //   Otherwise mdl_dead will be displayed at the map origin, and nobody would
22 //   want that!
23
24 .vector mins_save, maxs_save;
25
26 void func_breakable_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force);
27
28 //
29 // func_breakable
30 // - basically func_assault_destructible for general gameplay use
31 //
32 void LaunchDebris (string debrisname) =
33 {
34         local   entity dbr;
35
36         dbr = spawn();
37         dbr.origin = self.origin + self.absmin
38                    + '1 0 0' * random() * (self.absmax_x - self.absmin_x)
39                    + '0 1 0' * random() * (self.absmax_y - self.absmin_y)
40                    + '0 0 1' * random() * (self.absmax_z - self.absmin_z);
41         setmodel (dbr, debrisname );
42         dbr.movetype = MOVETYPE_BOUNCE;
43         dbr.solid = SOLID_NOT;
44         // TODO parametrize this
45         dbr.velocity_x = 70 * crandom();
46         dbr.velocity_y = 70 * crandom();
47         dbr.velocity_z = 140 + 70 * random();
48         dbr.avelocity_x = random()*600;
49         dbr.avelocity_y = random()*600;
50         dbr.avelocity_z = random()*600;
51         SUB_SetFade(dbr, time + 1 + random() * 5, 1);
52 };
53
54 void func_breakable_colormod()
55 {
56         float h;
57         if not(self.spawnflags & 2)
58                 return;
59         h = self.health / self.max_health;
60         if(h < 0.25)
61                 self.colormod = '1 0 0';
62         else if(h <= 0.75)
63                 self.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5);
64         else
65                 self.colormod = '1 1 1';
66 }
67
68 void func_breakable_look_destroyed()
69 {
70         local float floor_z;
71         
72         if(self.mdl_dead == "")
73                 self.model = "";
74         else {
75                 setmodel(self, self.mdl_dead);
76                 if (self.origin == '0 0 0')     {       // probably no origin brush, so don't spawn in the middle of the map..
77                         floor_z = self.absmin_z;
78                         self.origin = ((self.absmax+self.absmin)*.5);
79                         self.origin_z = floor_z;
80                 }
81         }
82                 
83         self.solid = SOLID_NOT;
84 }
85
86 void func_breakable_look_restore()
87 {
88         setmodel(self, self.mdl);
89         self.solid = SOLID_BSP;
90 }
91
92 void func_breakable_behave_destroyed()
93 {
94         self.health = self.max_health;
95         self.takedamage = DAMAGE_NO;
96         self.event_damage = SUB_Null;
97         self.state = 1;
98         setsize(self, '0 0 0', '0 0 0');
99         func_breakable_colormod();
100 }
101
102 void func_breakable_behave_restore()
103 {
104         self.health = self.max_health;
105         self.takedamage = DAMAGE_AIM;
106         self.event_damage = func_breakable_damage;
107         self.state = 0;
108         setsize(self, self.mins_save, self.maxs_save);
109         func_breakable_colormod();
110 }
111
112 void func_breakable_destroyed()
113 {
114         func_breakable_look_destroyed();
115         func_breakable_behave_destroyed();
116 }
117
118 void func_breakable_restore()
119 {
120         func_breakable_look_restore();
121         func_breakable_behave_restore();
122 }
123
124 void func_breakable_destroy() {
125         float n, i;
126
127         activator = self.owner;
128
129         func_breakable_destroyed();
130
131         if(self.noise)
132                 sound (self, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM);
133
134         if(self.dmg)
135                 RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, self.dmg_force, DEATH_HURTTRIGGER, world);
136
137         // now throw around the debris
138         n = tokenize_sane(self.debris);
139         for(i = 0; i < n; ++i)
140                 LaunchDebris(argv(i));
141
142         if(self.cnt)
143                 pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count);
144
145         SUB_UseTargets();
146 }
147
148 void func_breakable_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
149 {
150         if(self.state == 1)
151                 return;
152         if(self.team)
153                 if(attacker.team == self.team)
154                         return;
155         if(self.sprite)
156                 WaypointSprite_Ping(self.sprite);
157         self.health = self.health - damage;
158         func_breakable_colormod();
159
160         if(self.health <= 0)
161                 W_PrepareExplosionByDamage(attacker, func_breakable_destroy);
162 }
163
164 void func_breakable_reset()
165 {
166         self.team = self.team_saved;
167         func_breakable_look_restore();
168         if(self.spawnflags & 1)
169                 func_breakable_behave_destroyed();
170         else
171                 func_breakable_behave_restore();
172 }
173
174 // destructible walls that can be used to trigger target_objective_decrease
175 void spawnfunc_func_breakable() {
176         float n, i;
177         if(!self.health)
178                 self.health = 100;
179         self.max_health = self.health;
180
181         if(self.mdl != "")
182                 self.cnt = particleeffectnum(self.mdl);
183         if(self.count == 0)
184                 self.count = 1;
185
186         if(!self.message)
187                 self.message = "got too close to an explosion";
188         if(!self.message2)
189                 self.message2 = "was pushed into an explosion by";
190         if(!self.dmg_radius)
191                 self.dmg_radius = 150;
192         if(!self.dmg_force)
193                 self.dmg_force = 200;
194
195         self.mdl = self.model;
196         SetBrushEntityModel();
197         self.mins_save = self.mins;
198         self.maxs_save = self.maxs;
199
200         self.angles_x = self.angles_x + (random() * 2 - 1) * self.anglesjitter_x;
201         self.angles_y = self.angles_y + (random() * 2 - 1) * self.anglesjitter_y;
202         self.angles_z = self.angles_z + (random() * 2 - 1) * self.anglesjitter_z;
203         self.angles_y = self.angles_y + (random() * 2 - 1) * self.anglejitter;
204         
205         self.use = func_breakable_restore;
206
207         // precache all the models
208         if (self.mdl_dead)
209                 precache_model(self.mdl_dead);
210         n = tokenize_sane(self.debris);
211         for(i = 0; i < n; ++i)
212                 precache_model(argv(i));
213         if(self.noise)
214                 precache_sound(self.noise);
215
216         self.team_saved = self.team;
217
218         self.reset = func_breakable_reset;
219         func_breakable_reset();
220 }