/planet/System.c4g/Explode.c
C | 332 lines | 211 code | 60 blank | 61 comment | 57 complexity | cb65770747d981058e4b9fcf391f028f MD5 | raw file
Possible License(s): WTFPL, 0BSD, LGPL-2.1, CC-BY-3.0
1/* Everything about the explosion */
2
3// TODO: many comments are still in German
4
5// TODO: docs
6
7#strict 2
8
9global func Explode(int iLevel) {
10 // Viewport wackeln
11 ShakeViewPort(iLevel, nil, GetX(), GetY());
12
13 // Sound muss vor dem L�schen des Objektes erzeugt werden, damit die Position stimmt
14 var grade = BoundBy((iLevel/10)-1,1,3);
15 Sound(Format("Blast%d", grade), false);
16
17 // Explosionsparameter
18 var x=GetX(), y=GetY();
19 var cause_plr = GetController();
20 var container = Contained();
21 var exploding_id = GetID();
22 var layer = GetObjectLayer();
23
24 // Explosionsparameter gesichert: Jetzt das Objekt entfernen, damit es von der Explosion selber nicht betroffen ist
25 RemoveObject();
26
27 // Und die Explosion im globalen Kontext ausf�hren
28 // Leider gibt es keine M�glichkeit, auf den globalen Kontext zuzugreifen (au�er GameCall, aber das l�st die Funktion immer neu auf)
29 // Also zumindest den Objektkontext entfernen
30 exploding_id->DoExplosion(x, y, iLevel, container, cause_plr, layer);
31}
32
33global func DoExplosion(int x, int y, int level, object inobj, int cause_plr, object layer)
34{
35 // Container to ContainBlast
36 var container = inobj;
37 while(container)
38 {
39 if(container->GetID()->GetDefContainBlast()) break;
40 else container = container->Contained();
41 }
42
43 // Explosion outside: Explosion effects
44 if (!container)
45 {
46 // incinerate oil
47 if (!IncinerateLandscape(x,y))
48 if (!IncinerateLandscape(x,y-10))
49 if (!IncinerateLandscape(x-5,y-5))
50 IncinerateLandscape(x+5,y-5);
51
52 // graphic effects:
53
54 // blast particle
55 CreateParticle("Blast", x,y, 0,0, level*10, RGBa(255,255,255,100));
56 CastParticles("Spark",10,80+level,x,y,35,40,RGB(255,200,0),RGB(255,255,150));
57 //CastParticles("FSpark", level/5+1, level, x,y, level*5+10,level*10+10, 0x00ef0000,0xffff1010));
58
59 // smoke trails
60 var i=0, count = 3+level/8, angle = Random(360);
61 while(count > 0 && ++i < count*10) {
62 angle += RandomX(40,80);
63 var smokex = +Sin(angle,RandomX(level/4,level/2));
64 var smokey = -Cos(angle,RandomX(level/4,level/2));
65 if(GBackSolid(x+smokex,y+smokey))
66 continue;
67 var lvl = 16 * level / 10;
68 CreateSmokeTrail(lvl,angle,x+smokex,y+smokey);
69 count--;
70 }
71
72 }
73
74 // Schaden in den Objekten bzw. drau�en
75 BlastObjects(x+GetX(),y+GetY(), level, inobj, cause_plr, layer);
76 if (inobj != container) BlastObjects(x+GetX(),y+GetY(), level, container, cause_plr, layer);
77
78 // Landschaft zerst�ren. Nach BlastObjects, damit neu freigesprengte Materialien nicht betroffen sind
79 if (!container)
80 {
81 BlastFree(x,y, level, cause_plr);
82 }
83
84 return true;
85 }
86
87
88/* ----------------------- Blast objects & shockwaves --------------------- */
89
90// Objekte besch�digen und wegschleudern
91global func BlastObjects(int x, int y, int level, object container, int cause_plr, object layer)
92 {
93 var obj;
94
95 // Koordinaten sind immer global angegeben. In lokale Koordinaten umrechnen
96 var l_x = x - GetX(), l_y = y - GetY();
97
98 // Im Container?
99 if (container)
100 {
101 if (container->GetObjectLayer() == layer)
102 {
103 container->BlastObject(level, cause_plr);
104 if (!container) return true; // Container koennte inzwischen entfernt worden sein
105 for (obj in FindObjects(Find_Container(container), Find_Layer(layer)))
106 if (obj) obj->BlastObject(level, cause_plr);
107 }
108 }
109 else
110 {
111 // Objekt ist drau�en
112 // Objekte am Explosionspunkt besch�digen
113 for (var obj in FindObjects(Find_AtRect(l_x-5, l_y-5, 10,10), Find_NoContainer(), Find_Layer(layer)))
114 if (obj) obj->BlastObject(level, cause_plr);
115
116 // TODO: -> Shockwave in own global func(?)
117
118 // Objekte im Explosionsradius schleudern
119 var shockwave_objs = FindObjects(Find_Distance(level, l_x,l_y), Find_NoContainer(), Find_Layer(layer),
120 Find_Or(Find_Category(C4D_Object|C4D_Living|C4D_Vehicle), Find_Func("CanBeHitByShockwaves")), Find_Func("BlastObjectsShockwaveCheck",x,y));
121 var cnt = GetLength(shockwave_objs);
122 if (cnt)
123 {
124 // Die Schleuderenergie teilt sich bei vielen Objekten auf
125 //Log("Shockwave objs %v (%d)", shockwave_objs, cnt);
126 var shock_speed = Sqrt(2 * level * level / BoundBy(cnt, 2, 12));
127 for (var obj in shockwave_objs) if (obj) // obj noch pr�fen, weil OnShockwaveHit Objekte aus dem Array l�schen k�nnte
128 {
129 // Objekt hat benutzerdefinierte Reaktion auf die Schockwelle?
130 if (obj->~OnShockwaveHit(level, x,y)) continue;
131 // Lebewesen leiden besonders
132 var cat = obj->GetCategory();
133 if (cat & C4D_Living)
134 {
135 obj->DoEnergy(level/-2, false, FX_Call_EngBlast, cause_plr);
136 obj->DoDamage(level/2, FX_Call_DmgBlast, cause_plr);
137 }
138 // Killverfolgung bei Projektilen
139 if (cat & C4D_Object) obj->SetController(cause_plr);
140 // Schockwelle
141 var mass_fact = 20, mass_mul = 100; if (cat & C4D_Living) { mass_fact = 8; mass_mul = 80; }
142 mass_fact = BoundBy(obj->GetMass()*mass_mul/1000, 4, mass_fact);
143 var dx = 100*(obj->GetX()-x)+Random(51)-25;
144 var dy = 100*(obj->GetY()-y)+Random(51)-25;
145 var vx, vy;
146 if (dx)
147 {
148 vx = Abs(dx)/dx * (100*level-Abs(dx)) * shock_speed / level / mass_fact;
149 }
150 vy = (Abs(dy) - 100*level) * shock_speed / level / mass_fact;
151 if (cat & C4D_Object)
152 {
153 // Objekte nicht zu schnell werden lassen
154 var ovx = obj->GetXDir(100), ovy = obj->GetYDir(100);
155 if (ovx*vx > 0) vx = (Sqrt(vx*vx + ovx*ovx) - Abs(vx)) * Abs(vx)/vx;
156 if (ovy*vy > 0) vy = (Sqrt(vy*vy + ovy*ovy) - Abs(vy)) * Abs(vy)/vy;
157 }
158 //Log("%v v(%v %v) d(%v %v) m=%v l=%v s=%v", obj, vx,vy, dx,dy, mass_fact, level, shock_speed);
159 obj->Fling(vx,vy, 100, true);
160 }
161 }
162 }
163 // Fertig
164 return true;
165 }
166
167global func BlastObjectsShockwaveCheck(int x, int y)
168 {
169 var def = GetID();
170 // Einige Spezialf�lle, die schwer in FindObjects passen
171 if (def->GetDefHorizontalFix()) return false;
172 if (def->GetDefGrab() != 1)
173 {
174 if (GetCategory() & C4D_Vehicle) return false;
175 if (GetProcedure() == "FLOAT") return false;
176 }
177 // Projektile nicht wenn sie nach unten fliegen oder exakt am Explosionsort liegen
178 // Dies f�ngt die meisten F�lle ab, in denen mehrere Clonks gleichzeitig Flints werfen
179 if (GetCategory() & C4D_Object)
180 {
181 if (GetX() == x && GetY() == y) return false;
182 if (GetYDir() > 5) return false;
183 }
184 // Und keine feststeckenden Objekte
185 if (Stuck()) return false;
186 return true;
187 }
188
189
190/* ---------------------------- Shake view port --------------------------- */
191
192global func ShakeViewPort(int iLevel, int iOffX, int iOffY) {
193 if(iLevel <= 0) return false;
194
195 var eff=GetEffect("ShakeEffect",this);
196
197 if(eff)
198 {
199 EffectVar(0,this,eff)+=iLevel;
200 return true;
201 }
202
203 eff=AddEffect("ShakeEffect",this,200,1);
204 if (!eff) return false;
205
206 EffectVar(0,this,eff)=iLevel;
207
208 if(iOffX || iOffY)
209 {
210 EffectVar(1,this,eff)=iOffX;
211 EffectVar(2,this,eff)=iOffY;
212 }
213 else
214 {
215 EffectVar(1,this,eff)=GetX();
216 EffectVar(2,this,eff)=GetY();
217 }
218 return true;
219}
220
221// Variables:
222// 0 - level
223// 1 - x-pos
224// 2 - y-pos
225
226// Duration of the effect: as soon as iStrength==0
227// Strength of the effect: iStrength=iLevel/(1.5*iTime+3)-iTime^2/400
228
229global func FxShakeEffectTimer(object pTarget, int iEffectNumber, int iTime) {
230
231 var iStrength;
232
233 var str = EffectVar(0,pTarget,iEffectNumber);
234 var xpos = EffectVar(1,pTarget,iEffectNumber);
235 var ypos = EffectVar(2,pTarget,iEffectNumber);
236
237
238 for(var i=0; i<GetPlayerCount(); i++) {
239 var iPlr=GetPlayerByIndex(i);
240 var distance = Distance(GetCursor(iPlr)->GetX(),GetCursor(iPlr)->GetY(),xpos,ypos);
241
242 // Sch�tteleffekt verringert sich je nach Entfernung
243 var iLevel= (300*str) / Max(300,distance);
244
245 if((iStrength=iLevel/((3*iTime)/2 + 3) - iTime**2/400) <= 0) continue;
246
247 // FIXME: Use GetViewOffset, make this relative, not absolute
248 SetViewOffset(iPlr,Sin(iTime*100,iStrength),Cos(iTime*100,iStrength));
249 }
250
251 if(EffectVar(0,pTarget,iEffectNumber)/((3*iTime)/2 + 3) - iTime**2/400 <= 0) return -1;
252}
253
254global func FxShakeEffectStart(object pTarget, int iEffectNumber) {
255 FxShakeEffectTimer(pTarget, iEffectNumber, GetEffect (nil, pTarget, iEffectNumber, 6));
256}
257
258global func FxShakeEffectStop() {
259 for(var i=0; i<GetPlayerCount(); i++) {
260 // FIXME: Return the offset to the previous value, not zero
261 SetViewOffset(GetPlayerByIndex(i),0,0);
262 }
263}
264
265/* ----------------------------- Smoke trails ------------------------------ */
266
267global func CreateSmokeTrail(int iStrength, int iAngle, int iX, int iY) {
268 iX += GetX();
269 iY += GetY();
270 AddEffect("SmokeTrail", nil, 300, 1, nil, nil, iStrength, iAngle, iX, iY);
271}
272
273// Variables:
274// 0 - Strength
275// 1 - Current strength
276// 2 - X-Position
277// 3 - Y-Position
278// 4 - Starting-X-Speed
279// 5 - Starting-Y-Speed
280global func FxSmokeTrailStart(object pTarget, int iEffectNumber, int iTemp, iStrength, iAngle, iX, iY) {
281
282 if(iTemp)
283 return;
284
285 if(iAngle%90 == 1) iAngle += 1;
286 iStrength = Max(iStrength,5);
287
288 EffectVar(0, pTarget, iEffectNumber) = iStrength;
289 EffectVar(1, pTarget, iEffectNumber) = iStrength;
290 EffectVar(2, pTarget, iEffectNumber) = iX;
291 EffectVar(3, pTarget, iEffectNumber) = iY;
292 EffectVar(4, pTarget, iEffectNumber) = +Sin(iAngle,iStrength*40);
293 EffectVar(5, pTarget, iEffectNumber) = -Cos(iAngle,iStrength*40);
294}
295
296global func FxSmokeTrailTimer(object pTarget, int iEffectNumber, int iEffectTime) {
297 var iStrength = EffectVar(0, pTarget, iEffectNumber);
298 var iAStr = EffectVar(1, pTarget, iEffectNumber);
299 var iX = EffectVar(2, pTarget, iEffectNumber);
300 var iY = EffectVar(3, pTarget, iEffectNumber);
301 var iXDir = EffectVar(4, pTarget, iEffectNumber);
302 var iYDir = EffectVar(5, pTarget, iEffectNumber);
303
304 iAStr = Max(1,iAStr-iAStr/5);
305 iAStr--;
306 iYDir += GetGravity()*2/3;
307
308 var xdir = iXDir*iAStr/iStrength;
309 var ydir = iYDir*iAStr/iStrength;
310
311 // new: random
312 iX += RandomX(-3,3);
313 iY += RandomX(-3,3);
314
315 // draw
316 CreateParticle("ExploSmoke",iX,iY,RandomX(-2,2),RandomX(-2,4),150+iAStr*12,RGBa(130,130,130,90));
317 CreateParticle("Blast",iX,iY,0,0,10+iAStr*8,RGBa(255,100,50,150));
318
319 // then calc next position
320 iX += xdir/100;
321 iY += ydir/100;
322
323 if(GBackSemiSolid(iX,iY))
324 return -1;
325 if(iAStr <= 3)
326 return -1;
327
328 EffectVar(1, pTarget, iEffectNumber) = iAStr;
329 EffectVar(2, pTarget, iEffectNumber) = iX;
330 EffectVar(3, pTarget, iEffectNumber) = iY;
331 EffectVar(5, pTarget, iEffectNumber) = iYDir;
332}