Beispiel #1
0
 internal override void AddEntry(MoveLine moveLine)
 {
     if (moveLine.Amount < 0)
     {
         Debit += -moveLine.Amount;
     }
     else
     {
         Credit += moveLine.Amount;
     }
 }
Beispiel #2
0
 /// <summary>
 /// removes units from all other paths that, if seen, could cause specified units to be removed from specified segments;
 /// returns whether successful
 /// </summary>
 public bool deleteOtherPaths(IEnumerable<SegmentUnit> segmentUnits, bool addDeleteLines, bool addKeepLines)
 {
     HashSet<SegmentUnit> ancestors = new HashSet<SegmentUnit>();
     HashSet<SegmentUnit> prev = new HashSet<SegmentUnit>();
     HashSet<SegmentUnit> liveToNonLivePrev = new HashSet<SegmentUnit>(); // live prev segments whose next ancestor is not live
     bool success = true;
     bool deleted = false;
     foreach (SegmentUnit segmentUnit in segmentUnits) {
         addAncestors (segmentUnit, ancestors, prev, liveToNonLivePrev);
     }
     foreach (SegmentUnit ancestor in prev) {
         foreach (SegmentUnit segmentUnit in ancestor.next ()) {
             if (!ancestors.Contains (segmentUnit)) {
                 success &= segmentUnit.delete (addDeleteLines);
                 deleted = true;
             }
         }
     }
     foreach (SegmentUnit ancestor in liveToNonLivePrev) {
         foreach (SegmentUnit segmentUnit in ancestor.next ()) {
             if (segmentUnit.segment.path.timeSimPast != long.MaxValue && !ancestors.Contains (segmentUnit)) {
                 success &= segmentUnit.delete (addDeleteLines);
                 deleted = true;
             }
         }
     }
     if (addKeepLines && deleted) {
         // add kept unit lines
         // TODO: tweak time if deleted in past
         MoveLine keepLine = new MoveLine(timeSim, segmentUnits.First ().unit.player);
         foreach (SegmentUnit ancestor in ancestors) {
             if (segmentUnits.Where (u => u.unit == ancestor.unit).Any ()) {
                 keepLine.vertices.AddRange (ancestor.segment.path.moveLines (ancestor.segment.timeStart,
                     (ancestor.segment.nextOnPath () == null) ? keepLine.time : ancestor.segment.nextOnPath().timeStart));
             }
         }
         keepLines.Add (keepLine);
     }
     return success;
 }
Beispiel #3
0
 /// <returns>path that was moved (might not be original path)</returns>
 public Path moveTo(long time, List<Unit> units, FP.Vector pos, bool autoTimeTravel)
 {
     Path movedPath;
     FP.Vector goalPos = pos;
     // don't move off map edge
     if (goalPos.x < 0) goalPos.x = 0;
     if (goalPos.x > g.mapSize) goalPos.x = g.mapSize;
     if (goalPos.y < 0) goalPos.y = 0;
     if (goalPos.y > g.mapSize) goalPos.y = g.mapSize;
     if (autoTimeTravel && units.Find (u => {
         Waypoint waypoint = g.tileAt (goalPos).waypointWhen (u, time);
         return !Waypoint.active (waypoint) || Move.fromSpeed (waypoint.time, speed, waypoint.tile.centerPos (), goalPos).timeEnd > time;
     }) == null) {
         // move units with automatic time travel
         List<Path> movedPaths = new List<Path>();
         long stackTime = long.MinValue;
         foreach (Unit unit in units) {
             Waypoint waypoint = g.tileAt (goalPos).waypointWhen (unit, time);
             // make moves list using waypoints
             List<Move> waypointMoves = new List<Move> {
                 Move.fromSpeed (waypoint.time, speed, waypoint.tile.centerPos (), goalPos)
             };
             while (waypoint.prev != null) {
                 waypointMoves.Insert (0, new Move(waypoint.time - (waypointMoves[0].vecStart - waypoint.prev.tile.centerPos()).length () / speed,
                     waypoint.time, waypoint.prev.tile.centerPos (), waypointMoves[0].vecStart));
                 waypoint = waypoint.prev;
             }
             waypointMoves.Insert (0, new Move(waypoint.start[0].time, waypoint.time, waypoint.start[0].path.posWhen (waypoint.start[0].time), waypointMoves[0].vecStart));
             // do path smoothing
             for (int i = 0; i < waypointMoves.Count; i++) {
                 int j;
                 for (j = i + 1; j < waypointMoves.Count; j++) {
                     Move move = Move.fromSpeed(waypointMoves[i].timeStart, speed, waypointMoves[i].vecStart, waypointMoves[j].vecEnd);
                     long timeMove = (waypointMoves[i].timeStart / g.tileInterval + 1) * g.tileInterval;
                     while (timeMove < waypointMoves[j].timeEnd && g.tileAt(move.posWhen(timeMove)).exclusiveWhen(player, timeMove)) {
                         timeMove += g.tileInterval;
                     }
                     if (timeMove < waypointMoves[j].timeEnd) break;
                 }
                 j--;
                 if (j > i) {
                     waypointMoves[i] = Move.fromSpeed(waypointMoves[i].timeStart, speed, waypointMoves[i].vecStart, waypointMoves[j].vecEnd);
                     waypointMoves.RemoveRange(i + 1, j - i);
                 }
             }
             // if unit not found on start waypoint, add it back to past segments
             for (int i = 0; i < waypoint.start.Count - 1; i++) {
                 Segment segment = waypoint.start[i + 1].path.insertSegment (waypoint.start[i].time);
                 while (segment.timeStart != waypoint.start[i + 1].time) {
                     segment = segment.prevOnPath ();
                     if (segment.units.Contains (unit)) {
                         i = waypoint.start.Count;
                         break;
                     }
                     segment.units.Add (unit);
                     segment.deletedUnits.Remove(unit);
                 }
             }
             // make non-live path moving along waypoints
             if (!waypoint.start[0].segment().path.makePath (waypointMoves[0].timeStart, new List<Unit> { unit })) {
                 throw new SystemException("make auto time travel path failed when moving units");
             }
             g.paths.Last ().moves = waypointMoves;
             g.paths.Last ().updatePast(time, false);
             // add kept unit line
             MoveLine keepLine = new MoveLine(time, player);
             keepLine.vertices.AddRange (g.paths.Last ().moveLines (waypointMoves[0].timeStart, time));
             g.keepLines.Add (keepLine);
             g.alternatePaths.Add (g.paths.Last ());
             movedPaths.Add (g.paths.Last ());
             stackTime = Math.Max (stackTime, waypointMoves.Last ().timeEnd);
         }
         player.updatePast (time);
         if (units.Count > 1) new StackEvt(stackTime, movedPaths, nSeeUnits).apply (g);
         movedPath = movedPaths.Find (p => p.segments.Last ().units.Count > 0);
     } else {
         // move units normally (without automatic time travel)
         movedPath = this; // move this path by default
         if (time < g.timeSim) {
             // move non-live path if in past
             // if this path already isn't live, a better approach might be removing later segments and moves then moving this path, like pre-stacking versions (see ISSUE #27)
             if (!makePath (time, units)) throw new SystemException("make non-live path failed when moving units");
             movedPath = g.paths.Last ();
         } else {
             foreach (Unit unit in segmentWhen(time).units) {
                 if (!units.Contains (unit)) {
                     // some units in path aren't being moved, so make a new path
                     if (!makePath (time, units, true)) throw new SystemException("make new path failed when moving units");
                     movedPath = g.paths.Last ();
                     break;
                 }
             }
         }
         movedPath.moveToDirect (time, pos);
         if (movedPath != this) g.alternatePaths.Add (movedPath);
     }
     if (movedPath != this && (movedPath.timeSimPast == long.MaxValue || timeSimPast != long.MaxValue)) {
         // new path was moved, so try to remove units that moved from current path
         Segment segment = segmentWhen (time);
         foreach (Unit unit in units) {
             new SegmentUnit(segment, unit).delete ();
         }
     }
     return movedPath;
 }
 /// <summary>
 /// removes unit from this segment and fewest possible unseen segments such that all remaining possibilities are valid,
 /// returns whether successful
 /// </summary>
 public bool delete(bool addMoveLines = false)
 {
     // *****************
     // BEGIN DANGER ZONE
     // *****************
     // There are many non-trivial cases this function needs to handle, so only modify it if you know what you are doing.
     // Even then, you will likely break a corner case anyway but not notice until a week or two later.
     //
     // If what you want to do is actually some sort of postprocessing using the deleted SegmentUnits,
     // it's best to do it at the end of the function (after the "danger zone") by iterating over the "removed" variable.
     if (!segment.units.Contains (unit)) return true; // if this segment already doesn't contain this unit, return true
     List<SegmentUnit> ancestors = new List<SegmentUnit> { this };
     Dictionary<Segment, List<Unit>> removed = new Dictionary<Segment, List<Unit>>();
     long timeEarliestChild = long.MaxValue;
     int i;
     // find all ancestor segments to start removal from
     for (i = 0; i < ancestors.Count; i++) {
         if (ancestors[i].prev ().Any ()) {
             // if this ancestor has a sibling segment that we're not currently planning to remove unit from,
             // don't remove unit from previous segments shared by both
             Unit u = unit;
             if (!ancestors[i].segment.branches.Where(seg => seg.units.Contains(u) && !ancestors.Contains(new SegmentUnit(seg, u))
                 && (seg.path.timeSimPast == long.MaxValue || ancestors[i].segment.path.timeSimPast != long.MaxValue)).Any()) {
                 // indicate to remove unit from previous segments
                 ancestors.AddRange (ancestors[i].prev ());
                 ancestors.RemoveAt(i);
                 i--;
             }
         } else if (ancestors[i].segment.prev ().Any ()) {
             // unit has a parent but we're deleting its first segment, so may need to check resources starting at this time
             if (ancestors[i].unit.attacks.Count > 0) return false;
             if (ancestors[i].segment.timeStart < timeEarliestChild && ancestors[i].unit.type.rscCollectRate.Where (r => r > 0).Any ()) {
                 timeEarliestChild = ancestors[i].segment.timeStart;
             }
         } else {
             // reached a segment with no previous segment whatsoever, so return false (we assume other players know the scenario's starting state)
             return false;
         }
     }
     // remove unit recursively, starting at the ancestor segments we found
     for (i = 0; i < ancestors.Count; i++) {
         if (!ancestors[i].deleteAfter (ref removed, ref timeEarliestChild)) break;
     }
     // if a deleteAfter() call failed or removing unit led to player ever having negative resources,
     // add units back to segments they were removed from
     if (i < ancestors.Count || (timeEarliestChild != long.MaxValue && segment.path.player.checkNegRsc (timeEarliestChild, false) >= 0)) {
         foreach (KeyValuePair<Segment, List<Unit>> item in removed) {
             item.Key.units.AddRange (item.Value);
         }
         return false;
     }
     // ***************
     // END DANGER ZONE
     // ***************
     foreach (KeyValuePair<Segment, List<Unit>> item in removed) {
         // add deleted units to list
         if (item.Key.timeStart < g.timeSim) {
             if (item.Key.nextOnPath () == null) item.Key.path.insertSegment (g.timeSim);
             item.Key.deletedUnits.AddRange (item.Value);
         }
     }
     if (addMoveLines) {
         // add deleted unit lines
         // TODO: tweak time if deleted before timeSimPast
         MoveLine deleteLine = new MoveLine(Math.Min (Math.Max (segment.path.timeSimPast, segment.path.moves[0].timeStart), g.timeSim), unit.player);
         foreach (Segment seg in removed.Keys) {
             deleteLine.vertices.AddRange (seg.path.moveLines (seg.timeStart,
                 (seg.nextOnPath () == null || seg.nextOnPath ().timeStart > deleteLine.time) ? deleteLine.time : seg.nextOnPath ().timeStart));
         }
         g.deleteLines.Add (deleteLine);
     }
     return true;
 }