001package com.fs.starfarer.api.impl.campaign.abilities; 002 003import org.lwjgl.util.vector.Vector2f; 004 005import com.fs.starfarer.api.Global; 006import com.fs.starfarer.api.ui.TooltipMakerAPI; 007import com.fs.starfarer.api.util.Misc; 008 009/** 010 * Ability that's turned on and then plays out until it's finished. 011 * 012 * (Why use methods to pass in sound ids etc instead of passing them in to a constructor? 013 * Mainly so they don't need to go into the save file as they would if they were 014 * stored in member variables.) 015 * 016 * @author Alex Mosolov 017 * 018 * Copyright 2015 Fractal Softworks, LLC 019 */ 020public abstract class BaseDurationAbility extends BaseAbilityPlugin { 021 022 public static final float LOOP_FADE_TIME_DAYS = 0.1f; 023 024 public float getLoopSoundUIVolume() { 025 if (loopFadeLeft > 0) { 026 return loopFadeLeft / LOOP_FADE_TIME_DAYS; 027 } 028 if (level > 0) { 029 if (fadingOut) { 030 // keep loop volume at 1 when ability is winding down, fade it out after 031 return 1; 032 } else { 033 // ramp loop volume up as ability winds up 034 return level; 035 } 036 } 037 return 0; 038 } 039 public float getLoopSoundUIPitch() { return 1f; } 040 041 public float getLoopSoundWorldVolume() { return getLoopSoundUIVolume(); } 042 public float getLoopSoundWorldPitch() { return 1f; } 043 044 045 046 public float getCooldownDays() { return spec.getDeactivationCooldown(); } 047 public float getDurationDays() { return spec.getDurationDays(); } 048 public float getTotalDurationDays() { return spec.getDurationDays() + spec.getActivationDays() + spec.getDeactivationDays(); } 049 public float getActivationDays() { return spec.getActivationDays(); } 050 public float getDeactivationDays() { return spec.getDeactivationDays(); } 051 052 053 protected abstract void activateImpl(); 054 055 /** 056 * Will be called once when level is 0 and consistently when level >0. 057 * @param level 058 */ 059 protected abstract void applyEffect(float amount, float level); 060 protected abstract void deactivateImpl(); 061 protected abstract void cleanupImpl(); 062 063 064 protected boolean turnedOn = false; 065 protected float activeDaysLeft = 0; 066 protected float cooldownLeft = 0; 067 protected float level = 0; 068 069 protected float loopFadeLeft = 0; 070 protected boolean fadingOut = false; 071 072 @Override 073 public void advance(float amount) { 074 super.advance(amount); 075 076 if (entity.isInCurrentLocation() && entity.isVisibleToPlayerFleet() && 077 getLoopSoundWorldVolume() > 0 && !Global.getSector().isPaused()) { 078 String soundId = getLoopSoundWorld(); 079 if (soundId != null) { 080 Global.getSector().getCampaignUI().suppressMusic(spec.getMusicSuppression() * getLoopSoundWorldVolume()); 081 Global.getSoundPlayer().playLoop(soundId, entity, 082 getLoopSoundWorldPitch(), getLoopSoundWorldVolume(), 083 entity.getLocation(), entity.getVelocity()); 084 } 085 } 086 087 if (entity.isPlayerFleet() && getLoopSoundUIVolume() > 0 && !Global.getSector().isPaused()) { 088 String soundId = getLoopSoundUI(); 089 if (soundId != null) { 090 Global.getSector().getCampaignUI().suppressMusic(spec.getMusicSuppression() * getLoopSoundUIVolume()); 091 Global.getSoundPlayer().playLoop(soundId, entity, 092 getLoopSoundUIPitch(), getLoopSoundUIVolume(), 093 entity.getLocation(), entity.getVelocity()); 094 } 095 } 096 097 if (activeDaysLeft > 0) { 098 float days = Global.getSector().getClock().convertToDays(amount); 099 activeDaysLeft -= days; 100 101 if (activeDaysLeft <= 0) { 102 level = 1f; 103 applyEffect(amount, level); 104 activeDaysLeft = 0; 105 106 deactivate(); 107 } 108 } else if (cooldownLeft > 0) { 109 float days = Global.getSector().getClock().convertToDays(amount); 110 cooldownLeft -= days; 111 if (cooldownLeft < 0) cooldownLeft = 0; 112 } 113 114 if (loopFadeLeft > 0) { 115 float days = Global.getSector().getClock().convertToDays(amount); 116 loopFadeLeft -= days; 117 if (loopFadeLeft < 0) { 118 loopFadeLeft = 0; 119 } 120 } 121 122 123 float prevLevel = level; 124 if (activeDaysLeft > 0) { 125 float a = getActivationDays(); 126 float d = getDeactivationDays(); 127 float t = getTotalDurationDays(); 128 if (activeDaysLeft > t - a) { 129 if (a <= 0) { 130 level = 1; 131 } else { 132 level = 1f - (activeDaysLeft - (t - a)) / a; 133 } 134 } else if (activeDaysLeft < d) { 135 fadingOut = true; 136 if (d <= 0) { 137 level = 0; 138 } else { 139 level = activeDaysLeft / d; 140 } 141 } else { 142 level = 1; 143 } 144 } else { 145 level = 0; 146 } 147 148 if (prevLevel != level || level > 0) { 149 applyEffect(amount, level); 150 disableIncompatible(); 151 } 152 } 153 154 155 protected void addIncompatibleToTooltip(TooltipMakerAPI tooltip, boolean expanded) { 156 addIncompatibleToTooltip(tooltip, "Disables the following abilities while active:", 157 "Expand tooltip to view conflicting abilities", 158 expanded); 159 } 160 161 @Override 162 public float getCooldownFraction() { 163 if (cooldownLeft <= 0) return 1f; 164 return 1f - cooldownLeft / getCooldownDays(); 165 } 166 167 @Override 168 public float getProgressFraction() { 169 if (getTotalDurationDays() <= 0 || !turnedOn) return 0f; 170 return 1f - (activeDaysLeft / getTotalDurationDays()); 171 } 172 173 @Override 174 public boolean showProgressIndicator() { 175 return turnedOn; 176 } 177 178 @Override 179 public boolean showActiveIndicator() { 180 return false; 181 } 182 183 @Override 184 public boolean isUsable() { 185 return !isOnCooldown() && !isInProgress() && !turnedOn && disableFrames <= 0; 186 } 187 public void pressButton() { 188 if (isUsable() && !turnedOn) { 189 activate(); 190 if (entity.isPlayerFleet()) { 191 String soundId = getOnSoundUI(); 192 if (soundId != null) { 193 if (PLAY_UI_SOUNDS_IN_WORLD_SOURCES) { 194 Global.getSoundPlayer().playSound(soundId, 1f, 1f, Global.getSoundPlayer().getListenerPos(), new Vector2f()); 195 } else { 196 Global.getSoundPlayer().playUISound(soundId, 1f, 1f); 197 } 198 } 199 } 200 } 201 } 202 203 public void activate() { 204 if (isUsable() && !turnedOn) { 205 turnedOn = true; 206 loopFadeLeft = 0f; 207 fadingOut = false; 208 activeDaysLeft = getTotalDurationDays(); 209 210 if (entity.isInCurrentLocation() && entity.isVisibleToPlayerFleet() && !entity.isPlayerFleet()) { 211 String soundId = getOnSoundWorld(); 212 if (soundId != null) { 213 Global.getSoundPlayer().playSound(soundId, 1f, 1f, entity.getLocation(), entity.getVelocity()); 214 } 215 } 216 if (getActivationDays() <= 0) { 217 level = 1; 218 } 219 220 if (entity.isInCurrentLocation()) { 221 if (getActivationText() != null && entity.isVisibleToPlayerFleet()) { 222 entity.addFloatingText(getActivationText(), Misc.setAlpha(entity.getIndicatorColor(), 255), 0.5f); 223 } 224 } 225 226 activateImpl(); 227 applyEffect(0f, level); 228 interruptIncompatible(); 229 disableIncompatible(); 230 231 if (getTotalDurationDays() <= 0) { 232 deactivate(); 233 } 234 235 super.activate(); 236 } 237 } 238 239 public void deactivate() { 240 if (turnedOn) { 241 turnedOn = false; 242 activeDaysLeft = 0f; 243 loopFadeLeft = LOOP_FADE_TIME_DAYS; 244 if (entity.isInCurrentLocation() && entity.isVisibleToPlayerFleet() && !entity.isPlayerFleet()) { 245 String soundId = getOffSoundWorld(); 246 if (soundId != null) { 247 Global.getSoundPlayer().playSound(soundId, 1f, 1f, entity.getLocation(), entity.getVelocity()); 248 } 249 } 250 if (entity.isPlayerFleet()) { 251 String soundId = getOffSoundUI(); 252 if (soundId != null) { 253 if (PLAY_UI_SOUNDS_IN_WORLD_SOURCES) { 254 Global.getSoundPlayer().playSound(soundId, 1f, 1f, Global.getSoundPlayer().getListenerPos(), new Vector2f()); 255 } else { 256 Global.getSoundPlayer().playUISound(soundId, 1f, 1f); 257 } 258 } 259 } 260 cooldownLeft = getCooldownDays(); 261 262 level = 0; 263 applyEffect(0f, level); 264 265 266 if (entity.isInCurrentLocation()) { 267 if (getDeactivationText() != null && entity.isVisibleToPlayerFleet()) { 268 entity.addFloatingText(getDeactivationText(), Misc.setAlpha(entity.getIndicatorColor(), 255), 0.5f); 269 } 270 } 271 deactivateImpl(); 272 273 super.deactivate(); 274 } 275 } 276 277 278 @Override 279 public void cleanup() { 280 super.cleanup(); 281 282 applyEffect(0f, 0f); 283 284 cleanupImpl(); 285 } 286 287 public boolean isActive() { 288 return false; 289 } 290 291 @Override 292 public boolean isInProgress() { 293 return super.isInProgress(); 294 } 295 296 297 public boolean hasCustomButtonPressSounds() { 298 return getOnSoundUI() != null; 299 } 300 301 @Override 302 public boolean runWhilePaused() { 303 return false; 304 } 305 public float getActiveDaysLeft() { 306 return activeDaysLeft; 307 } 308 public void setActiveDaysLeft(float activeDaysLeft) { 309 this.activeDaysLeft = activeDaysLeft; 310 } 311 public float getCooldownLeft() { 312 return cooldownLeft; 313 } 314 public void setCooldownLeft(float cooldownLeft) { 315 this.cooldownLeft = cooldownLeft; 316 } 317 public boolean isFadingOut() { 318 return fadingOut; 319 } 320 public float getLevel() { 321 return level; 322 } 323 324} 325 326 327 328 329