diff --git a/planet/Objects.ocd/Effects.ocd/Particles.ocd/FireBorder.ocd/Graphics.png b/planet/Objects.ocd/Effects.ocd/Particles.ocd/FireBorder.ocd/Graphics.png new file mode 100644 index 000000000..2930d15f6 Binary files /dev/null and b/planet/Objects.ocd/Effects.ocd/Particles.ocd/FireBorder.ocd/Graphics.png differ diff --git a/planet/Objects.ocd/Effects.ocd/Particles.ocd/FireBorder.ocd/Particle.txt b/planet/Objects.ocd/Effects.ocd/Particles.ocd/FireBorder.ocd/Particle.txt new file mode 100644 index 000000000..ea88aa65f --- /dev/null +++ b/planet/Objects.ocd/Effects.ocd/Particles.ocd/FireBorder.ocd/Particle.txt @@ -0,0 +1,3 @@ +[Particle] +Name=FireBorder +Face=0,0,128,128,-64,-64 diff --git a/planet/Objects.ocd/Effects.ocd/Particles.ocd/FireSharp.ocd/Graphics.png b/planet/Objects.ocd/Effects.ocd/Particles.ocd/FireSharp.ocd/Graphics.png new file mode 100644 index 000000000..32c717668 Binary files /dev/null and b/planet/Objects.ocd/Effects.ocd/Particles.ocd/FireSharp.ocd/Graphics.png differ diff --git a/planet/Objects.ocd/Effects.ocd/Particles.ocd/FireSharp.ocd/Particle.txt b/planet/Objects.ocd/Effects.ocd/Particles.ocd/FireSharp.ocd/Particle.txt new file mode 100644 index 000000000..969e5011d --- /dev/null +++ b/planet/Objects.ocd/Effects.ocd/Particles.ocd/FireSharp.ocd/Particle.txt @@ -0,0 +1,3 @@ +[Particle] +Name=FireSharp +Face=0,0,100,150,-40,-75 diff --git a/planet/System.ocg/Fire.c b/planet/System.ocg/Fire.c index e30e9ce3d..a6335c1cc 100644 --- a/planet/System.ocg/Fire.c +++ b/planet/System.ocg/Fire.c @@ -67,7 +67,7 @@ global func Incinerate( return true; } else - AddEffect("Fire", this, 100, 2, this, nil, caused_by, !!blasted, incinerating_object, strength); + AddEffect("Fire", this, 100, 4, this, nil, caused_by, !!blasted, incinerating_object, strength); return true; } @@ -96,6 +96,7 @@ global func FxIntNonFlammableEffect(string new_name) return 0; } + global func FxFireStart(object target, proplist effect, int temp, int caused_by, bool blasted, object incinerating_object, strength) { // safety @@ -180,10 +181,89 @@ global func FxFireStart(object target, proplist effect, int temp, int caused_by, effect.blasted = blasted; effect.incinerating_obj = incinerating_object; effect.no_burn_decay = target.NoBurnDecay; + + // store fire particles + effect.smoke = + { + R = 255, + G = 255, + B = 255, + CollisionVertex = 1000, + OnCollision = PC_Stop(), + ForceX = PV_Wind(50), + DampingX = PV_Linear(900,999), + ForceY = -7, + DampingY = PV_Linear(1000,PV_Random(650,750)), + Rotation = PV_Random(0, 359), + Phase = PV_Random(0, 3) + }; + effect.chaoticspark = + { + Size = PV_Linear(1, 0), + ForceX = PV_KeyFrames(10, 0, PV_Random(-6, 6), 333, PV_Random(-6, -6), 666, PV_Random(6, 6), 1000, PV_Random(-6, 6)), + ForceY = PV_KeyFrames(10, 0, PV_Random(-8, 5), 333, PV_Random(-8, 5), 666, PV_Random(-10, 10), 1000, PV_Random(-10, 15)), + Stretch = PV_Speed(1000, 500), + Rotation = PV_Direction(), + CollisionVertex = 0, + OnCollision = PC_Die(), + R = 255, + G = PV_Linear(255,200), + B = PV_Random(0, 100), + DampingX=950, + DampingY=950, + Alpha = PV_Random(40,140), + BlitMode = GFX_BLIT_Additive + }; + effect.redfiredense = + { + R = PV_Random(0, 255), + G = 0, + B = 0, + Alpha = 120, + Rotation = PV_Random(0, 359), + Phase = PV_Random(0, 3), + OnCollision = 0 + }; + effect.flameborder = + { + R = 255, + G = PV_Linear(255,0), + B = 0, + Phase = PV_Random(0, 4), + OnCollision = PC_Die(), + OnCollision = 0, + Alpha = PV_KeyFrames(0, 0, 0, 100, 80, 600, 80, 1000, 1) + }; + effect.sharpflame = + { + R = 255, + G = PV_KeyFrames(0, 0, 255, 666, 0, 627, 0), + B = 0, + Rotation = PV_Random(-5, 5), + Phase = PV_Random(0, 5), + OnCollision = 0, + BlitMode = GFX_BLIT_Additive + }; + + // Optimization: Only for living beings the flame actually follows the object. That means for all other objects, the flames can be rendered as one batch. + if (target->GetOCF() & OCF_Alive) + { + effect.sharpflame.Attach = ATTACH_MoveRelative; + effect.flameborder.Attach = ATTACH_MoveRelative; + } + + // Prepare some more values. + EffectCall(target, effect, "UpdateEffectProperties"); + + // Reduce checks, smoke and a few other particles if there are other burning objects around. + effect.FreqReduction = 1; + for (var nearby in FindObjects (Find_Distance(effect.height))) + if (nearby->OnFire()) + ++effect.FreqReduction; + effect.FreqReduction = effect.Interval * BoundBy(effect.FreqReduction / 3, 1, 6); // Set values - //target->FirePhase=Random(MaxFirePhase); - if ((target->GetDefCoreVal("Width", "DefCore") * target->GetDefCoreVal("Height", "DefCore")) > 500) + if ((4 * effect.width * effect.height) > 500) target->Sound("Inflame", false, 100); if (target->GetMass() >= 100) if (target->Sound("Fire", false, 100, nil, 1)) @@ -196,6 +276,37 @@ global func FxFireStart(object target, proplist effect, int temp, int caused_by, return FX_OK; } + +global func FxFireUpdateEffectProperties(object target, proplist effect) +{ + effect.width = target->GetObjWidth() / 2; + effect.height = target->GetObjHeight() / 2; + + effect.fire_width = effect.width * effect.strength / 100 + 1; + effect.fire_height = effect.height * effect.strength / 100 + 2; + + effect.pspeed = -effect.fire_height/2; + + effect.redfiredense.Size = PV_KeyFrames(0, 0, 1, 500, PV_Random(effect.fire_width/10, effect.fire_width/6), 1000, 1); + + var smokesize = effect.fire_width * 2; + if (smokesize > 60) smokesize = 50; + effect.smoke.Size = PV_Linear(smokesize, smokesize * 5); + effect.smoke.Alpha = PV_KeyFrames(0, 0, 1, 50, effect.strength, 1000, 0); + + var stretch = 500 * effect.fire_height / effect.fire_width; + effect.maxhgt = effect.fire_height - effect.fire_width * stretch / 1500; + var sharpbottom = 0; + if (GetCategory() & C4D_Structure || GetCategory() & C4D_Vehicle) sharpbottom++; + + effect.flameborder.Stretch = stretch; + effect.flameborder.Size = PV_KeyFrames(0, 0, effect.fire_width * (1 + sharpbottom), 500, effect.fire_width * 2, 1000, effect.fire_width); + effect.border_active = (effect.strength > 49) && (effect.fire_height > 10); + effect.sharpflame.Size = PV_KeyFrames(0, 0, effect.fire_width * (2 + sharpbottom), 500, effect.fire_width * 3, 1000, effect.fire_width*2); + effect.sharpflame.Alpha = PV_KeyFrames(0, 0, 0, 350, 190 * effect.strength/100, 700, 190 * effect.strength/100, 1000, 0); + effect.sharpflame.Stretch = stretch; +} + global func FxFireTimer(object target, proplist effect, int time) { // safety @@ -205,30 +316,53 @@ global func FxFireTimer(object target, proplist effect, int time) //if(!GetPlayerName(effect.caused_by)) effect.caused_by=NO_OWNER;; // strength changes over time - if(time % 4 == 0) + if ((effect.strength < 100) && (time % 8 == 0)) { if (effect.strength < 50) { if (time % 8 == 0) + { effect.strength = Max(effect.strength - 1, 0); - if (effect.strength <= 0) - return FX_Execute_Kill; + if (effect.strength <= 0) + return FX_Execute_Kill; + } } else - effect.strength = Min(effect.strength + 1, 100); + { + effect.strength = Min(100, effect.strength + 2); + + if (effect.strength > 49) + { + effect.smoke.R = 127; + effect.smoke.G = 127; + effect.smoke.B = 127; + } + } + + // update particle properties that depend on the effect strength + EffectCall(target, effect, "UpdateEffectProperties"); } - - var width = target->GetObjWidth() / 2; - var height = target->GetObjHeight() / 2; - + // target is in liquid? - if (time % 24 == 0) + if (time % (20 + effect.FreqReduction) == 0) { var mat; if (mat = GetMaterial()) if (GetMaterialVal("Extinguisher", "Material", mat)) + { + var steam = + { + Size = PV_Linear(effect.width*2, effect.width*4), + Alpha = PV_Linear(87,0), + R = 255, + G = 255, + B = 255, + Rotation = PV_Random(0, 359) + }; + CreateParticle("Dust", 0, -5, 0, 0, 180, steam, 2); return FX_Execute_Kill; - + } + // check spreading of fire if (effect.strength > 10) { @@ -242,14 +376,14 @@ global func FxFireTimer(object target, proplist effect, int time) // Do not incinerate inventory objects, since this, too, would mostly affect Clonks and losing one's bow in a fight without noticing it is nothing but annoying to the player. else { - inc_objs = FindObjects(Find_AtRect(-width/2, -height/2, width, height), Find_Exclude(target), Find_Layer(target->GetObjectLayer()), Find_NoContainer()); + inc_objs = FindObjects(Find_AtRect(-effect.width/2, -effect.height/2, effect.width, effect.height), Find_Exclude(target), Find_Layer(target->GetObjectLayer()), Find_NoContainer()); } // Loop through the selected set of objects and check contact incinerate. for (var obj in inc_objs) { // Check if the obj still exists, an object could be have been removed in this loop. if (!obj) - continue; + continue; if (obj->GetCategory() & C4D_Living) if (!obj->GetAlive()) @@ -279,7 +413,6 @@ global func FxFireTimer(object target, proplist effect, int time) } } - //if(time % 20 == 0)target->Message(Format("%d", effect.strength)); // causes on object //target->ExecFire(effect_number, caused_by); if (target->GetAlive()) @@ -288,38 +421,51 @@ global func FxFireTimer(object target, proplist effect, int time) } else { - if ((time*10) % 100 <= effect.strength) + if ((time*10) % 120 <= effect.strength) { target->DoDamage(2, FX_Call_DmgFire, effect.caused_by); - if (target && !Random(2) && !effect.no_burn_decay) + if (target && !Random(2) && !effect.no_burn_decay) + { target->DoCon(-1); + EffectCall(target, effect, "UpdateEffectProperties"); + } } } if (!target) return FX_Execute_Kill; - //if(!(time % 2 == 0)) return FX_OK; - - //if(!target->OnFire()) {return FX_Execute_Kill;} if (target->Contained()) return FX_OK; - - // particles - if(time % 4 == 0) - { - var size = BoundBy(Max(width, height), 5, 50); - if (size > 40) - if (time % 8 == 0) - return; - - var smoke_color; - if(effect.strength < 50) - smoke_color = RGBa(255,255,255, 50); - else - smoke_color = RGBa(100, 100, 100, 50); - Smoke(RandomX(-width, width), RandomX(-height, height), size, smoke_color); - } + // Fire particles + // Fire denses, smoke, chaotic sparks + if (time % effect.FreqReduction == 0) + { + // A few red fire particles popping up in the higher area. Does not affect very small flames. + if (effect.fire_width + effect.fire_height > 40) + { + if (!Random(2)) CreateParticle("FireDense", PV_Random(-effect.fire_width, effect.pspeed), PV_Random(effect.pspeed, effect.fire_height), PV_Random(-3,3), effect.pspeed, effect.fire_height/2+6, effect.redfiredense); + if (!Random(2)) CreateParticle("FireDense", PV_Random(effect.fire_width/2, effect.fire_width), PV_Random(effect.pspeed, effect.fire_height), PV_Random(-3,3), effect.pspeed, effect.fire_height/2+6, effect.redfiredense); + } + + // Smoke + CreateParticle("SmokeThick", PV_Random(-effect.fire_width,effect.fire_width), -effect.fire_height, 0, -6, 300, effect.smoke); + + // Chaotic particles + if (!Random(3) && effect.FreqReduction < 14) + CreateParticle("Magic", PV_Random(-effect.fire_width, effect.fire_width), PV_Random(-effect.fire_height, effect.fire_height), PV_Random(25, -25), PV_Random(-25, 12), 50, effect.chaoticspark); + // Flameborders and sharp flames + + // Flame borders + if (effect.border_active) + { + CreateParticle("FireBorder", 0, effect.maxhgt, 0, effect.pspeed, 30, effect.flameborder); + } + + // Sharp flames + CreateParticle("FireSharp", 0, effect.maxhgt, 0, effect.pspeed, 30, effect.sharpflame); + } + return FX_OK; } diff --git a/src/object/C4Object.cpp b/src/object/C4Object.cpp index dd080a3a3..0efd5b69c 100644 --- a/src/object/C4Object.cpp +++ b/src/object/C4Object.cpp @@ -2039,29 +2039,6 @@ void C4Object::Draw(C4TargetFacet &cgo, int32_t iByPlayer, DrawMode eDrawMode, f if(pOldFoW && (Category & C4D_IgnoreFoW)) pDraw->SetFoW(NULL); - // Fire facet - always draw, even if particles are drawn as well - if (OnFire && eDrawMode!=ODM_BaseOnly) - { - C4Facet fgo; - // Straight: Full Shape.Rect on fire - if (fix_r == Fix0) - { - fgo.Set(cgo.Surface,offX + Shape.GetX(),offY + Shape.GetY(), - Shape.Wdt,Shape.Hgt-Shape.FireTop); - } - // Rotated: Reduced fire rect - else - { - C4Rect fr; - Shape.GetVertexOutline(fr); - fgo.Set(cgo.Surface, - offX + fr.x, - offY + fr.y, - fr.Wdt, fr.Hgt); - } - ::GraphicsResource.fctFire.Draw(fgo,false,(Number + Game.FrameCounter) % MaxFirePhase); - } - // color modulation (including construction sign...) if (ColorMod != 0xffffffff || BlitMode) if (!eDrawMode) PrepareDrawing();