--------------------------------------------------
-- RO Homuncls AI for "FILIR"
-- 
-- @auther parrot
-- @ver 2.0
-- 
-- "RAGNAROK ONLINE"
-- Gravity Corp. & Lee Myoungjin(studio DTDS). All Rights Reserved.
-- GungHo Online Entertainment, Inc. All Rights Reserved 
-- 
---- 修正履歴 ----
-- 
-- v1.0 2006/03/25 コメント挿入
-- v1.1 2006/03/27 GetMyEnemy修正
-- v1.2 2006/03/30 索敵範囲指定
-- v1.3 2006/04/03 横殴り防止
-- v1.4 2006/04/09 スキル自動使用
-- 
-- v2.0 2006/04/23 切り替えコマンド実装
--------------------------------------------------


require "./AI/Const.lua"
require "./AI/Util.lua"					

-----------------------------
-- state
-----------------------------
IDLE_ST					= 0
FOLLOW_ST					= 1
CHASE_ST					= 2
ATTACK_ST					= 3
MOVE_CMD_ST					= 4
STOP_CMD_ST					= 5
ATTACK_OBJECT_CMD_ST			= 6
ATTACK_AREA_CMD_ST			= 7
PATROL_CMD_ST				= 8
HOLD_CMD_ST					= 9
SKILL_OBJECT_CMD_ST			= 10
SKILL_AREA_CMD_ST				= 11
FOLLOW_CMD_ST				= 12
----------------------------



------------------------------------------
-- global variable
------------------------------------------
MyState				= IDLE_ST	-- 最初の状態は休息
MyEnemy				= 0		-- 敵 id
MyDestX				= 0		-- 目的地 x座標
MyDestY				= 0		-- 目的地 y座標
MyPatrolX				= 0		-- 偵察目的地 x座標
MyPatrolY				= 0		-- 偵察目的地 y座標
ResCmdList				= List.new()	-- 予約コマンドリスト
MyID					= 0		-- ホムンクルス id
MySkill					= 0		-- ホムンクルスのスキル
MySkillLevel				= 0		-- ホムンクルスのスキルレベル
------------------------------------------

------------------------------------------
-- skill id
------------------------------------------
S_MOONLIT = 8009
------------------------------------------

------------------------------------------
-- mode flag
------------------------------------------
MODE_ACTIVE       = 0	 -- 先攻モード
MODE_NONACTIVE    = 1	 -- 非先攻モード
MyActiveMode = MODE_NONACTIVE	 -- 先攻モードフラグ
ActiveModeCounter = 0	 -- コマンドカウンター

MODE_SKILL       = 0	 -- スキル使用不可
MODE_NONSKILL    = 1	 -- スキル使用可
MySkillMode = MODE_NONSKILL	 -- スキル使用フラグ
SkillModeCounter = 0	 -- コマンドカウンター
------------------------------------------




------------- command process  ---------------------

function	OnMOVE_CMD (x,y)
	
	TraceAI ("OnMOVE_CMD")

	if ( x == MyDestX and y == MyDestY and MOTION_MOVE == GetV(V_MOTION,MyID)) then
		return		-- 目的地と現在地が同一の場合は、処理しない
	end

	local curX, curY = GetV (V_POSITION,MyID)
	if (math.abs(x-curX)+math.abs(y-curY) > 15) then		-- 目的地が一定距離以上なら (サーバーで遠距離は処理しないため)
		List.pushleft (ResCmdList,{MOVE_CMD,x,y})			-- 元の目的地への移動を予約する
		x = math.floor((x+curX)/2)							-- 中間地点へ移動する
		y = math.floor((y+curY)/2)							-- 
	end

	Move (MyID,x,y)	
	
	MyState = MOVE_CMD_ST
	MyDestX = x
	MyDestY = y
	MyEnemy = 0
	MySkill = 0

	CheckMoveCommand()

end




function	OnSTOP_CMD ()

	TraceAI ("OnSTOP_CMD")

	if (GetV(V_MOTION,MyID) ~= MOTION_STAND) then
		Move (MyID,GetV(V_POSITION,MyID))
	end
	MyState = IDLE_ST
	MyDestX = 0
	MyDestY = 0
	MyEnemy = 0
	MySkill = 0

end




function	OnATTACK_OBJECT_CMD (id)

	TraceAI ("OnATTACK_OBJECT_CMD")

	MySkill = 0
	MyEnemy = id
	MyState = CHASE_ST

end




function	OnATTACK_AREA_CMD (x,y)

	TraceAI ("OnATTACK_AREA_CMD")

	if (x ~= MyDestX or y ~= MyDestY or MOTION_MOVE ~= GetV(V_MOTION,MyID)) then
		Move (MyID,x,y)	
	end
	MyDestX = x
	MyDestY = y
	MyEnemy = 0
	MyState = ATTACK_AREA_CMD_ST
	
end



function	OnPATROL_CMD (x,y)

	TraceAI ("OnPATROL_CMD")

	MyPatrolX , MyPatrolY = GetV (V_POSITION,MyID)
	MyDestX = x
	MyDestY = y
	Move (MyID,x,y)
	MyState = PATROL_CMD_ST

end




function	OnHOLD_CMD ()

	TraceAI ("OnHOLD_CMD")

	MyDestX = 0
	MyDestY = 0
	MyEnemy = 0
	MyState = HOLD_CMD_ST

end




function	OnSKILL_OBJECT_CMD (level,skill,id)

	TraceAI ("OnSKILL_OBJECT_CMD")

	MySkillLevel = level
	MySkill = skill
	MyEnemy = id
	MyState = CHASE_ST

	TraceAI ("SKILL_ID = "..skill)

end




function	OnSKILL_AREA_CMD (level,skill,x,y)

	TraceAI ("OnSKILL_AREA_CMD")

	Move (MyID,x,y)
	MyDestX = x
	MyDestY = y
	MySkillLevel = level
	MySkill = skill
	MyState = SKILL_AREA_CMD_ST

	TraceAI ("SKILL_ID = "..skill)
	
end




function	OnFOLLOW_CMD ()

	-- 待機命令は、待機状態と休息状態を互いに転換させる
	if (MyState ~= FOLLOW_CMD_ST) then
		MoveToOwner (MyID)
		MyState = FOLLOW_CMD_ST
		MyDestX, MyDestY = GetV (V_POSITION,GetV(V_OWNER,MyID))
		MyEnemy = 0 
		MySkill = 0
		TraceAI ("OnFOLLOW_CMD")
	else
		MyState = IDLE_ST
		MyEnemy = 0 
		MySkill = 0
		TraceAI ("FOLLOW_CMD_ST --> IDLE_ST")
	end

end




function	ProcessCommand (msg)

	if		(msg[1] == MOVE_CMD) then
		OnMOVE_CMD (msg[2],msg[3])
		TraceAI ("MOVE_CMD")
	elseif	(msg[1] == STOP_CMD) then
		OnSTOP_CMD ()
		TraceAI ("STOP_CMD")
	elseif	(msg[1] == ATTACK_OBJECT_CMD) then
		OnATTACK_OBJECT_CMD (msg[2])
		TraceAI ("ATTACK_OBJECT_CMD")
	elseif	(msg[1] == ATTACK_AREA_CMD) then
		OnATTACK_AREA_CMD (msg[2],msg[3])
		TraceAI ("ATTACK_AREA_CMD")
	elseif	(msg[1] == PATROL_CMD) then
		OnPATROL_CMD (msg[2],msg[3])
		TraceAI ("PATROL_CMD")
	elseif	(msg[1] == HOLD_CMD) then
		OnHOLD_CMD ()
		TraceAI ("HOLD_CMD")
	elseif	(msg[1] == SKILL_OBJECT_CMD) then
		OnSKILL_OBJECT_CMD (msg[2],msg[3],msg[4],msg[5])
		TraceAI ("SKILL_OBJECT_CMD")
	elseif	(msg[1] == SKILL_AREA_CMD) then
		OnSKILL_AREA_CMD (msg[2],msg[3],msg[4],msg[5])
		TraceAI ("SKILL_AREA_CMD")
	elseif	(msg[1] == FOLLOW_CMD) then
		OnFOLLOW_CMD ()
		TraceAI ("FOLLOW_CMD")
	end
end




-------------- state process  --------------------


function	OnIDLE_ST ()
	
	TraceAI ("OnIDLE_ST")

	local cmd = List.popleft(ResCmdList)
	if (cmd ~= nil) then		
		ProcessCommand (cmd)	-- 予約コマンド処理 
		return 
	end

	local	object = GetOwnerEnemy (MyID)
	if (object ~= 0) then							-- MYOWNER_ATTACKED_IN
		MyState = CHASE_ST
		MyEnemy = object
		TraceAI ("IDLE_ST -> CHASE_ST : MYOWNER_ATTACKED_IN")
		return 
	end

	object = GetMyEnemy (MyID)
	if (object ~= 0) then							-- ATTACKED_IN
		MyState = CHASE_ST
		MyEnemy = object
		TraceAI ("IDLE_ST -> CHASE_ST : ATTACKED_IN")
		return
	end

	local distance = GetDistanceFromOwner(MyID)
	if ( distance > 3 or distance == -1) then		-- MYOWNER_OUTSIGNT_IN
		MyState = FOLLOW_ST
		TraceAI ("IDLE_ST -> FOLLOW_ST")
		return;
	end

end




function	OnFOLLOW_ST ()

	TraceAI ("OnFOLLOW_ST")

	if (GetDistanceFromOwner(MyID) <= 3) then		--  DESTINATION_ARRIVED_IN 
		MyState = IDLE_ST
		TraceAI ("FOLLOW_ST -> IDLE_ST")
		return;
	elseif (GetV(V_MOTION,MyID) == MOTION_STAND) then
		MoveToOwner (MyID)
		TraceAI ("FOLLOW_ST -> FOLLOW_ST")
		return;
	end

end




function	OnCHASE_ST ()

	TraceAI ("OnCHASE_ST")

-- 敵を見失ったか?
	if (true == IsOutOfSight(MyID,MyEnemy)) then	-- ENEMY_OUTSIGHT_IN
		MyState = IDLE_ST
		MyEnemy = 0
		MyDestX, MyDestY = 0,0
		TraceAI ("CHASE_ST -> IDLE_ST : ENEMY_OUTSIGHT_IN")
		return
	end

-- 主人を見失ったか?
	local max_dis = 10 -- 主人との最大距離
	local dis = GetDistanceFromOwner(MyID)
	if (dis > max_dis) then	-- 主人から離れた?
		MoveToOwner (MyID) -- 主人のそばへ
		MyState = FOLLOW_ST -- 追尾状態へ
		MyEnemy = 0 -- 敵を初期化
		TraceAI ("CHASE_ST -> FOLLOW_ST : MASTER_OUTSIGHT_IN")
		return
	end

	if (true == IsInAttackSight(MyID,MyEnemy)) then  -- ENEMY_INATTACKSIGHT_IN
		MyState = ATTACK_ST
		TraceAI ("CHASE_ST -> ATTACK_ST : ENEMY_INATTACKSIGHT_IN")
		return
	end

	local x, y = GetV (V_POSITION,MyEnemy)
	if (MyDestX ~= x or MyDestY ~= y) then			-- DESTCHANGED_IN
		MyDestX, MyDestY = GetV (V_POSITION,MyEnemy);
		Move (MyID,MyDestX,MyDestY)
		TraceAI ("CHASE_ST -> CHASE_ST : DESTCHANGED_IN")
		return
	end

end




function	OnATTACK_ST ()

	TraceAI ("OnATTACK_ST")
	
	if (true == IsOutOfSight(MyID,MyEnemy)) then	-- ENEMY_OUTSIGHT_IN
		MyState = IDLE_ST
		TraceAI ("ATTACK_ST -> IDLE_ST")
		return 
	end

	if (MOTION_DEAD == GetV(V_MOTION,MyEnemy)) then   -- ENEMY_DEAD_IN
		MyState = IDLE_ST
		TraceAI ("ATTACK_ST -> IDLE_ST")
		return
	end

-- 主人を見失ったか?
	local max_dis = 10 -- 主人との最大距離
	local dis = GetDistanceFromOwner(MyID)
	if (dis > max_dis) then	-- 主人から離れた?
		MoveToOwner (MyID) -- 主人のそばへ
		MyState = FOLLOW_ST -- 追尾状態へ
		MyEnemy = 0 -- 敵を初期化
		TraceAI ("CHASE_ST -> FOLLOW_ST : MASTER_OUTSIGHT_IN")
		return
	end
		
	if (false == IsInAttackSight(MyID,MyEnemy)) then  -- ENEMY_OUTATTACKSIGHT_IN
		MyState = CHASE_ST
		MyDestX, MyDestY = GetV (V_POSITION,MyEnemy);
		Move (MyID,MyDestX,MyDestY)
		TraceAI ("ATTACK_ST -> CHASE_ST  : ENEMY_OUTATTACKSIGHT_IN")
		return
	end
	
	if (MySkill == 0) then
		local my_sp
		local ememy_hp
		my_sp = GetV (V_SP,MyID)/GetV (V_MAXSP,MyID)
		ememy_hp = GetV (V_HP,MyEnemy)/GetV (V_MAXHP,MyEnemy)
		if (my_sp > 0.5 and ememy_hp > 0.5 and MySkillMode == MODE_SKILL) then 
-- SP50%・HP50%でスキル使用可能モード
			SkillObject (MyID,5,S_MOONLIT,MyEnemy)
		else
			Attack (MyID,MyEnemy)
		end
	else
		SkillObject (MyID,MySkillLevel,MySkill,MyEnemy)
		MySkill = 0
	end
	TraceAI ("ATTACK_ST -> ATTACK_ST  : ENERGY_RECHARGED_IN")
	return


end




function	OnMOVE_CMD_ST ()

	TraceAI ("OnMOVE_CMD_ST")

	local x, y = GetV (V_POSITION,MyID)
	if (x == MyDestX and y == MyDestY) then				-- DESTINATION_ARRIVED_IN
		MyState = IDLE_ST
	end
end




function OnSTOP_CMD_ST ()


end




function OnATTACK_OBJECT_CMD_ST ()

	

end




function OnATTACK_AREA_CMD_ST ()

	TraceAI ("OnATTACK_AREA_CMD_ST")

	local	object = GetOwnerEnemy (MyID)
	if (object == 0) then							
		object = GetMyEnemy (MyID) 
	end

	if (object ~= 0) then							-- MYOWNER_ATTACKED_IN or ATTACKED_IN
		MyState = CHASE_ST
		MyEnemy = object
		return
	end

	local x , y = GetV (V_POSITION,MyID)
	if (x == MyDestX and y == MyDestY) then			-- DESTARRIVED_IN
			MyState = IDLE_ST
	end

end




function OnPATROL_CMD_ST ()

	TraceAI ("OnPATROL_CMD_ST")

	local	object = GetOwnerEnemy (MyID)
	if (object == 0) then							
		object = GetMyEnemy (MyID) 
	end

	if (object ~= 0) then							-- MYOWNER_ATTACKED_IN or ATTACKED_IN
		MyState = CHASE_ST
		MyEnemy = object
		TraceAI ("PATROL_CMD_ST -> CHASE_ST : ATTACKED_IN")
		return
	end

	local x , y = GetV (V_POSITION,MyID)
	if (x == MyDestX and y == MyDestY) then			-- DESTARRIVED_IN
		MyDestX = MyPatrolX
		MyDestY = MyPatrolY
		MyPatrolX = x
		MyPatrolY = y
		Move (MyID,MyDestX,MyDestY)
	end

end




function OnHOLD_CMD_ST ()

	TraceAI ("OnHOLD_CMD_ST")
	
	if (MyEnemy ~= 0) then
		local d = GetDistance(MyEnemy,MyID)
		if (d ~= -1 and d <= GetV(V_ATTACKRANGE,MyID)) then
				Attack (MyID,MyEnemy)
		else
			MyEnemy = 0;
		end
		return
	end


	local	object = GetOwnerEnemy (MyID)
	if (object == 0) then							
		object = GetMyEnemy (MyID)
		if (object == 0) then						
			return
		end
	end

	MyEnemy = object

end




function OnSKILL_OBJECT_CMD_ST ()
	
end




function OnSKILL_AREA_CMD_ST ()

	TraceAI ("OnSKILL_AREA_CMD_ST")

	local x , y = GetV (V_POSITION,MyID)
	if (GetDistance(x,y,MyDestX,MyDestY) <= GetV(V_SKILLATTACKRANGE,MyID,MySkill)) then	-- DESTARRIVED_IN
		SkillGround (MyID,MySkillLevel,MySkill,MyDestX,MyDestY)
		MyState = IDLE_ST
		MySkill = 0
	end

end







function OnFOLLOW_CMD_ST ()

	TraceAI ("OnFOLLOW_CMD_ST")

	local ownerX, ownerY, myX, myY
	ownerX, ownerY = GetV (V_POSITION,GetV(V_OWNER,MyID)) -- 持ち主
	myX, myY = GetV (V_POSITION,MyID)					  -- 自分 
	
	local d = GetDistance (ownerX,ownerY,myX,myY)

	if ( d <= 3) then									-- 3セル以下の距離なら
		return 
	end

	local motion = GetV (V_MOTION,MyID)
	if (motion == MOTION_MOVE) then					-- 移動中
		d = GetDistance (ownerX, ownerY, MyDestX, MyDestY)
		if ( d > 3) then									-- 4セル以上の距離なら
			MoveToOwner (MyID)
			MyDestX = ownerX
			MyDestY = ownerY
			return
		end
	else									-- 他の動作 
		MoveToOwner (MyID)
		MyDestX = ownerX
		MyDestY = ownerY
		return
	end
	
end








function	GetOwnerEnemy (myid)
	local result = 0
	local owner  = GetV (V_OWNER,myid)
	local actors = GetActors ()
	local enemys = {}
	local index = 1
	local target
	for i,v in ipairs(actors) do
		if (v ~= owner and v ~= myid) then
			target = GetV (V_TARGET,v)
			if (target == owner) then
				if (IsMonster(v) == 1) then
					enemys[index] = v
					index = index+1
				else
					local motion = GetV(V_MOTION,i)
					if (motion == MOTION_ATTACK or motion == MOTION_ATTACK2) then
						enemys[index] = v
						index = index+1
					end
				end
			end
		end
	end

	local min_dis = 100
	local dis
	for i,v in ipairs(enemys) do
		dis = GetDistance2 (myid,v)
		if (dis < min_dis) then
			result = v
			min_dis = dis
		end
	end
	
	return result
end





function	GetMyEnemy (myid)
	local result = 0
	local owner  = GetV (V_OWNER,myid)
	local actors = GetActors ()
	local enemys = {}
	local index = 1
	local target
	local get_flg = 0 -- 敵発見フラグ

-- 自分を攻撃する敵を探す
	for i,v in ipairs(actors) do
		if (v ~= owner and v ~= myid) then
			target = GetV (V_TARGET,v)
			if (target == myid) then
				enemys[index] = v
				index = index+1
				get_flg = 1 -- 敵発見
			end
		end
	end

-- 他の敵を探す
	if (get_flg == 0 and MyActiveMode == MODE_ACTIVE) then
	-- 自分を攻撃する敵がいない? & 自分が先攻モード?
		local max_dis = 10 -- 索敵範囲
		local target_dis
		for i,v in ipairs(actors) do -- 敵を探す
			if (v ~= owner and v ~= myid) then
				target_dis = GetDistance2 (myid,v) -- 距離計算
				if (target_dis < max_dis) then -- 近いところにいる?
					if (1 == IsMonster(v))	then
						target = GetV (V_TARGET,v) -- 対象のターゲット確認
						if (target == 0) then -- ターゲットがいない
							enemys[index] = v
							index = index+1
							get_flg = 1
						end
					end
				end
			end
		end
	end

-- 一番近い敵を探す
	if (get_flg == 1) then -- 敵がいる
		local min_dis = 100
		local dis
		for i,v in ipairs(enemys) do
			dis = GetDistance2 (myid,v)
			if (dis < min_dis) then
				result = v
				min_dis = dis
			end
		end
	end

	return result
end



function CheckMoveCommand()

	local ownerX, ownerY
	ownerX, ownerY = GetV (V_POSITION,GetV(V_OWNER,MyID)) -- 主人の座標

-- 先攻・非先攻切り替え
	if (MyDestY == ownerY) then
		if (ActiveModeCounter == 0 and MyDestX == ownerX+1) then -- 右
			ActiveModeCounter = 1
		elseif (ActiveModeCounter == 1 and MyDestX == ownerX-1) then -- 左
			ActiveModeCounter = 2
		elseif (ActiveModeCounter == 2 and MyDestX == ownerX+1) then -- 右
			ActiveModeCounter = 0
			if (MyActiveMode == MODE_NONACTIVE) then -- 先攻切り替え
				MyActiveMode = MODE_ACTIVE
			else
				MyActiveMode = MODE_NONACTIVE
			end
		else
			ActiveModeCounter = 0
		end
	else
		ActiveModeCounter = 0
	end


-- スキル使用切り替え
	if (MyDestX == ownerX) then
		if (SkillModeCounter == 0 and MyDestY == ownerY+1) then -- 上
			SkillModeCounter = 1
		elseif (SkillModeCounter == 1 and MyDestY == ownerY-1) then -- 下
			SkillModeCounter = 2
		elseif (SkillModeCounter == 2 and MyDestY == ownerY+1) then -- 上
			SkillModeCounter = 0
			if (MySkillMode == MODE_NONSKILL) then -- スキル切り替え
				MySkillMode = MODE_SKILL
			else
				MySkillMode = MODE_NONSKILL
			end
		else
			SkillModeCounter = 0
		end
	else
		SkillModeCounter = 0
	end

end




function AI(myid)

	MyID = myid
	local msg	= GetMsg (myid)			-- command
	local rmsg	= GetResMsg (myid)		-- reserved command

	
	if msg[1] == NONE_CMD then
		if rmsg[1] ~= NONE_CMD then
			if List.size(ResCmdList) < 10 then
				List.pushright (ResCmdList,rmsg) -- 予約コマンド保存
			end
		end
	else
		List.clear (ResCmdList)	-- 新しいコマンドが入力されたら、予約コマンドは削除する
		ProcessCommand (msg)	-- コマンド処理 
	end

		
	-- 状態処理 
 	if (MyState == IDLE_ST) then
		OnIDLE_ST ()
	elseif (MyState == CHASE_ST) then					
		OnCHASE_ST ()
	elseif (MyState == ATTACK_ST) then
		OnATTACK_ST ()
	elseif (MyState == FOLLOW_ST) then
		OnFOLLOW_ST ()
	elseif (MyState == MOVE_CMD_ST) then
		OnMOVE_CMD_ST ()
	elseif (MyState == STOP_CMD_ST) then
		OnSTOP_CMD_ST ()
	elseif (MyState == ATTACK_OBJECT_CMD_ST) then
		OnATTACK_OBJECT_CMD_ST ()
	elseif (MyState == ATTACK_AREA_CMD_ST) then
		OnATTACK_AREA_CMD_ST ()
	elseif (MyState == PATROL_CMD_ST) then
		OnPATROL_CMD_ST ()
	elseif (MyState == HOLD_CMD_ST) then
		OnHOLD_CMD_ST ()
	elseif (MyState == SKILL_OBJECT_CMD_ST) then
		OnSKILL_OBJECT_CMD_ST ()
	elseif (MyState == SKILL_AREA_CMD_ST) then
		OnSKILL_AREA_CMD_ST ()
	elseif (MyState == FOLLOW_CMD_ST) then
		OnFOLLOW_CMD_ST ()
	end

end