openclonk/planet/Objects.ocd/Animals.ocd/Shark.ocd/Script.c

429 lines
8.5 KiB
C

/**
Shark
Very strong water animal.
@author Armin
*/
local BiteStrength = 25;
local attacking, walking, swimming, swim_animation, base_transform, turned;
private func Construction()
{
StartGrowth(15);
SetAction("Swim");
if (!Random(2))
SetComDir(COMD_Right);
else
SetComDir(COMD_Left);
if (GetAlive())
AddTimer(this.Activity, 10);
return _inherited(...);
}
private func Attack(object target, int x, int y)
{
if (!attacking || (target->GetAction() != "Swim" && target->GetAction() != "Jump") || !target->GetAlive() || !PathFree(GetX(), GetY(), target->GetX(), target->GetY()))
{
SetCommand("None");
attacking = false;
SetAction("Swim");
return 1;
}
var dis = Distance(GetX(), GetY(), target->GetX(), target->GetY());
if (dis > 30)
{
var current_angle=Angle(GetX(), GetY(), target->GetX(), target->GetY());
if (current_angle < 0) current_angle = 360 + current_angle;
var t = current_angle - 270;
var t2 = Cos(t, 90) - 90; // Expand the areas around 0deg and 180deg a bit so you see fish from the side more
this.MeshTransformation = Trans_Mul(Trans_Rotate(t, 0, 0, 1), Trans_Rotate(t2, 1, 0, 0));
}
this->SetCommand("MoveTo", target);
if (dis < 20)
{
// Victim near. Bite him.
SetAction("Bite");
Sound("Animals::Fish::Munch*");
if (target->GetAlive())
target->DoEnergy(-BiteStrength);
DoEnergy(BiteStrength);
// Mission succesful. Swim back to the initial position.
SetCommand("None");
this["MeshTransformation"] = Trans_Identity();
this->SetCommand("MoveTo", nil, x, y);
ScheduleCall(this, "ResetHunger", 300);
return 1;
}
ScheduleCall(this, "Attack", 5, 0, target, x, y);
}
private func Turn()
{
if (GetCommand() != nil)
return 1;
SetComDir(COMD_None);
SetXDir(0);
SetYDir(0);
SetAction("Turn");
// The shark should now turn around in very short intervalls.
turned = true;
ScheduleCall(this, "ResetTurn", 80);
}
private func Activity()
{
if (GetAction() == "Turn")
return;
if (swimming)
{
if (Inside(GetXDir(), -6, 6) && !turned && GetAction() != "FastSwim")
{
SetCommand("None");
Turn();
}
else if (!attacking)
{
var target = FindObject(Find_ID(Clonk), Find_Distance(380), Find_Action("Swim"));
if (target)
if (PathFree(GetX(), GetY(), target->GetX(), target->GetY()))
{
attacking = true;
SetAction("FastSwim");
Attack(target, GetX(), GetY());
return 1;
}
// Avoid surface.
if (!GBackLiquid(0, -10))
{
if (GetDir())
SetComDir(COMD_DownRight);
else
SetComDir(COMD_DownLeft);
return 1;
}
// Avoid ground.
if (!GBackLiquid(0, 25))
{
if (GetDir())
SetComDir(COMD_UpRight);
else
SetComDir(COMD_UpLeft);
return 1;
}
if (!turned)
{
// Wall?
if ((GBackSolid(+75, 0) && GetDir() == DIR_Right) ^ (GBackSolid(-75, 0) && GetDir() == DIR_Left))
{
Turn();
return;
}
if (!Random(40))
{
Turn();
return 1;
}
}
}
}
else if (walking)
{
// PANIC WHERE THE WATER AT
var rx = RandomX(10, 25);
if (!Random(2)) rx *= -1;
var has_water = false;
for (var y = 0; y <= 12; y += 4)
{
if (!GBackLiquid(rx, y)) continue;
has_water = true;
break;
}
if (has_water)
{
if (rx < 0) SetComDir(COMD_Left);
else SetComDir(COMD_Right);
if (!Random(2))
DoJump();
return true;
}
else
{
if (!Random(2))
{
if (!Random(2)) SetComDir(COMD_Left);
else SetComDir(COMD_Right);
}
if (!Random(3))
DoJump();
}
}
return 1;
}
private func Place(int amount, proplist rectangle, proplist settings)
{
var max_tries = 2 * amount;
var loc_area = nil;
if (rectangle) loc_area = Loc_InArea(rectangle);
var f;
while ((amount > 0) && (--max_tries > 0))
{
var spot = FindLocation(Loc_Material("Water"), Loc_Space(20), loc_area);
if (!spot) continue;
f = CreateObjectAbove(this, spot.x, spot.y, NO_OWNER);
if (!f) continue;
// Randomly add some large/slim fish
if (Random(3))
{
// There are naturally smaller and larger sharks.
f->SetCon(RandomX(80, 120));
// make sure the smaller ones don't grow larger any more
f->StopGrowth();
// Slim fish.
if (f->GetCon() > 100 || !Random(3))
f->SetYZScale(1000 - Random(300));
}
if (f->Stuck())
{
f->RemoveObject();
continue;
}
--amount;
}
return f; // return last created fish
}
private func SetYZScale(int new_scale)
{
base_transform = Trans_Scale(1000, new_scale, new_scale);
return true;
}
private func Death()
{
RemoveTimer(this.UpdateSwim);
RemoveTimer(this.Activity);
this.MeshTransformation = Trans_Rotate(160 + Random(41), 1, 0, 0);
if (base_transform) this.MeshTransformation = Trans_Mul(base_transform, this.MeshTransformation);
StopAnimation(swim_animation);
Decay();
this.Collectible = true;
// Maybe respawn a new fish if roe is near.
var roe = FindObject(Find_Distance(200), Find_ID(FishRoe));
if (roe)
roe->Hatch(GetID());
return _inherited(...);
}
private func DoJump()
{
SetAction("Jump");
Sound("Hits::SoftTouch*");
var x_dir = RandomX(ActMap.Jump.Speed/2, ActMap.Jump.Speed);
if (GetComDir() == COMD_Left) x_dir *= -1;
var y_dir = -RandomX(ActMap.Jump.Speed/3, ActMap.Jump.Speed);
SetSpeed(x_dir, y_dir);
}
private func StartWalk()
{
var len = GetAnimationLength("Swim");
var pos = GetAnimationPosition(swim_animation);
SetAnimationPosition(swim_animation, Anim_Linear(pos, 0, len, 10, ANIM_Loop));
this.MeshTransformation = Trans_Mul(Trans_Rotate(90 + RandomX(-10, 10), 1, 0, 0), base_transform);
SetObjDrawTransform(0,0,0,0,0,0);
swim_animation = PlayAnimation("Swim", 5, Anim_Linear(0, 0, len, 100, ANIM_Loop), Anim_Const(500));
if (GBackLiquid())
{
SetAction("Swim");
return;
}
walking = true;
swimming = false;
ResetHunger();
}
private func StartSwim()
{
StopAnimation(GetRootAnimation(5));
swimming = true;
walking = false;
this["MeshTransformation"] = Trans_Identity();
if (GetCommand() != nil)
return 1;
if (GetDir() == DIR_Left)
{
SetDir(DIR_Right);
if (!Random(5)) SetComDir(COMD_UpRight);
else if (!Random(6)) SetComDir(COMD_DownRight);
else SetComDir(COMD_Right);
}
else
{
SetDir(DIR_Left);
if (!Random(5)) SetComDir(COMD_UpLeft);
else if (!Random(6)) SetComDir(COMD_DownLeft);
else SetComDir(COMD_Left);
}
}
private func StartJump()
{
if (GBackLiquid())
{
SetAction("Swim");
return;
}
swimming = false;
walking = false;
}
private func ResetHunger()
{
attacking = false;
}
private func ResetTurn()
{
turned = false;
}
local ActMap = {
Swim = {
Prototype = Action,
Name = "Swim",
Procedure = DFA_SWIM,
Speed = 100,
Accel = 64,
Decel = 16,
Length = 52,
Delay = 10,
Directions = 2,
FlipDir = 1,
StartCall = "StartSwim",
Animation = "Swim"
},
FastSwim = {
Prototype = Action,
Name = "FastSwim",
Procedure = DFA_SWIM,
Speed = 200,
Accel = 48,
Decel = 16,
Length = 20,
Delay = 5,
Directions = 2,
FlipDir = 0,
NextAction = "FastSwim",
Animation = "Swim"
},
Turn = {
Prototype = Action,
Name = "Turn",
Procedure = DFA_SWIM,
Speed = 100,
Accel = 16,
Decel = 16,
Length = 52,
Delay = 10,
Directions = 2,
FlipDir = 1,
NextAction = "Swim",
Animation = "Turn"
},
Bite = {
Prototype = Action,
Name = "Bite",
Procedure = DFA_SWIM,
Speed = 100,
Accel = 16,
Decel = 16,
Length = 2,
Delay = 10,
Directions = 2,
NextAction = "Swim",
Animation = "Bite"
},
Walk = {
Prototype = Action,
Name = "Walk",
Procedure = DFA_WALK,
Speed = 30,
Accel = 16,
Decel = 16,
Length = 1,
Delay = 0,
FacetBase=1,
Directions = 2,
FlipDir = 1,
NextAction = "Walk",
StartCall = "StartWalk",
InLiquidAction = "Swim",
},
Jump = {
Prototype = Action,
Name = "Jump",
Procedure = DFA_FLIGHT,
Speed = 30,
Accel = 16,
Decel = 16,
Length = 1,
Delay = 0,
FacetBase=1,
Directions = 2,
FlipDir = 1,
NextAction = "Jump",
StartCall = "StartJump",
InLiquidAction = "Swim",
},
Dead = {
Prototype = Action,
Name = "Dead",
Procedure = DFA_NONE,
Speed = 10,
Length = 1,
Delay = 0,
FacetBase=1,
Directions = 2,
FlipDir = 1,
NextAction = "Hold",
NoOtherAction = 1,
ObjectDisabled = 1,
Animation = "Death"
}
};
local Name = "$Name$";
local Description = "$Description$";
local MaxEnergy = 150000;
local MaxBreath = 360; // 360 = ten seconds
local Placement = 1;
local NoBurnDecay = 1;
local BreatheWater = 1;
local BorderBound = C4D_Border_Sides | C4D_Border_Top | C4D_Border_Bottom;
func IsPrey() { return false; }
func IsPredator() { return true; }
public func Definition(def)
{
SetProperty("PictureTransformation", Trans_Mul(Trans_Rotate(20,1,0,0),Trans_Rotate(70,0,1,0)), def);
}