#region Copyright & License Information /* * Copyright 2007-2015 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, * see COPYING. */ #endregion using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using OpenRA.Activities; using OpenRA.Support; namespace OpenRA.Traits { public static class Util { public static int TickFacing(int facing, int desiredFacing, int rot) { var leftTurn = (facing - desiredFacing) & 0xFF; var rightTurn = (desiredFacing - facing) & 0xFF; if (Math.Min(leftTurn, rightTurn) < rot) return desiredFacing & 0xFF; else if (rightTurn < leftTurn) return (facing + rot) & 0xFF; else return (facing - rot) & 0xFF; } public static int GetNearestFacing(int facing, int desiredFacing) { var turn = desiredFacing - facing; if (turn > 128) turn -= 256; if (turn < -128) turn += 256; return facing + turn; } public static int QuantizeFacing(int facing, int numFrames) { var step = 256 / numFrames; var a = (facing + step / 2) & 0xff; return a / step; } public static WPos BetweenCells(World w, CPos from, CPos to) { return WPos.Lerp(w.Map.CenterOfCell(from), w.Map.CenterOfCell(to), 1, 2); } public static Activity SequenceActivities(params Activity[] acts) { return acts.Reverse().Aggregate( (next, a) => { a.Queue(next); return a; }); } public static Activity RunActivity(Actor self, Activity act) { // PERF: If there are no activities we can bail straight away and save ourselves a call to // Stopwatch.GetTimestamp. if (act == null) return act; // PERF: This is a hot path and must run with minimal added overhead. // Calling Stopwatch.GetTimestamp is a bit expensive, so we enumerate manually to allow us to call it only // once per iteration in the normal case. // See also: DoTimed var longTickThresholdInStopwatchTicks = PerfTimer.LongTickThresholdInStopwatchTicks; var start = Stopwatch.GetTimestamp(); while (act != null) { var prev = act; act = act.Tick(self); var current = Stopwatch.GetTimestamp(); if (current - start > longTickThresholdInStopwatchTicks) { PerfTimer.LogLongTick(start, current, "Activity", prev); start = Stopwatch.GetTimestamp(); } else start = current; if (prev == act) break; } return act; } /* pretty crap */ public static IEnumerable Shuffle(this IEnumerable ts, MersenneTwister random) { var items = ts.ToList(); while (items.Count > 0) { var t = items.Random(random); yield return t; items.Remove(t); } } static IEnumerable Neighbours(CPos c, bool allowDiagonal) { yield return c; yield return new CPos(c.X - 1, c.Y); yield return new CPos(c.X + 1, c.Y); yield return new CPos(c.X, c.Y - 1); yield return new CPos(c.X, c.Y + 1); if (allowDiagonal) { yield return new CPos(c.X - 1, c.Y - 1); yield return new CPos(c.X + 1, c.Y - 1); yield return new CPos(c.X - 1, c.Y + 1); yield return new CPos(c.X + 1, c.Y + 1); } } public static IEnumerable ExpandFootprint(IEnumerable cells, bool allowDiagonal) { return cells.SelectMany(c => Neighbours(c, allowDiagonal)).Distinct(); } public static IEnumerable AdjacentCells(World w, Target target) { var cells = target.Positions.Select(p => w.Map.CellContaining(p)).Distinct(); return ExpandFootprint(cells, true); } public static int ApplyPercentageModifiers(int number, IEnumerable percentages) { // See the comments of PR#6079 for a faster algorithm if this becomes a performance bottleneck var a = (decimal)number; foreach (var p in percentages) a *= p / 100m; return (int)a; } public static IEnumerable RandomWalk(CPos p, MersenneTwister r) { for (;;) { var dx = r.Next(-1, 2); var dy = r.Next(-1, 2); if (dx == 0 && dy == 0) continue; p += new CVec(dx, dy); yield return p; } } } }