openclonk/planet/Objects.ocd/Vehicles.ocd/Airplane.ocd/Script.c

397 lines
7.9 KiB
C

/*--
Airplane
Author: Ringwaul
Acrobatic air-vehicle. Capable of firing lead shot.
--*/
local throttle;
local rdir;
local thrust;
local dir;
local propanim;
local reticle;
local health;
local clonkmesh;
protected func Construction(object byobj)
{
SetR(-90);
}
protected func Initialize()
{
propanim = PlayAnimation("Propellor", 15, Anim_Const(0),Anim_Const(1000));
AddEffect("IntPlane",this,1,1,this);
SetAction("Land");
throttle = 0;
thrust = 0;
rdir = 0;
dir = 0;
health = 50;
return;
}
/*-- Control --*/
public func ContainedUseStart(object clonk, int ix, int iy)
{
if(!clonk->FindContents(LeadShot))
{
CustomMessage("$NoShots$",this,clonk->GetOwner());
return 1;
}
else
{
reticle = CreateObject(GUI_Reticle);
reticle->SetOwner(clonk->GetController());
reticle->SetAction("Show", this);
}
return 1;
}
public func ContainedUseStop(object clonk, int ix, int iy)
{
if(reticle) reticle->RemoveObject();
var ammo = FindObject(Find_Container(clonk),Find_Func("IsMusketAmmo"));
if(GetEffect("IntCooldown",this)) return 1;
if(ammo)
{
var shot = ammo->TakeObject();
var angle = this->GetR();
shot->Launch(clonk, angle, 35, 200);
Sound("GunShoot*.ogg");
// Muzzle Flash & gun smoke
var IX = Sin(GetR(), 30);
var IY = -Cos(GetR(), 30);
for(var i=0; i<10; ++i)
{
var speed = RandomX(0,10);
var r = angle;
CreateParticle("ExploSmoke",IX,IY,+Sin(r,speed)+RandomX(-2,2) + GetXDir()/2,-Cos(r,speed)+RandomX(-2,2) + GetYDir()/2,RandomX(100,400),RGBa(255,255,255,50));
}
CreateParticle("MuzzleFlash",IX,IY,+Sin(angle,500),-Cos(angle,500),600,RGB(255,255,255),this);
CreateParticle("Flash",0,0,GetXDir(),GetYDir(),800,RGBa(255,255,64,150));
AddEffect("IntCooldown", this,1,1,this);
}
return 1;
}
public func ContainedUseCancel(object clonk, int ix, int iy)
{
if(reticle) reticle->RemoveObject();
return 1;
}
public func FxIntCooldownTimer(object target, effect, int timer)
{
if(timer > 50) return -1;
}
public func ContainedUp(object clonk)
{
//plane is broken?
if(GetDamage() > health)
return;
//engine start
if(GetAction() == "Land")
{
StartFlight(15);
return;
}
}
public func ContainedDown(object clonk)
{
//plane is broken?
if(GetDamage() > health)
return;
//engine shutoff
if(GetAction() == "Fly")
{
CancelFlight();
return;
}
//allow reverse
if(GetAction() == "Land")
{
StartFlight(-5);
return;
}
}
public func ContainedLeft(object clonk)
{
rdir = -1;
}
public func ContainedRight(object clonk)
{
rdir = 1;
}
public func ContainedStop(object clonk)
{
rdir = 0;
}
public func StartFlight(int new_throttle)
{
Sound("EngineStart.ogg");
AddEffect("IntSoundDelay",this,1,1,this);
SetAction("Fly");
throttle = new_throttle;
}
public func StartInstantFlight(int angle, int new_throttle)
{
angle -= 10;
Sound("EngineStart.ogg");
AddEffect("IntSoundDelay",this,1,1,this);
SetAction("Fly");
throttle = new_throttle;
thrust = new_throttle;
SetR(angle);
SetXDir(Cos(angle, thrust));
SetYDir(Sin(angle, thrust));
return;
}
public func CancelFlight()
{
RemoveEffect("IntSoundDelay",this);
Sound("EngineLoop.ogg",0,100,nil,-1);
Sound("EngineStop.ogg");
SetAction("Land");
rdir = 0;
throttle = 0;
}
private func FxIntSoundDelayTimer(object target, effect, int timer)
{
if(timer >= 78)
{
Sound("EngineLoop.ogg",0,100,nil,1);
return -1;
}
}
private func FxIntPlaneTimer(object target, effect, int timer)
{
//Lift
var lift = Distance(0,0,GetXDir(),GetYDir()) / 2;
if(lift > 20) lift = 20;
if(throttle < 1) lift = 0;
if(GetAction() == "Fly")
{
//--Ailerons--
//clockwise
if(rdir == 1)
if(GetRDir() < 5) SetRDir(GetRDir() + 1);
//counter-clockwise
if(rdir == -1)
if(GetRDir() > -5) SetRDir(GetRDir() - 1);
if(rdir == 0) SetRDir();
//Roll plane to movement direction
if(throttle > 0)
{
if(GetXDir() > 10 && dir != 1) RollPlane(1);
if(GetXDir() < -10 && dir != 0) RollPlane(0);
}
//Vfx
var colour = 255 - (GetDamage() * 3);
CreateParticle("EngineSmoke",0,0,0,0,RandomX(70,90),RGB(colour,colour,colour));
}
//Throttle-to-thrust lag
if(timer % 10 == 0)
{
if(throttle > thrust) ++thrust;
if(throttle < thrust) --thrust;
}
//propellor
var change = GetAnimationPosition(propanim) + thrust * 3;
if(change > GetAnimationLength("Propellor"))
change = (GetAnimationPosition(propanim) + thrust * 3) - GetAnimationLength("Propellor");
if(change < 0)
change = (GetAnimationLength("Propellor") - thrust * 3);
SetAnimationPosition(propanim, Anim_Const(change));
//Thrust
SetXDir(Sin(GetR(),thrust) + GetXDir(100), 100);
SetYDir(-Cos(GetR(),thrust) + GetYDir(100) - lift, 100);
//Drag
var maxspeed = 40;
var speed = Distance(0,0,GetXDir(),GetYDir());
if(speed > 40)
{
SetXDir(GetXDir(100)*maxspeed/speed,100);
SetYDir(GetYDir(100)*maxspeed/speed,100);
}
//No pilot?
var pilot = FindObject(Find_OCF(OCF_CrewMember),Find_Container(this));
if(!pilot && throttle != 0) CancelFlight();
//Planes cannot fly underwater!
if(GBackLiquid())
{
if(pilot) Ejection(pilot);
if(throttle != 0) CancelFlight();
}
//Pilot, but no mesh? In case they are scripted into the plane.
if(FindContents(Clonk) && !clonkmesh)
PlaneMount(FindContents(Clonk));
//Gun Sights
if(reticle)
{
var retcol = RGB(0,255,0);
if(GetEffect("IntCooldown",this)) retcol = RGB(255,0,0);
reticle->SetClrModulation(retcol);
}
}
local newrot;
public func RollPlane(int rolldir, bool instant)
{
if(dir != rolldir)
{
var i = 36;
if(instant) i = 1;
if(newrot) StopAnimation(newrot);
newrot = PlayAnimation(Format("Roll%d",rolldir), 10, Anim_Linear(0, 0, GetAnimationLength(Format("Roll%d",rolldir)), i, ANIM_Hold), Anim_Const(1000));
dir = rolldir;
}
}
//Quick command for scenario designers. The plane starts facing right instead of left.
public func FaceRight()
{
SetR(90);
RollPlane(1,true);
}
public func IsProjectileTarget(target,shooter) { return true; }
public func Damage(int change, int byplayer)
{
if(GetDamage() > health)
{
if(Random(2)) PlaneDeath();
else
CancelFlight();
}
}
private func PlaneDeath()
{
while(Contents(0))
Contents(0)->Exit();
Explode(50);
}
public func Hit()
{
if(GetDamage() > health) PlaneDeath();
}
public func ActivateEntrance(object clonk)
{
var cnt = ObjectCount(Find_Container(this), Find_OCF(OCF_CrewMember));
if(cnt > 0)
if(clonk->Contained() == this)
{
clonk->Exit();
return;
}
else
return;
//Clonk cannot get into the plane if it is underwater
if(GBackLiquid()) return;
if(cnt == 0)
{
clonk->Enter(this);
clonk->SetAction("Walk");
PlaneMount(clonk);
clonk->PlayAnimation("Drive", 5, Anim_Const(10), Anim_Const(1000));
}
}
public func Ejection(object obj)
{
PlaneDismount(obj);
if(obj->Contained()) Exit();
obj->SetSpeed(this->GetXDir(),this->GetYDir());
}
public func PlaneMount(object clonk)
{
SetOwner(clonk->GetController());
clonk->PlayAnimation("Stand", 15, 0, Anim_Const(1000));
clonkmesh = AttachMesh(clonk,"pilot","skeleton_body",Trans_Mul(Trans_Rotate(180,0,1,0), Trans_Translate(0,-3000,-1000)),AM_DrawBefore);
return true;
}
public func PlaneDismount(object clonk)
{
clonk->StopAnimation(clonk->GetRootAnimation(15));
DetachMesh(clonkmesh);
clonkmesh = nil;
return true;
}
func Definition(def) {
SetProperty("ActMap", {
Fly = {
Prototype = Action,
Name = "Fly",
Procedure = DFA_NONE,
Directions = 2,
FlipDir = 0,
Length = 10,
Delay = 1,
X = 0,
Y = 0,
Wdt = 40,
Hgt = 56,
NextAction = "Fly",
},
Land = {
Prototype = Action,
Name = "Land",
Procedure = DFA_NONE,
Directions = 2,
FlipDir = 0,
Length = 1,
Delay = 2,
X = 0,
Y = 0,
Wdt = 40,
Hgt = 56,
NextAction = "Land",
},
}, def);
SetProperty("Name", "$Name$", def);
SetProperty("MeshTransformation", Trans_Mul(Trans_Rotate(90,0,0,1), Trans_Translate(-10000,-3375,0), Trans_Rotate(25,0,1,0)));
SetProperty("PictureTransformation",Trans_Mul(Trans_Rotate(-5,1,0,0),Trans_Rotate(40,0,1,0),Trans_Translate(-20000,-4000,20000)),def);
}
local Rebuy = true;