/* * Take the BaseInitiative value from all components on the unit, remove the * normal phase modifiers HBS applies from SimGameConstants (-2 for light, etc), * then invert the value. Because HBS defines bonuses as negative modifiers, * invert this value to have it make sense elsewhere in the code. */ // TODO: Should this use the MechDef call below? public static int GetNormalizedComponentModifier(AbstractActor actor) { int unitInit = 0; WeightClass weightClass; if (actor.GetType() == typeof(Mech)) { weightClass = ((Mech)actor).weightClass; } else if (actor.GetType() == typeof(Vehicle)) { var vehicle = (Vehicle)actor; weightClass = ((Vehicle)actor).weightClass; } else // turret { TagSet actorTags = actor.GetTags(); if (actorTags != null && actorTags.Contains("unit_light")) { weightClass = WeightClass.LIGHT; } else if (actorTags != null && actorTags.Contains("unit_medium")) { weightClass = WeightClass.MEDIUM; } else if (actorTags != null && actorTags.Contains("unit_heavy")) { weightClass = WeightClass.HEAVY; } else { weightClass = WeightClass.ASSAULT; } } // TODO: Validate that vehicles are normalized properly - looks like HBS adjusts the phases, may not be working properly // TODO: Validate that turret are normalized properly - looks like HBS adjusts the phases, may not be working properly /* * HBS VALUES * "PhaseSpecial": 1, * "PhaseLight": 2, * "PhaseMedium": 3, * "PhaseHeavy": 4, * "PhaseAssault": 5, * "PhaseLightVehicle": 3, * "PhaseMediumVehicle": 4, * "PhaseHeavyVehicle": 5, * "PhaseAssaultVehicle": 5, * "PhaseLightTurret": 5, * "PhaseMediumTurret": 5, * "PhaseHeavyTurret": 5, * "PhaseAssaultTurret": 5 * * RT VALUE * "PhaseSpecial": 1, * "PhaseLight": 2, * "PhaseMedium": 3, * "PhaseHeavy": 4, * "PhaseAssault": 5, * "PhaseLightVehicle": 2, * "PhaseMediumVehicle": 3, * "PhaseHeavyVehicle": 4, * "PhaseAssaultVehicle": 5, * "PhaseLightTurret": 3, * "PhaseMediumTurret": 4, * "PhaseHeavyTurret": 5, * "PhaseAssaultTurret": 5 */ if (actor.StatCollection != null && actor.StatCollection.ContainsStatistic("BaseInitiative")) { int baseMod = actor.StatCollection.GetValue <int>("BaseInitiative"); // Normalize the value // TODO: These should come from CombatGameDef.PhaseConstantsDef, but I can't find a reference to pull // those values. May have to load at mod start and cache them. switch (weightClass) { case WeightClass.LIGHT: baseMod -= 2; break; case WeightClass.MEDIUM: baseMod -= 3; break; case WeightClass.HEAVY: baseMod -= 4; break; case WeightClass.ASSAULT: baseMod -= 5; break; default: Mod.Log.Debug($"Actor:{actor.DisplayName}_{actor.GetPilot().Name}" + $" has unknown or undefined weight class:{weightClass}!"); break; } // Because HBS init values were from 2-5, bonuses will be negative at this point and penalties positive. Invert these. unitInit = baseMod * -1; Mod.Log.Debug($"Normalized BaseInit for Actor:{actor.DisplayName}_{actor.GetPilot().Name}" + $" from {actor.StatCollection.GetValue<int>("BaseInitiative")} to unitInit:{unitInit}"); } return(unitInit); }
public static void Postfix(CombatAuraReticle __instance, ref AbstractActor ___owner, ref float ___currentAuraRange, ref CombatHUD ___HUD, ref Transform ___thisTransform) { //if(___HUD.SelectedActor != null && ___HUD.SelectionHandler.ActiveState is SelectionStateMoveBase && ___owner.GUID == ___HUD.SelectedActor.GUID) { //AuraPreviewRecord preview = //} }
public static bool isActivated(this AbstractActor unit) { return(activatedActors.Contains(unit)); }
public static void Postfix(ToHit __instance, ref string __result, CombatGameState ___combat, AbstractActor attacker, Weapon weapon, ICombatant target) { try { if (DemandingJumps.Settings.ToHitSelfJumpedSpareAI && !___combat.LocalPlayerTeam.IsActive) { return; } Logger.Info("[ToHit_GetAllModifiersDescription_POSTFIX] CombatGameState.LocalPlayerTeam.IsActive: " + ___combat.LocalPlayerTeam.IsActive); bool AttackerJumpedThisRound = attacker.HasMovedThisRound && attacker.JumpedLastRound; Logger.Info("[ToHit_GetAllModifiersDescription_POSTFIX] Fields.JumpPreview: " + Fields.JumpPreview); Logger.Info("[ToHit_GetAllModifiersDescription_POSTFIX] AttackerJumpedThisRound: " + AttackerJumpedThisRound); if (AttackerJumpedThisRound) { int ToHitSelfJumpedModifier = Utilities.GetAttackerJumpedAccuracyModifier(attacker); Logger.Debug("[ToHit_GetAllModifiersDescription_POSTFIX] Add description for ToHitSelfJumped: " + ToHitSelfJumpedModifier); __result = string.Format("{0}JUMPED {1:+#;-#}; ", __result, ToHitSelfJumpedModifier); } } catch (Exception e) { Logger.Error(e); } }
public static bool Prefix(CombatAuraReticle __instance, ref ButtonState __result, ref AbstractActor ___owner, ref CombatHUD ___HUD) { try { AuraBubble aura = __instance.AuraBubble(); if (aura == null) { __result = ButtonState.Disabled; return(false); } __result = __instance.isAuraVisible(aura, ___owner, ___HUD, false)? ButtonState.Enabled : ButtonState.Disabled; return(false); }catch (Exception e) { Log.LogWrite(e.ToString() + "\n"); } return(true); //__instance.GameRep.PlayVFXAt(__instance.GameRep.thisTransform, Vector3.zero, "vfxPrfPrtl_ECM_loop", true, Vector3.zero, false, -1f); }
public void SetPlayer(AbstractActor player) { _player = Objects.RequireNonNull(player); }
public static void Postfix(CombatHUDActorNameDisplay __instance, VisibilityLevel visLevel, AbstractActor ___displayedActor) { if (___displayedActor != null && ModState.LastPlayerActorActivated != null && ModState.TurnDirectorStarted && !___displayedActor.Combat.HostilityMatrix.IsLocalPlayerFriendly(___displayedActor.TeamId)) { SensorScanType scanType = SensorLockHelper.CalculateSharedLock(___displayedActor, ModState.LastPlayerActorActivated); // TODO: Needs to be hidden or localized string nameText = scanType >= SensorScanType.AllInformation ? ___displayedActor.GetPilot().Name : ""; __instance.PilotNameText.SetText(nameText); } }
static void Prefix(AbstractActor __instance) { Mod.MoveLog.Info?.Write($"Resetting pathing for actor: {__instance.DistinctId()}"); }
static void Prefix(Pathing __instance, Vector3 origin, float beginAngle, AbstractActor actor, bool justStoodUp) { Mod.MoveLog.Info?.Write($"Resetting path grid for actor: {actor.DistinctId()} for origin: {origin} and beginAngle: {beginAngle}"); }
public static bool IsOnSecondAction(AbstractActor unit) { return(!IsOnFirstAction(unit)); }
static void Postfix(AbstractActor __instance) { Mod.Log.Debug?.Write($"AA:IES entered- setting CanShootAfterSprinting for actor:{__instance.DisplayName}"); __instance.StatCollection.Set(ModStats.CanShootAfterSprinting, true); }
public Vector3 GetDestination(List <PointWithDistance> startPointList, Vector3 goal, float movementBudget, float maxSlope, AbstractActor unit, bool shouldSprint, List <AbstractActor> lanceUnits, PathNodeGrid pathGrid, out Vector3 lookAtPoint) { if (shouldSprint && unit.CanSprint) { unit.Pathing.SetSprinting(); } else { unit.Pathing.SetWalking(); } InclineIndexPoint goalPoint = WorldPointToInclineIndices(goal); List <PointWithDistance> pathLatticePoints = FindPath(startPointList, goalPoint, maxSlope, maxSlope); if ((pathLatticePoints == null) || (pathLatticePoints.Count == 0)) { // can't find a path(!) // set the lookAtPoint (out) variable to a point further out in the direction of start->goal lookAtPoint = goal * 2.0f - unit.CurrentPosition; return(goal); } List <PathNode> pathNodes = new List <PathNode>(); PathNode walkNode = pathLatticePoints[0].pathNode; while (walkNode != null) { pathNodes.Insert(0, walkNode); walkNode = walkNode.Parent; } List <Vector3> longRangePathWorldPoints = pathLatticePoints.ConvertAll(x => InclineIndicesToWorldPoint(x.point)); List <Vector3> pathWorldPoints = pathNodes.ConvertAll(x => x.Position); pathWorldPoints.AddRange(longRangePathWorldPoints); Vector3 destination; if (longRangePathWorldPoints.Count == 1) { destination = longRangePathWorldPoints[0]; if ((goal - destination).magnitude < 1.0f) { lookAtPoint = destination * 2.0f - unit.CurrentPosition; } else { lookAtPoint = goal; } return(destination); } // Debug Draw Path float scale = 1.0f; for (int pathIndex = 0; pathIndex < pathWorldPoints.Count - 1; ++pathIndex) { int nextIndex = pathIndex + 1; Vector3 p0 = pathWorldPoints[pathIndex]; Vector3 p1 = pathWorldPoints[nextIndex]; scale = (p1 - p0).magnitude; for (int dx = -1; dx <= 1; ++dx) { for (int dz = -1; dz <= 1; ++dz) { Vector3 offset = new Vector3(dx * scale * 0.1f, 0, dz * scale * 0.1f); Debug.DrawLine(p0 + offset, p1 + offset, Color.red, 30.0f); } } } drawCircle(unit.CurrentPosition, scale * 0.4f, Color.cyan, 30.0f); drawCircle(goal, scale * 0.4f, Color.magenta, 30.0f); drawCircle(pathWorldPoints[0], scale * 0.3f, Color.red, 30.0f); drawCircle(pathWorldPoints[0], scale * 0.35f, Color.white, 30.0f); drawCircle(pathWorldPoints[0], scale * 0.4f, Color.blue, 30.0f); // TODO/dlecompte push this up to filter our selection of path nodes, above. float spread = unit.BehaviorTree.GetBehaviorVariableValue( unit.Combat.TurnDirector.IsInterleaved ? BehaviorVariableName.Float_InterleavedLanceSpreadDistance : BehaviorVariableName.Float_NonInterleavedLanceSpreadDistance).FloatVal; drawCircle(unit.CurrentPosition, spread, Color.green, 30.0f); Debug.Assert(pathWorldPoints.Count >= 2); // already tested this, above. float accumDistance = 0.0f; // if we can get to the goal, look at a point further away in the direction of start -> goal lookAtPoint = 2 * goal - unit.CurrentPosition; Vector3 clipPoint = goal; // Now we walk along the pathWorldPoints, snapping them to grid points. // We want to take the point furthest along the path that doesn't alias to an earlier point. // Also, we want to make sure that it's the furthest point within our movement budget and within our lance spread. // MUST BE : within movement budget, not an alias to an earlier point // IF any points exist inside lance spread, pick last point inside lance spread, else last point. List <Vector3> dedupedSnappedPointsList = new List <Vector3>(); List <Vector3> snappedPointsInOrder = new List <Vector3>(); List <Vector3> nextPointsInOrder = new List <Vector3>(); List <bool> pointsInSpreadRangeList = new List <bool>(); List <bool> isNewGroundList = new List <bool>(); float ROUNDING_RADIUS = 1.0f; bool wasEverInside = false; for (int pointIndex = 0; (pointIndex < pathWorldPoints.Count) && (accumDistance <= movementBudget); ++pointIndex) { Vector3 thisPoint = pathWorldPoints[pointIndex]; Vector3 nextPoint = goal; if (pointIndex + 1 < pathWorldPoints.Count) { nextPoint = pathWorldPoints[pointIndex + 1]; } Vector3 thisSnappedPoint = unit.Combat.HexGrid.GetClosestPointOnGrid(thisPoint); snappedPointsInOrder.Add(thisSnappedPoint); nextPointsInOrder.Add(nextPoint); bool pointIsInsideSpread = AIUtil.IsPositionWithinLanceSpread(unit, lanceUnits, thisSnappedPoint); wasEverInside |= pointIsInsideSpread; pointsInSpreadRangeList.Add(pointIsInsideSpread); bool alreadyVisited = isPointInList(thisSnappedPoint, dedupedSnappedPointsList, ROUNDING_RADIUS); isNewGroundList.Add(!alreadyVisited); if (!alreadyVisited) { dedupedSnappedPointsList.Add(thisSnappedPoint); } if (pointIndex + 1 < pathWorldPoints.Count) { accumDistance += (nextPoint - thisPoint).magnitude; } } if (wasEverInside) { // find the last point of our list that is "new ground" and inside for (int i = snappedPointsInOrder.Count - 1; i >= 0; --i) { if (isNewGroundList[i] && pointsInSpreadRangeList[i]) { clipPoint = snappedPointsInOrder[i]; lookAtPoint = nextPointsInOrder[i]; break; } } } if ((!wasEverInside) || ((clipPoint - unit.CurrentPosition).magnitude < 1.0f)) { // find the last point of our list that is "new ground" for (int i = snappedPointsInOrder.Count - 1; i >= 0; --i) { if (isNewGroundList[i]) { clipPoint = snappedPointsInOrder[i]; lookAtPoint = nextPointsInOrder[i]; break; } } } for (int i = snappedPointsInOrder.Count - 1; i >= 0; --i) { drawCircle(snappedPointsInOrder[i], scale * 0.2f, new Color(0.5f, 0.5f, 0.0f), 30.0f); if (pointsInSpreadRangeList[i]) { drawCircle(snappedPointsInOrder[i], scale * 0.25f, new Color(0.0f, 1.0f, 0.0f), 30.0f); } } //float lookAngle = PathingUtil.GetAngle(lookAtPoint - clipPoint); //Vector3 resultPos = clipPoint; drawCircle(clipPoint, scale * 0.4f, new Color(1.0f, 0.5f, 0.0f), 30.0f); return(clipPoint); }
public Vector3 GetDestination(Vector3 goal, float movementBudget, float maxSlope, AbstractActor unit, bool shouldSprint, List <AbstractActor> lanceUnits, PathNodeGrid pathGrid, out Vector3 lookAtPoint) { List <PointWithDistance> startPointList = new List <PointWithDistance>(); List <PathNode> pathNodes = pathGrid.GetSampledPathNodes(); for (int pni = 0; pni < pathNodes.Count; ++pni) { PathNode pn = pathNodes[pni]; PointWithDistance pwd = new PointWithDistance(WorldPointToInclineIndices(pn.Position), pn.DepthInPath * 24, (goal - pn.Position).magnitude); pwd.pathNode = pn; startPointList.Add(pwd); } return(GetDestination(startPointList, goal, movementBudget, maxSlope, unit, shouldSprint, lanceUnits, pathGrid, out lookAtPoint)); }
/// <summary> /// Finds a destination point along a path from start to goal, where the distance from the destination should be /// approximately movementBudget. The path from start to goal will not have inclines nor declines exceeding maxSlope. /// </summary> /// <returns>The destination.</returns> /// <param name="start">Start.</param> /// <param name="goal">Goal.</param> /// <param name="movementBudget">Movement budget.</param> /// <param name="maxSlope">Max slope.</param> /// <param name="unit">unit that is moving</param> /// <param name="shouldSprint">whether to sprint or not</param> /// <param name="lanceUnits">observe lance spread from these units when choosing destinations</param> /// <param name="pathGrid">the pathing grid that indicates where a unit can get to on this turn</param> /// <param name="lookAtPoint">point to look at</param> public Vector3 GetDestination(Vector3 start, Vector3 goal, float movementBudget, float maxSlope, AbstractActor unit, bool shouldSprint, List <AbstractActor> lanceUnits, PathNodeGrid pathGrid, out Vector3 lookAtPoint) { List <PointWithDistance> startPointList = new List <PointWithDistance>(); startPointList.Add(new PointWithDistance(WorldPointToInclineIndices(start), 0, (goal - start).magnitude)); return(GetDestination(startPointList, goal, movementBudget, maxSlope, unit, shouldSprint, lanceUnits, pathGrid, out lookAtPoint)); }
public static bool SavedVsPanic(AbstractActor actor, float savingThrow) { try { AbstractActor defender = null; if (actor is Vehicle vehicle) { if (!modSettings.VehiclesCanPanic) { return(true); } defender = vehicle; } else if (actor is Mech mech) { defender = mech; } if (defender == null) { LogDebug($"defender null, passing save. actor {actor} is type {actor.GetType()}"); return(true); } if (modSettings.QuirksEnabled) { if (defender is Mech m) { if (m.pilot.pilotDef.PilotTags.Contains("pilot_brave")) { savingThrow -= modSettings.BraveModifier; LogReport($"{"Bravery",-20} | {modSettings.BraveModifier,10} | {savingThrow,10:F3}"); } } } var index = GetActorIndex(defender); float panicModifier = GetPanicModifier(TrackedActors[index].PanicStatus); savingThrow *= panicModifier; LogReport($"{"Panic multiplier",-20} | {panicModifier,10} | {savingThrow,10:F3}"); savingThrow = (float)Math.Max(0f, Math.Round(savingThrow)); if (savingThrow < 1) { LogReport(new string('-', 46)); LogReport("Negative saving throw| skipping"); return(true); } var roll = Random.Range(1, 100); LogReport(new string('-', 46)); LogReport($"{"Saving throw",-20} | {savingThrow,-5}{roll,5} | {"Roll",10}"); LogReport(new string('-', 46)); SaySpamFloatie(defender, $"{$"{modSettings.PanicSpamSaveString}:{savingThrow}",-6} {$"{modSettings.PanicSpamRollString}:{roll}!",3}"); // lower panic level on crit success if (roll == 100) { LogReport("Critical success"); SaySpamFloatie(defender, $"{modSettings.PanicSpamCritSaveString}"); TrackedActors[index].PanicStatus--; // just in case the status went down then back up on a crit save in the same round TrackedActors[index].PanicWorsenedRecently = false; return(true); } if (!modSettings.AlwaysPanic && roll >= savingThrow) { LogReport("Successful panic save"); SaySpamFloatie(defender, $"{modSettings.PanicSpamSaveString}!"); return(true); } LogReport("Failed panic save"); SaySpamFloatie(defender, $"{modSettings.PanicSpamFailString}!"); var originalStatus = TrackedActors[index].PanicStatus; if (defender is Vehicle) { TrackedActors[index].PanicStatus = PanicStatus.Panicked; } else { TrackedActors[index].PanicStatus++; } TrackedActors[index].PanicWorsenedRecently = true; // check for panic crit if (roll == 1 || ActorHealth(defender) <= modSettings.MechHealthForCrit && roll < Convert.ToInt32(savingThrow) - modSettings.CritOver) { LogReport("Critical failure on panic save"); defender.Combat.MessageCenter.PublishMessage( new AddSequenceToStackMessage( new ShowActorInfoSequence(defender, modSettings.PanicCritFailString, FloatieMessage.MessageNature.CriticalHit, true))); // ejection can only occur from a stressed or panicked state where panicked requirement is achieved regardless // no crit going from confident to panicked then ejection TrackedActors[index].PanicStatus = PanicStatus.Panicked; } TrackedActors[index].PreventEjection = originalStatus < PanicStatus.Stressed; } catch (Exception ex) { LogDebug(ex); } return(false); }
public static BehaviorNode InitRootNode(BehaviorTree behaviorTree, AbstractActor unit, GameInstance game) { LanceHasLOSNode lanceHasLOS0000 = new LanceHasLOSNode("lanceHasLOS0000", behaviorTree, unit); FindVisibleEnemiesNode findVisibleEnemies0000 = new FindVisibleEnemiesNode("findVisibleEnemies0000", behaviorTree, unit); IsMovementAvailableForUnitNode movementAvailable0000 = new IsMovementAvailableForUnitNode("movementAvailable0000", behaviorTree, unit); SortEnemiesByThreatNode sortEnemiesByThreat0000 = new SortEnemiesByThreatNode("sortEnemiesByThreat0000", behaviorTree, unit); MoveTowardsHighestPriorityEnemyNode moveTowardsHighestPriorityEnemy0000 = new MoveTowardsHighestPriorityEnemyNode("moveTowardsHighestPriorityEnemy0000", behaviorTree, unit); SequenceNode canMove = new SequenceNode("canMove", behaviorTree, unit); canMove.AddChild(movementAvailable0000); canMove.AddChild(sortEnemiesByThreat0000); canMove.AddChild(moveTowardsHighestPriorityEnemy0000); IsAttackAvailableForUnitNode attackAvailable0000 = new IsAttackAvailableForUnitNode("attackAvailable0000", behaviorTree, unit); SortEnemiesByEffectivenessNode sortEnemiesByEffectiveness0000 = new SortEnemiesByEffectivenessNode("sortEnemiesByEffectiveness0000", behaviorTree, unit); ShootAtHighestPriorityEnemyNode shootAtHighestPriorityEnemy0000 = new ShootAtHighestPriorityEnemyNode("shootAtHighestPriorityEnemy0000", behaviorTree, unit); SequenceNode canAttack = new SequenceNode("canAttack", behaviorTree, unit); canAttack.AddChild(attackAvailable0000); canAttack.AddChild(sortEnemiesByEffectiveness0000); canAttack.AddChild(shootAtHighestPriorityEnemy0000); SelectorNode selector0000 = new SelectorNode("selector0000", behaviorTree, unit); selector0000.AddChild(canMove); selector0000.AddChild(canAttack); SequenceNode free_engage = new SequenceNode("free_engage", behaviorTree, unit); free_engage.AddChild(lanceHasLOS0000); free_engage.AddChild(findVisibleEnemies0000); free_engage.AddChild(selector0000); IsMovementAvailableForUnitNode movementAvailable0001 = new IsMovementAvailableForUnitNode("movementAvailable0001", behaviorTree, unit); FindPreviouslySeenEnemiesNode findPreviouslySeenEnemies0000 = new FindPreviouslySeenEnemiesNode("findPreviouslySeenEnemies0000", behaviorTree, unit); SortEnemiesByProximityNode sortEnemiesByProximity0000 = new SortEnemiesByProximityNode("sortEnemiesByProximity0000", behaviorTree, unit); MoveTowardsHighestPriorityEnemyNode moveTowardsHighestPriorityEnemy0001 = new MoveTowardsHighestPriorityEnemyNode("moveTowardsHighestPriorityEnemy0001", behaviorTree, unit); SequenceNode hunt_previously_seen = new SequenceNode("hunt_previously_seen", behaviorTree, unit); hunt_previously_seen.AddChild(movementAvailable0001); hunt_previously_seen.AddChild(findPreviouslySeenEnemies0000); hunt_previously_seen.AddChild(sortEnemiesByProximity0000); hunt_previously_seen.AddChild(moveTowardsHighestPriorityEnemy0001); SelectorNode selector0001 = new SelectorNode("selector0001", behaviorTree, unit); selector0001.AddChild(hunt_previously_seen); FailNode fail0000 = new FailNode("fail0000", behaviorTree, unit); SelectorNode patrol = new SelectorNode("patrol", behaviorTree, unit); patrol.AddChild(fail0000); BraceNode brace0000 = new BraceNode("brace0000", behaviorTree, unit); SelectorNode dumb_AI_root = new SelectorNode("dumb_AI_root", behaviorTree, unit); dumb_AI_root.AddChild(free_engage); dumb_AI_root.AddChild(selector0001); dumb_AI_root.AddChild(patrol); dumb_AI_root.AddChild(brace0000); return(dumb_AI_root); }
// false is punchin' out public static bool SavedVsEject(AbstractActor actor, float savingThrow) { LogReport("Panic save failure requires eject save"); try { if (actor.IsPilotable && actor.GetPilot() != null && actor.GetPilot().StatCollection.GetValue <bool>("CanEject") == false) { LogReport($"Pilot CanEject Stat false - {(modSettings.ObeyPilotCanEjectStat ? "":"NOT")} obeying"); LogActor(actor, true); if (modSettings.ObeyPilotCanEjectStat) { return(true); } } if (actor.IsPilotable && actor.GetPilot() != null && actor.GetPilot().pilotDef.PilotTags.Contains("pilot_cannot_eject")) { LogReport($"Pilot pilot_cannot_eject Tag set - {(modSettings.ObeyPilotCannotEjectTag ? "" : "NOT")} obeying"); LogActor(actor, true); if (modSettings.ObeyPilotCannotEjectTag) { return(true); } } } catch (Exception ex) { LogDebug(ex); } var pilotTracker = TrackedActors.First(tracker => tracker.Guid == actor.GUID); if (pilotTracker.PreventEjection) { LogReport("Ejection forbidden after crit unless already stressed or panicked"); pilotTracker.PreventEjection = false; return(true); } DrawHeader(); if (actor is Mech mech && modSettings.QuirksEnabled) { if (mech.pilot.pilotDef.PilotTags.Contains("pilot_dependable")) { savingThrow -= modSettings.DependableModifier; LogReport($"{"Dependable",-20} | {modSettings.DependableModifier,10} | {savingThrow,10:F3}"); } } // calculate result if (modSettings.VehiclesCanPanic && actor is Vehicle) { savingThrow = Math.Max(0f, savingThrow - modSettings.BaseVehicleEjectionResist); LogReport($"{"Base ejection resist",-20} | {modSettings.BaseVehicleEjectionResist,10} | {savingThrow,10:F3}"); } else if (actor is Mech) { savingThrow = Math.Max(0f, savingThrow - modSettings.BaseEjectionResist); LogReport($"{"Base ejection resist",-20} | {modSettings.BaseEjectionResist,10} | {savingThrow,10:F3}"); } savingThrow = (float)Math.Round(savingThrow); LogReport($"{"Eject multiplier",-20} | {modSettings.EjectChanceFactor,10} | {savingThrow,10:F3}"); var roll = Random.Range(1, 100); LogReport(new string('-', 46)); LogReport($"{"Saving throw",-20} | {savingThrow,-5:###}{roll,5} | {"Roll",10}"); LogReport(new string('-', 46)); if (!modSettings.AlwaysPanic && savingThrow < 1) { LogReport("Negative saving throw| skipping"); SaySpamFloatie(actor, $"{modSettings.PanicSpamEjectResistString}"); return(true); } // cap the saving throw by the setting savingThrow = (int)Math.Min(savingThrow, modSettings.MaxEjectChance); SaySpamFloatie(actor, $"{modSettings.PanicSpamSaveString}:{savingThrow} {modSettings.PanicSpamRollString}:{roll}!"); if (!modSettings.AlwaysPanic && roll >= savingThrow) { LogReport("Successful ejection save"); SaySpamFloatie(actor, $"{modSettings.PanicSpamSaveString}! {ActorHealth(actor):#.#}%"); return(true); } // TODO can it be written if (mech != null) ? I don't know and testing it is a PITA! if (actor is Mech m) { if (modSettings.QuirksEnabled && m.MechDef.Chassis.ChassisTags.Contains("mech_quirk_noeject")) { LogReport("This mech can't eject (quirk)"); actor.Combat.MessageCenter.PublishMessage( new AddSequenceToStackMessage( new ShowActorInfoSequence(actor, "Mech quirk: Can't eject", FloatieMessage.MessageNature.PilotInjury, true))); return(true); } if (modSettings.QuirksEnabled && m.pilot.pilotDef.PilotTags.Contains("pilot_drunk") && m.pilot.pilotDef.TimeoutRemaining > 0) { LogReport("Drunkard - not ejecting"); actor.Combat.MessageCenter.PublishMessage( new AddSequenceToStackMessage( new ShowActorInfoSequence(actor, "Pilot quirk: Drunkard won't eject", FloatieMessage.MessageNature.PilotInjury, true))); return(true); } } LogReport("Failed ejection save: Punchin\' Out!!"); return(false); }
public FilterNonLOSOrLOFMovesNode(string name, BehaviorTree tree, AbstractActor unit) : base(name, tree, unit) { }
public static void Prefix(AbstractActor __instance) { DamageHelper.completedTurnFor(__instance); }
public static void Prefix(AbstractActor __instance) { VisibilityCacheGate.EnterGate(); GateActive = true; counter = VisibilityCacheGate.GetCounter; }
public static void Postfix(ToHit __instance, ref float __result, CombatGameState ___combat, AbstractActor attacker, Weapon weapon, ICombatant target) { try { if (DemandingJumps.Settings.ToHitSelfJumpedSpareAI && !___combat.LocalPlayerTeam.IsActive) { return; } Logger.Info("[ToHit_GetAllModifiers_POSTFIX] CombatGameState.LocalPlayerTeam.IsActive: " + ___combat.LocalPlayerTeam.IsActive); bool AttackerJumpedThisRound = attacker.HasMovedThisRound && attacker.JumpedLastRound; Logger.Info("[ToHit_GetAllModifiers_POSTFIX] Fields.JumpPreview: " + Fields.JumpPreview); Logger.Info("[ToHit_GetAllModifiers_POSTFIX] AttackerJumpedThisRound: " + AttackerJumpedThisRound); if (Fields.JumpPreview || AttackerJumpedThisRound) { int ToHitSelfJumpedModifier = Utilities.GetAttackerJumpedAccuracyModifier(attacker); Logger.Info("[ToHit_GetAllModifiers_POSTFIX] Unit previews jump or already jumped. Applying ToHit penalty."); __result = __result + (float)ToHitSelfJumpedModifier; } } catch (Exception e) { Logger.Error(e); } }
public static bool IsBeatingDeadMech(AbstractActor __instance) { return(__instance.IsFlaggedForDeath || __instance.IsDead); }
private static bool Prefix(ref float __result, AbstractActor unit, AbstractActor target, Vector3 targetPosition, int pipsRemoved) { Mod.Log.Trace($" ---- AE_ADFP: Building list."); List <AbstractActor> list = new List <AbstractActor>(); Dictionary <int, List <AbstractActor> > dictionary = new Dictionary <int, List <AbstractActor> >(); int i; for (i = Mod.MinPhase; i <= Mod.MaxPhase; i++) { dictionary[i] = new List <AbstractActor>(); } Mod.Log.Trace($" ---- AE_ADFP: Mapping lance to init."); for (int j = 0; j < unit.lance.unitGuids.Count; j++) { string text = unit.lance.unitGuids[j]; if (!(text == unit.GUID)) { AbstractActor itemByGUID = unit.Combat.ItemRegistry.GetItemByGUID <AbstractActor>(text); int initiative = itemByGUID.Initiative; dictionary[initiative].Add(itemByGUID); } } Mod.Log.Trace($" ---- AE_ADFP: Mapping all actors."); int currentPhase = unit.Combat.TurnDirector.CurrentPhase; for (int k = 0; k < dictionary[currentPhase].Count; k++) { AbstractActor abstractActor = dictionary[currentPhase][k]; if (!abstractActor.HasActivatedThisRound) { list.Add(abstractActor); } } Mod.Log.Trace($" ---- AE_ADFP: actors for init"); i = currentPhase; while (target.Initiative != i) { i++; if (i > Mod.MaxPhase) { i = Mod.MinPhase; } for (int l = 0; l < dictionary[i].Count; l++) { AbstractActor item = dictionary[i][l]; list.Add(item); } } Mod.Log.Trace($" ---- AE_ADFP: Calulating"); float num = 0f; float num2 = 0f; int evasivePipsCurrent = target.EvasivePipsCurrent; int evasivePipsCurrent2 = Mathf.Max(0, evasivePipsCurrent - pipsRemoved); for (int m = 0; m < list.Count; m++) { AbstractActor abstractActor2 = list[m]; for (int n = 0; n < abstractActor2.Weapons.Count; n++) { Weapon weapon = abstractActor2.Weapons[n]; if (weapon.CanFire && abstractActor2.HasLOFToTargetUnit(target, weapon)) { target.EvasivePipsCurrent = evasivePipsCurrent; float toHitFromPosition = weapon.GetToHitFromPosition(target, 1, abstractActor2.CurrentPosition, target.CurrentPosition, true, true, false); num2 += toHitFromPosition * weapon.DamagePerShot * (float)weapon.ShotsWhenFired; target.EvasivePipsCurrent = evasivePipsCurrent2; toHitFromPosition = weapon.GetToHitFromPosition(target, 1, abstractActor2.CurrentPosition, target.CurrentPosition, true, true, false); num += toHitFromPosition * weapon.DamagePerShot * (float)weapon.ShotsWhenFired; } } } target.EvasivePipsCurrent = evasivePipsCurrent; __result = num - num2; return(false); }
public AIMCritInfo(AbstractActor target, WeaponHitInfo hitInfo, Weapon weapon) { this.target = target; this.hitInfo = hitInfo; this.weapon = weapon; }
public static bool Prefix(CombatAuraReticle __instance, bool showActiveProbe, ref AbstractActor ___owner, ref float ___currentAPRange) { try { GameObject activeProbeRangeScaledObject = __instance.activeProbeRangeScaledObject(); if (showActiveProbe == false) { activeProbeRangeScaledObject.SetActive(false); return(false); } //AuraBubble mainSensorsBubble = __instance.MainSensors(); //Log.LogWrite("CombatAuraReticle.RefreshAuraRange " + (mainSensorsBubble == null ? "null" : mainSensorsBubble.collider.radius.ToString()) + "\n"); //if (mainSensorsBubble != null) { //auraRangeScaledObject.SetActive(true); //float b = mainSensorsBubble.collider.radius; //if (!Mathf.Approximately(___currentAuraRange, b)) { //auraRangeScaledObject.transform.localScale = new Vector3(b * 2f, 1f, b * 2f); //} //___currentAuraRange = b; //return false; //} AuraBubble auraBubble = __instance.AuraBubble(); if (auraBubble != null) { activeProbeRangeScaledObject.SetActive(true); float b = auraBubble.collider.radius; if (!Mathf.Approximately(___currentAPRange, b)) { activeProbeRangeScaledObject.transform.localScale = new Vector3(b * 2f, 1f, b * 2f); } ___currentAPRange = b; return(false); } } catch (Exception e) { Log.LogWrite(e.ToString() + "\n", true); } return(true); }
private static bool Prefix(AbstractActor __instance) { return(false); }
public static bool isAuraVisible(this CombatAuraReticle __instance, AuraBubble aura, AbstractActor ___owner, CombatHUD ___HUD, bool spinning) { if (CombatHUD_Update_HideReticlesHotKey.hideReticles == AuraShowState.HideAll) { return(false); } ; if ((___owner.IsVisibleToPlayer() == false) || (___owner.IsOperational == false)) { return(false); } if (aura != null) { if (aura.Def.isSpining != spinning) { return(false); } if (aura.isMainSensors) { if (CombatHUD_Update_HideReticlesHotKey.hideReticles == AuraShowState.ShowAll) { return(true); } if (aura.Def.NotShowOnSelected) { return(false); } ; if (aura.Def.HideOnNotSelected) { if (___HUD.SelectedActor != null) { if (___HUD.SelectedActor.GUID == ___owner.GUID) { return(true); } } return(false); } else { return(true); } } if (aura.source != null) { if (aura.Def.isSpining != spinning) { return(false); } Weapon weapon = aura.source as Weapon; if ((weapon == null) || (aura.Def.Id != "AMS")) { if (CombatHUD_Update_HideReticlesHotKey.hideReticles == AuraShowState.ShowAll) { return(true); } if (aura.Def.NotShowOnSelected) { return(false); } ; if (aura.Def.HideOnNotSelected) { if (___HUD.SelectedActor != null) { if (___HUD.SelectedActor.GUID == ___owner.GUID) { return(true); } } return(false); } else { return(true); } } else { if (weapon.isAMS() == false) { return(false); } if (weapon.IsEnabled == false) { return(false); } if (CombatHUD_Update_HideReticlesHotKey.hideReticles == AuraShowState.ShowAll) { return(true); } if (aura.Def.NotShowOnSelected) { return(false); } ; if (aura.Def.HideOnNotSelected) { if (___HUD.SelectedActor != null) { if (___HUD.SelectedActor.GUID == ___owner.GUID) { return(true); } } return(false); } else { return(true); } } } else { return(false); } } return(false); }
public static float GetSavingThrow(AbstractActor defender, AbstractActor attacker, int heatDamage, float damageIncludingHeatDamage) { var pilot = defender.GetPilot(); var weapons = defender.Weapons; var gutsAndTacticsSum = defender.SkillGuts * modSettings.GutsEjectionResistPerPoint + defender.SkillTactics * modSettings.TacticsEjectionResistPerPoint; float totalMultiplier = 0; DrawHeader(); LogReport($"{$"Unit health {ActorHealth(defender):F2}%",-20} | {"",10} |"); if (defender is Mech defendingMech) { try { if (modSettings.QuirksEnabled && attacker != null && attacker is Mech mech && mech.MechDef.Chassis.ChassisTags.Contains("mech_quirk_distracting")) { totalMultiplier += modSettings.DistractingModifier; LogReport($"{"Distracting mech",-20} | {modSettings.DistractingModifier,10:F3} | {totalMultiplier,10:F3}"); } #if NO_CAC if (modSettings.HeatDamageFactor > 0) { #else if (modSettings.HeatDamageFactor > 0 && defender.isHasHeat()) { #endif totalMultiplier += modSettings.HeatDamageFactor * heatDamage; LogReport($"{$"Heat damage {heatDamage}",-20} | {modSettings.HeatDamageFactor * heatDamage,10:F3} | {totalMultiplier,10:F3}"); } float percentPilot = PercentPilot(pilot); if (percentPilot < 1) { totalMultiplier += modSettings.PilotHealthMaxModifier * percentPilot; LogReport($"{"Pilot injuries",-20} | {modSettings.PilotHealthMaxModifier * percentPilot,10:F3} | {totalMultiplier,10:F3}"); } if (defendingMech.IsUnsteady) { totalMultiplier += modSettings.UnsteadyModifier; LogReport($"{"Unsteady",-20} | {modSettings.UnsteadyModifier,10} | {totalMultiplier,10:F3}"); } if (defendingMech.IsFlaggedForKnockdown) { totalMultiplier += modSettings.UnsteadyModifier; LogReport($"{"Knockdown",-20} | {modSettings.UnsteadyModifier,10} | {totalMultiplier,10:F3}"); } if (modSettings.OverheatedModifier > 0 && defendingMech.OverheatLevel < defendingMech.CurrentHeat) { totalMultiplier += modSettings.OverheatedModifier; LogReport($"{"Heat",-20} | {modSettings.OverheatedModifier,10:F3} | {totalMultiplier,10:F3}"); } if (modSettings.ShutdownModifier > 0 && defendingMech.IsShutDown) { totalMultiplier += modSettings.ShutdownModifier; LogReport($"{"Shutdown",-20} | {modSettings.ShutdownModifier,10:F3} | {totalMultiplier,10:F3}"); } float percentHead = PercentHead(defendingMech); if (percentHead < 1) { totalMultiplier += modSettings.HeadMaxModifier * (1 - percentHead); LogReport($"{"Head",-20} | {modSettings.HeadMaxModifier * (1 - percentHead),10:F3} | {totalMultiplier,10:F3}"); } float percentCenterTorso = PercentCenterTorso(defendingMech); if (percentCenterTorso < 1) { totalMultiplier += modSettings.CenterTorsoMaxModifier * (1 - percentCenterTorso); LogReport($"{"CT",-20} | {modSettings.CenterTorsoMaxModifier * (1 - percentCenterTorso),10:F3} | {totalMultiplier,10:F3}"); } float percentLeftTorso = PercentLeftTorso(defendingMech); if (percentLeftTorso < 1) { totalMultiplier += modSettings.SideTorsoMaxModifier * (1 - percentLeftTorso); LogReport($"{"LT",-20} | {modSettings.SideTorsoMaxModifier * (1 - percentLeftTorso),10:F3} | {totalMultiplier,10:F3}"); } float percentRightTorso = PercentRightTorso(defendingMech); if (percentRightTorso < 1) { totalMultiplier += modSettings.SideTorsoMaxModifier * (1 - percentRightTorso); LogReport($"{"RT",-20} | {modSettings.SideTorsoMaxModifier * (1 - percentRightTorso),10:F3} | {totalMultiplier,10:F3}"); } float percentLeftLeg = PercentLeftLeg(defendingMech); if (percentLeftLeg < 1) { totalMultiplier += modSettings.LeggedMaxModifier * (1 - percentLeftLeg); LogReport($"{"LL",-20} | {modSettings.LeggedMaxModifier * (1 - percentLeftLeg),10:F3} | {totalMultiplier,10:F3}"); } float percentRightLeg = PercentRightLeg(defendingMech); if (percentRightLeg < 1) { totalMultiplier += modSettings.LeggedMaxModifier * (1 - percentRightLeg); LogReport($"{"RL",-20} | {modSettings.LeggedMaxModifier * (1 - percentRightLeg),10:F3} | {totalMultiplier,10:F3}"); } // alone if (defendingMech.Combat.GetAllAlliesOf(defendingMech).TrueForAll(m => m.IsDead || m == defendingMech)) { if (Random.Range(1, 5) == 0) // 20% chance of appearing { SaySpamFloatie(defendingMech, $"{modSettings.PanicSpamAloneString}"); } totalMultiplier += modSettings.AloneModifier; LogReport($"{"Alone",-20} | {modSettings.AloneModifier,10} | {totalMultiplier,10:F3}"); } else if (defendingMech.Combat.GetAllAlliesOf(defendingMech).Count() > 0) { int alliesdead = defendingMech.Combat.GetAllAlliesOf(defendingMech).Where(m => m.IsDead).Count(); int alliestotal = defendingMech.Combat.GetAllAlliesOf(defendingMech).Count(); totalMultiplier += modSettings.AloneModifier * alliesdead / alliestotal; LogReport($"{$"Alone {alliesdead}/{alliestotal}",-20} | {modSettings.AloneModifier * alliesdead / alliestotal,10:F3} | {totalMultiplier,10:F3}"); } } catch (Exception ex) { // BOMB LogReport(ex); return(-1f); } } // weaponless if (weapons.TrueForAll(w => w.DamageLevel != ComponentDamageLevel.Functional || !w.HasAmmo)) // only fully unusable { if (Random.Range(1, 5) == 1) // 20% chance of appearing { SaySpamFloatie(defender, $"{modSettings.PanicSpamNoWeaponsString}"); } totalMultiplier += modSettings.WeaponlessModifier; LogReport($"{"Weaponless",-20} | {modSettings.WeaponlessModifier,10} | {totalMultiplier,10:F3}"); } // directly override the multiplier for vehicles if (modSettings.VehiclesCanPanic && defender is Vehicle defendingVehicle) { float percentTurret = PercentTurret(defendingVehicle); if (percentTurret < 1) { totalMultiplier += modSettings.VehicleDamageFactor * (1 - percentTurret); LogReport($"{"T",-20} | {modSettings.VehicleDamageFactor * (1 - percentTurret),10:F3} | {totalMultiplier,10:F3}"); } float percentLeft = PercentLeft(defendingVehicle); if (percentLeft < 1) { totalMultiplier += modSettings.VehicleDamageFactor * (1 - percentLeft); LogReport($"{"L",-20} | {modSettings.VehicleDamageFactor * (1 - percentLeft),10:F3} | {totalMultiplier,10:F3}"); } float percentRight = PercentRight(defendingVehicle); if (percentRight < 1) { totalMultiplier += modSettings.VehicleDamageFactor * (1 - percentRight); LogReport($"{"R",-20} | {modSettings.VehicleDamageFactor * (1 - percentRight),10:F3} | {totalMultiplier,10:F3}"); } float percentFront = PercentFront(defendingVehicle); if (percentFront < 1) { totalMultiplier += modSettings.VehicleDamageFactor * (1 - percentFront); LogReport($"{"F",-20} | {modSettings.VehicleDamageFactor * (1 - percentFront),10:F3} | {totalMultiplier,10:F3}"); } float percentRear = PercentRear(defendingVehicle); if (percentRear < 1) { totalMultiplier += modSettings.VehicleDamageFactor * (1 - percentRear); LogReport($"{"B",-20} | {modSettings.VehicleDamageFactor * (1 - percentRear),10:F3} | {totalMultiplier,10:F3}"); } LogReport($"{"Vehicle state",-20} | {modSettings.VehicleDamageFactor,10} | {totalMultiplier,10:F3}"); // alone if (defendingVehicle.Combat.GetAllAlliesOf(defendingVehicle).TrueForAll(m => m.IsDead || m == defendingVehicle)) { if (Random.Range(1, 5) == 0) // 20% chance of appearing { SaySpamFloatie(defendingVehicle, $"{modSettings.PanicSpamAloneString}"); } totalMultiplier += modSettings.AloneModifier; LogReport($"{"Alone",-20} | {modSettings.AloneModifier,10} | {totalMultiplier,10:F3}"); } else if (defendingVehicle.Combat.GetAllAlliesOf(defendingVehicle).Count() > 0) { int alliesdead = defendingVehicle.Combat.GetAllAlliesOf(defendingVehicle).Where(m => m.IsDead).Count(); int alliestotal = defendingVehicle.Combat.GetAllAlliesOf(defendingVehicle).Count(); totalMultiplier += modSettings.AloneModifier * alliesdead / alliestotal; LogReport($"{$"Alone {alliesdead}/{alliestotal}",-20} | {modSettings.AloneModifier * alliesdead / alliestotal,10:F3} | {totalMultiplier,10:F3}"); } } var resolveModifier = modSettings.ResolveMaxModifier * (defender.Combat.LocalPlayerTeam.Morale - modSettings.MedianResolve) / modSettings.MedianResolve; if (modSettings.VehiclesCanPanic && defender is Vehicle) { resolveModifier *= modSettings.VehicleResolveFactor; } totalMultiplier -= resolveModifier; LogReport($"{$"Resolve {defender.Combat.LocalPlayerTeam.Morale}",-20} | {resolveModifier * -1,10:F3} | {totalMultiplier,10:F3}"); if (modSettings.VehiclesCanPanic && defender is Vehicle) { gutsAndTacticsSum *= modSettings.VehicleGutAndTacticsFactor; } totalMultiplier -= gutsAndTacticsSum; LogReport($"{"Guts and Tactics",-20} | {$"-{gutsAndTacticsSum}",10} | {totalMultiplier,10:F3}"); return(totalMultiplier); }
public static void AbstractActor_InitEffectStats_Postfix(AbstractActor __instance) { Mod.Log.Trace("AA:IES entered."); __instance.StatCollection.AddStatistic <Int32>(ModStats.CalledShotMod, 0); __instance.StatCollection.AddStatistic <bool>(ModStats.CalledShowAlwaysAllow, false); }
internal static void PublishFloatieMessage(this AbstractActor actor, string text, FloatieMessage.MessageNature nature = FloatieMessage.MessageNature.CriticalHit) { actor.Combat.MessageCenter.PublishMessage(new FloatieMessage(actor.GUID, actor.GUID, text, nature)); }