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

332 lines
8.6 KiB
C

/*-- Sword --*/
#include Library_MeleeWeapon
static const Sword_Standard_StrikingLength = 15; // in frames
func Hit()
{
Sound("LightMetalHit?");
}
public func Initialize()
{
PlayAnimation("Base", 5, Anim_Const(0), Anim_Const(1000));
return _inherited(...);
}
public func GetCarryMode() { return CARRY_HandBack; }
public func GetCarryBone() { return "main"; }
public func GetCarrySpecial(clonk) { return carry_bone; }
public func GetCarryTransform(clonk, sec, back)
{
if(back) return Trans_Mul(Trans_Rotate(180,0,0,1), Trans_Rotate(90,0,1,0), Trans_Translate(0,-7000,0));
return Trans_Rotate(90, 0, 1, 0);
}
local magic_number;
local carry_bone;
public func ControlUse(object clonk, num x, num y)
{
// cooldown?
if(!CanStrikeWithWeapon(clonk)) return true;
// if the clonk doesn't have an action where he can use it's hands do nothing
if(!clonk->HasHandAction())
return true;
var slow=GetEffect("SwordStrikeSlow", clonk);
var arm = "R";
carry_bone = "pos_hand2";
if(clonk->GetHandPosByItemPos(clonk->GetItemPos(this)) == 1)
{
arm = "L";
carry_bone = "pos_hand1";
}
var rand = Random(2)+1;
var animation = Format("SwordSlash%d.%s", rand, arm);
var animation_sword = Format("Strike%d", rand);
var downwards_stab = false;
// figure out the kind of attack to use
var length = Sword_Standard_StrikingLength;
if(clonk->IsWalking())
{
if(!GetEffect("SwordStrikeStop", clonk))
AddEffect("SwordStrikeStop", clonk, 2, length, this);
} else
if(clonk->IsJumping())
{
rand = 1;
if(clonk->GetYDir() < -5) rand = 2;
animation = Format("SwordJump%d.%s",rand,arm);
if(!slow && !GetEffect("DelayTranslateVelocity", clonk))
{
// check whether the player aims below the Clonk
var a=Angle(0, 0, x,y);
var x_dir = Sin(a, 60);
if(Inside(a, 35+90, 35+180)) // the player aims downwards
if((BoundBy(x_dir, -1, 1) == BoundBy(clonk->GetXDir(), -1, 1)) || (clonk->GetXDir() == 0)) // the player aims into the direction the Clonk is already jumping
{
clonk->SetXDir(x_dir);
clonk->SetYDir(-Cos(a, 60));
AddEffect("DelayTranslateVelocity", clonk, 2, 3, nil, Library_MeleeWeapon);
// modified animation
length = 50;
animation = Format("SwordSlash1.%s", arm);
downwards_stab = true;
if(GetEffect("Fall", clonk)) RemoveEffect("Fall", clonk);
// visual effect
AddEffect("VisualJumpStrike", clonk, 1, 2, nil, Sword);
}
}
}
//else return true;*/
if(!clonk->IsWalking() && !clonk->IsJumping()) return true;
if(!downwards_stab)
{
PlayWeaponAnimation(clonk, animation, 10, Anim_Linear(0, 0, clonk->GetAnimationLength(animation), length, ANIM_Remove), Anim_Const(1000));
PlayAnimation(animation_sword, 10, Anim_Linear(0, 0, GetAnimationLength(animation_sword), length, ANIM_Remove), Anim_Const(1000));
}
else
{
PlayWeaponAnimation(clonk, animation, 10, Anim_Linear(0, 0, (clonk->GetAnimationLength(animation)*3)/4, 5, ANIM_Hold), Anim_Const(1000));
PlayAnimation(animation_sword, 10, Anim_Linear(GetAnimationLength(animation_sword)/2, 0, GetAnimationLength(animation_sword), length, ANIM_Remove), Anim_Const(1000));
}
clonk->UpdateAttach();
// this means that the sword can only hit an object every X frames
// change it to something that changes every strike if you want the sword to be able to hit the same enemy with different
// strikes regardless of the time in between
magic_number = ObjectNumber();
StartWeaponHitCheckEffect(clonk, length, 1);
this->Sound("WeaponSwing?", false, nil, nil, nil);
return true;
}
func FxVisualJumpStrikeStart(target, effect, temp)
{
if(temp) return;
effect.x_add = 20;
if(target->GetXDir() < 0) effect.x_add *= -1;
effect.visual = CreateObject(Sword_JumpEffect, 0, 0, nil);
effect.visual->Point({x = target->GetX() + effect.x_add, y = target->GetY() + 10}, {x = target->GetX() + effect.x_add, y = target->GetY() + 10});
}
func FxVisualJumpStrikeTimer(target, effect, time)
{
if(!target->~IsJumping())
{
effect.visual->FadeOut();
effect.visual = nil;
return -1;
}
effect.visual->Point(nil, {x = target->GetX() + effect.x_add, y = target->GetY() + 10});
}
func FxVisualJumpStrikeStop(target, effect, reason, temp)
{
if(temp) return;
if(!effect.visual) return;
effect.visual->FadeOut();
}
func OnWeaponHitCheckStop(clonk)
{
carry_bone = nil;
clonk->UpdateAttach();
if(GetEffect("SwordStrikeSpeedUp", clonk))
RemoveEffect("SwordStrikeSpeedUp", clonk);
if(clonk->IsJumping())
{
if(!GetEffect("Fall", clonk))
AddEffect("Fall",clonk,1,1,clonk);
}
if(GetEffect("SwordStrikeStop", clonk))
RemoveEffect("SwordStrikeStop", clonk);
return;
}
// called when the strike expired before end of length (aborted)
func WeaponStrikeExpired()
{
}
func SwordDamage(num shield)
{
return ((100-shield)*9*1000 / 100);
}
func CheckStrike(iTime)
{
//if(iTime < 20) return;
var offset_x=7;
var offset_y=0;
if(Contained()->GetDir() == DIR_Left) offset_x*=-1;
if(!(Contained()->GetContact(-1) & CNAT_Bottom))
offset_y=10;
var width=10;
var height=20;
var angle=0;
var doBash=Abs(Contained()->GetXDir()) > 5 || Abs(Contained()->GetYDir()) > 5;
if(!doBash) doBash=Contained()->GetContact(-1) & CNAT_Bottom;
if(doBash)
{
if(Contained()->GetDir() == DIR_Left)
angle=-(Max(5, Abs(Contained()->GetXDir())));
else angle=(Max(5, Abs(Contained()->GetXDir())));
}
for(var obj in FindObjects(Find_AtRect(offset_x - width/2, offset_y - height/2, width, height),
Find_NoContainer(),
Find_Exclude(Contained()),
Find_Layer(GetObjectLayer())))
{
if (obj->~IsProjectileTarget(this, Contained()) || obj->GetOCF() & OCF_Alive)
{
var effect_name=Format("HasBeenHitBySwordEffect%d", magic_number);
var sword_name=Format("HasBeenHitBySword%d", this->ObjectNumber());
var first=true;
// don't hit objects twice
if(!GetEffect(effect_name, obj))
{
AddEffect(effect_name, obj, 1, Sword_Standard_StrikingLength, nil, 0);
if(GetEffect(sword_name, obj))
{
//Log("successive hit");
first=false;
}
else
{
//Log("first hit overall");
AddEffect(sword_name, obj, 1, 40, nil, 0);
}
// Reduce damage by shield
var shield=ApplyShieldFactor(Contained(), obj, 0); // damage out of scope?
if(shield == 100)
continue;
// fixed damage (9)
var damage = SwordDamage(shield);
ProjectileHit(obj, damage, ProjectileHit_no_query_catch_blow_callback | ProjectileHit_exact_damage | ProjectileHit_no_on_projectile_hit_callback, FX_Call_EngGetPunched);
// object has not been deleted?
if(obj)
{
if(offset_y)
ApplyWeaponBash(obj, 100, 0);
else
if(!first)
ApplyWeaponBash(obj, damage/50, Angle(0, 0, angle, -10));
else
if(!offset_y)
DoWeaponSlow(obj, 300);
// Particle effect
var x=-1;
var p="Slice2";
if(Contained()->GetDir() == DIR_Right)
{
x=1;
p="Slice1";
}
CreateParticle(p, AbsX(obj->GetX())+RandomX(-1,1), AbsY(obj->GetY())+RandomX(-1,1), 0, 0, 100, RGB(255,255,255), obj);
}
// sound and done. We can only hit one target
Sound("WeaponHit?", false);
break;
}
}
}
}
func FxSwordStrikeStopStart(pTarget, effect, iTemp)
{
if(iTemp) return;
pTarget->PushActionSpeed("Walk", (pTarget.ActMap.Walk.Speed)/100);
}
func FxSwordStrikeStopStop(pTarget, effect, iCause, iTemp)
{
if(iTemp) return;
pTarget->PopActionSpeed("Walk");
}
func FxSwordStrikeStopTimer(pTarget, effect)
{
return -1;
}
func FxSwordStrikeSpeedUpStart(pTarget, effect, iTemp)
{
pTarget->PushActionSpeed("Walk", pTarget.ActMap.Walk.Speed * 3);
pTarget.ActMap.Walk.Accel = 210;
}
func FxSwordStrikeSpeedUpTimer(pTarget, effect, iEffectTime)
{
if(!pTarget->GetContact( -1) & CNAT_Bottom)
return -1;
if(iEffectTime > 35*2) return -1;
}
func FxSwordStrikeSpeedUpStop(pTarget, effect, iCause, iTemp)
{
pTarget->PopActionSpeed("Walk");
if(iTemp) return;
if(!pTarget->GetAlive()) return;
AddEffect("SwordStrikeSlow", pTarget, 1, 5, nil, Sword, effect.Time);
}
func FxSwordStrikeSlowStart(pTarget, effect, iTemp, iTime)
{
pTarget->PushActionSpeed("Walk", pTarget.ActMap.Walk.Speed / 3);
if(iTemp) return;
effect.starttime = iTime;
}
func FxSwordStrikeSlowTimer(pTarget, effect, iEffectTime)
{
if(iEffectTime > effect.starttime) return -1;
}
func FxSwordStrikeSlowStop(pTarget, effect, iCause, iTemp)
{
pTarget->PopActionSpeed("Walk");
}
public func IsWeapon() { return true; }
public func IsArmoryProduct() { return true; }
func Definition(def) {
SetProperty("PictureTransformation",Trans_Rotate(20, 0, 0, 1),def);
}
local Name = "$Name$";
local Description = "$Description$";
local UsageHelp = "$UsageHelp$";
local Collectible = 1;
local Rebuy = true;