001package com.fs.starfarer.api.impl.combat; 002 003import java.awt.Color; 004 005import com.fs.starfarer.api.Global; 006import com.fs.starfarer.api.combat.MutableShipStatsAPI; 007import com.fs.starfarer.api.combat.PhaseCloakSystemAPI; 008import com.fs.starfarer.api.combat.ShipAPI; 009import com.fs.starfarer.api.combat.ShipSystemAPI; 010import com.fs.starfarer.api.impl.campaign.ids.Stats; 011import com.fs.starfarer.api.impl.campaign.ids.Tags; 012 013public class PhaseCloakStats extends BaseShipSystemScript { 014 015 public static Color JITTER_COLOR = new Color(255,175,255,255); 016 public static float JITTER_FADE_TIME = 0.5f; 017 018 public static float SHIP_ALPHA_MULT = 0.25f; 019 //public static float VULNERABLE_FRACTION = 0.875f; 020 public static float VULNERABLE_FRACTION = 0f; 021 public static float INCOMING_DAMAGE_MULT = 0.25f; 022 023 024 public static float MAX_TIME_MULT = 3f; 025 026// /** 027// * Top speed multiplier when at 100% disruption. 028// */ 029// public static float DISRUPT_SPEED_MULT = 0.33f; 030// /** 031// * Disruption clears up at this rate. Always clears up fully when cloak is turned off. 032// */ 033// public static float DISRUPT_DECAY_RATE = 0.25f; 034// /** 035// * Seconds that need to elapse since a disruption increase before it starts to decay. 036// */ 037// public static float DISRUPT_DECAY_DELAY = 2f; 038// 039// // Compared to ship's max flux to figure out how quickly things disrupt. 040// public static float PROJECTILE_DAMAGE_MULT = 3f; 041// public static float BEAM_DAMAGE_MULT = 0.1f; 042// public static float MASS_DAMAGE_MULT = 1f; 043 044 public static boolean FLUX_LEVEL_AFFECTS_SPEED = true; 045 public static float MIN_SPEED_MULT = 0.33f; 046 public static float BASE_FLUX_LEVEL_FOR_MIN_SPEED = 0.5f; 047 048 protected Object STATUSKEY1 = new Object(); 049 protected Object STATUSKEY2 = new Object(); 050 protected Object STATUSKEY3 = new Object(); 051 protected Object STATUSKEY4 = new Object(); 052 053 054 public static float getMaxTimeMult(MutableShipStatsAPI stats) { 055 return 1f + (MAX_TIME_MULT - 1f) * stats.getDynamic().getValue(Stats.PHASE_TIME_BONUS_MULT); 056 } 057 058 protected boolean isDisruptable(ShipSystemAPI cloak) { 059 return cloak.getSpecAPI().hasTag(Tags.DISRUPTABLE); 060 } 061 062 protected float getDisruptionLevel(ShipAPI ship) { 063 //return disruptionLevel; 064 //if (true) return 0f; 065 if (FLUX_LEVEL_AFFECTS_SPEED) { 066 float threshold = ship.getMutableStats().getDynamic().getMod( 067 Stats.PHASE_CLOAK_FLUX_LEVEL_FOR_MIN_SPEED_MOD).computeEffective(BASE_FLUX_LEVEL_FOR_MIN_SPEED); 068 if (threshold <= 0) return 1f; 069 float level = ship.getHardFluxLevel() / threshold; 070 if (level > 1f) level = 1f; 071 return level; 072 } 073 return 0f; 074 } 075 076 protected void maintainStatus(ShipAPI playerShip, State state, float effectLevel) { 077 float level = effectLevel; 078 float f = VULNERABLE_FRACTION; 079 080 ShipSystemAPI cloak = playerShip.getPhaseCloak(); 081 if (cloak == null) cloak = playerShip.getSystem(); 082 if (cloak == null) return; 083 084 if (level > f) { 085// Global.getCombatEngine().maintainStatusForPlayerShip(STATUSKEY1, 086// cloak.getSpecAPI().getIconSpriteName(), cloak.getDisplayName(), "can not be hit", false); 087 Global.getCombatEngine().maintainStatusForPlayerShip(STATUSKEY2, 088 cloak.getSpecAPI().getIconSpriteName(), cloak.getDisplayName(), "time flow altered", false); 089 } else { 090// float INCOMING_DAMAGE_MULT = 0.25f; 091// float percent = (1f - INCOMING_DAMAGE_MULT) * getEffectLevel() * 100; 092// Global.getCombatEngine().maintainStatusForPlayerShip(STATUSKEY3, 093// spec.getIconSpriteName(), cloak.getDisplayName(), "damage mitigated by " + (int) percent + "%", false); 094 } 095 096 if (FLUX_LEVEL_AFFECTS_SPEED) { 097 if (level > f) { 098 if (getDisruptionLevel(playerShip) <= 0f) { 099 Global.getCombatEngine().maintainStatusForPlayerShip(STATUSKEY3, 100 cloak.getSpecAPI().getIconSpriteName(), "phase coils stable", "top speed at 100%", false); 101 } else { 102 //String disruptPercent = "" + (int)Math.round((1f - disruptionLevel) * 100f) + "%"; 103 //String speedMultStr = Strings.X + Misc.getRoundedValue(getSpeedMult()); 104 String speedPercentStr = (int) Math.round(getSpeedMult(playerShip, effectLevel) * 100f) + "%"; 105 Global.getCombatEngine().maintainStatusForPlayerShip(STATUSKEY3, 106 cloak.getSpecAPI().getIconSpriteName(), 107 //"phase coils at " + disruptPercent, 108 "phase coil stress", 109 "top speed at " + speedPercentStr, true); 110 } 111 } 112 } 113 } 114 115// protected float disruptionLevel = 0f; 116// //protected Set<CombatEntityAPI> hitBy = new LinkedHashSet<CombatEntityAPI>(); 117// protected TimeoutTracker<Object> hitBy = new TimeoutTracker<Object>(); 118// protected float sinceHit = 1000f; 119 120 public float getSpeedMult(ShipAPI ship, float effectLevel) { 121 if (getDisruptionLevel(ship) <= 0f) return 1f; 122 return MIN_SPEED_MULT + (1f - MIN_SPEED_MULT) * (1f - getDisruptionLevel(ship) * effectLevel); 123 } 124 125 /* 126 public void advanceDisrupt(float amount, ShipAPI ship, ShipSystemAPI cloak, 127 MutableShipStatsAPI stats, String id, State state, float effectLevel) { 128 if (Global.getCombatEngine().isPaused()) { 129 return; 130 } 131 132 sinceHit += amount; 133 if (state == State.COOLDOWN || state == State.IDLE || state == State.OUT) { 134 disruptionLevel = 0f; 135 hitBy.clear(); 136 } else { 137 checkForHits(ship, cloak); 138 } 139 hitBy.advance(amount); 140 141 if (sinceHit > DISRUPT_DECAY_DELAY) { 142 disruptionLevel -= DISRUPT_DECAY_RATE * amount; 143 if (disruptionLevel < 0) disruptionLevel = 0; 144 if (disruptionLevel > 1) disruptionLevel = 1; 145 } 146 147 ((PhaseCloakSystemAPI)cloak).setMinCoilJitterLevel(disruptionLevel * 1f); 148 149// float mult = getSpeedMult(effectLevel); 150// if (mult < 1f) { 151// stats.getMaxSpeed().modifyMult(id, mult); 152// } else { 153// stats.getMaxSpeed().unmodifyMult(id); 154// } 155 } 156 157 public void checkForHits(ShipAPI ship, ShipSystemAPI cloak) { 158 CombatEngineAPI engine = Global.getCombatEngine(); 159 160 Vector2f loc = new Vector2f(ship.getLocation()); 161 float radius = ship.getCollisionRadius(); 162 163 float fluxCap = ship.getMaxFlux(); 164 if (fluxCap < 1000) fluxCap = 1000; 165 166 Color core = cloak.getSpecAPI().getEffectColor1(); 167 core = Color.white; 168 Color fringe = cloak.getSpecAPI().getEffectColor2(); 169 fringe = Misc.interpolateColor(fringe, Color.white, 0.5f); 170// fringe = Misc.setAlpha(fringe, 255); 171 172 Iterator<Object> iter = engine.getAllObjectGrid().getCheckIterator(loc, radius * 2f, radius * 2f); 173 while (iter.hasNext()) { 174 Object curr = iter.next(); 175 if (!(curr instanceof CombatEntityAPI)) continue; 176 if (curr == ship) continue; 177 178 CombatEntityAPI entity = (CombatEntityAPI) curr; 179 if (hitBy.contains(entity)) continue; 180 181 float tr = Misc.getTargetingRadius(entity.getLocation(), ship, false); 182 tr *= 1.2f; 183 float dist = Misc.getDistance(entity.getLocation(), loc); 184 185 boolean hit = false; 186 Vector2f glowLoc = null; 187 float glowDir = 0f; 188 float damage = 0f; 189 float hitMult = 1f; 190 float timeout = 1f; 191 if (entity instanceof CombatAsteroidAPI) { 192 if (dist < tr + entity.getCollisionRadius()) { 193 hit = true; 194 hitMult = 1f - dist / (tr + entity.getCollisionRadius()); 195 hitMult = 0.5f + 0.5f * hitMult; 196 damage = entity.getMass() * MASS_DAMAGE_MULT * hitMult; 197 timeout = 0.5f; 198 199 glowLoc = entity.getLocation(); 200 float dirToEntity = Misc.getAngleInDegrees(loc, entity.getLocation()); 201 glowLoc = Misc.getUnitVectorAtDegreeAngle(dirToEntity); 202 glowLoc.scale(tr); 203 Vector2f.add(glowLoc, loc, glowLoc); 204 glowDir = Misc.getAngleInDegrees(entity.getLocation(), loc); 205 } 206 } else if (entity instanceof ShipAPI) { 207 if (dist < tr + entity.getCollisionRadius()) { 208 hit = true; 209 hitMult = 1f - dist / (tr + entity.getCollisionRadius()); 210 hitMult = 0.5f + 0.5f * hitMult; 211 damage = entity.getMass() * MASS_DAMAGE_MULT * hitMult; 212 timeout = 0.5f; 213 214 glowLoc = entity.getLocation(); 215 float dirToEntity = Misc.getAngleInDegrees(loc, entity.getLocation()); 216 glowLoc = Misc.getUnitVectorAtDegreeAngle(dirToEntity); 217 glowLoc.scale(tr); 218 Vector2f.add(glowLoc, loc, glowLoc); 219 glowDir = Misc.getAngleInDegrees(entity.getLocation(), loc); 220 } 221 } else if (entity instanceof DamagingProjectileAPI) { 222 DamagingProjectileAPI proj = (DamagingProjectileAPI) entity; 223 if (proj.getSource() == ship) continue; 224 225 float check = tr + entity.getCollisionRadius(); 226 check = tr; 227 if (dist < check) { 228 hit = true; 229 hitMult = 1f - dist / (check); 230 hitMult = 0.5f + 0.5f * hitMult; 231 damage = proj.getDamageAmount() * PROJECTILE_DAMAGE_MULT * hitMult; 232 //damage *= proj.getDamageType().getShieldMult(); 233 damage += proj.getDamage().getFluxComponent() * hitMult; 234 if (entity instanceof MissileAPI) { 235 timeout = 0.5f; 236 } else { 237 timeout = 10f; 238 } 239 glowLoc = entity.getLocation(); 240 glowDir = Misc.getAngleInDegrees(entity.getVelocity()); 241 } 242 } 243 244 if (hit && damage > 0) { 245 float disruptAmount = damage / fluxCap; 246 disruptionLevel += disruptAmount; 247 if (disruptionLevel > 1f) disruptionLevel = 1f; 248 hitBy.add(entity, timeout); 249 sinceHit = 0f; 250 if (glowLoc != null) { 251 //float size = 2000f * hitMult * Math.min(damage/2000f, 1f); 252 //float size = 1000f * hitMult * Math.max(0.05f, disruptAmount + 0.1f); 253 float size = 1000f * hitMult * (disruptAmount + 0.05f); 254 if (size < 20) size = 20; 255 if (size > 150) size = 150; 256 257 //size *= 0.5f; 258 Vector2f glow = new Vector2f(glowLoc); 259// Vector2f per = Misc.getUnitVectorAtDegreeAngle(glowDir); 260// per.scale(size * 0.1f); 261// engine.addHitParticle(glow, ship.getVelocity(), size, hitMult, 1f, fringe); 262// engine.addNegativeParticle(glow, ship.getVelocity(), size * 0.5f, 0f, 1f, core); 263 Vector2f vel = new Vector2f(ship.getVelocity()); 264 Vector2f move = Misc.getUnitVectorAtDegreeAngle(glowDir); 265 move.scale(Math.max(300f, entity.getVelocity().length() * (0.5f + (float) Math.random() * 0.5f))); 266 move.scale(-0.1f); 267 Vector2f.add(vel, move, vel); 268 269 engine.addNebulaParticle( 270 glow, vel, size, 1.5f, 0.3f, 0.5f, 1f, Misc.scaleAlpha(fringe, hitMult)); 271 engine.addNegativeNebulaParticle( 272 glow, vel, size * 0.67f, 1.5f, 0.3f, 0.5f, 1f, core); 273 if (entity instanceof DamagingProjectileAPI) { 274 engine.removeEntity(entity); 275 } 276 } 277 } 278 } 279 280 for (BeamAPI beam : engine.getBeams()) { 281 if (beam.getDamage().getMultiplier() <= 0) continue; 282 Vector2f p = Misc.closestPointOnSegmentToPoint(beam.getFrom(), beam.getTo(), loc); 283 float tr = Misc.getTargetingRadius(p, ship, false); 284 //tr *= 1.2f; 285 tr += 20f; 286 float dist = Misc.getDistance(p, loc); 287 if (dist < tr) { 288 float hitMult = 1f - dist / tr; 289 hitMult = 0.5f + 0.5f * hitMult; 290 float damage = beam.getDamage().getDamage() * BEAM_DAMAGE_MULT * hitMult; 291 //damage *= beam.getDamage().getType().getShieldMult(); 292 damage += beam.getDamage().getFluxComponent() * hitMult; 293 float disruptAmount = damage / fluxCap; 294 disruptionLevel += disruptAmount; 295 if (disruptionLevel > 1f) disruptionLevel = 1f; 296 sinceHit = 0f; 297// float dirToEntity = Misc.getAngleInDegrees(loc, p); 298// Vector2f glowLoc = Misc.getUnitVectorAtDegreeAngle(dirToEntity); 299// glowLoc.scale(tr); 300// Vector2f.add(glowLoc, loc, glowLoc); 301 Vector2f glowLoc = Misc.intersectSegmentAndCircle(beam.getFrom(), beam.getTo(), loc, tr * 1.2f); 302 if (glowLoc != null) { 303 //beam.getTo().set(glowLoc); 304 float size = 1000f * hitMult * (disruptAmount + 0.05f); 305 if (size < 20) size = 20; 306 if (size > 150) size = 150; 307 //size *= 0.5f; 308// engine.addHitParticle(glowLoc, ship.getVelocity(), size, hitMult, 0.3f, fringe); 309// //engine.addHitParticle(glowLoc, ship.getVelocity(), size * 0.25f, hitMult, 0.1f, core); 310// engine.addNegativeParticle(glowLoc, ship.getVelocity(), size * 0.5f, 0f, 0.3f, core); 311 float glowDir = Misc.getAngleInDegrees(beam.getTo(), beam.getFrom()); 312 glowDir += (float) Math.random() * 180f - 90f; 313 Vector2f move = Misc.getUnitVectorAtDegreeAngle(glowDir); 314 move.scale(500f * (0.5f + (float) Math.random() * 0.5f)); 315 move.scale(0.2f); 316 Vector2f vel = new Vector2f(); 317 Vector2f.add(vel, move, vel); 318 319 engine.addNebulaParticle( 320 glowLoc, vel, size, 1.5f, 0.3f, 0.5f, 0.5f, Misc.scaleAlpha(fringe, hitMult)); 321 engine.addNegativeNebulaParticle( 322 glowLoc, vel, size * 0.67f, 1.5f, 0.3f, 0.5f, 0.5f, core); 323 } 324 } 325 } 326 } 327 */ 328 329 330 public void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel) { 331 ShipAPI ship = null; 332 boolean player = false; 333 if (stats.getEntity() instanceof ShipAPI) { 334 ship = (ShipAPI) stats.getEntity(); 335 player = ship == Global.getCombatEngine().getPlayerShip(); 336 id = id + "_" + ship.getId(); 337 } else { 338 return; 339 } 340 341 342// if (effectLevel > 0) { 343// stats.getMaxSpeed().modifyMult(id, 0.3333333f); 344// } else { 345// stats.getMaxSpeed().unmodifyMult(id); 346// } 347 348 if (player) { 349 maintainStatus(ship, state, effectLevel); 350 } 351 352 if (Global.getCombatEngine().isPaused()) { 353 return; 354 } 355 356 ShipSystemAPI cloak = ship.getPhaseCloak(); 357 if (cloak == null) cloak = ship.getSystem(); 358 if (cloak == null) return; 359 360// if (isDisruptable(cloak)) { 361// advanceDisrupt(Global.getCombatEngine().getElapsedInLastFrame(), 362// ship, cloak, stats, id, state, effectLevel); 363// } 364 365 if (FLUX_LEVEL_AFFECTS_SPEED) { 366 if (state == State.ACTIVE || state == State.OUT || state == State.IN) { 367 float mult = getSpeedMult(ship, effectLevel); 368 if (mult < 1f) { 369 stats.getMaxSpeed().modifyMult(id + "_2", mult); 370 } else { 371 stats.getMaxSpeed().unmodifyMult(id + "_2"); 372 } 373 ((PhaseCloakSystemAPI)cloak).setMinCoilJitterLevel(getDisruptionLevel(ship)); 374 } 375 } 376 377 if (state == State.COOLDOWN || state == State.IDLE) { 378 unapply(stats, id); 379 return; 380 } 381 382 float speedPercentMod = stats.getDynamic().getMod(Stats.PHASE_CLOAK_SPEED_MOD).computeEffective(0f); 383 float accelPercentMod = stats.getDynamic().getMod(Stats.PHASE_CLOAK_ACCEL_MOD).computeEffective(0f); 384 stats.getMaxSpeed().modifyPercent(id, speedPercentMod * effectLevel); 385 stats.getAcceleration().modifyPercent(id, accelPercentMod * effectLevel); 386 stats.getDeceleration().modifyPercent(id, accelPercentMod * effectLevel); 387 388 float speedMultMod = stats.getDynamic().getMod(Stats.PHASE_CLOAK_SPEED_MOD).getMult(); 389 float accelMultMod = stats.getDynamic().getMod(Stats.PHASE_CLOAK_ACCEL_MOD).getMult(); 390 stats.getMaxSpeed().modifyMult(id, speedMultMod * effectLevel); 391 stats.getAcceleration().modifyMult(id, accelMultMod * effectLevel); 392 stats.getDeceleration().modifyMult(id, accelMultMod * effectLevel); 393 394 float level = effectLevel; 395 //float f = VULNERABLE_FRACTION; 396 397 398 399 float jitterLevel = 0f; 400 float jitterRangeBonus = 0f; 401 float levelForAlpha = level; 402 403// ShipSystemAPI cloak = ship.getPhaseCloak(); 404// if (cloak == null) cloak = ship.getSystem(); 405 406 407 if (state == State.IN || state == State.ACTIVE) { 408 ship.setPhased(true); 409 levelForAlpha = level; 410 } else if (state == State.OUT) { 411 if (level > 0.5f) { 412 ship.setPhased(true); 413 } else { 414 ship.setPhased(false); 415 } 416 levelForAlpha = level; 417// if (level >= f) { 418// ship.setPhased(true); 419// if (f >= 1) { 420// levelForAlpha = level; 421// } else { 422// levelForAlpha = (level - f) / (1f - f); 423// } 424// float time = cloak.getChargeDownDur(); 425// float fadeLevel = JITTER_FADE_TIME / time; 426// if (level >= f + fadeLevel) { 427// jitterLevel = 0f; 428// } else { 429// jitterLevel = (fadeLevel - (level - f)) / fadeLevel; 430// } 431// } else { 432// ship.setPhased(false); 433// levelForAlpha = 0f; 434// 435// float time = cloak.getChargeDownDur(); 436// float fadeLevel = JITTER_FADE_TIME / time; 437// if (level < fadeLevel) { 438// jitterLevel = level / fadeLevel; 439// } else { 440// jitterLevel = 1f; 441// } 442// //jitterLevel = level / f; 443// //jitterLevel = (float) Math.sqrt(level / f); 444// } 445 } 446 447// ship.setJitter(JITTER_COLOR, jitterLevel, 1, 0, 0 + jitterRangeBonus); 448// ship.setJitterUnder(JITTER_COLOR, jitterLevel, 11, 0f, 7f + jitterRangeBonus); 449 //ship.getEngineController().fadeToOtherColor(this, spec.getEffectColor1(), new Color(0,0,0,0), jitterLevel, 1f); 450 //ship.getEngineController().extendFlame(this, -0.25f, -0.25f, -0.25f); 451 452 ship.setExtraAlphaMult(1f - (1f - SHIP_ALPHA_MULT) * levelForAlpha); 453 ship.setApplyExtraAlphaToEngines(true); 454 455 456 //float shipTimeMult = 1f + (MAX_TIME_MULT - 1f) * levelForAlpha; 457 float extra = 0f; 458// if (isDisruptable(cloak)) { 459// extra = disruptionLevel; 460// } 461 float shipTimeMult = 1f + (getMaxTimeMult(stats) - 1f) * levelForAlpha * (1f - extra); 462 stats.getTimeMult().modifyMult(id, shipTimeMult); 463 if (player) { 464 Global.getCombatEngine().getTimeMult().modifyMult(id, 1f / shipTimeMult); 465// if (ship.areAnyEnemiesInRange()) { 466// Global.getCombatEngine().getTimeMult().modifyMult(id, 1f / shipTimeMult); 467// } else { 468// Global.getCombatEngine().getTimeMult().modifyMult(id, 2f / shipTimeMult); 469// } 470 } else { 471 Global.getCombatEngine().getTimeMult().unmodify(id); 472 } 473 474// float mitigationLevel = jitterLevel; 475// if (mitigationLevel > 0) { 476// stats.getHullDamageTakenMult().modifyMult(id, 1f - (1f - INCOMING_DAMAGE_MULT) * mitigationLevel); 477// stats.getArmorDamageTakenMult().modifyMult(id, 1f - (1f - INCOMING_DAMAGE_MULT) * mitigationLevel); 478// stats.getEmpDamageTakenMult().modifyMult(id, 1f - (1f - INCOMING_DAMAGE_MULT) * mitigationLevel); 479// } else { 480// stats.getHullDamageTakenMult().unmodify(id); 481// stats.getArmorDamageTakenMult().unmodify(id); 482// stats.getEmpDamageTakenMult().unmodify(id); 483// } 484 } 485 486 487 public void unapply(MutableShipStatsAPI stats, String id) { 488// stats.getHullDamageTakenMult().unmodify(id); 489// stats.getArmorDamageTakenMult().unmodify(id); 490// stats.getEmpDamageTakenMult().unmodify(id); 491 492 ShipAPI ship = null; 493 //boolean player = false; 494 if (stats.getEntity() instanceof ShipAPI) { 495 ship = (ShipAPI) stats.getEntity(); 496 //player = ship == Global.getCombatEngine().getPlayerShip(); 497 //id = id + "_" + ship.getId(); 498 } else { 499 return; 500 } 501 502 Global.getCombatEngine().getTimeMult().unmodify(id); 503 stats.getTimeMult().unmodify(id); 504 505 stats.getMaxSpeed().unmodify(id); 506 stats.getMaxSpeed().unmodifyMult(id + "_2"); 507 stats.getAcceleration().unmodify(id); 508 stats.getDeceleration().unmodify(id); 509 510 ship.setPhased(false); 511 ship.setExtraAlphaMult(1f); 512 513 ShipSystemAPI cloak = ship.getPhaseCloak(); 514 if (cloak == null) cloak = ship.getSystem(); 515 if (cloak != null) { 516 ((PhaseCloakSystemAPI)cloak).setMinCoilJitterLevel(0f); 517 } 518 519// stats.getMaxSpeed().unmodify(id); 520// stats.getMaxTurnRate().unmodify(id); 521// stats.getTurnAcceleration().unmodify(id); 522// stats.getAcceleration().unmodify(id); 523// stats.getDeceleration().unmodify(id); 524 } 525 526 public StatusData getStatusData(int index, State state, float effectLevel) { 527// if (index == 0) { 528// return new StatusData("time flow altered", false); 529// } 530// float percent = (1f - INCOMING_DAMAGE_MULT) * effectLevel * 100; 531// if (index == 1) { 532// return new StatusData("damage mitigated by " + (int) percent + "%", false); 533// } 534 return null; 535 } 536}