public FP.Vector vecStart; // if rotation is later implemented, can store it in z value #endregion Fields #region Constructors public Move(long timeStartVal, long timeEndVal, FP.Vector vecStartVal, FP.Vector vecEndVal) { timeStart = timeStartVal; timeEnd = timeEndVal; vecStart = vecStartVal; vecEnd = vecEndVal; }
public FP.Vector pos; // where to move to #endregion Fields #region Constructors public MoveCmdEvt(long timeVal, long timeCmdVal, Dictionary<int, int[]> pathsVal, FP.Vector posVal, Formation formationVal, bool autoTimeTravelVal) : base(timeVal, timeCmdVal, pathsVal) { pos = posVal; formation = formationVal; autoTimeTravel = autoTimeTravelVal; }
public MakeUnitCmdEvt(long timeVal, long timeCmdVal, Dictionary<int, int[]> pathsVal, int typeVal, FP.Vector posVal, bool autoTimeTravelVal, bool autoRepeatVal = false) : base(timeVal, timeCmdVal, pathsVal) { type = typeVal; pos = posVal; autoTimeTravel = autoTimeTravelVal; autoRepeat = autoRepeatVal; }
/// <summary> /// returns where new unit of specified type can move out of the way after specified path makes it /// </summary> /// <remarks>chooses a random location between makeUnitMinDist and makeUnitMaxDist away from path</remarks> private FP.Vector makeUnitMovePos(long time, Path path, UnitType type) { FP.Vector ret; do { ret = new FP.Vector((long)((UnityEngine.Random.value - 0.5) * type.makeUnitMaxDist * 2), (long)((UnityEngine.Random.value - 0.5) * type.makeUnitMaxDist * 2)); } while (ret.lengthSq() < type.makeUnitMinDist * type.makeUnitMinDist || ret.lengthSq() > type.makeUnitMaxDist * type.makeUnitMaxDist); return ret + path.posWhen(time); }
/// <summary> /// returns where new path with specified units can move out of the way after specified path makes it /// </summary> /// <remarks>chooses a random location between makePathMinDist() and makePathMaxDist() away from path</remarks> private FP.Vector makePathMovePos(long time, Path path, List<Unit> units) { long makePathMinDist = path.makePathMinDist (time, units); long makePathMaxDist = path.makePathMaxDist (time, units); FP.Vector ret; do { ret = new FP.Vector((long)((UnityEngine.Random.value - 0.5) * makePathMaxDist * 2), (long)((UnityEngine.Random.value - 0.5) * makePathMaxDist * 2)); } while (ret.lengthSq() < makePathMinDist * makePathMinDist || ret.lengthSq() > makePathMaxDist * makePathMaxDist); return ret + path.posWhen(time); }
public override void apply(Sim g) { Dictionary<Path, List<Unit>> exPaths = existingPaths (g); List<List<Path>> formationOrder = new List<List<Path>>(); List<int> nSeeUnits = new List<int>(); FP.Vector goal, rows = new FP.Vector(), offset = new FP.Vector(); long spacing = 0; // order paths in formation foreach (KeyValuePair<Path, List<Unit>> path in exPaths) { if (path.Key.canMove(timeCmd, path.Value)) { int i; for (i = 0; i < formationOrder.Count; i++) { if (path.Key.moves.Last ().vecEnd.x == formationOrder[i][0].moves.Last ().vecEnd.x && path.Key.moves.Last ().vecEnd.y == formationOrder[i][0].moves.Last ().vecEnd.y) { SimEvt evt = g.events.FindLast (e => e is StackEvt && (e as StackEvt).paths.Contains (path.Key) && formationOrder[i].Find (p => (e as StackEvt).paths.Contains (p)) != null); if (evt != null) { // path is trying to stack onto another commanded path formationOrder[i].Add (path.Key); nSeeUnits[i] = (evt as StackEvt).nSeeUnits; break; } } } if (i == formationOrder.Count) { // path isn't trying to stack onto another commanded path formationOrder.Add (new List<Path> { path.Key }); nSeeUnits.Add (int.MaxValue); } if (formation == Formation.Tight) { // calculate spacing for tight formation foreach (Unit unit in path.Value) { if (unit.type.tightFormationSpacing > spacing) spacing = unit.type.tightFormationSpacing; } } } } if (formationOrder.Count == 0) return; // calculate spacing // (if tight formation, then spacing was already calculated above) // ISSUE #28: loose formation should be triangular if (formation == Formation.Loose) { spacing = FP.mul(g.visRadius, FP.sqrt2) >> FP.precision << FP.precision; } else if (formation == Formation.Ring) { spacing = (g.visRadius * 2 >> FP.precision) - 1 << FP.precision; } if (formation == Formation.Tight || formation == Formation.Loose) { rows.x = FP.sqrt(formationOrder.Count); rows.y = (formationOrder.Count - 1) / rows.x + 1; offset = (rows - new FP.Vector(1, 1)) * spacing / 2; } else if (formation == Formation.Ring) { offset.x = (formationOrder.Count == 1) ? 0 : FP.div(spacing / 2, FP.sin(FP.pi / formationOrder.Count)); offset.y = offset.x; } else { throw new NotImplementedException("requested formation is not implemented"); } if (pos.x < Math.Min(offset.x, g.mapSize / 2)) pos.x = Math.Min(offset.x, g.mapSize / 2); if (pos.x > g.mapSize - Math.Min(offset.x, g.mapSize / 2)) pos.x = g.mapSize - Math.Min(offset.x, g.mapSize / 2); if (pos.y < Math.Min(offset.y, g.mapSize / 2)) pos.y = Math.Min(offset.y, g.mapSize / 2); if (pos.y > g.mapSize - Math.Min(offset.y, g.mapSize / 2)) pos.y = g.mapSize - Math.Min(offset.y, g.mapSize / 2); // move units for (int i = 0; i < formationOrder.Count; i++) { List<Path> movedPaths = new List<Path>(); foreach (Path path in formationOrder[i]) { if (formation == Formation.Tight || formation == Formation.Loose) { goal = pos + new FP.Vector((i % rows.x) * spacing - offset.x, i / rows.x * spacing - offset.y); } else if (formation == Formation.Ring) { goal = pos + offset.x * new FP.Vector(FP.cos(2 * FP.pi * i / formationOrder.Count), FP.sin(2 * FP.pi * i / formationOrder.Count)); } else { throw new NotImplementedException("requested formation is not implemented"); } movedPaths.Add (path.moveTo(timeCmd, new List<Unit>(exPaths[path]), goal, autoTimeTravel)); } g.addStackEvts (movedPaths, nSeeUnits[i]); } }
public void exclusiveAdd(Player player, long time) { if (exclusiveLatest (player)) throw new InvalidOperationException("tile is already exclusive"); exclusive[player.id].Add (time); // add waypoints to let units that can move to adjacent tile (using automatic time travel) move to this tile for (int tX = Math.Max (0, x - 1); tX <= Math.Min (g.tileLen () - 1, x + 1); tX++) { for (int tY = Math.Max (0, y - 1); tY <= Math.Min (g.tileLen () - 1, y + 1); tY++) { if (tX != x || tY != y) { foreach (var waypoint in g.tiles[tX, tY].waypoints) { long halfMoveInterval = new FP.Vector(tX - x << FP.precision, tY - y << FP.precision).length() / g.units[waypoint.Key].type.speed / 2; if (player == g.units[waypoint.Key].player && Waypoint.active (waypoint.Value.Last ()) && time >= waypoint.Value.Last ().time + halfMoveInterval) { g.events.addEvt (new WaypointAddEvt(time + halfMoveInterval, g.units[waypoint.Key], this, waypoint.Value.Last (), null)); } } } } } }
/// <summary> /// returns minimum absolute position where clicking would select the path /// </summary> public FP.Vector selMinPos(long time) { FP.Vector ret = new FP.Vector(int.MaxValue, int.MaxValue); foreach (Unit unit in segmentWhen(time).units) { ret.x = Math.Min (ret.x, unit.type.selMinPos.x); ret.y = Math.Min (ret.y, unit.type.selMinPos.y); } return ret + posWhen(time); }