001package com.fs.starfarer.api.impl.campaign;
002
003import java.util.ArrayList;
004import java.util.List;
005import java.util.Random;
006
007import com.fs.starfarer.api.Global;
008import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;
009import com.fs.starfarer.api.characters.MutableCharacterStatsAPI.SkillLevelAPI;
010import com.fs.starfarer.api.characters.PersonAPI;
011import com.fs.starfarer.api.characters.SkillSpecAPI;
012import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
013import com.fs.starfarer.api.impl.campaign.ids.Skills;
014import com.fs.starfarer.api.impl.campaign.ids.Stats;
015import com.fs.starfarer.api.plugins.OfficerLevelupPlugin;
016import com.fs.starfarer.api.util.Misc;
017import com.fs.starfarer.api.util.SkillData;
018import com.fs.starfarer.api.util.SkillData.SkillsForAptitude;
019import com.fs.starfarer.api.util.WeightedRandomPicker;
020
021public class OfficerLevelupPluginImpl implements OfficerLevelupPlugin {
022
023        //public static float XP_MULT = 5f;
024        public static float XP_MULT = Global.getSettings().getFloat("officerXPRequiredMult");
025        
026        public static float EXPONENT_BEYOND_MAX_SPECIFIED_LEVEL = 1.1f;
027        
028        public static long [] XP_PER_LEVEL = new long [] {
029                0,              // level 1
030                12000,
031                20000,
032                35000,
033                45000,  // level 5
034                50000,
035                50000,
036                50000,
037                50000,
038                50000,
039        };
040        
041        public static long [] TOTAL_XP_PER_LEVEL = new long [XP_PER_LEVEL.length];
042        
043        static {
044                long total = 0;
045                for (int i = 0; i < XP_PER_LEVEL.length; i++) {
046                        total += XP_PER_LEVEL[i];
047                        TOTAL_XP_PER_LEVEL[i] = total;
048                }
049        }
050        
051        public long getXPForLevel(int level) {
052                if (level <= 1) return 0;
053                
054                if (level - 1 < TOTAL_XP_PER_LEVEL.length) {
055                        return (long) (TOTAL_XP_PER_LEVEL[level - 1] * XP_MULT);
056                }
057                
058                int maxSpecified = TOTAL_XP_PER_LEVEL.length;
059                long curr = TOTAL_XP_PER_LEVEL[maxSpecified - 1];
060                long last = XP_PER_LEVEL[maxSpecified - 1];
061                for (int i = maxSpecified; i < level; i++) {
062                        last *= EXPONENT_BEYOND_MAX_SPECIFIED_LEVEL;
063                        curr += last;
064                }
065
066                return (long) (curr * XP_MULT);
067        }
068        
069        public static void main(String[] args) {
070                for (int i = 1; i <= 6; i++) {
071                        System.out.println("Level " + i + ": " + new OfficerLevelupPluginImpl().getXPForLevel(i));
072                }
073        }
074
075        public int getMaxLevel(PersonAPI person) {
076                if (person != null && person.getMemoryWithoutUpdate().contains(MemFlags.OFFICER_MAX_LEVEL)) {
077                        return person.getMemoryWithoutUpdate().getInt(MemFlags.OFFICER_MAX_LEVEL);
078                }
079                int bonus = 0;
080                if (person != null) {
081                        MutableCharacterStatsAPI stats = person.getFleetCommanderStats();
082                        if (stats != null) {
083                                bonus = (int) stats.getDynamic().getMod(Stats.OFFICER_MAX_LEVEL_MOD).computeEffective(0);
084                        }
085                }
086                return (int) Global.getSettings().getFloat("officerMaxLevel") + bonus;
087        }
088        
089        public int getMaxEliteSkills(PersonAPI person) {
090                if (person != null && person.getMemoryWithoutUpdate().contains(MemFlags.OFFICER_MAX_ELITE_SKILLS)) {
091                        return person.getMemoryWithoutUpdate().getInt(MemFlags.OFFICER_MAX_ELITE_SKILLS);
092                }
093                int bonus = 0;
094                if (person != null) {
095                        MutableCharacterStatsAPI stats = person.getFleetCommanderStats();
096                        if (stats != null) {
097                                bonus = (int) stats.getDynamic().getMod(Stats.OFFICER_MAX_ELITE_SKILLS_MOD).computeEffective(0);
098                        }
099                }
100                return (int) Global.getSettings().getFloat("officerMaxEliteSkills") + bonus;
101        }
102
103        public List<String> pickLevelupSkillsV2(PersonAPI person, Random random) {
104                if (random == null) random = new Random();
105                
106                
107                List<SkillSpecAPI> leftovers = new ArrayList<SkillSpecAPI>();
108                List<List<SkillSpecAPI>> unknownTiers = new ArrayList<List<SkillSpecAPI>>();
109                
110                MutableCharacterStatsAPI stats = person.getStats();
111                int level = stats.getLevel();
112                
113                for (String ap : SkillData.getAptitudes().keySet()) {
114                        SkillsForAptitude skills = SkillData.getSkills(ap);
115                        int tier = 0;
116                        for (List<SkillSpecAPI> list : skills.tiers) {
117                                tier++;
118                                
119                                List<SkillSpecAPI> unknown = new ArrayList<SkillSpecAPI>();
120                                for (SkillSpecAPI skill : list) {
121                                        if (!skill.isCombatOfficerSkill()) continue;
122                                        if (skill.hasTag(Skills.TAG_DEPRECATED)) continue;
123                                        if (skill.hasTag(Skills.TAG_PLAYER_ONLY)) continue;
124                                        if (stats.getSkillLevel(skill.getId()) <= 0) {
125                                                unknown.add(skill);
126                                        }
127                                }
128                                if (list.size() == unknown.size() && (tier < 4 || level >= 3)) {
129                                        unknownTiers.add(list);
130                                } else {
131                                        leftovers.addAll(unknown);
132                                }
133                        }
134                }
135                
136                int max = 4;
137                if (Misc.isMentored(person)) {
138                        max = 6;
139                }
140                List<String> result = new ArrayList<String>();
141                
142                if (!unknownTiers.isEmpty()) {
143                        WeightedRandomPicker<List<SkillSpecAPI>> picker = new WeightedRandomPicker<List<SkillSpecAPI>>(random);
144                        picker.addAll(unknownTiers);
145                        while (!picker.isEmpty() && result.size() < max) {
146                                List<SkillSpecAPI> pick = picker.pickAndRemove();
147                                for (SkillSpecAPI s : pick) {
148                                        if (result.size() >= max) break; 
149                                        result.add(s.getId());
150                                }
151                        }
152                }
153                
154                if (!leftovers.isEmpty()) {
155                        WeightedRandomPicker<SkillSpecAPI> picker = new WeightedRandomPicker<SkillSpecAPI>(random);
156                        picker.addAll(leftovers);
157                        while (!picker.isEmpty() && result.size() < max) {
158                                SkillSpecAPI pick = picker.pickAndRemove();
159                                result.add(pick.getId());
160                        }
161                }
162                
163                return result;
164        }
165        
166        
167        public List<String> pickLevelupSkillsV3(PersonAPI person, Random random) {
168                if (random == null) random = new Random();
169                
170                
171                List<SkillSpecAPI> top = new ArrayList<SkillSpecAPI>();
172                List<SkillSpecAPI> leftovers = new ArrayList<SkillSpecAPI>();
173                
174                MutableCharacterStatsAPI stats = person.getStats();
175                int level = stats.getLevel();
176                
177                for (String ap : SkillData.getAptitudes().keySet()) {
178                        SkillsForAptitude skills = SkillData.getSkills(ap);
179                        for (List<SkillSpecAPI> list : skills.tiers) {
180                                boolean topTier = false;
181                                for (SkillSpecAPI skill : list) {
182                                        if (!skill.isCombatOfficerSkill()) continue;
183                                        if (skill.hasTag(Skills.TAG_DEPRECATED)) continue;
184                                        if (skill.hasTag(Skills.TAG_PLAYER_ONLY)) continue;
185                                        if (stats.getSkillLevel(skill.getId()) <= 0) {
186                                                if (skill.getTier() == 5) topTier = true;
187                                                if (!topTier || level >= 3) {
188                                                        if (topTier) {
189                                                                top.add(skill);
190                                                        } else {
191                                                                leftovers.add(skill);
192                                                        }
193                                                }
194                                        }
195                                }
196                        }
197                }
198                
199                int max = 4;
200                if (Misc.isMentored(person)) {
201                        max = 6;
202                }
203                if (person.getMemoryWithoutUpdate().contains(MemFlags.OFFICER_SKILL_PICKS_PER_LEVEL)) {
204                        max = person.getMemoryWithoutUpdate().getInt(MemFlags.OFFICER_SKILL_PICKS_PER_LEVEL);
205                }
206                List<String> result = new ArrayList<String>();
207                
208                if (!top.isEmpty()) {
209                        WeightedRandomPicker<SkillSpecAPI> picker = new WeightedRandomPicker<SkillSpecAPI>(random);
210                        picker.addAll(top);
211                        while (!picker.isEmpty() && result.size() < max) {
212                                SkillSpecAPI pick = picker.pickAndRemove();
213                                result.add(pick.getId());
214                        }
215                }
216                if (!leftovers.isEmpty()) {
217                        WeightedRandomPicker<SkillSpecAPI> picker = new WeightedRandomPicker<SkillSpecAPI>(random);
218                        picker.addAll(leftovers);
219                        while (!picker.isEmpty() && result.size() < max) {
220                                SkillSpecAPI pick = picker.pickAndRemove();
221                                result.add(pick.getId());
222                        }
223                }
224                
225                return result;
226        }
227        
228        
229        public List<String> pickLevelupSkills(PersonAPI person, Random random) {
230                if (true) return pickLevelupSkillsV3(person, random);
231                if (random == null) random = new Random();
232                
233                boolean hasCarrierSkills = false;
234                for (SkillLevelAPI skill : person.getStats().getSkillsCopy()) {
235                        if (!skill.getSkill().isCombatOfficerSkill()) continue;
236                        
237                        if (skill.getSkill().hasTag(Skills.TAG_CARRIER)) {
238                                hasCarrierSkills = true;
239                                break;
240                        }
241                }
242                
243                WeightedRandomPicker<String> nonMaxedSkills = new WeightedRandomPicker<String>(random);
244                WeightedRandomPicker<String> knownSkills = new WeightedRandomPicker<String>(random);
245                WeightedRandomPicker<String> carrierSkills = new WeightedRandomPicker<String>(random);
246                WeightedRandomPicker<String> knownCarrierSkills = new WeightedRandomPicker<String>(random);
247                WeightedRandomPicker<String> nonCarrierSkills = new WeightedRandomPicker<String>(random);
248                WeightedRandomPicker<String> knownNonCarrierSkills = new WeightedRandomPicker<String>(random);
249                List<String> allSkillIds = Global.getSettings().getSortedSkillIds();
250                int knownSkillCount = 0;
251                for (String skillId : allSkillIds) {
252                        SkillSpecAPI skill = Global.getSettings().getSkillSpec(skillId);
253                        if (skill.isCombatOfficerSkill()) {
254                                addSkill(person, nonMaxedSkills, skillId);
255                                float level = person.getStats().getSkillLevel(skillId);
256                                if (level > 0) {
257                                        knownSkillCount++;
258                                        addSkill(person, knownSkills, skillId);
259                                }
260                                if (skill.hasTag(Skills.TAG_CARRIER)) {
261                                        addSkill(person, carrierSkills, skillId);
262                                        if (level > 0) addSkill(person, knownCarrierSkills, skillId);
263                                } else {
264                                        addSkill(person, nonCarrierSkills, skillId);
265                                        if (level > 0) addSkill(person, knownNonCarrierSkills, skillId);
266                                }
267                        }
268                }
269                List<String> result = new ArrayList<String>();
270
271                if (!knownSkills.isEmpty()) {
272                        String pick = knownSkills.pickAndRemove();
273                        nonMaxedSkills.remove(pick);
274                        carrierSkills.remove(pick);
275                        nonCarrierSkills.remove(pick);
276                        knownCarrierSkills.remove(pick);
277                        knownNonCarrierSkills.remove(pick);
278                        result.add(pick);
279                }
280                
281                int maxSkillsPerOfficer = 10;
282                if (knownSkillCount >= maxSkillsPerOfficer) {
283                        if (hasCarrierSkills && !knownCarrierSkills.isEmpty()) {
284                                String pick = knownCarrierSkills.pickAndRemove();
285                                result.add(pick);
286                        } else if (!knownNonCarrierSkills.isEmpty() && result.size() == 0) {
287                                String pick = knownNonCarrierSkills.pickAndRemove();
288                                result.add(pick);
289                        } else if (!knownSkills.isEmpty()) {
290                                String pick = knownSkills.pickAndRemove();
291                                result.add(pick);
292                        }
293                } else {
294                        if (hasCarrierSkills && !carrierSkills.isEmpty()) {
295                                String pick = carrierSkills.pickAndRemove();
296                                result.add(pick);
297                        } else if (!nonCarrierSkills.isEmpty() && result.size() == 0) {
298                                String pick = nonCarrierSkills.pickAndRemove();
299                                result.add(pick);
300                        } else if (!nonMaxedSkills.isEmpty()) {
301                                String pick = nonMaxedSkills.pickAndRemove();
302                                result.add(pick);
303                        }
304                }
305                
306                if (result.size() < 2 && knownSkillCount < maxSkillsPerOfficer) {
307                        for (String id : result) {
308                                nonMaxedSkills.remove(id);
309                        }
310                        if (!nonMaxedSkills.isEmpty()) {
311                                String pick = nonMaxedSkills.pickAndRemove();
312                                knownSkills.remove(pick);
313                                result.add(pick);
314                        }
315                }
316                
317                return result;
318        }
319        
320        
321        
322        private void addSkill(PersonAPI person, WeightedRandomPicker<String> picker, String skill) {
323                if (person.getStats().getSkillLevel(skill) >= 3) return;
324                picker.add(skill);
325        }
326        
327        
328
329//      public static void main(String[] args) {
330//              LevelupPluginImpl impl = new LevelupPluginImpl();
331//              for (int i = 0; i <= 100; i++) {
332//                      System.out.println(String.format("% 4d: % 20d", i, impl.getXPForLevel(i)));
333//              }
334//      }
335
336}
337
338
339
340
341