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 void ConversionRoundtrip() { var zeroTile = LocAndOffsets.FromInches(0, 0); zeroTile.location.locx.Should().Be(0); zeroTile.location.locy.Should().Be(0); zeroTile.off_x.Should().Be(-locXY.INCH_PER_HALFTILE); zeroTile.off_y.Should().Be(-locXY.INCH_PER_HALFTILE); var conversionBack = zeroTile.ToInches2D(); conversionBack.X.Should().Be(0); conversionBack.Y.Should().Be(0); }
private bool IsBlocked(Vector2 pos, float radius) { var loc = LocAndOffsets.FromInches(pos); using var raycastPacket = new RaycastPacket(); raycastPacket.origin = loc; raycastPacket.targetLoc = loc; raycastPacket.radius = radius; raycastPacket.flags |= RaycastFlag.StopAfterFirstFlyoverFound | RaycastFlag.StopAfterFirstBlockerFound | RaycastFlag.ExcludeItemObjects | RaycastFlag.HasSourceObj | RaycastFlag.HasRadius; return(raycastPacket.RaycastShortRange() > 0); }
private LocAndOffsets AverageLocation(LocAndOffsets a, LocAndOffsets b) { var averaged = (a.ToInches3D() + b.ToInches3D()) / 2; return(LocAndOffsets.FromInches(averaged)); }
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); }
// TODO: Separate logic+rendering public void AdvanceAndRender( IGameViewport viewport, GameObject giantFrog, AnimatedModelParams animParams, IAnimatedModel model, IList <Light3d> lights, float alpha) { var grappleState = GetGrappleState(giantFrog); if (grappleState.state == 0) { return; } model.GetBoneWorldMatrixByName(animParams, "Tongue_Ref", out var worldMatrixFrog); var grappledOpponent = GetGrappledOpponent(giantFrog); float tongueLength, tonguePosX, tonguePosZ; if (grappledOpponent != null) { var opponentModel = grappledOpponent.GetOrCreateAnimHandle(); var opponentAnimParams = grappledOpponent.GetAnimParams(); opponentModel.GetBoneWorldMatrixByName(opponentAnimParams, "Bip01 Spine1", out var worldMatrixOpponent); tonguePosX = worldMatrixOpponent.M41; tonguePosZ = worldMatrixOpponent.M43; var tongueDirX = worldMatrixOpponent.M41 - worldMatrixFrog.M41; var tongueDirY = worldMatrixOpponent.M42 - worldMatrixFrog.M42; var tongueDirZ = worldMatrixOpponent.M43 - worldMatrixFrog.M43; tongueLength = MathF.Sqrt(tongueDirX * tongueDirX + tongueDirY * tongueDirY + tongueDirZ * tongueDirZ); worldMatrixFrog.M31 = tongueDirX / tongueLength; worldMatrixFrog.M32 = tongueDirY / tongueLength; worldMatrixFrog.M33 = tongueDirZ / tongueLength; if (tongueLength > 0) { tongueLength -= 6.0f; } } else { tongueLength = 120.0f; tonguePosX = worldMatrixFrog.M31 * 120.0f + worldMatrixFrog.M41; tonguePosZ = worldMatrixFrog.M33 * 120.0f + worldMatrixFrog.M43; } switch (grappleState.state) { // This state seems to mean . Extending tongue to full length case 1: grappleState.currentLength += locXY.INCH_PER_TILE; if (grappleState.currentLength > tongueLength) { grappleState.state = 2; grappleState.currentLength = tongueLength; } break; // This state seems to mean . Retracting tongue case 2: grappleState.currentLength -= locXY.INCH_PER_TILE; if (grappleState.currentLength <= 0) { grappleState.state = 0; grappleState.currentLength = 0; } break; case 3: grappleState.currentLength += locXY.INCH_PER_TILE; if (grappleState.currentLength > tongueLength) { grappleState.state = 4; grappleState.currentLength = tongueLength; var frogAnim = GameSystems.Critter.GetAnimId(giantFrog, WeaponAnim.Special2); giantFrog.SetAnimId(frogAnim); var opponentAnim = GameSystems.Critter.GetAnimId(grappledOpponent, WeaponAnim.Panic); grappledOpponent.SetAnimId(opponentAnim); } break; case 4: // Maintain Tongue between frog and opponent without progressing grappleState.currentLength = tongueLength; break; case 5: case 6: { if (grappleState.state == 5) { grappleState.targetLength = tongueLength - 12.0f; if (grappleState.targetLength < 0) { grappleState.targetLength = 0; } grappleState.state = 6; } grappleState.currentLength = grappleState.currentLength - locXY.INCH_PER_HALFTILE; // Move the opponent closer to the frog float newX = tonguePosX - worldMatrixFrog.M31 * locXY.INCH_PER_HALFTILE; float newZ = tonguePosZ - worldMatrixFrog.M33 * locXY.INCH_PER_HALFTILE; var newLoc = LocAndOffsets.FromInches(newX, newZ); GameSystems.MapObject.Move(grappledOpponent, newLoc); if (grappleState.currentLength < grappleState.targetLength) { newX = worldMatrixFrog.M41 + grappleState.targetLength * worldMatrixFrog.M31; newZ = worldMatrixFrog.M43 + grappleState.targetLength * worldMatrixFrog.M33; newLoc = LocAndOffsets.FromInches(newX, newZ); GameSystems.MapObject.Move(grappledOpponent, newLoc); grappleState.currentLength = grappleState.targetLength; grappleState.state = 4; } } break; case 7: { grappleState.currentLength = grappleState.currentLength - locXY.INCH_PER_HALFTILE; // Move the opponent closer to the frog float newX = tonguePosX - worldMatrixFrog.M31 * locXY.INCH_PER_HALFTILE; float newZ = tonguePosZ - worldMatrixFrog.M33 * locXY.INCH_PER_HALFTILE; var newLoc = LocAndOffsets.FromInches(newX, newZ); GameSystems.MapObject.Move(grappledOpponent, newLoc); if (grappleState.currentLength < 0) { newX = worldMatrixFrog.M41; newZ = worldMatrixFrog.M43; newLoc = LocAndOffsets.FromInches(newX, newZ); GameSystems.MapObject.Move(grappledOpponent, newLoc); GameSystems.ObjFade.FadeTo(grappledOpponent, 0, 0, 16, 0); grappleState.currentLength = 0; grappleState.state = 0; // Probably the swallow animation var animId = GameSystems.Critter.GetAnimId(giantFrog, WeaponAnim.Special3); giantFrog.SetAnimId(animId); } } break; default: break; } // Update to the new grapple state SetGrappleState(giantFrog, grappleState); // The directional vector of the tongue ref point on the frog var tongueUp = worldMatrixFrog.GetRow(0).ToVector3(); var tongueRight = worldMatrixFrog.GetRow(1).ToVector3(); var tongueDir = worldMatrixFrog.GetRow(2).ToVector3(); var tonguePos = worldMatrixFrog.GetRow(3).ToVector3(); RenderTongue(viewport, grappleState, tongueDir, tongueUp, tongueRight, tonguePos, lights, alpha); }