openclonk/planet/Objects.ocd/Items.ocd/Weapons.ocd/Shield.ocd/Script.c

307 lines
7.1 KiB
C

/**
Shield
Offers protection against all kinds of attacks.
*/
#include Library_MeleeWeapon
local iAngle; // -180 .. 180
local aim_anim;
local carry_bone;
local mTrans;
local solid_mask_helper;
/*-- Engine Callbacks --*/
func Hit()
{
Sound("Hits::Materials::Metal::DullMetalHit?");
}
// Colour by owner
func Entrance(object container)
{
if(container->GetOwner() != NO_OWNER) SetColor(GetPlayerColor(container->GetOwner()));
}
/*-- Callbacks --*/
public func HitByWeapon(by, iDamage)
{
var object_angle = Angle(GetX(), GetY(), by->GetX(), by->GetY());
var shield_angle = iAngle;
// angle difference: 0..180
var angle_diff = Abs(Normalize(shield_angle - object_angle, -180));
if (angle_diff > 45)
return 0;
Sound("Objects::Weapons::Shield::MetalHit?");
// bash him hard!
ApplyWeaponBash(by, 100, iAngle);
// uber advantage in melee combat
AddEffect("ShieldBlockWeaponCooldown", by, 1, 30, this);
// shield factor
return 100;
}
/*-- Usage --*/
public func HoldingEnabled() { return true; }
public func RejectUse(object clonk)
{
return !clonk->HasHandAction() || !clonk->IsWalking() || !CanStrikeWithWeapon(clonk);
}
public func ControlUseStart(object clonk, int x, int y)
{
StartUsage(clonk);
UpdateShieldAngle(clonk, x, y);
return true;
}
public func ControlUseHolding(object clonk, int x, int y)
{
UpdateShieldAngle(clonk, x, y);
}
public func ControlUseCancel(object clonk)
{
EndUsage(clonk);
if (GetEffect("IntShieldSuspend", clonk))
RemoveEffect("IntShieldSuspend", clonk);
}
public func ControlUseStop(object clonk)
{
ControlUseCancel(clonk);
}
func StartUsage(object clonk)
{
var hand;
// which animation to use? (which hand)
if (clonk->GetHandPosByItemPos(clonk->GetItemPos(this)) == 0)
{
carry_bone = "pos_hand2";
hand = "ShieldArms.R";
}
else
{
carry_bone = "pos_hand1";
hand = "ShieldArms.L";
}
aim_anim = clonk->PlayAnimation(hand, CLONK_ANIM_SLOT_Arms, Anim_Const(clonk->GetAnimationLength(hand)/2), Anim_Const(1000));
var handLR;
if (clonk->GetHandPosByItemPos(clonk->GetItemPos(this)) == 0)
handLR = "R";
else
handLR = "L";
clonk->UpdateAttach();
clonk->ReplaceAction("Stand", [Format("ShieldStandUp.%s",handLR), Format("ShieldStandDown.%s",handLR), 500]);
clonk->ReplaceAction("Walk", [Format("ShieldWalkUp.%s",handLR), Format("ShieldWalkDown.%s",handLR), 500]);
clonk->ReplaceAction("Run", [Format("ShieldWalkUp.%s",handLR), Format("ShieldWalkDown.%s",handLR), 500]);
StartWeaponHitCheckEffect(clonk, -1, 1);
if (!GetEffect("ShieldStopControl", clonk))
AddEffect("ShieldStopControl", clonk, 2, 5, this);
clonk->SetTurnType(1, 1);
}
func EndUsage(object clonk)
{
carry_bone = nil;
aim_anim = nil;
iAngle = 0;
clonk->StopAnimation(clonk->GetRootAnimation(10));
clonk->UpdateAttach();
clonk->ReplaceAction("Stand", nil);
clonk->ReplaceAction("Walk", nil);
clonk->ReplaceAction("Run", nil);
AdjustSolidMaskHelper();
if (GetEffect("ShieldStopControl", clonk))
RemoveEffect("ShieldStopControl", clonk);
clonk->SetTurnForced(-1);
clonk->SetTurnType(0, -1);
StopWeaponHitCheckEffect(clonk);
}
func UpdateShieldAngle(object clonk, int x, int y)
{
var angle = Normalize(Angle(0, 0, x, y), -180);
angle = BoundBy(angle, -150, 150);
iAngle = angle;
var weight = 0;
if (Abs(angle) > 90)
weight = 1000 * (Abs(angle) - 60) / 90;
var handLR;
if (clonk->GetHandPosByItemPos(clonk->GetItemPos(this)) == 0)
handLR = "R";
else
handLR = "L";
clonk->ReplaceAction("Stand", [Format("ShieldStandUp.%s",handLR), Format("ShieldStandDown.%s",handLR), weight]);
clonk->ReplaceAction("Walk", [Format("ShieldWalkUp.%s",handLR), Format("ShieldWalkDown.%s",handLR), weight]);
clonk->ReplaceAction("Run", [Format("ShieldWalkUp.%s",handLR), Format("ShieldWalkDown.%s",handLR), weight]);
if(angle > 0) clonk->SetTurnForced(DIR_Right);
else clonk->SetTurnForced(DIR_Left);
clonk->SetAnimationPosition(aim_anim, Anim_Const(Abs(angle) * 11111 / 1000));
AdjustSolidMaskHelper();
}
// Adjust solid mask of shield
// if the shield is held up, it has a solid mask on which other clonks can climb onto
func AdjustSolidMaskHelper()
{
if (aim_anim && Contained() && Abs(iAngle) <= 20)
{
if (!solid_mask_helper)
{
solid_mask_helper = CreateObjectAbove(Shield_SolidMask, 0, 0, NO_OWNER);
solid_mask_helper->SetAction("Attach", Contained());
}
}
else
{
if (solid_mask_helper)
return solid_mask_helper->RemoveObject();
else
return;
}
var angle = BoundBy(iAngle, -115, 115);
solid_mask_helper->SetR(angle - 90);
var distance = 8;
var y_adjust = -5;
var x_adjust = 1;
if (Contained()->GetDir() == DIR_Left)
x_adjust = -1;
var x = Sin(angle, distance) + x_adjust;
var y = -Cos(angle, distance) + y_adjust;
solid_mask_helper->SetVertexXY(0, -x, -y);
}
func FxShieldStopControlStart(object target, effect, temp)
{
target->PushActionSpeed("Walk", 84);
if(temp) return;
}
func FxShieldStopControlStop(object target, effect, iCause, temp)
{
target->PopActionSpeed("Walk");
if(temp) return;
}
func FxShieldStopControlTimer(object target, effect)
{
// suspend usage if not walking
if (!target->IsWalking())
{
target->PauseUse(this);
return -1;
}
return 1;
}
func FxShieldStopControlQueryCatchBlow(object target, effect, object obj)
{
if (obj->GetOCF() & OCF_Alive)
return false;
// angle of shield
var shield_angle = iAngle;
// angle of object
var object_angle;
if (obj->GetXDir() || obj->GetYDir())
object_angle = Angle(obj->GetXDir(), obj->GetYDir(), 0, 0);
else
object_angle = Angle(GetX(), GetY(), obj->GetX(), obj->GetY());
// angle difference: 0..180
var angle_diff = Abs(Normalize(shield_angle - object_angle, -180));
// objects hits if the angle difference is greater than 45 degrees
if (angle_diff > 45)
return false;
// projectile bounces off
var sxd = Sin(shield_angle, 15);
var syd = -Cos(shield_angle, 15);
var xd = obj->GetXDir();
var yd = obj->GetYDir();
obj->SetXDir(-xd / 3 + sxd);
obj->SetYDir(-yd / 3 + syd);
// dont collect blocked objects
AddEffect("NoCollection", obj, 1, 30);
Sound("Objects::Weapons::Shield::MetalHit?");
return true;
}
/*-- Production --*/
public func IsWeapon() { return true; }
public func IsArmoryProduct() { return true; }
/*-- Display --*/
public func GetCarryMode() { return CARRY_HandBack; }
public func GetCarrySpecial(clonk) { return carry_bone; }
public func GetCarryTransform(clonk, sec, back)
{
if (aim_anim && !sec)
return Trans_Mul(Trans_Rotate(180, 0, 1, 0), Trans_Rotate(90, 0, 1, 0));
if (aim_anim && sec)
return Trans_Mul(Trans_Rotate(180, 0, 0, 1), Trans_Rotate(90, 0, 1, 1));
if (mTrans != nil)
return mTrans;
if (!sec)
{
if (back)
return Trans_Mul(Trans_Rotate(-90, 0, 1, 0), Trans_Translate(0, -400, 0));
return nil;
}
if(back) return Trans_Mul(Trans_Mul(Trans_Rotate(90, 0, 1, 0),Trans_Rotate(180, 1, 0, 0)),Trans_Translate(0,-400,1000));
return Trans_Rotate(180, 0, 0, 1);
}
func Definition(def)
{
SetProperty("PictureTransformation",Trans_Mul(Trans_Translate(1000,-500),Trans_Rotate(20,1,1,-1),Trans_Scale(1200)),def);
}
/*-- Properties --*/
local Name = "$Name$";
local Description = "$Description$";
local Collectible = true;
local Components = {Wood = 1, Metal = 1};