public static float RotationTo(this LocAndOffsets from, LocAndOffsets to) { var fromLoc = from.ToInches2D(); var toLoc = to.ToInches2D(); return((toLoc - fromLoc).GetWorldRotation()); }
private void PartySelectedFormationMoveToPosition_FindPath(GameObject partyMember, int count, LocAndOffsets from, LocAndOffsets plannedPos, out LocAndOffsets actualPos) { actualPos = plannedPos; if (IsBlocked(plannedPos.ToInches2D(), partyMember.GetRadius())) { var pq = new PathQuery(); pq.flags2 = (int)AnimPathDataFlags.UNK_2000; pq.flags = PathQueryFlags.PQF_FORCED_STRAIGHT_LINE | PathQueryFlags.PQF_HAS_CRITTER; pq.critter = partyMember; pq.from = from; pq.to = plannedPos; pq.flags = PathQueryFlags.PQF_FORCED_STRAIGHT_LINE | PathQueryFlags.PQF_HAS_CRITTER | PathQueryFlags.PQF_TO_EXACT | PathQueryFlags.PQF_800; pq.distanceToTargetMin = count * locXY.INCH_PER_TILE; if (!GameSystems.Combat.IsCombatActive()) { pq.flags |= PathQueryFlags.PQF_IGNORE_CRITTERS; } if (GameSystems.PathX.FindPath(pq, out var pathResult)) { actualPos = pathResult.nodes[pathResult.nodeCount - 1]; } } }
private bool FindFreeTargetPos(ref LocAndOffsets loc, float radius) { var loc2d = loc.ToInches2D(); if (!IsBlocked(loc2d, radius)) { return(true); } var i = 0; for (var j = -1; j > -18; j--) { int k; var yOffset = i * locXY.INCH_PER_SUBTILE; var altPos = loc2d; for (k = j; k < i; k++) { var xOffset = k * locXY.INCH_PER_SUBTILE; altPos = loc2d + new Vector2(xOffset, -yOffset); if (!IsBlocked(altPos, radius)) { break; } altPos = loc2d + new Vector2(-xOffset, yOffset); if (!IsBlocked(altPos, radius)) { break; } altPos = loc2d + new Vector2(-xOffset, -yOffset); if (!IsBlocked(altPos, radius)) { break; } altPos = loc2d + new Vector2(xOffset, yOffset); if (!IsBlocked(altPos, radius)) { break; } } if (k != i) { loc = LocAndOffsets.FromInches(altPos); return(true); } i++; } return(i != 0); }
public bool SetConeTargets(LocAndOffsets targetLocation) { result.flags = PickerResultFlags.PRF_HAS_LOCATION | PickerResultFlags.PRF_HAS_MULTI_OBJ; result.location = targetLocation; result.offsetz = 0; result.handle = null; var originPos = caster.GetLocationFull(); var coneOrigin = originPos.ToInches2D(); var coneTarget = targetLocation.ToInches2D(); float radiusInches; if (!flagsTarget.HasFlag(UiPickerFlagsTarget.FixedRadius)) { radiusInches = (coneTarget - coneOrigin).Length(); } else { radiusInches = radiusTarget * locXY.INCH_PER_FEET; } var arcRad = Angles.ToRadians(degreesTarget); var arcStart = (coneTarget - coneOrigin).GetWorldRotation() - arcRad; using var objList = ObjList.ListCone(originPos, radiusInches, arcStart, arcRad, ObjectListFilter.OLC_ALL); result.objList = new List <GameObject>(objList); if (!flagsTarget.HasFlag(UiPickerFlagsTarget.Unknown80h)) { RemoveTargetsNotInLineOfSightOf(originPos); } DoExclusions(); return(true); }
public void TestRaycast() { var origin = new LocAndOffsets(new locXY(496, 458), -9.42809f, -8.42809f); var target = new LocAndOffsets(new locXY(512, 611), -7.42809f, -7.42809f); var radius = 17.5f; var ray_search = new RaycastPointSearchPacket(); ray_search.origin = origin.ToInches2D(); ray_search.target = target.ToInches2D(); ray_search.radius = radius; ray_search.direction = ray_search.target - ray_search.origin; ray_search.rangeInch = ray_search.direction.Length(); ray_search.direction /= ray_search.rangeInch; ray_search.absOdotU = Vector2.Dot(ray_search.direction, ray_search.origin); var locBeforeCover = target; SearchPointAlongRay search_func = RaycastPacket.IsPointCloseToSegment; var tileRadius = 1 + (int)(radius / locXY.INCH_PER_TILE); TestContext.Out.WriteLine(tileRadius.ToString()); var tile_rect = RaycastPacket.BuildSearchRectangle(ray_search.origin, ray_search.target); var canFly = false; foreach (var s in tile_rect) { //int left = Math.Max(0, s.TileRectangle.Left - tileRadius); //int top = Math.Max(0, s.TileRectangle.Top - tileRadius); //int right = Math.Min(64, s.TileRectangle.Right + tileRadius); //int bottom = Math.Min(64, s.TileRectangle.Bottom + tileRadius); int left = s.TileRectangle.Left; int top = s.TileRectangle.Top; int right = s.TileRectangle.Right; int bottom = s.TileRectangle.Bottom; TestContext.Out.WriteLine($"{s.SectorLoc.X} {s.SectorLoc.Y} {left},{top} {right},{bottom}"); } TestContext.Out.WriteLine("OLD OLD OLD"); if (!PreciseSectorRows.Build(tile_rect, out var sector_tiles)) { return; } var local_254 = Math.Min(origin.location.locx, target.location.locx); var local_25c = Math.Max(origin.location.locx, target.location.locx); var local_2a4 = Math.Min(origin.location.locy, target.location.locy); var local_2b4 = Math.Max(origin.location.locy, target.location.locy); /* * local_254 -= tileRadius; * local_2a4 -= tileRadius; * local_25c += tileRadius; * local_2b4 += tileRadius; */ for (var local_2dc = 0; local_2dc < sector_tiles.RowCount; local_2dc++) { ref var pPVar2 = ref sector_tiles.Rows[local_2dc]; Span <int> local_208 = stackalloc int[pPVar2.colCount]; Span <int> local_1d8 = stackalloc int[pPVar2.colCount]; for (var i = 0; i < pPVar2.colCount; i++) { local_208[i] = pPVar2.startTiles[i]; local_1d8[i] = 64 - pPVar2.strides[i]; } for (var local_2f0 = 0; local_2f0 < pPVar2.colCount; local_2f0++) { var sectorBaseTile = pPVar2.sectors[local_2f0].GetBaseTile(); var sectorTileMinX = local_254 - sectorBaseTile.locx; var sectorTileMaxX = local_25c - sectorBaseTile.locx; var sectorTileMinY = local_2a4 - sectorBaseTile.locy; var sectorTileMaxY = local_2b4 - sectorBaseTile.locy; if (sectorTileMinX < 0) { sectorTileMinX = 0; } if (sectorTileMinY < 0) { sectorTileMinY = 0; } if (sectorTileMaxX > 63) { sectorTileMaxX = 63; } if (sectorTileMaxY > 63) { sectorTileMaxY = 63; } TestContext.Out.WriteLine($"{pPVar2.sectors[local_2f0].X} {pPVar2.sectors[local_2f0].Y} {sectorTileMinX},{sectorTileMinY}->{sectorTileMaxX},{sectorTileMaxY}"); } }
public List <Light3d> FindLights(LocAndOffsets atLocation, float radius) { List <Light3d> lights = new List <Light3d>(); if (GameSystems.Light.IsGlobalLightEnabled) { Light3d light = new Light3d(); var legacyLight = GameSystems.Light.GlobalLight; light.type = (Light3dType)legacyLight.type; light.color = legacyLight.Color; light.dir.X = legacyLight.dir.X; light.dir.Y = legacyLight.dir.Y; light.dir.Z = legacyLight.dir.Z; light.pos.X = legacyLight.pos.X; light.pos.Y = legacyLight.pos.Y; light.pos.Z = legacyLight.pos.Z; light.range = legacyLight.range; light.phi = legacyLight.phi; lights.Add(light); } if (radius == 0) { return(lights); } // Build a box that has twice the radius convert to tiles as it's width/height // For some reason, ToEE will add one more INCH_PER_TILE here, which translates to // roughly 28 tiles more search radius than is needed var boxDimensions = (int)(radius / locXY.INCH_PER_TILE + locXY.INCH_PER_TILE); var tileX1 = atLocation.location.locx - 1 - boxDimensions; var tileX2 = atLocation.location.locx + 1 + boxDimensions; var tileY1 = atLocation.location.locy - 1 - boxDimensions; var tileY2 = atLocation.location.locy + 1 + boxDimensions; using var sectorIterator = new SectorIterator(tileX1, tileX2, tileY1, tileY2); var atPos = atLocation.ToInches2D(); while (sectorIterator.HasNext) { using var sector = sectorIterator.Next(); foreach (ref var light in sector.Lights) { int type; LinearColor color; Vector3 direction; float range, phi; var lightPos = light.position.ToInches2D(); if ((light.flags & 0x40) != 0) { if (GameSystems.Light.IsNight) { type = light.light2.type; color = light.light2.color; direction = light.light2.direction; range = light.range; // Notice how it's using light 1's range phi = light.light2.phi; /* * Kill the daytime particle system if it's night and the * daytime particle system is still alive. */ if (light.partSys.handle != null) { GameSystems.ParticleSys.Remove(light.partSys.handle); light.partSys.handle = null; } /* * If the nighttime particle system has not yet been started, * do it here. */ ref var nightPartSys = ref light.light2.partSys; if (nightPartSys.handle == null && nightPartSys.hashCode != 0) { var centerOfTile = light.position.ToInches3D(light.offsetZ); nightPartSys.handle = GameSystems.ParticleSys.CreateAt( nightPartSys.hashCode, centerOfTile ); } } else { type = light.type; color = light.color; direction = light.direction; range = light.range; phi = light.phi; // This is just the inverse of what we're doing at night (see above) ref var nightPartSys = ref light.light2.partSys; if (nightPartSys.handle != null) { GameSystems.ParticleSys.Remove(nightPartSys.handle); nightPartSys.handle = null; } ref var dayPartSys = ref light.partSys; if (dayPartSys.handle == null && dayPartSys.hashCode != 0) { var centerOfTile = light.position.ToInches3D(light.offsetZ); dayPartSys.handle = GameSystems.ParticleSys.CreateAt( dayPartSys.hashCode, centerOfTile ); } } }
public int PartySelectedFormationMoveToPosition(LocAndOffsets loc, bool mayWalk) { var selectedMembers = GameSystems.Party.Selected; if (selectedMembers.Count == 0) { return(1); } var maxRadius = float.MinValue; foreach (var selectedMember in selectedMembers) { if (!GameSystems.Critter.IsDeadNullDestroyed(selectedMember)) { maxRadius = Math.Max(maxRadius, selectedMember.GetRadius()); } } Span <LocAndOffsets> plannedPositions = stackalloc LocAndOffsets[selectedMembers.Count]; if (!FindFreeTargetPos(ref loc, maxRadius)) { return(1); } // NOTE: There seems to have been a bug in Vanilla where it wanted to // find the average center of the party, but never actually updated the obj // it took the position of. So it ended up just taking the leader's position. var leaderPos = selectedMembers[0].GetLocationFull().ToInches2D(); var targetPos = loc.ToInches2D(); // This rotation is used to rotate the formation at the target point var formationRotation = MathF.PI - MathF.Atan2(targetPos.X - leaderPos.X, targetPos.Y - leaderPos.Y); if (!GameSystems.Critter.IsDeadNullDestroyed(selectedMembers[0])) { plannedPositions[0] = loc; } for (var selectedIdx = 0; selectedIdx < selectedMembers.Count; selectedIdx++) { var obj = selectedMembers[selectedIdx]; if (GameSystems.Critter.IsDeadNullDestroyed(obj)) { continue; } var partyMemberRadius = obj.GetRadius(); GetFormationOffset(obj, formationRotation, out var formationOffX, out var formationOffY); if (selectedMembers.Count < 2) { plannedPositions[selectedIdx] = loc; } else { var formationOffLen = MathF.Sqrt(formationOffY * formationOffY + formationOffX * formationOffX); // Remember that the length of the formation offset can be 0 in case it's dead-center if (formationOffLen > locXY.INCH_PER_SUBTILE) { // Normalize the formation offset var formationOffNormX = formationOffX / formationOffLen; var formationOffNormY = formationOffY / formationOffLen; var formationOffDir = new Vector2(formationOffNormX, formationOffNormY); var local_90 = 0.0f; for (; local_90 < formationOffLen; local_90 += locXY.INCH_PER_SUBTILE) { var formationShift = local_90 * formationOffDir; var formationPos = targetPos + formationShift; if (!IsBlocked(formationPos, maxRadius)) { plannedPositions[selectedIdx] = LocAndOffsets.FromInches(formationPos); break; } } if (local_90 >= locXY.INCH_PER_SUBTILE) { // It somehow falls back to the previous location even if unblocked ??? var formationPos = targetPos + (local_90 - locXY.INCH_PER_SUBTILE) * formationOffDir; plannedPositions[selectedIdx] = LocAndOffsets.FromInches(formationPos); } } else { plannedPositions[selectedIdx] = loc; } } var previousPositions = plannedPositions.Slice(0, selectedIdx); // We have to do conflict resolution if another party member before this one // chose a colliding position if (FindIntersectingPosition(selectedMembers, previousPositions, plannedPositions[selectedIdx].ToInches2D(), partyMemberRadius)) { int upperBound; for (upperBound = 1; upperBound < 18; upperBound++) { var local_84 = upperBound * locXY.INCH_PER_SUBTILE; int local_8c; var alternatePos = plannedPositions[selectedIdx].ToInches2D(); for (local_8c = -upperBound; local_8c < upperBound; local_8c++) { alternatePos = plannedPositions[selectedIdx].ToInches2D(); alternatePos.X += local_8c * locXY.INCH_PER_SUBTILE; alternatePos.Y -= local_84; if (!FindIntersectingPosition(selectedMembers, previousPositions, alternatePos, partyMemberRadius) && !IsBlocked(alternatePos, partyMemberRadius)) { break; } alternatePos = plannedPositions[selectedIdx].ToInches2D(); alternatePos.X -= local_8c * locXY.INCH_PER_SUBTILE; alternatePos.Y += local_84; if (!FindIntersectingPosition(selectedMembers, previousPositions, alternatePos, partyMemberRadius) && !IsBlocked(alternatePos, partyMemberRadius)) { break; } alternatePos = plannedPositions[selectedIdx].ToInches2D(); alternatePos.X -= local_8c * locXY.INCH_PER_SUBTILE; alternatePos.Y -= local_84; if (!FindIntersectingPosition(selectedMembers, previousPositions, alternatePos, partyMemberRadius) && !IsBlocked(alternatePos, partyMemberRadius)) { break; } alternatePos = plannedPositions[selectedIdx].ToInches2D(); alternatePos.X += local_8c * locXY.INCH_PER_SUBTILE; alternatePos.Y += local_84; if (!FindIntersectingPosition(selectedMembers, previousPositions, alternatePos, partyMemberRadius) && !IsBlocked(alternatePos, partyMemberRadius)) { break; } } if (local_8c != upperBound) { plannedPositions[selectedIdx] = LocAndOffsets.FromInches(alternatePos); break; } } if (upperBound == 18) { Logger.Info("Unable to find suitable formation position after 18 iterations."); } } } for (var i = 0; i < selectedMembers.Count; i++) { var obj = selectedMembers[i]; if (!GameSystems.Critter.IsDeadNullDestroyed(obj)) { PartySelectedFormationMoveToPosition_FindPath(obj, selectedMembers.Count, loc, plannedPositions[i], out var actualTargetPos); if (!mayWalk) { GameSystems.Anim.PushRunToTile(obj, actualTargetPos); GameSystems.Anim.TurnOnRunning(); } else { GameSystems.Anim.PushMoveToTile(obj, actualTargetPos); } } } return(1); }