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