001package com.fs.starfarer.api.impl.campaign.procgen.themes;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.HashMap;
006import java.util.List;
007import java.util.Map;
008import java.util.Random;
009
010import com.fs.starfarer.api.Global;
011import com.fs.starfarer.api.campaign.AICoreOfficerPlugin;
012import com.fs.starfarer.api.campaign.CampaignFleetAPI;
013import com.fs.starfarer.api.campaign.FactionAPI;
014import com.fs.starfarer.api.campaign.FactionDoctrineAPI;
015import com.fs.starfarer.api.campaign.GenericPluginManagerAPI;
016import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;
017import com.fs.starfarer.api.characters.PersonAPI;
018import com.fs.starfarer.api.combat.WeaponAPI.AIHints;
019import com.fs.starfarer.api.combat.WeaponAPI.WeaponSize;
020import com.fs.starfarer.api.combat.WeaponAPI.WeaponType;
021import com.fs.starfarer.api.fleet.FleetMemberAPI;
022import com.fs.starfarer.api.impl.campaign.events.OfficerManagerEvent.SkillPickPreference;
023import com.fs.starfarer.api.impl.campaign.fleets.BaseGenerateFleetOfficersPlugin;
024import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3;
025import com.fs.starfarer.api.impl.campaign.fleets.FleetParamsV3;
026import com.fs.starfarer.api.impl.campaign.ids.Commodities;
027import com.fs.starfarer.api.impl.campaign.ids.Factions;
028import com.fs.starfarer.api.impl.campaign.ids.Ranks;
029import com.fs.starfarer.api.impl.campaign.ids.Skills;
030import com.fs.starfarer.api.impl.campaign.missions.hub.HubMissionWithTriggers.OfficerQuality;
031import com.fs.starfarer.api.loading.WeaponSpecAPI;
032import com.fs.starfarer.api.util.Misc;
033import com.fs.starfarer.api.util.WeightedRandomPicker;
034
035public class RemnantOfficerGeneratorPlugin extends BaseGenerateFleetOfficersPlugin {
036        
037        protected boolean putCoresOnCivShips = false;
038        protected boolean forceIntegrateCores = false;
039        protected boolean forceNoCommander = false;
040        protected boolean derelictMode = false;
041        protected float coreMult = 1f;
042        
043        public RemnantOfficerGeneratorPlugin() {
044        }
045
046        public RemnantOfficerGeneratorPlugin(boolean derelictMode, float coreMult) {
047                this.derelictMode = derelictMode;
048                this.coreMult = coreMult;
049        }
050        
051
052        
053        public boolean isForceNoCommander() {
054                return forceNoCommander;
055        }
056
057        public void setForceNoCommander(boolean forceNoCommander) {
058                this.forceNoCommander = forceNoCommander;
059        }
060
061        public boolean isPutCoresOnCivShips() {
062                return putCoresOnCivShips;
063        }
064
065        public void setPutCoresOnCivShips(boolean putCoresOnCivShips) {
066                this.putCoresOnCivShips = putCoresOnCivShips;
067        }
068
069        public boolean isForceIntegrateCores() {
070                return forceIntegrateCores;
071        }
072
073        public void setForceIntegrateCores(boolean forceIntegrateCores) {
074                this.forceIntegrateCores = forceIntegrateCores;
075        }
076
077        @Override
078        public int getHandlingPriority(Object params) {
079                if (!(params instanceof GenerateFleetOfficersPickData)) return -1;
080                
081                GenerateFleetOfficersPickData data = (GenerateFleetOfficersPickData) params;
082                
083                if (data.params != null && !data.params.withOfficers) return -1;
084                
085                if (data.params.aiCores != null) return GenericPluginManagerAPI.CORE_SUBSET;
086                
087                if (data.fleet == null || !data.fleet.getFaction().getId().equals(Factions.REMNANTS)) return -1;
088                
089                return GenericPluginManagerAPI.CORE_SUBSET;
090        }
091
092        
093        @Override
094        public void addCommanderAndOfficers(CampaignFleetAPI fleet, FleetParamsV3 params, Random random) {
095                if (random == null) random = Misc.random;
096                FactionAPI faction = fleet.getFaction();
097                FactionDoctrineAPI doctrine = faction.getDoctrine();
098                if (!derelictMode && params != null && params.doctrineOverride != null) {
099                        doctrine = params.doctrineOverride;
100                }
101                List<FleetMemberAPI> members = fleet.getFleetData().getMembersListCopy();
102                if (members.isEmpty()) return;
103                
104                Map<String, AICoreOfficerPlugin> plugins = new HashMap<String, AICoreOfficerPlugin>();
105                
106                plugins.put(Commodities.OMEGA_CORE, Misc.getAICoreOfficerPlugin(Commodities.OMEGA_CORE));
107                plugins.put(Commodities.ALPHA_CORE, Misc.getAICoreOfficerPlugin(Commodities.ALPHA_CORE));
108                plugins.put(Commodities.BETA_CORE, Misc.getAICoreOfficerPlugin(Commodities.BETA_CORE));
109                plugins.put(Commodities.GAMMA_CORE, Misc.getAICoreOfficerPlugin(Commodities.GAMMA_CORE));
110                String nothing = "nothing";
111                
112                float fleetFP = 0f; //fleet.getFleetPoints(); <- doesn't work here, requires a call to fleet.forceSync()
113                for (FleetMemberAPI member : members) {
114                        fleetFP += member.getFleetPointCost();
115                }
116                boolean allowAlphaAnywhere = fleetFP > 150f;
117                boolean allowBetaAnywhere = fleetFP > 75f;
118                
119                //boolean integrate = fleetFP > 200f || params.forceIntegrateAICores;
120                boolean integrate = params != null && !params.doNotIntegrateAICores;
121                integrate |= forceIntegrateCores;
122                
123                int numCommanderSkills = 0;
124                if (allowBetaAnywhere) numCommanderSkills++;
125                if (allowAlphaAnywhere) numCommanderSkills++;
126                if (params != null && params.noCommanderSkills != null && params.noCommanderSkills) numCommanderSkills = 0;
127                
128                
129                //float fpPerCore = 20f;
130                float fpPerCore = Global.getSettings().getFloat("baseFPPerAICore");
131                
132                if (derelictMode) {
133                        fpPerCore = 30 - 20f * coreMult;
134                }
135                
136                int minCores = (int) (fleetFP / fpPerCore * (params != null ? params.officerNumberMult : 1f));
137                if (params != null) {
138                        minCores += params.officerNumberBonus;
139                }
140                if (minCores < 1) minCores = 1;
141                
142                boolean debug = true;
143                debug = false;
144                
145                WeightedRandomPicker<FleetMemberAPI> withOfficers = new WeightedRandomPicker<FleetMemberAPI>(random);
146                
147                int maxSize = 0;
148                for (FleetMemberAPI member : members) {
149                        if (!Misc.isAutomated(member)) continue;
150                        if (member.isFighterWing()) continue;
151                        if (member.isCivilian() && !putCoresOnCivShips) continue;
152                        int size = member.getHullSpec().getHullSize().ordinal();
153                        if (size > maxSize) {
154                                maxSize = size;
155                        }
156                }
157                
158                List<FleetMemberAPI> allWithOfficers = new ArrayList<FleetMemberAPI>();
159                int addedCores = 0;
160                for (FleetMemberAPI member : members) {
161                        if (!Misc.isAutomated(member)) continue;
162                        if (member.isCivilian() && !putCoresOnCivShips) continue;
163                        if (member.isFighterWing()) continue;
164                        
165                        float fp = member.getFleetPointCost();
166                        
167                        WeightedRandomPicker<String> picker = new WeightedRandomPicker<String>(random);
168                        
169                        if (params != null && params.aiCores == OfficerQuality.AI_GAMMA) {
170                                picker.add(Commodities.GAMMA_CORE, fp);
171                        } else if (params != null && params.aiCores == OfficerQuality.AI_BETA) {
172                                picker.add(Commodities.BETA_CORE, fp);
173                        } else if (params != null && params.aiCores == OfficerQuality.AI_ALPHA) {
174                                picker.add(Commodities.ALPHA_CORE, fp);
175                        } else if (params != null && params.aiCores == OfficerQuality.AI_OMEGA) {
176                                picker.add(Commodities.OMEGA_CORE, fp);
177                        } else if (params != null && params.aiCores == OfficerQuality.AI_BETA_OR_GAMMA) {
178                                if (member.isCapital() || member.isCruiser()) {
179                                        picker.add(Commodities.BETA_CORE, fp);
180                                } else if (allowAlphaAnywhere) {
181                                        picker.add(Commodities.BETA_CORE, fp);
182                                } else {
183                                        picker.add(Commodities.BETA_CORE, fp/2f);
184                                }
185                                picker.add(Commodities.GAMMA_CORE, fp);
186                        } else {
187                                if (derelictMode) {
188                                        picker.add(Commodities.GAMMA_CORE, fp);
189                                } else {
190                                        if (member.isCapital() || member.isCruiser()) {
191                                                picker.add(Commodities.ALPHA_CORE, fp);
192                                        } else if (allowAlphaAnywhere) {
193                                                picker.add(Commodities.ALPHA_CORE, fp);
194                                        }
195                                        
196                                        if (member.isCruiser() || member.isDestroyer()) {
197                                                picker.add(Commodities.BETA_CORE, fp/2f);
198                                        } else if (allowBetaAnywhere && member.isFrigate()) {
199                                                picker.add(Commodities.BETA_CORE, fp);
200                                        }
201                                        
202                                        if (member.isDestroyer() || member.isFrigate()) {
203                                                picker.add(Commodities.GAMMA_CORE, fp);
204                                        }
205                                }
206                        }
207                        
208                        if (addedCores >= minCores) {
209                                picker.add(nothing, 10f * picker.getTotal()/fp);
210                        }
211                        
212                        String pick = picker.pick();
213                        if (debug) {
214                                System.out.println("Picked [" + pick + "] for " + member.getHullId());
215                        }
216                        AICoreOfficerPlugin plugin = plugins.get(pick);
217                        if (plugin != null) {
218                                addedCores++;
219                                
220                                PersonAPI person = plugin.createPerson(pick, fleet.getFaction().getId(), random);
221                                member.setCaptain(person);
222                                if (integrate && !Commodities.OMEGA_CORE.equals(pick)) {
223                                        integrateAndAdaptCoreForAIFleet(member);
224                                }
225                                
226                                if (!member.isFighterWing() && !member.isCivilian()) {
227                                        withOfficers.add(member, fp);
228                                }
229                                
230                                allWithOfficers.add(member);
231                        }
232                        
233                        if (addedCores > 0 && params != null && params.officerNumberMult <= 0) {
234                                break; // only want to add the fleet commander
235                        }
236                }
237                
238                if (withOfficers.isEmpty() && !allWithOfficers.isEmpty()) {
239                        withOfficers.add(allWithOfficers.get(0), 1f);
240                }
241                
242                
243                FleetMemberAPI flagship = withOfficers.pick();
244                if (!derelictMode && !forceNoCommander && flagship != null) {
245                        PersonAPI commander = flagship.getCaptain();
246                        commander.setRankId(Ranks.SPACE_COMMANDER);
247                        commander.setPostId(Ranks.POST_FLEET_COMMANDER);
248                        fleet.setCommander(commander);
249                        fleet.getFleetData().setFlagship(flagship);
250                        addCommanderSkills(commander, fleet, params, numCommanderSkills, random);                       
251                }
252        }
253        
254        public static void integrateAndAdaptCoreForAIFleet(FleetMemberAPI member) {
255                PersonAPI person = member.getCaptain();
256                if (!person.isAICore()) return;
257                
258                person.getStats().setLevel(person.getStats().getLevel() + 1);
259                
260                person.getStats().setSkipRefresh(true);
261                
262//              if (member.isCarrier()) {
263//                      person.getStats().setSkillLevel(Skills.STRIKE_COMMANDER, 2);
264//                      if (person.getStats().getSkillLevel(Skills.POINT_DEFENSE) <= 0) {
265//                              person.getStats().setSkillLevel(Skills.POINT_DEFENSE, 2);
266//                              person.getStats().setSkillLevel(Skills.RELIABILITY_ENGINEERING, 0);
267//                      }
268//              } else {
269                if (member.getVariant() != null && member.getVariant().getWeaponGroups() != null) {
270                        float weight = 0f;
271                        float pdWeight = 0f;
272                        float missileWeight = 0f;
273                        for (String slotId : member.getVariant().getFittedWeaponSlots()) {
274                                WeaponSpecAPI spec = member.getVariant().getWeaponSpec(slotId);
275                                if (spec == null) continue;
276                                float w = 1f;
277                                if (spec.getSize() == WeaponSize.MEDIUM) w = 2f;
278                                if (spec.getSize() == WeaponSize.LARGE) w = 4f;
279                                weight += w;
280                                if (spec.getAIHints().contains(AIHints.PD)) {
281                                        pdWeight += w;
282                                }
283                                if (spec.getType() == WeaponType.MISSILE) {
284                                        missileWeight += w;
285                                }
286                                
287                        }
288                        
289                        float decks = member.getNumFlightDecks();
290                        if (decks > 0) {
291                                weight += decks * 4f;
292                                pdWeight += decks * 4f;
293                        }
294
295                        boolean hasUsefulPD = pdWeight > weight * 0.25f;
296                        boolean hasEnoughMissiles = missileWeight > weight * 0.2f;
297                        
298                        
299                        if (hasUsefulPD && !hasEnoughMissiles) {
300                                person.getStats().setSkillLevel(Skills.POINT_DEFENSE, 2);
301                                person.getStats().setSkipRefresh(false);
302                                return;
303                        }
304                }
305                
306                
307                        if (member.getHullSpec() != null && member.getHullSpec().hasTag(Factions.DERELICT) &&
308                                        person.getStats().getSkillLevel(Skills.BALLISTIC_MASTERY) <= 0) {
309                                person.getStats().setSkillLevel(Skills.BALLISTIC_MASTERY, 2);
310                        } else {
311                                if (person.getStats().getSkillLevel(Skills.ENERGY_WEAPON_MASTERY) <= 0) {
312                                        person.getStats().setSkillLevel(Skills.ENERGY_WEAPON_MASTERY, 2);
313                                } else {
314                                        person.getStats().setSkillLevel(Skills.MISSILE_SPECIALIZATION, 2);
315                                }
316                        }
317                        
318                        if (member.isCapital() || member.isStation()) {
319                                if (person.getStats().getSkillLevel(Skills.POLARIZED_ARMOR) <= 0) {
320                                        person.getStats().setSkillLevel(Skills.COMBAT_ENDURANCE, 0);
321                                        person.getStats().setSkillLevel(Skills.POLARIZED_ARMOR, 2);
322                                }
323                        }
324                //}
325                
326                person.getStats().setSkipRefresh(false);
327        }
328        
329        
330        public static SkillPickPreference getSkillPrefForShip(FleetMemberAPI member) {
331                return FleetFactoryV3.getSkillPrefForShip(member);
332//              float weight = FleetFactoryV3.getMemberWeight(member);
333//              float fighters = member.getVariant().getFittedWings().size();
334//              boolean wantCarrierSkills = weight > 0 && fighters / weight >= 0.5f;
335//              SkillPickPreference pref = SkillPickPreference.GENERIC;
336//              if (wantCarrierSkills) {
337//                      pref = SkillPickPreference.CARRIER;
338//              } else if (member.isPhaseShip()) {
339//                      pref = SkillPickPreference.PHASE;
340//              }
341//              
342//              return pref;
343        }
344        
345        
346        public static void addCommanderSkills(PersonAPI commander, CampaignFleetAPI fleet, FleetParamsV3 params, int numSkills, Random random) {
347                if (random == null) random = new Random();
348                if (numSkills <= 0) return;
349                
350                MutableCharacterStatsAPI stats = commander.getStats();
351                
352                FactionDoctrineAPI doctrine = fleet.getFaction().getDoctrine();
353                if (params != null && params.doctrineOverride != null) {
354                        doctrine = params.doctrineOverride;
355                }
356                
357                List<String> skills = new ArrayList<String>(doctrine.getCommanderSkills());
358                if (skills.isEmpty()) return;
359                
360                if (random.nextFloat() < doctrine.getCommanderSkillsShuffleProbability()) {
361                        Collections.shuffle(skills, random);
362                }
363
364                stats.setSkipRefresh(true);
365                
366                boolean debug = true;
367                debug = false;
368                if (debug) System.out.println("Generating commander skills, person level " + stats.getLevel() + ", skills: " + numSkills);
369                int picks = 0;
370                for (String skillId : skills) {
371                        if (debug) System.out.println("Selected skill: [" + skillId + "]");
372                        stats.setSkillLevel(skillId, 1);
373                        picks++;
374                        if (picks >= numSkills) {
375                                break;
376                        }
377                }
378                if (debug) System.out.println("Done generating commander skills\n");
379                
380                stats.setSkipRefresh(false);
381                stats.refreshCharacterStatsEffects();
382        }
383        
384        
385        
386        
387        
388}
389
390
391
392
393
394
395
396
397
398
399
400