public void InCone() { var centerPos = new Position(0, 0); var topPos = new Position(0, 500); var bottomPos = new Position(0, -500); var leftPos = new Position(-500, 0); var rightPos = new Position(500, 0); var up = MabiMath.DegreeToRadian(90); var down = MabiMath.DegreeToRadian(270); var left = MabiMath.DegreeToRadian(180); var right = MabiMath.DegreeToRadian(0); Assert.Equal(true, topPos.InCone(centerPos, up, 600, MabiMath.DegreeToRadian(10))); Assert.Equal(true, bottomPos.InCone(centerPos, down, 600, MabiMath.DegreeToRadian(10))); Assert.Equal(true, leftPos.InCone(centerPos, left, 600, MabiMath.DegreeToRadian(10))); Assert.Equal(true, rightPos.InCone(centerPos, right, 600, MabiMath.DegreeToRadian(10))); Assert.Equal(true, topPos.InCone(centerPos, up, 500, MabiMath.DegreeToRadian(10))); Assert.Equal(false, topPos.InCone(centerPos, up, 500, MabiMath.DegreeToRadian(180))); Assert.Equal(true, topPos.InCone(centerPos, up, 600, MabiMath.DegreeToRadian(180))); Assert.Equal(true, topPos.InCone(centerPos, up, 1000, MabiMath.DegreeToRadian(180))); Assert.Equal(true, topPos.InCone(centerPos, up, 1000, MabiMath.DegreeToRadian(190))); Assert.Equal(true, topPos.InCone(centerPos, up, 1000, MabiMath.DegreeToRadian(-190))); Assert.Equal(true, topPos.InCone(centerPos, up, 1000, MabiMath.DegreeToRadian(9999))); Assert.Equal(true, topPos.InCone(centerPos, up, 1000, MabiMath.DegreeToRadian(-9999))); Assert.Equal(false, topPos.InCone(centerPos, MabiMath.DegreeToRadian(45), 2000, MabiMath.DegreeToRadian(45))); Assert.Equal(true, topPos.InCone(centerPos, MabiMath.DegreeToRadian(68), 2000, MabiMath.DegreeToRadian(45))); }
/// <summary> /// Uses skill, the actual usage is in Complete. /// </summary> /// <param name="creature"></param> /// <param name="skill"></param> /// <param name="packet"></param> public void Use(Creature creature, Skill skill, Packet packet) { var location = packet.GetLong(); var unkInt1 = packet.GetInt(); var unkInt2 = packet.GetInt(); var areaPosition = new Position(location); // Check range if (!creature.GetPosition().InRange(areaPosition, Range)) { this.Cancel(creature, skill); Send.SkillUseSilentCancel(creature); Send.Notice(creature, Localization.Get("Out of range.")); return; } // Reduce Dice if (creature.Inventory.RightHand != null) creature.Inventory.Decrement(creature.Inventory.RightHand); var number = (byte)(RandomProvider.Get().Next(6)); Send.UseMotion(creature, 27, 2, false, false); Send.Effect(creature, Effect.Dice, 0, "process", location, number); // [200200, NA233 (2016-08-12)] New 0 int after effect id Send.SkillUse(creature, skill.Info.Id, location, unkInt1, unkInt2); skill.Stacks = 0; }
/// <summary> /// Uses skill, the actual usage is in Complete. /// </summary> /// <param name="creature"></param> /// <param name="skill"></param> /// <param name="packet"></param> public void Use(Creature creature, Skill skill, Packet packet) { var location = packet.GetLong(); var unkInt1 = packet.GetInt(); var unkInt2 = packet.GetInt(); var areaPosition = new Position(location); // Check range if (!creature.GetPosition().InRange(areaPosition, Range)) { this.Cancel(creature, skill); Send.SkillUseSilentCancel(creature); Send.Notice(creature, Localization.Get("Out of range.")); return; } // Reduce Dice if (creature.Inventory.RightHand != null) creature.Inventory.Decrement(creature.Inventory.RightHand); Send.UseMotion(creature, 27, 2, false, false); Send.Effect(creature, Effect.Dice, "process", location, (byte)3); Send.SkillUse(creature, skill.Info.Id, location, unkInt1, unkInt2); skill.Stacks = 0; }
/// <summary> /// Broadcasts Running|Walking in range of creature. /// </summary> public static void Move(Creature creature, Position from, Position to, bool walking) { var packet = new Packet(!walking ? Op.Running : Op.Walking, creature.EntityId); packet.PutInt(from.X); packet.PutInt(from.Y); packet.PutInt(to.X); packet.PutInt(to.Y); creature.Region.Broadcast(packet, creature); }
/// <summary> /// Broadcasts CollectAnimation in creature's range. /// </summary> /// <param name="creature"></param> /// <param name="entityId"></param> /// <param name="collectId"></param> /// <param name="pos"></param> public static void CollectAnimation(Creature creature, long entityId, int collectId, Position pos) { var packet = new Packet(Op.CollectAnimation, creature.EntityId); packet.PutLong(entityId); packet.PutInt(collectId); packet.PutFloat(pos.X); packet.PutFloat(pos.Y); packet.PutFloat(1); creature.Region.Broadcast(packet, creature); }
public void InRange() { var pos1 = new Position(0, 0); var pos2 = new Position(0, 500); var pos4 = new Position(500, 500); var pos5 = new Position(-500, -500); Assert.Equal(true, pos1.InRange(pos2, 500)); Assert.Equal(false, pos1.InRange(pos2, 499)); Assert.Equal(true, pos4.InRange(pos5, 1415)); Assert.Equal(false, pos4.InRange(pos5, 1413)); }
public void GetDistance() { var pos1 = new Position(0, 0); var pos2 = new Position(0, 500); var pos3 = new Position(0, -500); var pos4 = new Position(500, 500); var pos5 = new Position(-500, -500); Assert.Equal(500, pos1.GetDistance(pos2)); Assert.Equal(500, pos1.GetDistance(pos3)); Assert.Equal(707, pos1.GetDistance(pos4)); Assert.Equal(707, pos1.GetDistance(pos5)); Assert.Equal(1414, pos4.GetDistance(pos5)); }
/// <summary> /// Returns true if this position is in a cone, based on the parameters. /// </summary> /// <param name="tip">Tip of the cone.</param> /// <param name="direction">Cone's direction as radian.</param> /// <param name="radius">Cone's radius.</param> /// <param name="angle">Cone's angle as radian.</param> /// <returns></returns> public bool InCone(Position tip, double direction, int radius, double angle) { // Cap angle at +180°, to prevent possibly unexpected inverting // behavior. (The cone would go into the opposite direction.) angle = Math.Min(Math.PI, Math.Abs(angle)); var halfAngle = angle / 2; var tx1 = tip.X + (Math.Cos(-halfAngle + direction) * radius); var ty1 = tip.Y + (Math.Sin(-halfAngle + direction) * radius); var tx2 = tip.X + (Math.Cos(halfAngle + direction) * radius); var ty2 = tip.Y + (Math.Sin(halfAngle + direction) * radius); var tx3 = tip.X; var ty3 = tip.Y; var px = X; var py = Y; // Check if position is inside the triangle part of the cone. // http://stackoverflow.com/questions/2049582/how-to-determine-a-point-in-a-2d-triangle var A = 1.0 / 2.0 * (-ty2 * tx3 + ty1 * (-tx2 + tx3) + tx1 * (ty2 - ty3) + tx2 * ty3); var sign = (A < 0 ? -1 : 1); var s = (ty1 * tx3 - tx1 * ty3 + (ty3 - ty1) * px + (tx1 - tx3) * py) * sign; var t = (tx1 * ty2 - ty1 * tx2 + (ty1 - ty2) * px + (tx2 - tx1) * py) * sign; var isInTriangle = (s > 0 && t > 0 && (s + t) < 2 * A * sign); if (isInTriangle) return true; // Check if position is on the triangle part's side of the cone. // If it is, we can stop, since we already checked for the // triangle. The only way the position could now be in the cone // is if it was on the circle's side. // http://stackoverflow.com/a/3461533/1171898 var isOnTriangleSide = (((tx2 - tx1) * (py - ty1) - (ty2 - ty1) * (px - tx1)) > 0); if (isOnTriangleSide) return false; // Check if position is inside the circle. var tx4 = (int)((tx1 + tx2) / 2); var ty4 = (int)((ty1 + ty2) / 2); var circleRadius = Math.Sqrt(Math.Pow(tx2 - tx1, 2) + Math.Pow(ty2 - ty1, 2)) / 2; var targetDistance = Math.Sqrt(Math.Pow(tx4 - px, 2) + Math.Pow(ty4 - py, 2)); var isInCircle = (targetDistance < circleRadius); return isInCircle; }
/// <summary> /// Starts fishing at target location. /// </summary> /// <param name="creature"></param> /// <param name="skill"></param> /// <param name="packet"></param> public void Use(Creature creature, Skill skill, Packet packet) { var targetPositionId = packet.GetLong(); var unkInt1 = packet.GetInt(); var unkInt2 = packet.GetInt(); var pos = new Position(targetPositionId); creature.Temp.FishingProp = new Prop(274, creature.RegionId, pos.X, pos.Y, 1, 1, 0, "empty"); creature.Region.AddProp(creature.Temp.FishingProp); creature.TurnTo(pos); Send.Effect(creature, Effect.Fishing, (byte)FishingEffectType.Cast, true); Send.SkillUse(creature, skill.Info.Id, targetPositionId, unkInt1, unkInt2); this.StartFishing(creature, 1000); }
/// <summary> /// Broadcasts ForceWalkTo in creature's range. /// </summary> /// <param name="creature"></param> /// <param name="to"></param> public static void ForceWalkTo(Creature creature, Position to) { var pos = creature.GetPosition(); var packet = new Packet(Op.ForceWalkTo, creature.EntityId); // From packet.PutInt(pos.X); packet.PutInt(pos.Y); // To packet.PutInt(to.X); packet.PutInt(to.Y); packet.PutByte(1); packet.PutByte(0); creature.Region.Broadcast(packet, creature); }
public void Instantiation() { var pos1 = new Position(); Assert.Equal(0, pos1.X); Assert.Equal(0, pos1.Y); var pos2 = new Position(123, 456); Assert.Equal(123, pos2.X); Assert.Equal(456, pos2.Y); var locationId = 0x3000000100020003; var pos3 = new Position(locationId); Assert.Equal(2 * 20, pos3.X); Assert.Equal(3 * 20, pos3.Y); var pos4 = new Position(pos2); Assert.Equal(123, pos4.X); Assert.Equal(456, pos4.Y); }
/// <summary> /// Returns true if this position is in a cone, based on the parameters. /// </summary> /// <param name="tip">Tip of the cone.</param> /// <param name="direction">Cone's direction as radian.</param> /// <param name="radius">Cone's radius.</param> /// <param name="angle">Cone's angle as radian.</param> /// <returns></returns> public bool InCone(Position tip, double direction, int radius, double angle) { var halfAngle = angle / 2; var tx1 = tip.X + (Math.Cos(-halfAngle + direction) * radius); var ty1 = tip.Y + (Math.Sin(-halfAngle + direction) * radius); var tx2 = tip.X + (Math.Cos(halfAngle + direction) * radius); var ty2 = tip.Y + (Math.Sin(halfAngle + direction) * radius); var tx3 = tip.X; var ty3 = tip.Y; var px = X; var py = Y; // http://stackoverflow.com/questions/2049582/how-to-determine-a-point-in-a-2d-triangle var A = 1.0 / 2.0 * (-ty2 * tx3 + ty1 * (-tx2 + tx3) + tx1 * (ty2 - ty3) + tx2 * ty3); var sign = (A < 0 ? -1 : 1); var s = (ty1 * tx3 - tx1 * ty3 + (ty3 - ty1) * px + (tx1 - tx3) * py) * sign; var t = (tx1 * ty2 - ty1 * tx2 + (ty1 - ty2) * px + (tx2 - tx1) * py) * sign; return (s > 0 && t > 0 && (s + t) < 2 * A * sign); }
public void Use(Creature creature, Skill skill, Packet packet) { var targetPos = new Position(packet.GetLong()); // Check distance to target position if (!creature.GetPosition().InRange(targetPos, (int)skill.RankData.Var2 + DistanceBuffer)) { Send.Notice(creature, Localization.Get("Out of range.")); Send.SkillUse(creature, skill.Info.Id, 0); return; } // Stop creature's movement. creature.StopMove(); // Teleport effect (does not actually teleport) Send.Effect(creature, Effect.SilentMoveTeleport, (byte)2, targetPos.X, targetPos.Y); // Teleport player to target position creature.SetPosition(targetPos.X, targetPos.Y); Send.SkillTeleport(creature, targetPos.X, targetPos.Y); Send.SkillUse(creature, skill.Info.Id, 1); }
/// <summary> /// Handles skill usage. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="packet"></param> public void Use(Creature attacker, Skill skill, Packet packet) { var targetAreaEntityId = packet.GetLong(); Send.Effect(attacker, 5, (byte)1, targetAreaEntityId); var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.Attacker, attacker, skill.Info.Id, targetAreaEntityId); aAction.Options |= AttackerOptions.Result; aAction.Stun = UseStun; cap.Add(aAction); var attackerPosition = attacker.GetPosition(); // Calculate rectangular target area var targetAreaPos = new Position(targetAreaEntityId); var poe = targetAreaPos.GetRelative(attackerPosition, -800); var r = (Math.PI / 2) + Math.Atan2(attackerPosition.Y - targetAreaPos.Y, attackerPosition.X - targetAreaPos.X); var pivot = new Point(poe.X, poe.Y); var p1 = new Point(pivot.X - LaserRectWidth / 2, pivot.Y - LaserRectHeight / 2); var p2 = new Point(pivot.X - LaserRectWidth / 2, pivot.Y + LaserRectHeight / 2); var p3 = new Point(pivot.X + LaserRectWidth / 2, pivot.Y + LaserRectHeight / 2); var p4 = new Point(pivot.X + LaserRectWidth / 2, pivot.Y - LaserRectHeight / 2); p1 = this.RotatePoint(p1, pivot, r); p2 = this.RotatePoint(p2, pivot, r); p3 = this.RotatePoint(p3, pivot, r); p4 = this.RotatePoint(p4, pivot, r); // Attack targets var targets = attacker.Region.GetCreaturesInPolygon(p1, p2, p3, p4); foreach (var target in targets.Where(cr => !cr.IsDead && !cr.Has(CreatureStates.NamedNpc))) { var targetPosition = target.GetPosition(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Options = TargetOptions.Result | TargetOptions.KnockDown; tAction.Stun = TargetStun; tAction.Delay = 1200; cap.Add(tAction); // Var2: 300/1000, based on rank. Could be damage? var damage = skill.RankData.Var2; // Increase damage CriticalHit.Handle(attacker, attacker.GetTotalCritChance(target.Protection), ref damage, tAction); // Reduce damage SkillHelper.HandleDefenseProtection(target, ref damage); ManaShield.Handle(target, ref damage, tAction); // Apply damage target.TakeDamage(tAction.Damage = 300, attacker); target.Stability = Creature.MinStability; // Aggro target.Aggro(attacker); // Check death if (target.IsDead) tAction.Options |= TargetOptions.FinishingKnockDown; // Knock back attacker.Shove(target, KnockbackDistance); } cap.Handle(); Send.SkillUse(attacker, skill.Info.Id, 0); }
/// <summary> /// Returns true if given position is on the street. /// </summary> /// <param name="pos"></param> /// <returns></returns> public bool IsOnStreet(Position pos) { var events = this.GetClientEvents(a => a.Data.Type == EventType.Street && a.IsInside(pos)); return (events.Length != 0); }
/// <summary> /// Returns new list of all creatures within range of position. /// </summary> /// <param name="pos"></param> /// <param name="range"></param> /// <returns></returns> public List<Creature> GetCreaturesInRange(Position pos, int range) { var result = new List<Creature>(); _creaturesRWLS.EnterReadLock(); try { result.AddRange(_creatures.Values.Where(a => a.GetPosition().InRange(pos, range))); } finally { _creaturesRWLS.ExitReadLock(); } return result; }
/// <summary> /// Creates new Location based on region id and position. /// </summary> /// <param name="regionId"></param> /// <param name="pos"></param> public Location(int regionId, Position pos) : this(regionId, pos.X, pos.Y) { }
/// <summary> /// Returns true if any intersections are found in range of /// the position. /// </summary> /// <remarks> /// Runs 4 intersection checks to cover 8 directions around position. /// </remarks> /// <param name="pos"></param> /// <param name="range"></param> /// <returns></returns> public bool AnyInRange(Position pos, int range) { Position intersection; return this.Find(new Position(pos.X - range, pos.Y), new Position(pos.X + range, pos.Y), out intersection) || this.Find(new Position(pos.X, pos.Y - range), new Position(pos.X, pos.Y + range), out intersection) || this.Find(new Position(pos.X - range, pos.Y - range), new Position(pos.X + range, pos.Y + range), out intersection) || this.Find(new Position(pos.X - range, pos.Y + range), new Position(pos.X + range, pos.Y - range), out intersection); }
/// <summary> /// Returns true if a campfire can be built at the given position. /// </summary> /// <param name="creature"></param> /// <param name="pos"></param> /// <returns></returns> public static bool IsValidPosition(Creature creature, Position pos) { var validPosition = true; // Collisions betwen player and position if (creature.Region.Collisions.Any(creature.GetPosition(), pos)) validPosition = false; // Too close to a creature else if (creature.Region.GetCreaturesInRange(pos, 90).Count != 0) validPosition = false; // Too close to a collision else if (creature.Region.Collisions.AnyInRange(creature.GetPosition(), 250)) validPosition = false; return validPosition; }
public Position(Position pos) { this.X = pos.X; this.Y = pos.Y; }
public LinePath(Position p1, Position p2) : this(new Point(p1.X, p1.Y), new Point(p2.X, p2.Y)) { }
/// <summary> /// Returns whether the lines x1/y1-x2/y2 and x3/y3-x4/y4 intersect. /// The intersection point is returned in the corresponding out-variable. /// </summary> private bool FindIntersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4, out Position intersection) { intersection = Position.Zero; double denom = ((x2 - x1) * (y4 - y3)) - ((y2 - y1) * (x4 - x3)); if (denom == 0) return false; // parallel double numer = ((y1 - y3) * (x4 - x3)) - ((x1 - x3) * (y4 - y3)); double r = numer / denom; double numer2 = ((y1 - y3) * (x2 - x1)) - ((x1 - x3) * (y2 - y1)); double s = numer2 / denom; if ((r < 0 || r > 1) || (s < 0 || s > 1)) return false; // nointersect double interX = x1 + (r * (x2 - x1)); double interY = y1 + (r * (y2 - y1)); intersection = new Position((int)interX, (int)interY); return true; }
/// <summary> /// Returns true if the path between from and to intersects with /// anything and returns the intersection position via out. /// </summary> /// <param name="region"></param> /// <param name="from"></param> /// <param name="to"></param> /// <param name="intersection"></param> /// <returns></returns> public bool Find(Position from, Position to, out Position intersection) { intersection = to; double x1 = from.X, y1 = from.Y; double x2 = to.X, y2 = to.Y; var intersections = new List<Position>(); // Query lines List<LinePath> lines; lock (_tree) lines = _tree.Query(new LinePath(from, to).Rect); // Get intersections foreach (var line in lines) { Position inter; if (this.FindIntersection(x1, y1, x2, y2, line.P1.X, line.P1.Y, line.P2.X, line.P2.Y, out inter)) intersections.Add(inter); } // No collisions if (intersections.Count < 1) return false; // One collision if (intersections.Count == 1) { intersection = intersections[0]; return true; } // Select nearest intersection double distance = double.MaxValue; foreach (var inter in intersections) { var interDist = Math.Pow(x1 - inter.X, 2) + Math.Pow(y1 - inter.Y, 2); if (interDist < distance) { intersection = inter; distance = interDist; } } return true; }
/// <summary> /// Returns all player creatures in range. /// </summary> /// <param name="range"></param> /// <returns></returns> public List<Creature> GetPlayersInRange(Position pos, int range = VisibleRange) { _creaturesRWLS.EnterReadLock(); try { return _creatures.Values.Where(a => a.IsPlayer && a.GetPosition().InRange(pos, range)).ToList(); } finally { _creaturesRWLS.ExitReadLock(); } }
/// <summary> /// Activates AIs in range of the movement path. /// </summary> /// <param name="from"></param> /// <param name="to"></param> public void ActivateAis(Creature creature, Position from, Position to) { // Bounding rectangle var minX = Math.Min(from.X, to.X) - VisibleRange; var minY = Math.Min(from.Y, to.Y) - VisibleRange; var maxX = Math.Max(from.X, to.X) + VisibleRange; var maxY = Math.Max(from.Y, to.Y) + VisibleRange; // Activation _creaturesRWLS.EnterReadLock(); try { foreach (NPC npc in _creatures.Values.Where(a => a.Is(EntityType.NPC))) { if (npc.AI == null) continue; var pos = npc.GetPosition(); if (!(pos.X >= minX && pos.X <= maxX && pos.Y >= minY && pos.Y <= maxY)) continue; var time = (from.GetDistance(to) / creature.GetSpeed()) * 1000; npc.AI.Activate(time); } } finally { _creaturesRWLS.ExitReadLock(); } }
/// <summary> /// Returns distance between this and another position. /// </summary> /// <param name="otherPos"></param> /// <returns></returns> public int GetDistance(Position otherPos) { return (int)Math.Sqrt(Math.Pow(X - otherPos.X, 2) + Math.Pow(Y - otherPos.Y, 2)); }
/// <summary> /// Returns position on the line between position and other. /// </summary> /// <remarks> /// When you knock someone back, he gets pushed in the opposite /// direction. The other position would be the enemy, the distance /// the amount how far to push him away. A negative distance will /// return a position between you two. /// </remarks> public Position GetRelative(Position other, int distance) { if (this == other) return this; var deltaX = (double)other.X - this.X; var deltaY = (double)other.Y - this.Y; var deltaXY = Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2)); var newX = other.X + (distance / deltaXY) * (deltaX); var newY = other.Y + (distance / deltaXY) * (deltaY); return new Position((int)newX, (int)newY); }
/// <summary> /// Returns true if the other position is within range. /// </summary> /// <param name="otherPos"></param> /// <param name="range"></param> /// <returns></returns> public bool InRange(Position otherPos, int range) { return this.InRange(otherPos.X, otherPos.Y, range); }
/// <summary> /// Completes skill, placing the campfire. /// </summary> /// <param name="creature"></param> /// <param name="skill"></param> /// <param name="packet"></param> public void Complete(Creature creature, Skill skill, Packet packet) { var positionId = packet.GetLong(); var unkInt1 = packet.GetInt(); var unkInt2 = packet.GetInt(); var propId = PropId; // Handle items if (skill.Info.Id == SkillId.Campfire) { // Check Firewood, the client should stop the player long before Complete. if (creature.Inventory.Count(creature.Temp.FirewoodItemId) < FirewoodCost) throw new ModerateViolation("Used Campfire without Firewood."); // Remove Firewood creature.Inventory.Remove(creature.Temp.FirewoodItemId, FirewoodCost); } else { // Check kit var item = creature.Inventory.GetItem(creature.Temp.CampfireKitItemEntityId); if (item == null) throw new ModerateViolation("Used CampfireKit with invalid kit."); propId = this.GetPropId(item); // Change the prop ID based on what campfire kit was used // Reduce kit creature.Inventory.Decrement(item); } // Set up Campfire var pos = new Position(positionId); var effect = (skill.Info.Rank < SkillRank.RB ? "campfire_01" : "campfire_02"); var prop = new Prop(propId, creature.RegionId, pos.X, pos.Y, MabiMath.ByteToRadian(creature.Direction), 1); // Logs prop.State = "single"; if (prop.Data.Id != HalloweenPropId) prop.Xml.SetAttributeValue("EFFECT", effect); // Fire effect prop.DisappearTime = DateTime.Now.AddMinutes(this.GetDuration(skill.Info.Rank, creature.RegionId)); // Disappear after x minutes // Temp data for Rest prop.Temp.CampfireSkillRank = skill.RankData; if (skill.Info.Id == SkillId.Campfire) prop.Temp.CampfireFirewood = AuraData.ItemDb.Find(creature.Temp.FirewoodItemId); creature.Region.AddProp(prop); // Training if (skill.Info.Rank == SkillRank.Novice) skill.Train(1); // Use Campfire. // Complete Send.SkillComplete(creature, skill.Info.Id, positionId, unkInt1, unkInt2); }
/// <summary> /// Returns direction the other position is in as radian. /// </summary> /// <param name="otherPos"></param> /// <returns></returns> public float GetDirection(Position otherPos) { var direction = Math.Atan2(otherPos.Y - Y, otherPos.X - X); return (float)direction; }