added Armin's particle fire

http://forum.openclonk.org/topic_show.pl?tid=3070
Only some optimizations have been added.
stable-6.1
David Dormagen 2015-03-22 10:22:15 +01:00 committed by Maikel de Vries
parent 2a112b1d00
commit 569e0d0cb3
6 changed files with 189 additions and 60 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@ -0,0 +1,3 @@
[Particle]
Name=FireBorder
Face=0,0,128,128,-64,-64

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -0,0 +1,3 @@
[Particle]
Name=FireSharp
Face=0,0,100,150,-40,-75

View File

@ -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("<c ee0000>%d</c>", 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;
}

View File

@ -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();