/planet/System.c4g/Explode.c

https://bitbucket.org/randrian/openclonk2 · C · 332 lines · 211 code · 60 blank · 61 comment · 59 complexity · cb65770747d981058e4b9fcf391f028f MD5 · raw file

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