static void Postfix(LineOfSight __instance, Point p0, float height0, Point p1, float height1, Vector3 unitDelta, string targetGuid, ref float __result, CombatGameState ___Combat) { if (ModState.CurrentTurretForLOS == null) { return; } if (p0.X == p1.X && p0.Z == p1.Z) { __result = 0f; return; } if (!___Combat.MapMetaData.IsWithinBounds(p0) || !___Combat.MapMetaData.IsWithinBounds(p1)) { __result = float.MaxValue; return; } float numCellsX = Mathf.Abs(unitDelta.x) * (float)MapMetaDataExporter.cellSize; float numCellsY = Mathf.Abs(unitDelta.z) * (float)MapMetaDataExporter.cellSize; float lineDeltaX = (float)(p1.X - p0.X); float lineDeltaZ = (float)(p1.Z - p0.Z); float greatestDivisor = Mathf.Max(Mathf.Abs(lineDeltaX), Mathf.Abs(lineDeltaZ)); float stepHeight = (height1 - height0) / greatestDivisor; float sumVisionCost = 0f; Traverse projectedHeightAtT = Traverse.Create(__instance).Method("getProjectedHeightAt", new Type[] { typeof(Point), typeof(float), typeof(Point), typeof(float) }); Traverse visCostOfCellT = Traverse.Create(__instance).Method("visCostOfCell", new Type[] { typeof(MapTerrainDataCell), typeof(float) }); string shellBuildingGUID = ModState.AmbushTurretGUIDtoBuilding[ModState.CurrentTurretForLOS.GUID].GUID; EncounterLayerData encounterLayerData = ___Combat.EncounterLayerData; List <Point> list = BresenhamLineUtil.BresenhamLine(p0, p1); for (int i = 1; i < list.Count; i++) { float stepDelta; if (list[i].X != list[i - 1].X) { stepDelta = numCellsX; } else { stepDelta = numCellsY; } // Increment vision cost only slightly if it's inside our shell building if (encounterLayerData.mapEncounterLayerDataCells[list[i].Z, list[i].X].HasSpecifiedBuilding(shellBuildingGUID)) { Mod.Log.Trace?.Write($" Point x={list[i].X} z={list[i].Z} is inside the shell building, adding vision cost normally."); sumVisionCost += stepDelta; } else { float projectedHeightAt = projectedHeightAtT.GetValue <float>(new object[] { p0, height0, list[i], stepHeight }); MapTerrainDataCell mapTerrainDataCell = ___Combat.MapMetaData.mapTerrainDataCells[list[i].Z, list[i].X]; if (mapTerrainDataCell.cachedHeight > projectedHeightAt) { if (mapTerrainDataCell.MapEncounterLayerDataCell.HasBuilding) { for (int j = 0; j < mapTerrainDataCell.MapEncounterLayerDataCell.buildingList.Count; j++) { if (ObstructionGameLogic.GuidsMatchObjectOrRep(mapTerrainDataCell.MapEncounterLayerDataCell.buildingList[j].buildingGuid, targetGuid)) { __result = sumVisionCost; return; } } } __result = float.MaxValue; return; } sumVisionCost += visCostOfCellT.GetValue <float>(new object[] { mapTerrainDataCell, projectedHeightAt }) * stepDelta; } } __result = sumVisionCost; return; }
public CacheNodeLink(CacheNode from, CacheNode to, float distance, int angle, MapMetaData mapMetaData) { this.From = from; this.To = to; this.Reciprocal = to.NeighborLinks[(angle + 4) % 8]; if (this.Reciprocal != null) { this.Reciprocal.Reciprocal = this; } this.PathCells = new List <MapTerrainDataCell>(); this.TargetDistCells = new List <DistCell[]>(); List <Point> indexLine = BresenhamLineUtil.BresenhamLine(from.CellIndex, to.CellIndex); for (int i = 0; i < indexLine.Count - 1; i++) { // Targets are indexLine[i + 1] and the cells in the adjacent cardinal directions DistCell[] targets = new DistCell[3]; Point current = indexLine[i]; int xDiff = indexLine[i + 1].X - current.X; int zDiff = indexLine[i + 1].Z - current.Z; if (xDiff >= 0) { targets[0] = new DistCell(cellDelta, mapMetaData.GetCellAt(current.X + 1, current.Z)); } else { targets[0] = new DistCell(cellDelta, mapMetaData.GetCellAt(current.X - 1, current.Z)); } if (zDiff >= 0) { targets[1] = new DistCell(cellDelta, mapMetaData.GetCellAt(current.X, current.Z + 1)); } else { targets[1] = new DistCell(cellDelta, mapMetaData.GetCellAt(current.X, current.Z - 1)); } if (xDiff == 0) { targets[2] = new DistCell(cellDelta, mapMetaData.GetCellAt(current.X - 1, current.Z)); } else if (zDiff == 0) { targets[2] = new DistCell(cellDelta, mapMetaData.GetCellAt(current.X, current.Z - 1)); } else { // this should be impossible with the modified bresenham they're using targets[2] = new DistCell(cellDeltaDiag, mapMetaData.GetCellAt(indexLine[i])); } this.PathCells.Add(mapMetaData.GetCellAt(current)); this.TargetDistCells.Add(targets); } this.Distance = distance; this.UpdateGrade(); this.UpdateMaxGrade(); }
static void Postfix(LineOfSight __instance, Point p0, float height0, Point p1, float height1, string targetedBuildingGuid, ref Point collisionWorldPos, ref bool __result, CombatGameState ___Combat) { if (ModState.CurrentTurretForLOF == null) { return; } Mod.Log.Trace?.Write($"Recalculating LOF from {CombatantUtils.Label(ModState.CurrentTurretForLOF)} due to collision on building shell. " + $"CollisonWorldPos=> x={collisionWorldPos.X} z={collisionWorldPos.Z}"); collisionWorldPos = p1; // If the origin and target points are the same, there is a collision if (p0.X == p1.X && p0.Z == p1.Z) { __result = true; return; } // If the origin or target points are outsie the bounds of the map, there is no collision (because how could there be) if (!___Combat.MapMetaData.IsWithinBounds(p0) || !___Combat.MapMetaData.IsWithinBounds(p1)) { __result = false; return; } MapMetaData mapMetaData = ___Combat.MapMetaData; EncounterLayerData encounterLayerData = ___Combat.EncounterLayerData; bool targetIsABuilding = !string.IsNullOrEmpty(targetedBuildingGuid); string shellBuildingGUID = ModState.AmbushTurretGUIDtoBuilding[ModState.CurrentTurretForLOF.GUID].GUID; List <Point> bresenhamLinePoints = BresenhamLineUtil.BresenhamLine(p0, p1); float heightDeltaPerPoint = (height1 - height0) / (float)bresenhamLinePoints.Count; float collisionPointHeight = height0; // Walk the bresenham Line, evaluation collision at a speciifc height as we go. for (int i = 0; i < bresenhamLinePoints.Count; i++) { collisionPointHeight += heightDeltaPerPoint; Point point = bresenhamLinePoints[i]; if (encounterLayerData.mapEncounterLayerDataCells[point.Z, point.X].HasSpecifiedBuilding(shellBuildingGUID)) { Mod.Log.Trace?.Write($" Point x={point.X} z={point.Z} is inside the shell building, continuing."); continue; } if (targetIsABuilding && encounterLayerData.mapEncounterLayerDataCells[point.Z, point.X].HasSpecifiedBuilding(targetedBuildingGuid)) { Mod.Log.Trace?.Write($" Building {targetedBuildingGuid} conflicts with the LoS, collision at x={collisionWorldPos.X} z={collisionWorldPos.Z}"); collisionWorldPos = bresenhamLinePoints[i]; __result = true; return; } if (mapMetaData.mapTerrainDataCells[point.Z, point.X].cachedHeight > collisionPointHeight) { Mod.Log.Trace?.Write($" Collision on terrain at position x={collisionWorldPos.X} z={collisionWorldPos.Z}"); collisionWorldPos = bresenhamLinePoints[i]; __result = false; return; } } Mod.Log.Trace?.Write($"No collision detected, changing LoF to true. CollisonWorldPos => x ={ collisionWorldPos.X} z ={ collisionWorldPos.Z}"); __result = true; return; }
private static bool NotPrefix( ref bool __result, Vector3 from, Vector3 to, MapMetaData ___mapMetaData, CombatGameState ___combat, List <Point> ___fbbLine, float ___maxGrade, float ___cellDelta, float ___cellDeltaDiag, Point ___incrementZ, Point ___incrementX, Point ___decrementX, Point ___decrementZ) { _stopwatch.Start(); Counter++; ___mapMetaData = ___combat.MapMetaData; Point index = ___mapMetaData.GetIndex(from); Point index2 = ___mapMetaData.GetIndex(to); if (!___mapMetaData.IsWithinBounds(index) || !___mapMetaData.IsWithinBounds(index)) { return(true); } ___fbbLine = BresenhamLineUtil.BresenhamLine(index, index2); if (___fbbLine.Count < 3) { return(false); } float cachedHeight = ___mapMetaData.GetCellAt(___fbbLine[___fbbLine.Count - 1]).cachedHeight; using (CancellationTokenSource cts = new CancellationTokenSource()) { availableThreads = Mod.MaxConcurrency; Task <bool>[] tasks = new Task <bool> [3]; CancellationToken token = cts.Token; for (int i = -1; i <= 1; i++) { while (availableThreads <= 0) { } Interlocked.Decrement(ref availableThreads); int k = i; tasks[i + 1] = Task.Run(() => { for (int j = -1; j <= 1; j++) { if (token.IsCancellationRequested) { return(false); } if (k == 0 && j == 0) { continue; } int num = ___fbbLine[___fbbLine.Count - 1].X + k; int num2 = ___fbbLine[___fbbLine.Count - 1].Z + j; if (___mapMetaData.IsWithinBounds(num, num2)) { float cachedHeight2 = ___mapMetaData.GetCellAt(num, num2).cachedHeight; if (PathNodeGrid.CheckForBlocker(cachedHeight, cachedHeight2, ___maxGrade, (k != 0 && j != 0) ? ___cellDeltaDiag : ___cellDelta)) { Interlocked.Increment(ref availableThreads); cts.Cancel(); return(true); } } } Interlocked.Increment(ref availableThreads); return(false); }); } if (token.IsCancellationRequested) { __result = true; PrintExceptions(tasks); return(false); } foreach (Task <bool> t in tasks) { // busy wait while (!t.IsCompleted) { } } PrintExceptions(tasks); if (token.IsCancellationRequested) { __result = true; return(false); } } availableThreads = Mod.MaxConcurrency; float fromHeight = ___mapMetaData.GetCellAt(___fbbLine[0]).cachedHeight; using (CancellationTokenSource cts = new CancellationTokenSource()) { CancellationToken token = cts.Token; Task <bool>[] tasks = new Task <bool> [___fbbLine.Count - 1]; for (int k = 1; k < ___fbbLine.Count; k++) { while (availableThreads <= 0) { } Interlocked.Decrement(ref availableThreads); int i = k - 1; tasks[i] = Task.Run(() => { cachedHeight = ___mapMetaData.GetCellAt(___fbbLine[i + 1]).cachedHeight; if (___fbbLine[i + 1].Z == ___fbbLine[i].Z) { if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight, ___maxGrade, ___cellDelta)) { cts.Cancel(); Interlocked.Increment(ref availableThreads); return(true); } float cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___incrementZ).cachedHeight; if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta)) { cts.Cancel(); Interlocked.Increment(ref availableThreads); return(true); } cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___decrementZ).cachedHeight; if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta)) { cts.Cancel(); Interlocked.Increment(ref availableThreads); return(true); } } else if (___fbbLine[i + 1].X == ___fbbLine[i].X) { if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight, ___maxGrade, ___cellDelta)) { cts.Cancel(); Interlocked.Increment(ref availableThreads); return(true); } float cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___decrementX).cachedHeight; if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta)) { cts.Cancel(); Interlocked.Increment(ref availableThreads); return(true); } cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___incrementX).cachedHeight; if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta)) { cts.Cancel(); Interlocked.Increment(ref availableThreads); return(true); } } else { if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight, ___maxGrade, ___cellDeltaDiag)) { cts.Cancel(); Interlocked.Increment(ref availableThreads); return(true); } if (___fbbLine[i + 1].X > ___fbbLine[i].X) { float cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___incrementX).cachedHeight; if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta)) { cts.Cancel(); Interlocked.Increment(ref availableThreads); return(true); } if (___fbbLine[i + 1].Z > ___fbbLine[i].Z) { cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___incrementZ).cachedHeight; if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta)) { cts.Cancel(); Interlocked.Increment(ref availableThreads); return(true); } } else if (___fbbLine[i + 1].Z < ___fbbLine[i].Z) { cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___decrementZ).cachedHeight; if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta)) { cts.Cancel(); Interlocked.Increment(ref availableThreads); return(true); } } } else if (___fbbLine[i + 1].X < ___fbbLine[i].X) { float cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___decrementX).cachedHeight; if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta)) { cts.Cancel(); Interlocked.Increment(ref availableThreads); return(true); } if (___fbbLine[i + 1].Z > ___fbbLine[i].Z) { cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___incrementZ).cachedHeight; if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta)) { cts.Cancel(); Interlocked.Increment(ref availableThreads); return(true); } } else if (___fbbLine[i + 1].Z < ___fbbLine[i].Z) { cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___decrementZ).cachedHeight; if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta)) { cts.Cancel(); Interlocked.Increment(ref availableThreads); return(true); } } } } fromHeight = cachedHeight; Interlocked.Increment(ref availableThreads); return(false); }); if (token.IsCancellationRequested) { PrintExceptions(tasks); __result = true; return(false); } } if (token.IsCancellationRequested) { PrintExceptions(tasks); __result = true; return(false); } foreach (Task <bool> t in tasks) { // busy wait while (!t.IsCompleted) { if (token.IsCancellationRequested) { PrintExceptions(tasks); __result = true; return(false); } } } PrintExceptions(tasks); } __result = false; return(false); void PrintExceptions(Task <bool>[] tasks) { List <Task <bool> > faultyTasks = tasks.Where(t => t?.IsFaulted ?? false).ToList(); if (faultyTasks.Any()) { StringBuilder stringBuilder = new StringBuilder(); foreach (Task task in faultyTasks) { stringBuilder.AppendLine(task.Exception.Flatten().ToString()); } Utils.Logger.LogError($"{Utils.LOG_HEADER}" + stringBuilder); } } }