2012-04-11 20:41:08 +00:00
|
|
|
/**
|
|
|
|
Meteor
|
|
|
|
A burning rock falling from the sky, explodes on impact.
|
|
|
|
|
2015-08-29 17:53:52 +00:00
|
|
|
@author Maikel, Zapper
|
2012-04-11 20:41:08 +00:00
|
|
|
*/
|
|
|
|
|
2015-08-29 17:53:52 +00:00
|
|
|
// A meteor can spawn objects on impact.
|
|
|
|
local spawn_id, spawn_amount;
|
2012-04-11 20:41:08 +00:00
|
|
|
|
|
|
|
/*-- Disaster Control --*/
|
|
|
|
|
2015-08-29 17:53:52 +00:00
|
|
|
/**
|
|
|
|
Enables meteorites.
|
|
|
|
The meteorites can be set to spawn objects on impact. The amount to spawn will be spawn_amount randomized by 50%.
|
|
|
|
*/
|
|
|
|
public func SetChance(int chance, id spawn_id, int spawn_amount)
|
2012-04-11 20:41:08 +00:00
|
|
|
{
|
2015-08-29 17:53:52 +00:00
|
|
|
spawn_amount = spawn_amount ?? 1;
|
|
|
|
if (GetType(this) != C4V_Def) return FatalError("Must be called as a definition call.");
|
|
|
|
|
|
|
|
var effect = FindMeteoriteEffectFor(spawn_id);
|
2012-04-12 20:15:54 +00:00
|
|
|
if (!effect)
|
2015-08-29 17:53:52 +00:00
|
|
|
effect = AddEffect("IntMeteorControl", nil, 100, 20, nil, this, spawn_id, spawn_amount);
|
2012-04-11 20:41:08 +00:00
|
|
|
effect.chance = chance;
|
2015-08-29 17:53:52 +00:00
|
|
|
|
|
|
|
return true;
|
2012-04-11 20:41:08 +00:00
|
|
|
}
|
|
|
|
|
2015-08-29 17:53:52 +00:00
|
|
|
/**
|
|
|
|
Returns the chance of meteorites that is currently set. spawn_id can be nil for the normal meteorite.
|
|
|
|
*/
|
|
|
|
public func GetChance(id spawn_id)
|
2012-04-11 20:41:08 +00:00
|
|
|
{
|
2015-08-29 17:53:52 +00:00
|
|
|
if (GetType(this) != C4V_Def) return FatalError("Must be called as a definition call.");
|
|
|
|
var effect = FindMeteoriteEffectFor(spawn_id);
|
2012-04-11 20:41:08 +00:00
|
|
|
if (effect)
|
|
|
|
return effect.chance;
|
2015-08-29 17:53:52 +00:00
|
|
|
return 0;
|
2012-04-11 20:41:08 +00:00
|
|
|
}
|
|
|
|
|
2015-08-29 17:53:52 +00:00
|
|
|
// Finds the meteorite effect that matches a specific spawn id.
|
|
|
|
private func FindMeteoriteEffectFor(id spawn_id)
|
2012-04-11 20:41:08 +00:00
|
|
|
{
|
2015-08-29 17:53:52 +00:00
|
|
|
var i = 0, fx;
|
|
|
|
while (fx = GetEffect("IntMeteorControl", nil, i++))
|
|
|
|
{
|
|
|
|
if (fx.spawn_id == spawn_id) return fx;
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
private func FxIntMeteorControlStart(object target, effect fx, temp, id spawn_id, int spawn_amount)
|
|
|
|
{
|
|
|
|
if (temp) return;
|
|
|
|
fx.spawn_id = spawn_id;
|
|
|
|
fx.spawn_amount = spawn_amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
private func FxIntMeteorControlTimer(object target, effect fx, int time)
|
|
|
|
{
|
|
|
|
if (Random(100) < fx.chance && !Random(10))
|
2012-04-11 20:41:08 +00:00
|
|
|
{
|
|
|
|
// Launch a meteor.
|
|
|
|
var x = Random(LandscapeWidth());
|
|
|
|
var y = 0;
|
|
|
|
var size = RandomX(60, 90);
|
|
|
|
var xdir = RandomX(-22, 22);
|
|
|
|
var ydir = RandomX(28, 36);
|
2015-08-29 17:53:52 +00:00
|
|
|
var real_spawn_amount = Max(1, RandomX(fx.spawn_amount / 2, 3 * fx.spawn_amount / 2));
|
|
|
|
LaunchMeteor(x, y, size, xdir, ydir, fx.spawn_id, real_spawn_amount);
|
2012-04-11 20:41:08 +00:00
|
|
|
}
|
|
|
|
return FX_OK;
|
|
|
|
}
|
|
|
|
|
2013-12-22 22:47:40 +00:00
|
|
|
// Scenario saving
|
2015-08-29 17:53:52 +00:00
|
|
|
public func FxIntMeteorControlSaveScen(obj, fx, props)
|
2013-12-22 22:47:40 +00:00
|
|
|
{
|
2015-08-29 17:53:52 +00:00
|
|
|
props->Add("Meteor", "Meteor->SetChance(%d, %v, %d)", fx.chance, fx.spawn_id, fx.spawn_amount);
|
2013-12-22 22:47:40 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-04-11 20:41:08 +00:00
|
|
|
/*-- Meteor --*/
|
|
|
|
|
2015-08-29 17:53:52 +00:00
|
|
|
public func Launch(int x, int y, int size, int xdir, int ydir, id spawn_id, int spawn_amount)
|
2012-04-11 20:41:08 +00:00
|
|
|
{
|
|
|
|
// Launch from indicated position.
|
|
|
|
SetPosition(x, y);
|
|
|
|
// Set the meteor's size.
|
|
|
|
SetCon(BoundBy(size, 20, 100));
|
|
|
|
// Set the initial velocity.
|
|
|
|
SetXDir(xdir);
|
|
|
|
SetYDir(ydir);
|
2015-08-29 17:53:52 +00:00
|
|
|
// Remember spawning information.
|
|
|
|
this.spawn_id = spawn_id;
|
|
|
|
this.spawn_amount = spawn_amount ?? 1;
|
2012-04-11 20:41:08 +00:00
|
|
|
// Safety check.
|
|
|
|
if (!IsLaunchable())
|
|
|
|
return false;
|
2015-08-29 17:53:52 +00:00
|
|
|
// Allow for some more effects (overloadable).
|
|
|
|
this->OnAfterLaunch();
|
2017-07-23 08:33:09 +00:00
|
|
|
return this;
|
2015-08-29 17:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public func OnAfterLaunch()
|
|
|
|
{
|
|
|
|
// Set random rotation.
|
|
|
|
SetR(Random(360));
|
|
|
|
SetRDir(RandomX(-10, 10));
|
|
|
|
// Emit light
|
2015-09-02 09:57:19 +00:00
|
|
|
SetLightRange(300, 30);
|
|
|
|
SetLightColor(RGB(255, 160, 120));
|
2012-04-11 20:41:08 +00:00
|
|
|
// Set right action.
|
|
|
|
AddEffect("IntMeteor", this, 100, 1, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
private func IsLaunchable()
|
|
|
|
{
|
|
|
|
if (GBackSemiSolid() || Stuck())
|
|
|
|
{
|
|
|
|
RemoveObject();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-08-29 17:53:52 +00:00
|
|
|
private func FxIntMeteorStart(object target, effect fx, bool temp)
|
|
|
|
{
|
|
|
|
if (temp) return;
|
|
|
|
fx.smoketrail =
|
|
|
|
{
|
|
|
|
R = 255,
|
|
|
|
B = PV_KeyFrames(0, 0,100, 30,0, 100,255, 1000,255),
|
|
|
|
G = PV_KeyFrames(0, 0,150, 30,0, 100,255, 1000,255),
|
|
|
|
|
|
|
|
Alpha = PV_KeyFrames(1000, 0, 0, 30, 255, 1000, 0),
|
|
|
|
Size = PV_Linear(20,60),
|
|
|
|
Stretch = 1000,
|
|
|
|
Phase = PV_Random(0,4),
|
|
|
|
Rotation = PV_Random(-GetR() - 15, -GetR() + 15),
|
|
|
|
DampingX = 1000,
|
|
|
|
DampingY = 1000,
|
|
|
|
BlitMode = 0,
|
|
|
|
CollisionVertex = 0,
|
|
|
|
OnCollision = PC_Stop(),
|
|
|
|
Attach = nil
|
|
|
|
};
|
|
|
|
fx.brighttrail =
|
|
|
|
{
|
|
|
|
Prototype = fx.smoketrail,
|
|
|
|
Alpha = PV_Linear(180,0),
|
|
|
|
Size = PV_Linear(20,30),
|
|
|
|
BlitMode = GFX_BLIT_Additive,
|
|
|
|
};
|
|
|
|
fx.frontburn =
|
|
|
|
{
|
|
|
|
R = 255,
|
|
|
|
B = 50,
|
|
|
|
G = 190,
|
|
|
|
|
|
|
|
Alpha = PV_KeyFrames(0, 0, 0, 500, 25, 1000, 0),
|
|
|
|
Size = PV_Linear(4,5),
|
|
|
|
Stretch = 1000,
|
|
|
|
Phase = PV_Random(0,4),
|
|
|
|
Rotation = PV_Random(-GetR() - 15, -GetR() + 15),
|
|
|
|
DampingX = 1000,
|
|
|
|
DampingY = 1000,
|
|
|
|
BlitMode = GFX_BLIT_Additive,
|
|
|
|
CollisionVertex = 0,
|
|
|
|
OnCollision = PC_Stop(),
|
|
|
|
Attach = ATTACH_Front | ATTACH_MoveRelative
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
protected func FxIntMeteorTimer(object target, effect fx, bool temp)
|
2012-04-11 20:41:08 +00:00
|
|
|
{
|
|
|
|
var size = GetCon();
|
|
|
|
// Air drag.
|
|
|
|
var ydir = GetYDir(100);
|
|
|
|
ydir -= size * ydir ** 2 / 11552000; // Magic number.
|
|
|
|
SetYDir(ydir, 100);
|
|
|
|
// Smoke trail.
|
2015-08-29 17:53:52 +00:00
|
|
|
CreateParticle("SmokeThick", 0, 0, PV_Random(-3, 3), PV_Random(-3, 3), 200, fx.smoketrail, 5);
|
|
|
|
// Flash
|
|
|
|
CreateParticle("SmokeThick", 0, -4, PV_Random(-3, 3), PV_Random(-3, 3), 3, fx.brighttrail, 2);
|
|
|
|
// left and right burn
|
|
|
|
CreateParticle("FireDense", PV_Random(-5, 5), 15, PV_Random(-5, -20), PV_Random(-15, -30), 20, fx.frontburn, 30);
|
|
|
|
CreateParticle("FireDense", PV_Random(-5, 5), 15, PV_Random(5, 20), PV_Random(-15, -30), 20, fx.frontburn, 30);
|
2012-04-11 20:41:08 +00:00
|
|
|
// Sound.
|
|
|
|
|
|
|
|
// Burning and friction decrease size.
|
2013-11-28 19:44:31 +00:00
|
|
|
if (size > 10 && !Random(5))
|
2012-04-11 20:41:08 +00:00
|
|
|
DoCon(-1);
|
2013-11-28 19:44:31 +00:00
|
|
|
|
2017-07-23 08:33:09 +00:00
|
|
|
return FX_OK;
|
2012-04-11 20:41:08 +00:00
|
|
|
}
|
|
|
|
|
2015-08-29 17:53:52 +00:00
|
|
|
// Scenario saving
|
|
|
|
func FxIntMeteorSaveScen(obj, fx, props)
|
|
|
|
{
|
|
|
|
props->AddCall("Meteor", obj, "AddEffect", "\"IntMeteor\"", obj, 100, 1, obj);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-04-11 20:41:08 +00:00
|
|
|
protected func Hit(int xdir, int ydir)
|
|
|
|
{
|
|
|
|
var size = 10 + GetCon();
|
|
|
|
var speed2 = 20 + (xdir ** 2 + ydir ** 2) / 10000;
|
|
|
|
// Some fire sparks.
|
2013-10-26 14:33:05 +00:00
|
|
|
var particles =
|
2012-04-11 20:41:08 +00:00
|
|
|
{
|
2013-10-26 14:33:05 +00:00
|
|
|
Prototype = Particles_Fire(),
|
|
|
|
Attach = nil
|
|
|
|
};
|
2013-12-17 20:40:40 +00:00
|
|
|
CreateParticle("Fire", PV_Random(-size / 4, size / 4), PV_Random(-size / 4, size / 4), PV_Random(-size/4, size/4), PV_Random(-size/4, size/4), 30, particles, 20 + size);
|
2012-04-11 20:41:08 +00:00
|
|
|
// Explode meteor, explode size scales with the energy of the meteor.
|
2013-11-28 19:44:31 +00:00
|
|
|
var dam = size * speed2 / 500;
|
|
|
|
dam = BoundBy(size/2, 5, 30);
|
2015-08-29 17:53:52 +00:00
|
|
|
// Blow up a dummy object so that the explosion can happen before we spawn items.
|
|
|
|
var dummy = CreateObject(Dummy, 0, 0, GetController());
|
|
|
|
dummy->Explode(dam);
|
|
|
|
|
|
|
|
// Fling around some objects?
|
|
|
|
if (spawn_id)
|
|
|
|
{
|
|
|
|
for (var i = 0; i < spawn_amount; ++i)
|
|
|
|
{
|
|
|
|
var angle = Angle(0, 0, -xdir, -ydir) + RandomX(-45, 45);
|
|
|
|
var force = BoundBy(GetCon() / 2, 10, 50) + RandomX(-10, 10);
|
|
|
|
var item = CreateObject(spawn_id, 0, 0, GetOwner());
|
|
|
|
item->SetSpeed(Sin(angle, force), -Cos(angle, force));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RemoveObject();
|
2012-04-11 20:41:08 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-09 16:34:52 +00:00
|
|
|
|
|
|
|
/*-- Target --*/
|
|
|
|
|
|
|
|
public func OnLightningStrike(object lightning, int damage)
|
|
|
|
{
|
|
|
|
SetController(lightning->GetController());
|
2017-07-23 08:33:09 +00:00
|
|
|
if (GetDamage() + damage >= 6)
|
|
|
|
Hit();
|
2017-07-09 16:34:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
public func IsProjectileTarget(object projectile)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public func OnProjectileHit(object projectile)
|
|
|
|
{
|
|
|
|
SetController(projectile->GetController());
|
|
|
|
Hit();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-12 18:24:39 +00:00
|
|
|
public func IsMeteor() { return true; }
|
|
|
|
|
2017-07-09 16:34:52 +00:00
|
|
|
|
2012-04-11 20:41:08 +00:00
|
|
|
/*-- Proplist --*/
|
|
|
|
|
|
|
|
local Name = "$Name$";
|