[HELP] FireCircle Delay

08/04/2022 11:42 Soulfly25#1
Hello,

Quick question.

How can you make the FireCircle skill make a delay either 1-3 seconds for the small circle effect (impulse05) to before it fadeaway the FireCircle will launch?

My Firecircle was when I hit rightclick the ImpulseEffect and the WholeCircle Effect are in the same launch and take damage to the target.

I want to make first a delay on the first effect and then after that it will launch the second effect where it target the monster.
08/04/2022 19:50 pintinho12#2
Your system doesn't handle intone skills. You must have an intone timer which will be started when using an intone skill.

If it's an intone skill you save it's ID for later use (or the object w/e) and then the threading system will call the skill usage after the intone timer expires. Remember that intone skills are canceled when the user (moves or attacks (or user is hit by crowd control skills) depending on the skill, Fire Circle allows you to move while casting, Intensify do not).
08/04/2022 20:11 Soulfly25#3
Quote:
Originally Posted by pintinho12 View Post
Your system doesn't handle intone skills. You must have an intone timer which will be started when using an intone skill.

If it's an intone skill you save it's ID for later use (or the object w/e) and then the threading system will call the skill usage after the intone timer expires. Remember that intone skills are canceled when the user (moves or attacks (or user is hit by crowd control skills) depending on the skill, Fire Circle allows you to move while casting, Intensify do not).

Thanks for the quick respond. Do you have any sample code to give me a reference?
08/05/2022 01:04 pintinho12#4
XD good luck

Code:
public sealed partial class MagicData
{
	private const int MAX_TARGET_NUM = 25;
	private const int TC_PK_ARENA_ID = 1005;

	private readonly TimeOutMS mDelay = new(MAGIC_DELAY);
	private readonly TimeOutMS mIntone = new();
	private readonly TimeOutMS mMagicDelay = new(MAGIC_DELAY);

	private ushort mTypeMagic;

	private Point mTargetPos;
	private uint mIdTarget;

	private bool mAutoAttack;
	private int mAutoAttackNum;

	public MagicState State { get; private set; } = MagicState.None;

	public async Task<(bool Success, ushort X, ushort Y)> CheckConditionAsync(Magic magic, uint idTarget, ushort x,
		ushort y)
	{
		int delay = mOwner.Map.IsTrainingMap()
						? MAGIC_DELAY
						: MAGIC_DELAY - magic.Level * MAGIC_DECDELAY_PER_LEVEL;
		if (!mMagicDelay.IsTimeOut(delay) &&
			magic.Sort != MagicSort.Collide)
			return (false, x, y);

		if (!magic.IsReady())
			return (false, x, y);

		if (mOwner.Map.IsLineSkillMap() && magic.Sort != MagicSort.Line)
			return (false, x, y);

		if (!((magic.AutoActive & 1) == 1
			  || (magic.AutoActive & 4) == 4) && magic.Type != 6001)
			if (!await Kernel.ChanceCalcAsync(magic.Percent))
				return (false, x, y);

		GameMap map = mOwner.Map;
		var user = mOwner as Character;
		Role role = null;
		if (user != null && user.Map.QueryRegion(RegionTypes.PkProtected, user.MapX, user.MapY))
		{
			if (magic.Ground > 0)
			{
				if (magic.Crime > 0)
					return (false, x, y);
			}
			else
			{
				role = map.QueryAroundRole(user, idTarget);
				if (role is Character && magic.Crime > 0)
					return (false, x, y);
			}
		}

		if (map.IsLineSkillMap() && magic.Sort != MagicSort.Line)
			return (false, x, y);

		if (user?.CurrentEvent != null && !user.CurrentEvent.IsAttackEnable(user) &&
			magic.Sort != MagicSort.Attachstatus)
			return (false, x, y);

		if (!map.IsTrainingMap() && user != null)
		{
			if (user.Mana < magic.UseMana)
				return (false, x, y);
			if (user.Energy < magic.UseStamina)
				return (false, x, y);

			if (magic.UseItem > 0 && user.CheckWeaponSubType(magic.UseItem, magic.UseItemNum))
				return (false, x, y);
		}

		if (magic.UseXp == BattleSystem.MagicType.Normal)
		{
			IStatus pStatus = mOwner.QueryStatus(StatusSet.START_XP);
			if (pStatus == null && magic.Status != StatusSet.VORTEX)
				return (false, x, y);
		}

		if (magic.WeaponSubtype > 0 && user != null)
		{
			if (!user.CheckWeaponSubType(magic.WeaponSubtype))
				return (false, x, y);

			if (magic.Type == TWOFOLDBLADES_ID
				&& user.UserPackage[Item.ItemPosition.RightHand].GetItemSubType() !=
				user.UserPackage[Item.ItemPosition.LeftHand]?.GetItemSubType())
				return (false, x, y);
		}

		if (user != null && user.TransformationMesh != 0)
			return (false, x, y);

		if (mOwner.IsWing && magic.Sort == MagicSort.Transform)
			return (false, x, y);

		if (map.IsWingDisable() && magic.Sort == MagicSort.Attachstatus && magic.Status == StatusSet.FLY)
			return (false, x, y);

		if (magic.Ground == 0 && magic.Sort != MagicSort.Groundsting
							  && magic.Sort != MagicSort.Vortex
							  && magic.Sort != MagicSort.Dashwhirl
							  && magic.Sort != MagicSort.Dashdeadmark
							  && magic.Sort != MagicSort.Mountwhirl)
		{
			role = map.QueryAroundRole(mOwner, idTarget);
			if (role == null)
				return (false, x, y);

			if (!role.IsAlive
				&& magic.Sort != MagicSort.Attachstatus
				&& magic.Sort != MagicSort.Detachstatus)
				return (false, x, y);

			if (magic.Sort == MagicSort.Declife)
				if (role.Life * 100 / role.MaxLife >= 15)
					return (false, x, y);

			x = role.MapX;
			y = role.MapY;
		}

		if (HitByMagic(magic) != 0 && !HitByWeapon() && mOwner.GetDistance(x, y) > magic.Distance + 1)
			return (false, x, y);

		if (mOwner.GetDistance(x, y) > mOwner.GetAttackRange(0) + magic.Distance + 1)
			return (false, x, y);

		if (role is DynamicNpc dyna)
			if (dyna.IsGoal() && mOwner.Level < dyna.Level)
				return (false, x, y);

		return (true, x, y);
	}

	public async Task<bool> ProcessMagicAttackAsync(ushort usMagicType, uint idTarget, ushort x, ushort y,
													uint ucAutoActive = 0)
	{
		switch (State)
		{
			case MagicState.Intone:
				await AbortMagicAsync(true);
				break;
		}

		State = MagicState.None;
		mTypeMagic = usMagicType;

		if (!Magics.TryGetValue(usMagicType, out Magic magic)
			&& (ucAutoActive == 0 || (magic?.AutoActive ?? 0 & ucAutoActive) != 0))
		{
			await Log.GmLogAsync(
				"cheat", $"invalid magic type: {usMagicType}, user[{mOwner.Name}][{mOwner.Identity}]");
			return false;
		}

		if (magic == null)
			return false;

		(bool Success, ushort X, ushort Y) result = await CheckConditionAsync(magic, idTarget, x, y);
		if (!result.Success)
		{
			if (magic.Sort == MagicSort.Collide)
				await ProcessCollideFailAsync(x, y, (int) idTarget);

			await AbortMagicAsync(true);
			return false;
		}

		mIdTarget = idTarget;

		mTargetPos = new Point(x, y);

		var user = mOwner as Character;
		GameMap map = mOwner.Map;
		if (user != null && !map.IsTrainingMap() && map.Identity != TC_PK_ARENA_ID)
		{
			if (magic.UseMana > 0)
				await user.AddAttributesAsync(ClientUpdateType.Mana, magic.UseMana * -1);
			if (magic.UseStamina > 0)
				await user.AddAttributesAsync(ClientUpdateType.Stamina, magic.UseStamina * -1);
			if (magic.UseItemNum > 0)
				await user.SpendEquipItemAsync(magic.WeaponSubtype, magic.UseItemNum, true);
			if (magic.UseItemNum > 0 && user.UserPackage[Item.ItemPosition.RightHand]?.IsBow() == true)
				await user.SpendEquipItemAsync(50, magic.UseItemNum, true);
		}

		if (magic.UseXp == BattleSystem.MagicType.Normal && user != null)
		{
			if (magic.Status == StatusSet.VORTEX && mOwner.QueryStatus(StatusSet.VORTEX) != null)
			{
			}
			else
			{
				IStatus pStatus = mOwner.QueryStatus(StatusSet.START_XP);
				if (pStatus == null)
				{
					await AbortMagicAsync(true);
					return false;
				}

				await user.DetachStatusAsync(StatusSet.START_XP);
				await user.ClsXpValAsync();
			}
		}

		if (!IsWeaponMagic(magic.Type))
			await mOwner.BroadcastRoomMsgAsync(new MsgInteract
			{
				Action = MsgInteractType.MagicAttack,
				TargetIdentity = idTarget,
				SenderIdentity = mOwner.Identity,
				PosX = mOwner.MapX,
				PosY = mOwner.MapY
			}, true);

		mTypeMagic = magic.Type; // for auto attack!

		if (magic.UseMana != 0)
		{
			if (!map.IsTrainingMap() && user != null)
				await user.DecEquipmentDurabilityAsync(false, (int) HitByMagic(magic), (ushort) magic.UseItemNum);

			if (await Kernel.ChanceCalcAsync(7) && user != null)
				await user.SendGemEffectAsync();
		}

		mMagicDelay.Update();
		await mOwner.ProcessOnAttackAsync();

		if (magic.IntoneSpeed <= 0)
		{
			if (!await LaunchAsync(magic)) // pode ocorrer caso o monstro desapareça, morra antes da hora
			{
				ResetDelay();
			}
			else
			{
				if (mOwner.Map.IsTrainingMap() || IsAutoAttack())
				{
					SetAutoAttack(magic.Type);
					mDelay.Startup(Math.Max(MAGIC_DELAY, magic.DelayMs));
					State = MagicState.Delay;
					return true;
				}

				State = MagicState.None;
			}
		}
		else
		{
			State = MagicState.Intone;
			mIntone.Startup((int) magic.IntoneSpeed);
		}

		return true;
	}

	#region Processing

	private async Task<bool> LaunchAsync(Magic magic)
	{
		var result = false;
		try
		{
			if (magic == null)
				return false;

			if (!mOwner.IsAlive)
				return false;

			magic.Use();

			switch (magic.Sort)
			{
				case MagicSort.Attack:
					result = await ProcessAttackAsync(magic);
					break;
				case MagicSort.Recruit:
					result = await ProcessRecruitAsync(magic);
					break;
				case MagicSort.Fan:
					result = await ProcessFanAsync(magic);
					break;
				case MagicSort.Bomb:
					result = await ProcessBombAsync(magic);
					break;
				case MagicSort.Attachstatus:
					result = await ProcessAttachAsync(magic);
					break;
				case MagicSort.Detachstatus:
					result = await ProcessDetachAsync(magic);
					break;
				case MagicSort.Dispatchxp:
					result = await ProcessDispatchXpAsync(magic);
					break;
				case MagicSort.Line:
					result = await ProcessLineAsync(magic);
					break;
				case MagicSort.Atkstatus:
					result = await ProcessAttackStatusAsync(magic);
					break;
				case MagicSort.Transform:
					result = await ProcessTransformAsync(magic);
					break;
				case MagicSort.Addmana:
					result = await ProcessAddManaAsync(magic);
					break;
				case MagicSort.Callpet:
					result = await ProcessCallPetAsync(magic);
					break;
				case MagicSort.Groundsting:
					result = await ProcessGroundStingAsync(magic);
					break;
				case MagicSort.Vortex:
					result = await ProcessVortexAsync(magic);
					break;
				case MagicSort.Activateswitch:
					result = await ProcessActivateSwitchAsync(magic);
					break;
				case MagicSort.Spook:
					result = await ProcessSpookAsync(magic);
					break;
				case MagicSort.Warcry:
					result = await ProcessWarCryAsync(magic);
					break;
				case MagicSort.Riding:
					result = await ProcessRidingAsync(magic);
					break;

				default:
					await Log.WriteLogAsync(LogLevel.Warning,
											$"MagicProcessing::LaunchAsync {magic.Sort} not handled!!!");
					result = true;
					break;
			}
		}
		catch (Exception ex)
		{
			await Log.WriteLogAsync(LogLevel.Error, "Error ocurred on MagicProcessing::LaunchAsync");
			await Log.WriteLogAsync(LogLevel.Exception, ex.ToString());
		}

		mAutoAttackNum++;
		return result;
	}

	#endregion

	#region Magic Processing Manage

	private void ResetDelay()
	{
		if (!Magics.TryGetValue(mTypeMagic, out Magic magic))
			return;
		State = MagicState.Delay;
		mDelay.Update();
		magic.SetDelay();
	}

	private void SetAutoAttack(ushort type)
	{
		mTypeMagic = type;
		mAutoAttack = true;
	}

	private void BreakAutoAttack()
	{
		mTypeMagic = 0;
		mAutoAttack = false;
	}

	public bool IsAutoAttack()
	{
		return mAutoAttack && mTypeMagic != 0;
	}

	#endregion

	#region Abort Magic

	public async Task<bool> AbortMagicAsync(bool bSynchro)
	{
		BreakAutoAttack();

		if (State == MagicState.Intone) mIntone.Clear();

		State = MagicState.None;

		if (bSynchro && mOwner is Character)
			await mOwner.SendAsync(new MsgAction
			{
				Identity = mOwner.Identity,
				Action = MsgAction<Client>.ActionType.AbortMagic
			});

		return true;
	}

	#endregion

	#region On Timer

	public async Task OnTimerAsync()
	{
		if (!Magics.TryGetValue(mTypeMagic, out Magic magic))
		{
			State = MagicState.None;
			return;
		}

		switch (State)
		{
			case MagicState.Intone: // intone
			{
				if (mIntone != null && !mIntone.IsTimeOut())
					return;

				if (mIntone != null && mIntone.IsTimeOut() && !await LaunchAsync(magic)) ResetDelay();

				State = MagicState.None;

				if (IsAutoAttack())
				{
					State = MagicState.Delay;
					mDelay.Startup(Math.Max(MAGIC_DELAY, magic.DelayMs));
				}

				break;
			}

			case MagicState.Delay: // delay
			{
				if ((mOwner.Map.IsTrainingMap() || IsAutoAttack())
					&& mDelay.IsActive()
					&& magic.Sort != MagicSort.Atkstatus)
				{
					if (mDelay.IsTimeOut())
					{
						State = MagicState.None;
						if (!await mOwner.ProcessMagicAttackAsync(magic.Type, mIdTarget, (ushort) mTargetPos.X,
																  (ushort) mTargetPos.Y))
							State = MagicState.Delay;
					}

					return;
				}

				if (!mDelay.IsActive())
				{
					State = MagicState.None;
					await AbortMagicAsync(true);
					return;
				}

				if (mAutoAttack && mDelay.IsTimeOut())
				{
					if (mDelay.IsActive() && !mDelay.TimeOver())
						return;

					State = MagicState.None;
					await mOwner.ProcessMagicAttackAsync(magic.Type, mIdTarget, (ushort) mTargetPos.X,
														 (ushort) mTargetPos.Y);

					if (mIdTarget != 0 && mOwner.Map.QueryAroundRole(mOwner, mIdTarget)?.IsPlayer() == true)
						await AbortMagicAsync(false);
				}

				if (mDelay.IsActive() && mDelay.TimeOver())
				{
					State = MagicState.None;
					await AbortMagicAsync(false);
				}

				break;
			}
		}
	}

	#endregion

	public enum MagicState
	{
		None = 0,
		Intone = 1,
		Delay = 2
	}
}
08/06/2022 17:42 Soulfly25#5
Seems like I can't make it work and I don't know where to start. Just a correction.

What column line should IntoneSpeed placed in magictype.dat? Cpuld it be the column line 11?

Quote:
Originally Posted by pintinho12 View Post
Your system doesn't handle intone skills. You must have an intone timer which will be started when using an intone skill.

If it's an intone skill you save it's ID for later use (or the object w/e) and then the threading system will call the skill usage after the intone timer expires. Remember that intone skills are canceled when the user (moves or attacks (or user is hit by crowd control skills) depending on the skill, Fire Circle allows you to move while casting, Intensify do not).


Thank you bro. It's working now I just add Task.Delay(spell.IntoneSpeed).Wait(); this Task Delay before the MagicProcess :D If you have a good advice am I doing right?