001package com.fs.starfarer.api.impl.hullmods; 002 003import java.awt.Color; 004 005import com.fs.starfarer.api.Global; 006import com.fs.starfarer.api.combat.BaseHullMod; 007import com.fs.starfarer.api.combat.MutableShipStatsAPI; 008import com.fs.starfarer.api.combat.ShipAPI; 009import com.fs.starfarer.api.combat.ShipAPI.HullSize; 010import com.fs.starfarer.api.combat.WeaponAPI; 011import com.fs.starfarer.api.combat.WeaponAPI.AIHints; 012import com.fs.starfarer.api.combat.WeaponAPI.WeaponSize; 013import com.fs.starfarer.api.combat.WeaponAPI.WeaponType; 014import com.fs.starfarer.api.combat.listeners.WeaponBaseRangeModifier; 015import com.fs.starfarer.api.loading.WeaponSlotAPI; 016import com.fs.starfarer.api.ui.Alignment; 017import com.fs.starfarer.api.ui.TooltipMakerAPI; 018import com.fs.starfarer.api.util.Misc; 019 020public class BallisticRangefinder extends BaseHullMod { 021 022 public static float BONUS_MAX_1 = 800; 023 public static float BONUS_MAX_2 = 800; 024 public static float BONUS_MAX_3 = 900; 025 public static float BONUS_SMALL_1 = 100; 026 public static float BONUS_SMALL_2 = 100; 027 public static float BONUS_SMALL_3 = 200; 028 public static float BONUS_MEDIUM_3 = 100; 029 030 public static float HYBRID_MULT = 2f; 031 public static float HYBRID_BONUS_MIN = 100f; 032 033 034 public void applyEffectsBeforeShipCreation(HullSize hullSize, MutableShipStatsAPI stats, String id) { 035 } 036 037 public static WeaponSize getLargestBallisticSlot(ShipAPI ship) { 038 if (ship == null) return null; 039 WeaponSize largest = null; 040 for (WeaponSlotAPI slot : ship.getHullSpec().getAllWeaponSlotsCopy()) { 041 if (slot.isDecorative() ) continue; 042 if (slot.getWeaponType() == WeaponType.BALLISTIC) { 043 if (largest == null || largest.ordinal() < slot.getSlotSize().ordinal()) { 044 largest = slot.getSlotSize(); 045 } 046 } 047 } 048 return largest; 049 } 050 051 @Override 052 public void applyEffectsAfterShipCreation(ShipAPI ship, String id) { 053 WeaponSize largest = getLargestBallisticSlot(ship); 054 if (largest == null) return; 055 float small = 0f; 056 float medium = 0f; 057 float max = 0f; 058 switch (largest) { 059 case LARGE: 060 small = BONUS_SMALL_3; 061 medium = BONUS_MEDIUM_3; 062 max = BONUS_MAX_3; 063 break; 064 case MEDIUM: 065 small = BONUS_SMALL_2; 066 max = BONUS_MAX_2; 067 break; 068 case SMALL: 069 small = BONUS_SMALL_1; 070 max = BONUS_MAX_1; 071 break; 072 } 073 074 ship.addListener(new RangefinderRangeModifier(small, medium, max)); 075 } 076 077 public static class RangefinderRangeModifier implements WeaponBaseRangeModifier { 078 public float small, medium, max; 079 public RangefinderRangeModifier(float small, float medium, float max) { 080 this.small = small; 081 this.medium = medium; 082 this.max = max; 083 } 084 085 public float getWeaponBaseRangePercentMod(ShipAPI ship, WeaponAPI weapon) { 086 return 0; 087 } 088 public float getWeaponBaseRangeMultMod(ShipAPI ship, WeaponAPI weapon) { 089 return 1f; 090 } 091 public float getWeaponBaseRangeFlatMod(ShipAPI ship, WeaponAPI weapon) { 092// if (weapon.getSlot() == null || weapon.getSlot().getWeaponType() != WeaponType.BALLISTIC) { 093// return 0f; 094// } 095 if (weapon.getSpec() == null) { 096 return 0f; 097 } 098 if (weapon.getSpec().getMountType() != WeaponType.BALLISTIC && 099 weapon.getSpec().getMountType() != WeaponType.HYBRID) { 100 return 0f; 101 } 102 if (weapon.hasAIHint(AIHints.PD)) { 103 return 0f; 104 } 105 106 float bonus = 0; 107 if (weapon.getSize() == WeaponSize.SMALL) { 108 bonus = small; 109 } else if (weapon.getSize() == WeaponSize.MEDIUM) { 110 bonus = medium; 111 } 112 if (weapon.getSpec().getMountType() == WeaponType.HYBRID) { 113 bonus *= HYBRID_MULT; 114 if (bonus < HYBRID_BONUS_MIN) { 115 bonus = HYBRID_BONUS_MIN; 116 } 117 } 118 if (bonus == 0f) return 0f; 119 120 float base = weapon.getSpec().getMaxRange(); 121 if (base + bonus > max) { 122 bonus = max - base; 123 } 124 if (bonus < 0) bonus = 0; 125 return bonus; 126 } 127 } 128 129 public String getDescriptionParam(int index, HullSize hullSize) { 130 //if (index == 0) return "" + (int)RANGE_PENALTY_PERCENT + "%"; 131 return null; 132 } 133 134 @Override 135 public boolean shouldAddDescriptionToTooltip(HullSize hullSize, ShipAPI ship, boolean isForModSpec) { 136 return false; 137 } 138 139 @Override 140 public void addPostDescriptionSection(TooltipMakerAPI tooltip, HullSize hullSize, ShipAPI ship, float width, boolean isForModSpec) { 141 float pad = 3f; 142 float opad = 10f; 143 Color h = Misc.getHighlightColor(); 144 Color bad = Misc.getNegativeHighlightColor(); 145 Color t = Misc.getTextColor(); 146 Color g = Misc.getGrayColor(); 147 148 WeaponSize largest = getLargestBallisticSlot(ship); 149 150// LabelAPI label = tooltip.addPara("If the largest Ballistic slot on the ship is large:" 151// + " increases the base range of small weapons in Ballistic slots by %s," 152// + " and of medium weapons by %s, up to %s maximum.", opad, h, 153// "" + (int)BONUS_SMALL_3, "" + (int)BONUS_MEDIUM_3, "" + (int)BONUS_MAX_3); 154//// label.setHighlight("Ballistic", "base", "Ballistic", "" + (int)BONUS_SMALL_3, "" + (int)BONUS_MEDIUM_3, "" + (int)BONUS_MAX_3); 155//// label.setHighlightColors(Misc.MOUNT_BALLISTIC, h, Misc.MOUNT_BALLISTIC, h, h, h); 156// label.setHighlight("" + (int)BONUS_SMALL_3, "" + (int)BONUS_MEDIUM_3, "" + (int)BONUS_MAX_3); 157// label.setHighlightColors(h, h, h); 158// if (largest != null && largest != WeaponSize.LARGE) { 159// label.setColor(Misc.getGrayColor()); 160// } 161// 162// label = tooltip.addPara("Otherwise:" 163// + " increases the base range of small weapons in Ballistic slots by %s," 164// + " up to %s maximum.", opad, h, 165// "" + (int)BONUS_SMALL_1, "" + (int)BONUS_MAX_1); 166//// label.setHighlight("base", "Ballistic", "" + (int)BONUS_SMALL_1, "" + (int)BONUS_MAX_1); 167//// label.setHighlightColors(h, Misc.MOUNT_BALLISTIC, h, h); 168// label.setHighlight("" + (int)BONUS_SMALL_1, "" + (int)BONUS_MAX_1); 169// label.setHighlightColors(h, h); 170// if (largest != null && largest == WeaponSize.LARGE) { 171// label.setColor(Misc.getGrayColor()); 172// } 173// 174//// if (ship != null) { 175//// tooltip.addSectionHeading("Effect on this ship", Alignment.MID, opad); 176//// } 177// 178// tooltip.addSectionHeading("Exceptions", Alignment.MID, opad); 179// label = tooltip.addPara("Does not affect point-defense weapons, " 180// + "or Ballistic weapons in Composite, Hybrid, and Universal slots.", opad); 181//// label.setHighlight("Ballistic", "Composite", "Hybrid", "Universal"); 182//// label.setHighlightColors(Misc.MOUNT_BALLISTIC, Misc.MOUNT_COMPOSITE, Misc.MOUNT_HYBRID, Misc.MOUNT_UNIVERSAL); 183// label.setHighlight("Composite", "Hybrid", "Universal"); 184// label.setHighlightColors(Misc.MOUNT_COMPOSITE, Misc.MOUNT_HYBRID, Misc.MOUNT_UNIVERSAL); 185// 186// label = tooltip.addPara("Hybrid weapons in Ballistic slots receive %s the bonus. " 187//// + "In addition, the bonus will be at least %s for all Hybrid weapons in Ballistic slots, including large ones," 188//// + " subject to the maximum.", opad, h, 189// + "In addition, the range bonus for all non-PD Hybrid weapons in Ballistic slots will be at least %s, " 190// + "regardless of size or other factors, but still subject to the maximum.", opad, h, 191//// + "In addition, non-PD Hybrid weapons in Ballistic slots, including large ones, will receive %s bonus range," 192//// + " subject to the maximum, in cases where other weapons of the same size would receive no bonus.", opad, h, 193// "" + (int)Math.round(HYBRID_MULT) + Strings.X, "" + (int)Math.round(HYBRID_BONUS_MIN)); 194//// label.setHighlight("Hybrid", "Ballistic", "" + (int)Math.round(HYBRID_MULT) + Strings.X); 195//// label.setHighlightColors(Misc.MOUNT_HYBRID, Misc.MOUNT_BALLISTIC, h); 196// label.setHighlight("Hybrid", "" + (int)Math.round(HYBRID_MULT) + Strings.X, "Hybrid", "" + (int)Math.round(HYBRID_BONUS_MIN)); 197// label.setHighlightColors(Misc.MOUNT_HYBRID, h, Misc.MOUNT_HYBRID, h); 198 199 200 201 tooltip.addPara("Utilizes targeting data from the ship's largest ballistic slot " 202 + "to benefit certain weapons, extending the base range of " 203 + "typical ballistic weapons to match similar but larger weapons. " 204 + "Greatly benefits hybrid weapons. Point-defense weapons are unaffected.", 205 opad, h, "ship's largest ballistic slot", "base range", "Greatly benefits hybrid weapons"); 206 207 //tooltip.addPara("The maximum range is capped, based on the largest slot.", opad); 208 tooltip.addPara("The range bonus is based on the size of the largest ballistic slot, " 209 + "and the increased base range is capped, but still subject to other modifiers.", opad); 210 211// tooltip.addPara("Affects small and medium ballistic weapons, and all hybrid weapons. " 212// + "Point-defense weapons are not affected.", opad, h, 213// ""); 214 //"small and medium", "hybrid", "Point-defense"); 215 216 tooltip.addSectionHeading("Ballistic weapon range", Alignment.MID, opad); 217 218 219 tooltip.addPara("Affects small and medium ballistic weapons.", opad); 220 221 float col1W = 120; 222 float colW = (int) ((width - col1W - 12f) / 3f); 223 float lastW = colW; 224 225 tooltip.beginTable(Misc.getBasePlayerColor(), Misc.getDarkPlayerColor(), Misc.getBrightPlayerColor(), 226 20f, true, true, 227 new Object [] {"Largest b. slot", col1W, "Small wpn", colW, "Medium wpn", colW, "Range cap", lastW}); 228 229 Color reallyG = g; 230 if (Global.CODEX_TOOLTIP_MODE) { 231 g = h; 232 } 233 234 Color c = null; 235 if (largest == WeaponSize.SMALL) c = h; 236 else if (largest == WeaponSize.MEDIUM) c = h; 237 else c = g; 238 239 240 tooltip.addRow(Alignment.MID, c, "Small / Medium", 241 Alignment.MID, c, "+" + (int) BONUS_SMALL_1, 242 Alignment.MID, reallyG, "---", 243 Alignment.MID, c, "" + (int)BONUS_MAX_1); 244 245 if (largest == WeaponSize.LARGE) c = h; 246 else c = g; 247 tooltip.addRow(Alignment.MID, c, "Large", 248 Alignment.MID, c, "+" + (int) BONUS_SMALL_3, 249 Alignment.MID, c, "+" + (int) BONUS_MEDIUM_3, 250 Alignment.MID, c, "" + (int)BONUS_MAX_3); 251 252 tooltip.addTable("", 0, opad); 253 254 255 tooltip.addSectionHeading("Hybrid weapon range", Alignment.MID, opad + 7f); 256 257 tooltip.addPara("Affects hybrid weapons (those that can fit into both ballistic and energy slots)" 258 + " of all sizes.", opad); 259 260 col1W = 120; 261 colW = (int) ((width - col1W - lastW - 15f) / 3f); 262 263 tooltip.beginTable(Misc.getBasePlayerColor(), Misc.getDarkPlayerColor(), Misc.getBrightPlayerColor(), 264 20f, true, true, 265 new Object [] {"Largest b. slot", col1W, "Small", colW, "Medium", colW, "Large", colW, "Range cap", lastW}); 266 267 268 c = null; 269 if (largest == WeaponSize.SMALL) c = h; 270 else if (largest == WeaponSize.MEDIUM) c = h; 271 else c = g; 272 tooltip.addRow(Alignment.MID, c, "Small / Medium", 273 Alignment.MID, c, "+" + (int) (BONUS_SMALL_1 * HYBRID_MULT), 274 Alignment.MID, c, "+" + (int) HYBRID_BONUS_MIN, 275 Alignment.MID, c, "+" + (int) HYBRID_BONUS_MIN, 276 Alignment.MID, c, "" + (int)BONUS_MAX_1); 277 278 if (largest == WeaponSize.LARGE) c = h; 279 else c = g; 280 tooltip.addRow(Alignment.MID, c, "Large", 281 Alignment.MID, c, "+" + (int) (BONUS_SMALL_3 * HYBRID_MULT), 282 Alignment.MID, c, "+" + (int) (BONUS_MEDIUM_3 * HYBRID_MULT), 283 Alignment.MID, c, "+" + (int) HYBRID_BONUS_MIN, 284 Alignment.MID, c, "" + (int)BONUS_MAX_3); 285 286 tooltip.addTable("", 0, opad); 287 288 289 tooltip.addSectionHeading("Interactions with other modifiers", Alignment.MID, opad + 7f); 290 tooltip.addPara("Since the base range is increased, this modifier" 291 + " - unlike most other flat modifiers - " 292 + "is increased by percentage modifiers from other hullmods and skills.", opad); 293 } 294 295 public float getTooltipWidth() { 296 return 412f; 297 } 298 299 @Override 300 public boolean isApplicableToShip(ShipAPI ship) { 301 WeaponSize largest = getLargestBallisticSlot(ship); 302 if (ship != null && largest == null) { 303 return false; 304 } 305 return getUnapplicableReason(ship) == null; 306 } 307 308 public String getUnapplicableReason(ShipAPI ship) { 309 WeaponSize largest = getLargestBallisticSlot(ship); 310 if (ship != null && largest == null) { 311 return "Ship has no ballistic weapon slots"; 312 } 313 if (ship != null && 314 ship.getHullSize() != HullSize.CAPITAL_SHIP && 315 ship.getHullSize() != HullSize.DESTROYER && 316 ship.getHullSize() != HullSize.CRUISER) { 317 return "Can only be installed on destroyer-class hulls and larger"; 318 } 319 return null; 320 } 321 322} 323 324 325 326 327 328 329 330 331