PageRenderTime 105ms CodeModel.GetById 25ms app.highlight 55ms RepoModel.GetById 1ms app.codeStats 1ms

/planet/System.c4g/Explode.c

https://bitbucket.org/randrian/openclonk2
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}