public static void Postfix(StatTooltipData __instance, MechDef def) { try { var movement = def.GetEngineMovement(); if (movement == null) { return; } var tooltipData = __instance; tooltipData.dataList.Remove("Max Move"); tooltipData.dataList.Remove("Max Sprint"); var combat = CombatGameConstants.GetInstance(UnityGameInstance.BattleTechGame); var hexWidth = combat.MoveConstants.ExperimentalGridDistance; __instance.dataList.Add("Max Move", $"{movement.WalkSpeed}m / {Mathf.Round(movement.WalkSpeed / hexWidth)} hex"); __instance.dataList.Add("Max Sprint", $"{movement.RunSpeed}m / {Mathf.Round(movement.RunSpeed / hexWidth)} hex"); __instance.dataList.Add("TT Walk MP", $"{movement.MovementPoint}"); } catch (Exception e) { Control.mod.Logger.LogError(e); } }
private static AbstractActor InitAbstractActor(AbstractActor actor) { // Init the combat ref for constants ConstructorInfo constantsCI = AccessTools.Constructor(typeof(CombatGameConstants), new Type[] { }); CombatGameConstants constants = (CombatGameConstants)constantsCI.Invoke(new object[] { }); CombatGameState cgs = new CombatGameState(); Traverse constantsT = Traverse.Create(cgs).Property("Constants"); constantsT.SetValue(constants); Traverse combatT = Traverse.Create(actor).Property("Combat"); combatT.SetValue(cgs); // Init any required stats actor.StatCollection = new StatCollection(); // ModStats //actor.StatCollection.AddStatistic<int>(ModStats.PunchAttackMod, 0); // Vanilla actor.StatCollection.AddStatistic <float>("SensorSignatureModifier", 1.0f); return(actor); }
static UnitRole getRoleByPriorityIndex(CombatGameConstants constants, int index) { // TODO - I'm ignoring the priority value for now and just hardcoding it to be brawler, sniper, scout int[] counts = new int[3]; counts[0] = constants.DynamicAIRoleConstants.brawlerMinAbs; counts[1] = constants.DynamicAIRoleConstants.sniperMinAbs; UnitRole[] dynamicRoles = { UnitRole.Brawler, UnitRole.Sniper }; for (int roleIndex = 0; roleIndex < 3; ++roleIndex) { if (index < counts[roleIndex]) { return(dynamicRoles[roleIndex]); } index -= counts[roleIndex]; } return(UnitRole.Undefined); }
static void fillOutMinimums(List <AbstractActor> units) { if (units.Count == 0) { return; } CombatGameConstants constants = units[0].Combat.Constants; // TODO be smarter about doing an assignment based on priority and by fit int constrainedMinimumCount = constants.DynamicAIRoleConstants.brawlerMinAbs + constants.DynamicAIRoleConstants.sniperMinAbs; if (units.Count < constrainedMinimumCount) { // this is where we'd fill out by priority for (int i = 0; i < units.Count; ++i) { UnitRole role = getRoleByPriorityIndex(constants, i); units[i].DynamicUnitRole = role; } } else { for (int i = 0; i < constrainedMinimumCount; ++i) { UnitRole role = getRoleByPriorityIndex(constants, i); units[i].DynamicUnitRole = role; } } }
public static HeatConstantsDef OverrideHeat(this CombatGameConstants @this) { var heat = @this.Heat; heat.ShutdownCausesInjury = overrideShutdownCausesInjury || heat.ShutdownCausesInjury; return(heat); }
static void Postfix(MechBayMechInfoWidget __instance) { ModBase.Sim = __instance.sim; if (ModBase.combatConstants == null) { ModBase.combatConstants = CombatGameConstants.CreateFromSaved(UnityGameInstance.BattleTechGame); } }
public static HeatConstantsDef OverrideHeat(this CombatGameConstants @this) { var heat = @this.Heat; if (ShutdownInjuryProtectionFeature.settings.ShutdownInjuryEnabled) { heat.ShutdownCausesInjury = receiveShutdownInjury; } return(heat); }
private static AbstractActor InitAbstractActor(AbstractActor actor) { // Init the combat ref for constants ConstructorInfo constantsCI = AccessTools.Constructor(typeof(CombatGameConstants), new Type[] { }); CombatGameConstants constants = (CombatGameConstants)constantsCI.Invoke(new object[] { }); VisibilityConstantsDef visibilityDef = constants.Visibility; visibilityDef.UseAsymmetricalSensors = true; visibilityDef.ShutDownSignatureModifier = 0.5f; visibilityDef.ShutDownVisibilityModifier = 0.5f; Traverse visibilityT = Traverse.Create(constants).Property("Visibility"); visibilityT.SetValue(visibilityDef); CombatGameState cgs = new CombatGameState(); Traverse constantsT = Traverse.Create(cgs).Property("Constants"); constantsT.SetValue(constants); Traverse combatT = Traverse.Create(actor).Property("Combat"); combatT.SetValue(cgs); // Init any required stats actor.StatCollection = new StatCollection(); // ModStats actor.StatCollection.AddStatistic <int>(ModStats.TacticsMod, 0); actor.StatCollection.AddStatistic <int>(ModStats.CurrentRoundEWCheck, 0); actor.StatCollection.AddStatistic <int>(ModStats.ECMShield, 0); actor.StatCollection.AddStatistic <int>(ModStats.ECMJamming, 0); actor.StatCollection.AddStatistic <int>(ModStats.AdvancedSensors, 0); actor.StatCollection.AddStatistic <int>(ModStats.ProbeCarrier, 0); actor.StatCollection.AddStatistic <int>(ModStats.PingedByProbe, 0); actor.StatCollection.AddStatistic <string>(ModStats.StealthEffect, ""); actor.StatCollection.AddStatistic <string>(ModStats.MimeticEffect, ""); actor.StatCollection.AddStatistic <int>(ModStats.MimeticCurrentSteps, 0); actor.StatCollection.AddStatistic <string>(ModStats.HeatVision, ""); actor.StatCollection.AddStatistic <string>(ModStats.ZoomVision, ""); actor.StatCollection.AddStatistic <string>(ModStats.NarcEffect, ""); actor.StatCollection.AddStatistic <string>(ModStats.TagEffect, ""); actor.StatCollection.AddStatistic <bool>(ModStats.SharesVision, false); actor.StatCollection.AddStatistic <bool>(ModStats.NightVision, false); actor.StatCollection.AddStatistic <int>(ModStats.DisableSensors, 2); // Vanilla actor.StatCollection.AddStatistic <float>("SensorSignatureModifier", 1.0f); return(actor); }
public static bool Override_CombatGameConstants_LoadFromManifest(CombatGameConstants __instance) { try { if (CombatConstantJSON == null) { return(true); } __instance.FromJSON(UnZipStr(CombatConstantJSON)); LoadMoraleResources?.Invoke(__instance, null); LoadMaintenanceResources?.Invoke(__instance, null); return(false); } catch (Exception ex) { return(Error(ex)); } }
static bool unitsMeetMinimums(List <AbstractActor> units) { if (units.Count == 0) { return(true); } Dictionary <UnitRole, int> counts = countRolesAfterAssignment(null, units); CombatGameConstants constants = units[0].Combat.Constants; return((counts[UnitRole.Brawler] >= constants.DynamicAIRoleConstants.brawlerMinAbs) && (counts[UnitRole.Sniper] >= constants.DynamicAIRoleConstants.sniperMinAbs)); }
internal static void ModifiedCombatGameConstantsViaAdvangedMerge() { try { var constants = CombatGameConstants.GetInstance(UnityGameInstance.BattleTechGame); var hit = constants.HitTables.HitMechLocationFromFront[ArmorLocation.CenterTorso]; var expected = 9; if (hit == expected) { Control.Logger.Log($"{LogPrefix}CombatGameConstants HitMechLocationFromFront[CenterTorso] was expected {expected}."); } else { Control.Logger.LogError($"{LogPrefix}CombatGameConstants HitMechLocationFromFront[CenterTorso] was not expected {expected}, it was {hit}."); } } catch (Exception e) { Control.Logger.LogError($"{LogPrefix}Could not validate CombatGameConstants modification", e); } }
internal static void ModifiedCombatGameConstantsViaNormaldMerge() { try { var constants = CombatGameConstants.GetInstance(UnityGameInstance.BattleTechGame); var hit = constants.Heat.InternalHeatSinkCount; var expected = 42; if (hit == expected) { Control.Logger.Log($"{LogPrefix}CombatGameConstants.Heat.InternalHeatSinkCount was expected {expected}."); } else { Control.Logger.LogError($"{LogPrefix}CombatGameConstants.Heat.InternalHeatSinkCount was not expected {expected} it was {hit}."); } } catch (Exception e) { Control.Logger.LogError($"{LogPrefix}Could not validate CombatGameConstants modification", e); } }
public static void Postfix(StatTooltipData __instance, MechDef def) { try { if (!def.Chassis.ChassisTags.Contains("chassis_heatsinks")) { return; } Logger.Debug("[StatTooltipData_SetHeatData_POSTFIX] (" + def.Description.Id + ") def.Chassis.Heatsinks: " + def.Chassis.Heatsinks); CombatGameConstants cgc = CombatGameConstants.GetInstance(UnityGameInstance.BattleTechGame); float num = ((float)def.Chassis.Heatsinks + (float)cgc.Heat.InternalHeatSinkCount) * cgc.Heat.DefaultHeatSinkDissipationCapacity; Logger.Debug("[StatTooltipData_SetHeatData_POSTFIX] (" + def.Description.Id + ") ChassisDissipationCapacity: " + num); for (int i = 0; i < def.Inventory.Length; i++) { if (def.Inventory[i].ComponentDefType == ComponentType.HeatSink) { HeatSinkDef heatSinkDef = def.Inventory[i].Def as HeatSinkDef; if (heatSinkDef != null) { num += heatSinkDef.DissipationCapacity; } } } Logger.Debug("[StatTooltipData_SetHeatData_POSTFIX] (" + def.Description.Id + ") TotalDissipationCapacity: " + num); __instance.dataList.Remove("Heat Sinking"); __instance.dataList.Add(Strings.T("Heat Sinking"), Strings.T("{0} Heat", new object[] { num.ToString() })); } catch (Exception e) { Logger.Error(e); } }
static void Postfix(object data, ref bool __result, BattleTech.UI.Tooltips.TooltipPrefab_Generic __instance) { if (!__result || ModBase.currentMech == null) { return; } if ((ModBase.Sim == null || ModBase.Sim.CurRoomState != DropshipLocation.MECH_BAY) && !ModBase.inMechLab) { return; } if (ModBase.combatConstants == null) { ModBase.combatConstants = CombatGameConstants.CreateFromSaved(UnityGameInstance.BattleTechGame); } MechDef currentMech = ModBase.currentMech; ChassisDef currentChassis = currentMech.Chassis; BaseDescriptionDef baseDescriptionDef = (BaseDescriptionDef)data; string extra_stats = string.Empty; switch (baseDescriptionDef.Id) { case "TooltipMechPerformanceFirepower": float alpha_damage = 0; float alpha_instability = 0; float alpha_heat = 0; // Calculate alpha strike total damage, heat damage and instability. foreach (MechComponentRef mechComponentRef in currentMech.Inventory) { if (mechComponentRef.Def is WeaponDef weapon) { alpha_damage += weapon.Damage * weapon.ShotsWhenFired; alpha_instability += weapon.Instability * weapon.ShotsWhenFired; alpha_heat += weapon.HeatDamage * weapon.ShotsWhenFired; } } if (alpha_damage > 0) { if (alpha_heat > 0) { extra_stats += string.Format("Alpha strike damage: <b>{0}</b> ( <b>{1} H</b> )\n", alpha_damage, alpha_heat); } else { extra_stats += string.Format("Alpha strike damage: <b>{0}</b>\n", alpha_damage); } } if (alpha_instability > 0) { extra_stats += string.Format("Alpha strike stability damage: <b>{0}</b>\n", alpha_instability); } break; case "TooltipMechPerformanceHeat": HeatConstantsDef heatConstants = ModBase.combatConstants.Heat; float total_heat_sinking = heatConstants.InternalHeatSinkCount * heatConstants.DefaultHeatSinkDissipationCapacity; float extra_engine_heat_sinking = BTMechDef.GetExtraEngineSinking(currentMech); total_heat_sinking += extra_engine_heat_sinking; float heat_sinking_ratio = 1; float total_weapon_heat = 0; float weapon_heat_ratio = 1; float jump_distance = BTMechDef.GetJumpJetsMaxDistance(currentMech); int max_heat = heatConstants.MaxHeat; foreach (MechComponentRef mechComponentRef in currentMech.Inventory) { if (mechComponentRef.Def == null) { mechComponentRef.RefreshComponentDef(); } // Weapon total heat if (mechComponentRef.Def is WeaponDef weapon) { total_weapon_heat += (float)weapon.HeatGenerated; } // Heat sink total dissipation else if (mechComponentRef.Def is HeatSinkDef heat_sink) { total_heat_sinking += heat_sink.DissipationCapacity; } // Bank/Exchanger effects if (mechComponentRef.Def.statusEffects != null) { foreach (EffectData effect in mechComponentRef.Def.statusEffects) { StatisticEffectData statisticData = effect.statisticData; if (statisticData.statName == "MaxHeat") { BTStatistics.ApplyEffectStatistic(statisticData, ref max_heat); } else if (statisticData.statName == "HeatGenerated" && statisticData.targetCollection == StatisticEffectData.TargetCollection.Weapon) { BTStatistics.ApplyEffectStatistic(statisticData, ref weapon_heat_ratio); } else if (statisticData.statName == "JumpDistanceMultiplier") { BTStatistics.ApplyEffectStatistic(statisticData, ref jump_distance); } } } } total_weapon_heat *= weapon_heat_ratio; total_heat_sinking *= heat_sinking_ratio; extra_stats += string.Format("Heat dissipation: <b>{0}</b>\n", (int)total_heat_sinking); if (extra_engine_heat_sinking > 0f) { extra_stats += "Engine heat sinks: <b>double</b>\n"; } if (total_weapon_heat > 0) { extra_stats += string.Format("Alpha strike heat: <b>{0}</b>\n", (int)total_weapon_heat); extra_stats += string.Format("Alpha strike heat delta: <b>{0}</b>\n", (int)(total_weapon_heat - total_heat_sinking)); } extra_stats += string.Format("Max heat capacity: <b>{0}</b>\n", (int)max_heat); if (jump_distance > 0) { float max_jump_heat = ((jump_distance / heatConstants.JumpHeatUnitSize) + 1) * heatConstants.JumpHeatPerUnit; max_jump_heat *= heatConstants.GlobalHeatIncreaseMultiplier; max_jump_heat = Mathf.Max(heatConstants.JumpHeatMin, max_jump_heat); extra_stats += string.Format("Max jump heat: <b>{0}</b>\n", (int)max_jump_heat); } break; case "TooltipMechPerformanceSpeed": float max_walk_distance = currentChassis.MovementCapDef.MaxWalkDistance; float max_sprint_distance = currentChassis.MovementCapDef.MaxSprintDistance; float max_jump_distance = BTMechDef.GetJumpJetsMaxDistance(currentMech); foreach (MechComponentRef mechComponentRef in currentMech.Inventory) { if (mechComponentRef.Def == null) { mechComponentRef.RefreshComponentDef(); } // Various movement effects if (mechComponentRef.Def.statusEffects != null) { foreach (EffectData effect in mechComponentRef.Def.statusEffects) { StatisticEffectData statisticData = effect.statisticData; if (statisticData.statName == "WalkSpeed") { BTStatistics.ApplyEffectStatistic(statisticData, ref max_walk_distance); } else if (statisticData.statName == "RunSpeed") { BTStatistics.ApplyEffectStatistic(statisticData, ref max_sprint_distance); } else if (statisticData.statName == "JumpDistanceMultiplier") { BTStatistics.ApplyEffectStatistic(statisticData, ref max_jump_distance); } } } } extra_stats += string.Format("Walk distance: <b>{0}m</b>\n", (int)max_walk_distance); extra_stats += string.Format("Sprint distance: <b>{0}m</b>\n", (int)max_sprint_distance); if (max_jump_distance > 0) { extra_stats += string.Format("Jump distance: <b>{0}m</b>\n", (int)max_jump_distance); } break; case "TooltipMechPerformanceMelee": float melee_damage = currentChassis.MeleeDamage; float melee_instability = currentChassis.MeleeInstability; float dfa_damage = currentChassis.DFADamage * 2; float dfa_instability = currentChassis.DFAInstability; float dfa_self_damage = currentChassis.DFASelfDamage; float support_damage = 0; float support_heat = 0; foreach (MechComponentRef mechComponentRef in currentMech.Inventory) { if (mechComponentRef.Def == null) { mechComponentRef.RefreshComponentDef(); } // Take Melee/DFA upgrades into account if (mechComponentRef.Def.statusEffects != null) { foreach (EffectData effect in mechComponentRef.Def.statusEffects) { if (effect.effectType != EffectType.StatisticEffect) { continue; } if (effect.statisticData.targetWeaponSubType == WeaponSubType.Melee) { if (effect.statisticData.statName == "DamagePerShot") { BTStatistics.ApplyEffectStatistic(effect.statisticData, ref melee_damage); } else if (effect.statisticData.statName == "Instability") { BTStatistics.ApplyEffectStatistic(effect.statisticData, ref melee_instability); } } else if (effect.statisticData.targetWeaponSubType == WeaponSubType.DFA) { if (effect.statisticData.statName == "DamagePerShot") { BTStatistics.ApplyEffectStatistic(effect.statisticData, ref dfa_damage); } else if (effect.statisticData.statName == "Instability") { BTStatistics.ApplyEffectStatistic(effect.statisticData, ref dfa_instability); } else if (effect.statisticData.statName == "DFASelfDamage") { BTStatistics.ApplyEffectStatistic(effect.statisticData, ref dfa_self_damage); } } } } // Calculate support weapon damage if (mechComponentRef.Def is WeaponDef weapon && weapon.Category == WeaponCategory.AntiPersonnel) { support_damage += weapon.Damage * weapon.ShotsWhenFired; support_heat += weapon.HeatDamage * weapon.ShotsWhenFired; } } extra_stats += string.Format("Melee damage: <b>{0}</b> ( Stability: <b>{1}</b> )\n", (int)melee_damage, (int)melee_instability); if (BTMechDef.GetJumpJetsAmount(currentMech) > 0) { extra_stats += string.Format("DFA damage: <b>{0}</b> ( Stability: <b>{1}</b> )\n", (int)dfa_damage, (int)dfa_instability); extra_stats += string.Format("DFA self-damage: <b>{0}</b> ( per leg )\n", (int)dfa_self_damage); } if (support_damage > 0) { if (support_heat > 0) { extra_stats += string.Format("Support weapons damage: <b>{0}</b> ( <b>{1} H</b> )\n", support_damage, support_heat); } else { extra_stats += string.Format("Support weapons damage: <b>{0}</b>\n", support_damage); } } break; // No idea what to put on those two. Feel free to contribute. case "TooltipMechPerformanceRange": break; case "TooltipMechPerformanceDurability": break; } if (extra_stats.Length != 0) { TextMeshProUGUI body = (TextMeshProUGUI)ReflectionHelper.GetPrivateField(__instance, "body"); body.text = string.Format("{0}\n{1}", extra_stats, body.text); } }
public static bool Prefix(MechDef mechDef, ref float currentValue, ref float maxValue) { try { if (Combat == null) { Combat = CombatGameConstants.CreateFromSaved(UnityGameInstance.BattleTechGame); } var totalHeatSinkDissipation = Combat.Heat.InternalHeatSinkCount * Combat.Heat.DefaultHeatSinkDissipationCapacity; var heatGenerationWeapons = 0f; var numberOfJumpJets = 0; foreach (var mechComponentRef in mechDef.Inventory) { if (mechComponentRef.Def == null) { mechComponentRef.RefreshComponentDef(); } if (mechComponentRef.Def is WeaponDef) { var weaponDef = (WeaponDef)mechComponentRef.Def; heatGenerationWeapons += weaponDef.HeatGenerated; } else if (mechComponentRef.ComponentDefType == ComponentType.JumpJet) { if (mechComponentRef.DamageLevel < ComponentDamageLevel.NonFunctional) { numberOfJumpJets++; } } else if (mechComponentRef.Def is HeatSinkDef) { var heatSinkDef = (HeatSinkDef)mechComponentRef.Def; totalHeatSinkDissipation += heatSinkDef.DissipationCapacity; } } Control.mod.Logger.LogDebug("heatGenerationWeapons=" + heatGenerationWeapons); Control.mod.Logger.LogDebug("totalHeatSinkDissipation=" + totalHeatSinkDissipation); var maxHeat = Combat.Heat.MaxHeat; { var stats = new StatCollection(); var maxHeatStatistic = stats.AddStatistic("MaxHeat", maxHeat); var heatGeneratedStatistic = stats.AddStatistic("HeatGenerated", heatGenerationWeapons); foreach (var mechComponentRef in mechDef.Inventory) { if (mechComponentRef.Def == null || mechComponentRef.Def.statusEffects == null) { continue; } var statusEffects = mechComponentRef.Def.statusEffects; foreach (var effect in statusEffects) { switch (effect.statisticData.statName) { case "MaxHeat": stats.PerformOperation(maxHeatStatistic, effect.statisticData); break; case "HeatGenerated": if (effect.statisticData.targetCollection == StatisticEffectData.TargetCollection.Weapon) { stats.PerformOperation(heatGeneratedStatistic, effect.statisticData); } break; } } } maxHeat = maxHeatStatistic.CurrentValue.Value <int>(); heatGenerationWeapons = heatGeneratedStatistic.CurrentValue.Value <float>(); } Control.mod.Logger.LogDebug("maxHeat=" + maxHeat); Control.mod.Logger.LogDebug("heatGenerationWeapons=" + heatGenerationWeapons); if (numberOfJumpJets >= Combat.MoveConstants.MoveTable.Length) { numberOfJumpJets = Combat.MoveConstants.MoveTable.Length - 1; } var heatGenerationJumpJets = 0f; var jumpHeatDivisor = 3; if (numberOfJumpJets > 0) { heatGenerationJumpJets += numberOfJumpJets * Combat.Heat.JumpHeatUnitSize / jumpHeatDivisor; } else { heatGenerationJumpJets = 0f; } totalHeatSinkDissipation *= Combat.Heat.GlobalHeatSinkMultiplier; var totalHeatGeneration = (heatGenerationWeapons + heatGenerationJumpJets) * Combat.Heat.GlobalHeatIncreaseMultiplier; Control.mod.Logger.LogDebug("totalHeatGeneration=" + totalHeatGeneration); // rounding steps for heatSinkDissipation var heatDissipationPercent = Mathf.Min(totalHeatSinkDissipation / totalHeatGeneration * 100f, UnityGameInstance.BattleTechGame.MechStatisticsConstants.MaxHeatEfficiency); heatDissipationPercent = Mathf.Max(heatDissipationPercent, UnityGameInstance.BattleTechGame.MechStatisticsConstants.MinHeatEfficiency); Control.mod.Logger.LogDebug("heatDissipationPercent=" + heatDissipationPercent); totalHeatSinkDissipation = totalHeatGeneration * (heatDissipationPercent / 100f); Control.mod.Logger.LogDebug("totalHeatSinkDissipation=" + totalHeatSinkDissipation); var heatLeftOver = totalHeatGeneration - totalHeatSinkDissipation; var unusedHeatCapacity = maxHeat - heatLeftOver; Control.mod.Logger.LogDebug("heatLeftOver=" + heatLeftOver); Control.mod.Logger.LogDebug("unusedHeatCapacity=" + unusedHeatCapacity); currentValue = Mathf.Round((unusedHeatCapacity / maxHeat) * 10f); currentValue = Mathf.Max(Mathf.Min(currentValue, 10f), 1f); maxValue = 10f; return(false); } catch (Exception e) { Control.mod.Logger.LogError(e); return(true); } }