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