/**
	Axe
	Used for chopping down trees. Can also harvest wood from fallen trees,
	but will not yield as many logs as a sawmill.
	
	@author: Ringwaul, Clonkonaut
*/

#include Library_MeleeWeapon

#include Library_Flammable

local swing_anim;
local using;
local carry_bone;
local magic_number;

local movement_effect;

/*-- Engine Callbacks --*/

func Hit(int x, int y)
{
	StonyObjectHit(x,y);
	return 1;
}

func Departure(object container)
{
	// Always end the movement impairing effect when exiting
	if (movement_effect)
	{
		RemoveEffect(nil, container, movement_effect);
		movement_effect = nil;
	}
}

/*-- Usage --*/

public func RejectUse(object clonk)
{
	return !clonk->IsWalking() && !clonk->IsJumping();
}

// used by this object
func ReadyToBeUsed(proplist data)
{
	var clonk = data.clonk;
	return !RejectUse(clonk) && CanStrikeWithWeapon(clonk) && clonk->HasHandAction();
}

public func HoldingEnabled()
{
	return GetEffect("IntAxe", this);
}

public func ControlUseStart(object clonk, int iX, int iY)
{
	/* Chopping */

	// Find tree that is closest to the clonk's axe when swung
	var x_offs = 10;
	if(clonk->GetDir() == DIR_Left) {
		x_offs = -x_offs;
	}

	if (clonk->IsWalking()) for (var tree in FindObjects(Find_AtPoint(x_offs,0), Find_Func("IsTree"), Sort_Distance(x_offs, 0), Find_NoContainer()))
	{
		//treedist - the x-distance the clonk is from the centre of a tree-trunk
		var treedist = Abs(clonk->GetX() + x_offs - tree->GetX());
		if(tree->IsStanding() && treedist <= 6)
		{
			using = 1;

			//The clonk cannot hold other items in hand while swinging an axe
			clonk->SetHandAction(1);

			//Update the axe position in the clonk' hands and disable turning while he chops the tree
			clonk->UpdateAttach();
			clonk->SetTurnForced(clonk->GetDir());

			//Make sure the clonk is holding the axe in the correct position
			var hand = "Chop.R";
			if((clonk->GetDir() == 0) != (clonk.Plane < tree.Plane)) hand = "Chop.L";
			swing_anim = clonk->PlayAnimation(hand, CLONK_ANIM_SLOT_Arms, Anim_Linear(0, 0, clonk->GetAnimationLength(hand), SwingTime, ANIM_Loop), Anim_Const(1000));

			//The timed effect for when the axe actually hits the tree
			AddEffect("IntAxe", clonk, 1, 1, this, nil, tree);
			return true;
		}
		if(! tree->IsStanding())
		{
			// Tree has already been felled
			using = 1;

			//The clonk cannot hold other items in hand while swinging an axe
			clonk->SetHandAction(1);

			//Refresh hands
			clonk->UpdateAttach();

			//Make sure the clonk is holding the axe in the correct position
			var hand = "Chop.R";
			if(clonk->GetDir() == 0) hand = "Chop.L";
			swing_anim = clonk->PlayAnimation(hand, CLONK_ANIM_SLOT_Arms, Anim_Linear(0, 0, clonk->GetAnimationLength("Chop.R"), SwingTime, ANIM_Loop), Anim_Const(1000));

			//clonk cannot turn around to face the screen while chopping
			clonk->SetTurnForced(clonk->GetDir());

			//The timed effect for when the axe actually hits the tree
			AddEffect("IntSplit", clonk, 1, 1, this, nil, tree);
			return true;
		}
	}
	// Do combat strike if no tree can be found.
	return ControlUse(clonk, iX, iY);
}

// Strike with the axe.
public func ControlUse(object clonk, int iX, int iY)
{
	if(!CanStrikeWithWeapon(clonk) || !clonk->HasHandAction())
	{
		clonk->PauseUse(this, "ReadyToBeUsed", {clonk = clonk});
		return true;
	}

	var rand = Random(2)+1;
	var arm = "R";
	var animation = Format("SwordSlash%d.%s", rand, arm);
	carry_bone = "pos_hand2";

	if(clonk->IsWalking())
	{
		if(!GetEffect("AxeStrikeStop", clonk, 0))
			AddEffect("AxeStrikeStop", clonk, 2, StrikingLength, this);
	}
	if(clonk->GetHandPosByItemPos(clonk->GetItemPos(this)) == 1)
	{
		arm = "L";
		carry_bone = "pos_hand1";
		animation  = Format("SwordSlash%d.%s", rand, arm);
	}
	if(clonk->IsJumping())
	{
		rand = 1;
		if(clonk->GetYDir() < -5) rand = 2;
		animation = Format("SwordJump%d.%s",rand,arm);
	}

	PlayWeaponAnimation(clonk, animation, 10, Anim_Linear(0, 0, clonk->GetAnimationLength(animation), StrikingLength, ANIM_Remove), Anim_Const(1000));

	clonk->UpdateAttach();

	Sound("Objects::Weapons::WeaponSwing?", {pitch = -Random(10)});

	magic_number=((magic_number+1)%10) + (ObjectNumber()*10);
	StartWeaponHitCheckEffect(clonk, StrikingLength, 1);

	return true;
}

public func ControlUseHolding(object clonk, int new_x, int new_y)
{
	// Can clonk use axe?
	if (!clonk->IsWalking() || GetXDir() != 0)
	{
		clonk->CancelUse();
		return true;
	}
	return true;
}

func ControlUseCancel(object clonk, int ix, int iy)
{
	Reset(clonk);
	return true;
}

public func ControlUseStop(object clonk, int ix, int iy)
{
	Reset(clonk);
	return true;
}

public func Reset(clonk)
{
	//Reset the clonk to normal control
	using = 0;
	clonk->SetHandAction(0);
	clonk->UpdateAttach();
	clonk->SetTurnForced(-1);
	clonk->StopAnimation(swing_anim);
	swing_anim = nil;
	RemoveEffect("IntAxe", clonk);
	RemoveEffect("IntSplit", clonk);
	RemoveEffect("AxeStrike", clonk);
}

/* Chopping */

func FxIntAxeStart(object clonk, effect, int temp, object target_tree)
{
	if (temp) return;
	effect.tree = target_tree;
}

func FxIntAxeTimer(object clonk, effect, int time)
{
	// What happen...?
	if (this->Contained() != clonk) return -1;
	// Tree vanished
	if (!effect.tree) return -1;
	// Tree fell
	if (!effect.tree->IsStanding()) return -1;
	// Clonk did something
	if (!clonk->IsWalking()) return -1;

	//This block is executed when the axe hits the tree
	if((time + 25) % SwingTime == 1)
	{
		Sound("Environment::Tree::Chop?");

		//Which direction does the clonk face?
		var x = 10;
		if(clonk->GetDirection() == COMD_Left) x = x * -1;
		
		//Create the woodchip particles
		var particles = Particles_WoodChip();
		// need to be behind the Clonk?
		if (clonk.Plane > effect.tree.Plane)
			particles = {Prototype = Particles_WoodChip(), Attach = ATTACH_Back};
		clonk->CreateParticle("WoodChip", x, 4, PV_Random(-12, 12), PV_Random(-13, -6), PV_Random(36 * 3, 36 * 10), particles, 10);
		// Damage tree
		effect.tree->DoDamage(this.ChopStrength, FX_Call_DmgChop, clonk->GetOwner()); 
	}
	//Make sure the clonk does not move
	clonk->SetComDir(COMD_Stop);
}

func FxIntAxeStop(object clonk, effect, int temp)
{
	if (temp) return;
	if (this->Contained() == clonk) Reset(clonk);
}

/* Splitting */

func FxIntSplitStart(object clonk, effect, int temp, object target_tree)
{
	if (temp) return;
	effect.tree = target_tree;
}

func FxIntSplitTimer(object clonk, effect, int time)
{
	// What happen...?
	if (this->Contained() != clonk) return -1;
	// Tree vanished
	if (!effect.tree) return -1;
	// Tree moved away
	if (ObjectDistance(effect.tree, clonk) > Distance(0,0, effect.tree->GetObjWidth()/2, effect.tree->GetObjHeight()/2)) return -1;
	// Clonk did something
	if (!clonk->IsWalking()) return -1;

	//This block is executed when the axe hits the tree
	if ((time + 25) % SwingTime == 1)
	{
		Sound("Environment::Tree::Chop?");

		//Which direction does the clonk face?
		var x = 10;
		if(clonk->GetDirection() == COMD_Left) x = x * -1;

		//Create the woodchip particle
		clonk->CreateParticle("WoodChip", x, 4, PV_Random(-12, 12), PV_Random(-13, -6), PV_Random(36 * 3, 36 * 10), Particles_WoodChip(), 10);
	}
	// Tree split!
	if ((SwingTime * 12) / time == 1)
	{
		var wood_count = effect.tree->GetComponent(Wood) / 2;
		CastObjects(Wood, wood_count, 5, AbsX(effect.tree->GetX()), AbsY(effect.tree->GetY()));
		effect.tree->RemoveObject();
	}
	//Make sure the clonk does not move
	clonk->SetComDir(COMD_Stop);
}

func FxIntSplitStop(object clonk, effect, int temp)
{
	if (temp) return;
	if (this->Contained() == clonk) Reset(clonk);
}

/* Combat */

func CheckStrike(iTime)
{
	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;

	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()))
		{
			var effect_name=Format("HasBeenHitByAxeEffect%d", magic_number);
			var axe_name=Format("HasBeenHitByAxe%d", this->ObjectNumber());
			var first=true;
			// don't hit objects twice
			if(!GetEffect(effect_name, obj))
			{
				AddEffect(effect_name, obj, 1, 60 /* arbitrary */);

				if(GetEffect(axe_name, obj))
				{
					first=false;
				}
				else
				{
					AddEffect(axe_name, obj, 1, 40);
				}

				// Reduce damage by shield
				var shield=ApplyShieldFactor(Contained(), obj, 0); // damage out of scope?
				if(shield == 100)
					continue;

				// fixed damage (3)
				var damage = ((100-shield) * this.WeaponStrength * 1000 / 100);
				WeaponDamage(obj, damage, FX_Call_EngGetPunched, true);

				if (obj)
					DoWeaponSlow(obj, 200);

				// sound and done. We can only hit one target
				Sound("Objects::Weapons::WeaponHit?", false);
				break;
			}
		}
	}
}

func WeaponStrikeExpired()
{
	if(GetEffect("AxeStrikeStop", Contained()))
		RemoveEffect("AxeStrikeStop", Contained());
}

func OnWeaponHitCheckStop(clonk)
{
	carry_bone = nil;
	clonk->UpdateAttach();
}

func FxAxeStrikeStopStart(pTarget, effect, iTemp)
{
	if(iTemp) return;
	pTarget->PushActionSpeed("Walk", (pTarget.ActMap.Walk.Speed)/100);
}

func FxAxeStrikeStopStop(pTarget, effect, iCause, iTemp)
{
	if(iTemp) return;
	pTarget->PopActionSpeed("Walk");
	movement_effect = nil;
}

func FxAxeStrikeStopTimer(pTarget, effect)
{
	return -1;
}

/*-- Production --*/

public func IsTool() { return true; }
public func IsToolProduct() { return true; }

/*-- Display --*/

public func GetCarryMode(object clonk, bool idle)
{
	if (!idle)
		return CARRY_HandBack;
	else
		return CARRY_Belt;
}

public func GetCarryTransform(object clonk, bool idle)
{
	if (idle) return;

	var act = clonk->GetAction();

	if(act != "Walk" && act != "Jump")
		return Trans_Mul(Trans_Translate(4500,0,0), Trans_Rotate(90,1,0,0), Trans_Rotate(180,0,1,0) );

	return Trans_Rotate(-90, 1, 0, 0);
}

public func GetCarrySpecial(clonk)
{
	if(using == 1) 
	{
		if(clonk->GetDir() == 1)
			return "pos_hand2";
		else
			return "pos_hand1";
	}
	return carry_bone;
}

/*-- Properties --*/

local Collectible = true;
local Name = "$Name$";
local Description = "$Description$";
local Components = {Wood = 1, Metal = 1};
local BlastIncinerate = 30;
local MaterialIncinerate = true;
local BurnDownTime = 140;
// Damage dealt to trees when chopping.
local ChopStrength = 10;
// Damage dealt to living beings when hit with an axe.
local WeaponStrength = 6;
// When using the axe to chop a tree.
local SwingTime = 30;
// When using the axe as a weapon (without trees).
local StrikingLength = 20; // in frames