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 can be toggled on and off.
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 BaseToggleAbility extends BaseAbilityPlugin {
021
022        public float getLoopSoundUIVolume() { return level; }
023        public float getLoopSoundUIPitch() { return 1f; }
024        
025        public float getLoopSoundWorldVolume() { return level; }
026        public float getLoopSoundWorldPitch() { return 1f; }
027        
028        
029        public float getActivateCooldownDays() { return spec.getActivationCooldown(); }
030        public float getDeactivateCooldownDays() { return spec.getDeactivationCooldown(); }
031        
032        public float getActivationDays() { return spec.getActivationDays(); }
033        public float getDeactivationDays() { return spec.getDeactivationDays(); }
034        
035        
036        protected abstract void activateImpl();
037        
038        /**
039         * Will be called once when level is 0 and consistently when level >0.
040         * @param level
041         */
042        protected abstract void applyEffect(float amount, float level);
043        protected abstract void deactivateImpl();
044        protected abstract void cleanupImpl();
045        
046
047        protected boolean turnedOn = false;
048        protected float cooldownLeft = 0;
049        protected boolean isActivateCooldown = false;
050        
051        protected float level = 0;
052        
053        public float getCooldownLeft() {
054                return cooldownLeft;
055        }
056        public void setCooldownLeft(float cooldownLeft) {
057                this.cooldownLeft = cooldownLeft;
058        }
059        
060        
061        @Override
062        public void advance(float amount) {
063                super.advance(amount);
064                
065                if (entity.isInCurrentLocation() && entity.isVisibleToPlayerFleet() &&
066                                getLoopSoundWorldVolume() > 0 && !Global.getSector().isPaused()) {
067                        String soundId = getLoopSoundWorld();
068                        if (soundId != null) {
069                                Global.getSector().getCampaignUI().suppressMusic(spec.getMusicSuppression() * getLoopSoundWorldVolume());
070                                Global.getSoundPlayer().playLoop(soundId, entity, 
071                                                        getLoopSoundWorldPitch(), getLoopSoundWorldVolume(),
072                                                        entity.getLocation(), entity.getVelocity());
073                        }
074                }
075                
076                if (entity.isPlayerFleet() && getLoopSoundUIVolume() > 0 && !Global.getSector().isPaused()) {
077                        String soundId = getLoopSoundUI();
078                        if (soundId != null) {
079                                Global.getSector().getCampaignUI().suppressMusic(spec.getMusicSuppression() * getLoopSoundUIVolume());
080                                Global.getSoundPlayer().playLoop(soundId, entity, 
081                                                        getLoopSoundUIPitch(), getLoopSoundUIVolume(),
082                                                        entity.getLocation(), entity.getVelocity());
083                        }
084                }
085                
086                
087                if (cooldownLeft > 0) {
088                        float days = Global.getSector().getClock().convertToDays(amount);
089                        cooldownLeft -= days;
090                        if (cooldownLeft < 0) cooldownLeft = 0;
091                }
092                
093                float prevLevel = level;
094                if (turnedOn && level < 1) {
095                        float days = Global.getSector().getClock().convertToDays(amount);
096                        level += days / getActivationDays();
097                        if (level > 1) level = 1;
098                } else if (!turnedOn && level > 0) {
099                        float days = Global.getSector().getClock().convertToDays(amount);
100                        level -= days / getDeactivationDays();
101                        if (level < 0) level = 0;
102                }
103                if (prevLevel != level || level > 0) {
104                        applyEffect(amount, level);
105                        //disableIncompatible();
106                }
107        }
108        
109        protected void addIncompatibleToTooltip(TooltipMakerAPI tooltip, boolean expanded) {
110                addIncompatibleToTooltip(tooltip, "Interrupts the following abilities when activated:",
111                                                                                  "Expand tooltip to view conflicting abilities",
112                                                                                  expanded);
113        }
114        
115        @Override
116        public boolean isUsable() {
117                if (!isActivateCooldown && 
118                                getProgressFraction() > 0 && getProgressFraction() < 1 && 
119                                getDeactivationDays() > 0) return false;
120                return super.isUsable();
121        }
122        
123        @Override
124        public float getCooldownFraction() {
125                if (cooldownLeft <= 0) return 1f;
126                if (isActivateCooldown) {
127                        float max = getActivateCooldownDays();
128                        if (max <= 0f) {
129                                return 1f - cooldownLeft / 1f;
130                        }
131                        return 1f - cooldownLeft / max;
132                } else {
133                        float max = getDeactivateCooldownDays();
134                        if (max <= 0f) {
135                                return 1f - cooldownLeft / 1f;
136                        }
137                        return 1f - cooldownLeft / max;
138                }
139        }
140        
141        @Override
142        public float getProgressFraction() {
143                return level;
144        }
145        
146        @Override
147        public boolean showProgressIndicator() {
148                return level > 0 && (!turnedOn || level < 1);
149        }
150        
151        @Override
152        public boolean showActiveIndicator() {
153                return isActive() && level >= 1;
154        }
155        
156        public void pressButton() {
157                if (isActive()) {
158                        deactivate();
159                } else {
160                        activate();
161                }
162                if (entity.isPlayerFleet()) {
163                        if (isActive()) {
164                                String soundId = getOnSoundUI();
165                                if (soundId != null) {
166                                        if (PLAY_UI_SOUNDS_IN_WORLD_SOURCES) {
167                                                Global.getSoundPlayer().playSound(soundId, 1f, 1f, Global.getSoundPlayer().getListenerPos(), new Vector2f());
168                                        } else {
169                                                Global.getSoundPlayer().playUISound(soundId, 1f, 1f);
170                                        }
171                                }
172                        } else {
173                                String soundId = getOffSoundUI();
174                                if (soundId != null) {
175                                        if (PLAY_UI_SOUNDS_IN_WORLD_SOURCES) {
176                                                Global.getSoundPlayer().playSound(soundId, 1f, 1f, Global.getSoundPlayer().getListenerPos(), new Vector2f());
177                                        } else {
178                                                Global.getSoundPlayer().playUISound(soundId, 1f, 1f);
179                                        }
180                                }
181                        }
182                }
183        }
184        
185        public void activate() {
186                if (!isActive() && isUsable()) {
187                        turnedOn = true;
188                        if (entity.isInCurrentLocation() && entity.isVisibleToPlayerFleet() && !entity.isPlayerFleet()) {
189                                String soundId = getOnSoundWorld();
190                                if (soundId != null) {
191                                        Global.getSoundPlayer().playSound(soundId, 1f, 1f, entity.getLocation(), entity.getVelocity());
192                                }
193                        }
194                        if (getActivationDays() <= 0) {
195                                level = 1;
196                        }
197                        
198                        cooldownLeft = getActivateCooldownDays();
199                        isActivateCooldown = true;
200                        
201                        if (entity.isInCurrentLocation()) {
202                                if (getActivationText() != null && entity.isVisibleToPlayerFleet()) {
203                                        entity.addFloatingText(getActivationText(), Misc.setAlpha(entity.getIndicatorColor(), 255), 0.5f);
204                                }
205                        }
206                        activateImpl();
207                        applyEffect(0f, level);
208                        interruptIncompatible();
209                        
210                        super.activate();
211                }
212        }
213
214        public void deactivate() {
215                if (isActive()) {// && isUsable()) {
216                        turnedOn = false;
217                        if (entity.isInCurrentLocation() && entity.isVisibleToPlayerFleet() && !entity.isPlayerFleet()) {
218                                String soundId = getOffSoundWorld();
219                                if (soundId != null) {
220                                        Global.getSoundPlayer().playSound(soundId, 1f, 1f, entity.getLocation(), entity.getVelocity());
221                                }
222                        }
223                        if (getDeactivationDays() <= 0) {
224                                level = 0;
225                        }
226                        cooldownLeft = getDeactivateCooldownDays();
227                        isActivateCooldown = false;
228                        
229                        applyEffect(0f, level);
230                        
231                        if (entity.isInCurrentLocation()) {
232                                if (getDeactivationText() != null && entity.isVisibleToPlayerFleet()) {
233                                        entity.addFloatingText(getDeactivationText(), Misc.setAlpha(entity.getIndicatorColor(), 255), 0.5f);
234                                }
235                        }
236                        deactivateImpl();
237                        
238                        super.deactivate();
239                }
240        }
241        
242        
243        @Override
244        public void cleanup() {
245                super.cleanup();
246                
247                applyEffect(0, 0);
248                
249                cleanupImpl();
250        }
251        
252        public boolean isActive() {
253                return turnedOn;
254        }
255        
256        public boolean hasCustomButtonPressSounds() {
257                return getOnSoundUI() != null;
258        }
259        
260        @Override
261        public boolean runWhilePaused() {
262                return false;
263        }
264        public float getLevel() {
265                return level;
266        }
267        
268}
269
270
271
272
273