Starsector API
Loading...
Searching...
No Matches
RemnantOfficerGeneratorPlugin.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.procgen.themes;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.HashMap;
6import java.util.List;
7import java.util.Map;
8import java.util.Random;
9
10import com.fs.starfarer.api.Global;
11import com.fs.starfarer.api.campaign.AICoreOfficerPlugin;
12import com.fs.starfarer.api.campaign.CampaignFleetAPI;
13import com.fs.starfarer.api.campaign.FactionAPI;
14import com.fs.starfarer.api.campaign.FactionDoctrineAPI;
15import com.fs.starfarer.api.campaign.GenericPluginManagerAPI;
16import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;
17import com.fs.starfarer.api.characters.PersonAPI;
18import com.fs.starfarer.api.combat.WeaponAPI.AIHints;
19import com.fs.starfarer.api.combat.WeaponAPI.WeaponSize;
20import com.fs.starfarer.api.combat.WeaponAPI.WeaponType;
21import com.fs.starfarer.api.fleet.FleetMemberAPI;
22import com.fs.starfarer.api.impl.campaign.events.OfficerManagerEvent.SkillPickPreference;
23import com.fs.starfarer.api.impl.campaign.fleets.BaseGenerateFleetOfficersPlugin;
24import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3;
25import com.fs.starfarer.api.impl.campaign.fleets.FleetParamsV3;
26import com.fs.starfarer.api.impl.campaign.ids.Commodities;
27import com.fs.starfarer.api.impl.campaign.ids.Factions;
28import com.fs.starfarer.api.impl.campaign.ids.Ranks;
29import com.fs.starfarer.api.impl.campaign.ids.Skills;
30import com.fs.starfarer.api.impl.campaign.missions.hub.HubMissionWithTriggers.OfficerQuality;
31import com.fs.starfarer.api.loading.WeaponSpecAPI;
32import com.fs.starfarer.api.util.Misc;
33import com.fs.starfarer.api.util.WeightedRandomPicker;
34
35public class RemnantOfficerGeneratorPlugin extends BaseGenerateFleetOfficersPlugin {
36
37 protected boolean forceIntegrateCores = false;
38 protected boolean derelictMode = false;
39 protected float coreMult = 1f;
40
43
45 this.derelictMode = derelictMode;
46 this.coreMult = coreMult;
47 }
48
49
50 public boolean isForceIntegrateCores() {
52 }
53
55 this.forceIntegrateCores = forceIntegrateCores;
56 }
57
58 @Override
59 public int getHandlingPriority(Object params) {
60 if (!(params instanceof GenerateFleetOfficersPickData)) return -1;
61
62 GenerateFleetOfficersPickData data = (GenerateFleetOfficersPickData) params;
63
64 if (data.params != null && !data.params.withOfficers) return -1;
65
66 if (data.params.aiCores != null) return GenericPluginManagerAPI.CORE_SUBSET;
67
68 if (data.fleet == null || !data.fleet.getFaction().getId().equals(Factions.REMNANTS)) return -1;
69
70 return GenericPluginManagerAPI.CORE_SUBSET;
71 }
72
73
74 @Override
75 public void addCommanderAndOfficers(CampaignFleetAPI fleet, FleetParamsV3 params, Random random) {
76 if (random == null) random = Misc.random;
77 FactionAPI faction = fleet.getFaction();
78 FactionDoctrineAPI doctrine = faction.getDoctrine();
79 if (!derelictMode && params != null && params.doctrineOverride != null) {
80 doctrine = params.doctrineOverride;
81 }
82 List<FleetMemberAPI> members = fleet.getFleetData().getMembersListCopy();
83 if (members.isEmpty()) return;
84
85 Map<String, AICoreOfficerPlugin> plugins = new HashMap<String, AICoreOfficerPlugin>();
86
87 plugins.put(Commodities.OMEGA_CORE, Misc.getAICoreOfficerPlugin(Commodities.OMEGA_CORE));
88 plugins.put(Commodities.ALPHA_CORE, Misc.getAICoreOfficerPlugin(Commodities.ALPHA_CORE));
89 plugins.put(Commodities.BETA_CORE, Misc.getAICoreOfficerPlugin(Commodities.BETA_CORE));
90 plugins.put(Commodities.GAMMA_CORE, Misc.getAICoreOfficerPlugin(Commodities.GAMMA_CORE));
91 String nothing = "nothing";
92
93 float fleetFP = 0f; //fleet.getFleetPoints(); <- doesn't work here, requires a call to fleet.forceSync()
94 for (FleetMemberAPI member : members) {
95 fleetFP += member.getFleetPointCost();
96 }
97 boolean allowAlphaAnywhere = fleetFP > 150f;
98 boolean allowBetaAnywhere = fleetFP > 75f;
99
100 //boolean integrate = fleetFP > 200f || params.forceIntegrateAICores;
101 boolean integrate = params != null && !params.doNotIntegrateAICores;
102 integrate |= forceIntegrateCores;
103
104 int numCommanderSkills = 0;
105 if (allowBetaAnywhere) numCommanderSkills++;
106 if (allowAlphaAnywhere) numCommanderSkills++;
107 if (params != null && params.noCommanderSkills != null && params.noCommanderSkills) numCommanderSkills = 0;
108
109
110 //float fpPerCore = 20f;
111 float fpPerCore = Global.getSettings().getFloat("baseFPPerAICore");
112
113 if (derelictMode) {
114 fpPerCore = 30 - 20f * coreMult;
115 }
116
117 int minCores = (int) (fleetFP / fpPerCore * (params != null ? params.officerNumberMult : 1f));
118 if (params != null) {
119 minCores += params.officerNumberBonus;
120 }
121 if (minCores < 1) minCores = 1;
122
123 boolean debug = true;
124 debug = false;
125
126 WeightedRandomPicker<FleetMemberAPI> withOfficers = new WeightedRandomPicker<FleetMemberAPI>(random);
127
128 int maxSize = 0;
129 for (FleetMemberAPI member : members) {
130 if (member.isFighterWing()) continue;
131 if (member.isCivilian()) continue;
132 int size = member.getHullSpec().getHullSize().ordinal();
133 if (size > maxSize) {
134 maxSize = size;
135 }
136 }
137
138 List<FleetMemberAPI> allWithOfficers = new ArrayList<FleetMemberAPI>();
139 int addedCores = 0;
140 for (FleetMemberAPI member : members) {
141
142 if (member.isCivilian()) continue;
143 if (member.isFighterWing()) continue;
144
145 float fp = member.getFleetPointCost();
146
147 WeightedRandomPicker<String> picker = new WeightedRandomPicker<String>(random);
148
149 if (params != null && params.aiCores == OfficerQuality.AI_GAMMA) {
150 picker.add(Commodities.GAMMA_CORE, fp);
151 } else if (params != null && params.aiCores == OfficerQuality.AI_BETA) {
152 picker.add(Commodities.BETA_CORE, fp);
153 } else if (params != null && params.aiCores == OfficerQuality.AI_ALPHA) {
154 picker.add(Commodities.ALPHA_CORE, fp);
155 } else if (params != null && params.aiCores == OfficerQuality.AI_OMEGA) {
156 picker.add(Commodities.OMEGA_CORE, fp);
157 } else if (params != null && params.aiCores == OfficerQuality.AI_BETA_OR_GAMMA) {
158 if (member.isCapital() || member.isCruiser()) {
159 picker.add(Commodities.BETA_CORE, fp);
160 } else if (allowAlphaAnywhere) {
161 picker.add(Commodities.BETA_CORE, fp);
162 } else {
163 picker.add(Commodities.BETA_CORE, fp/2f);
164 }
165 picker.add(Commodities.GAMMA_CORE, fp);
166 } else {
167 if (derelictMode) {
168 picker.add(Commodities.GAMMA_CORE, fp);
169 } else {
170 if (member.isCapital() || member.isCruiser()) {
171 picker.add(Commodities.ALPHA_CORE, fp);
172 } else if (allowAlphaAnywhere) {
173 picker.add(Commodities.ALPHA_CORE, fp);
174 }
175
176 if (member.isCruiser() || member.isDestroyer()) {
177 picker.add(Commodities.BETA_CORE, fp/2f);
178 } else if (allowBetaAnywhere && member.isFrigate()) {
179 picker.add(Commodities.BETA_CORE, fp);
180 }
181
182 if (member.isDestroyer() || member.isFrigate()) {
183 picker.add(Commodities.GAMMA_CORE, fp);
184 }
185 }
186 }
187
188 if (addedCores >= minCores) {
189 picker.add(nothing, 10f * picker.getTotal()/fp);
190 }
191
192 String pick = picker.pick();
193 if (debug) {
194 System.out.println("Picked [" + pick + "] for " + member.getHullId());
195 }
196 AICoreOfficerPlugin plugin = plugins.get(pick);
197 if (plugin != null) {
198 addedCores++;
199
200 PersonAPI person = plugin.createPerson(pick, fleet.getFaction().getId(), random);
201 member.setCaptain(person);
202 if (integrate) {
204 }
205
206 if (!member.isFighterWing() && !member.isCivilian()) {
207 withOfficers.add(member, fp);
208 }
209
210 allWithOfficers.add(member);
211 }
212
213 if (addedCores > 0 && params != null && params.officerNumberMult <= 0) {
214 break; // only want to add the fleet commander
215 }
216 }
217
218 if (withOfficers.isEmpty() && !allWithOfficers.isEmpty()) {
219 withOfficers.add(allWithOfficers.get(0), 1f);
220 }
221
222
223 FleetMemberAPI flagship = withOfficers.pick();
224 if (!derelictMode && flagship != null) {
225 PersonAPI commander = flagship.getCaptain();
226 commander.setRankId(Ranks.SPACE_COMMANDER);
227 commander.setPostId(Ranks.POST_FLEET_COMMANDER);
228 fleet.setCommander(commander);
229 fleet.getFleetData().setFlagship(flagship);
230 addCommanderSkills(commander, fleet, params, numCommanderSkills, random);
231 }
232 }
233
234 public static void integrateAndAdaptCoreForAIFleet(FleetMemberAPI member) {
235 PersonAPI person = member.getCaptain();
236 if (!person.isAICore()) return;
237
238 person.getStats().setLevel(person.getStats().getLevel() + 1);
239
240 person.getStats().setSkipRefresh(true);
241
242// if (member.isCarrier()) {
243// person.getStats().setSkillLevel(Skills.STRIKE_COMMANDER, 2);
244// if (person.getStats().getSkillLevel(Skills.POINT_DEFENSE) <= 0) {
245// person.getStats().setSkillLevel(Skills.POINT_DEFENSE, 2);
246// person.getStats().setSkillLevel(Skills.RELIABILITY_ENGINEERING, 0);
247// }
248// } else {
249 if (member.getVariant() != null && member.getVariant().getWeaponGroups() != null) {
250 float weight = 0f;
251 float pdWeight = 0f;
252 float missileWeight = 0f;
253 for (String slotId : member.getVariant().getFittedWeaponSlots()) {
254 WeaponSpecAPI spec = member.getVariant().getWeaponSpec(slotId);
255 if (spec == null) continue;
256 float w = 1f;
257 if (spec.getSize() == WeaponSize.MEDIUM) w = 2f;
258 if (spec.getSize() == WeaponSize.LARGE) w = 4f;
259 weight += w;
260 if (spec.getAIHints().contains(AIHints.PD)) {
261 pdWeight += w;
262 }
263 if (spec.getType() == WeaponType.MISSILE) {
264 missileWeight += w;
265 }
266
267 }
268
269 float decks = member.getNumFlightDecks();
270 if (decks > 0) {
271 weight += decks * 4f;
272 pdWeight += decks * 4f;
273 }
274
275 boolean hasUsefulPD = pdWeight > weight * 0.25f;
276 boolean hasEnoughMissiles = missileWeight > weight * 0.2f;
277
278
279 if (hasUsefulPD && !hasEnoughMissiles) {
280 person.getStats().setSkillLevel(Skills.POINT_DEFENSE, 2);
281 person.getStats().setSkipRefresh(false);
282 return;
283 }
284 }
285
286
287 if (member.getHullSpec() != null && member.getHullSpec().hasTag(Factions.DERELICT) &&
288 person.getStats().getSkillLevel(Skills.BALLISTIC_MASTERY) <= 0) {
289 person.getStats().setSkillLevel(Skills.BALLISTIC_MASTERY, 2);
290 } else {
291 if (person.getStats().getSkillLevel(Skills.ENERGY_WEAPON_MASTERY) <= 0) {
292 person.getStats().setSkillLevel(Skills.ENERGY_WEAPON_MASTERY, 2);
293 } else {
294 person.getStats().setSkillLevel(Skills.MISSILE_SPECIALIZATION, 2);
295 }
296 }
297
298 if (member.isCapital() || member.isStation()) {
299 if (person.getStats().getSkillLevel(Skills.POLARIZED_ARMOR) <= 0) {
300 person.getStats().setSkillLevel(Skills.COMBAT_ENDURANCE, 0);
301 person.getStats().setSkillLevel(Skills.POLARIZED_ARMOR, 2);
302 }
303 }
304 //}
305
306 person.getStats().setSkipRefresh(false);
307 }
308
309
310 public static SkillPickPreference getSkillPrefForShip(FleetMemberAPI member) {
311 return FleetFactoryV3.getSkillPrefForShip(member);
312// float weight = FleetFactoryV3.getMemberWeight(member);
313// float fighters = member.getVariant().getFittedWings().size();
314// boolean wantCarrierSkills = weight > 0 && fighters / weight >= 0.5f;
315// SkillPickPreference pref = SkillPickPreference.GENERIC;
316// if (wantCarrierSkills) {
317// pref = SkillPickPreference.CARRIER;
318// } else if (member.isPhaseShip()) {
319// pref = SkillPickPreference.PHASE;
320// }
321//
322// return pref;
323 }
324
325
326 public static void addCommanderSkills(PersonAPI commander, CampaignFleetAPI fleet, FleetParamsV3 params, int numSkills, Random random) {
327 if (random == null) random = new Random();
328 if (numSkills <= 0) return;
329
330 MutableCharacterStatsAPI stats = commander.getStats();
331
332 FactionDoctrineAPI doctrine = fleet.getFaction().getDoctrine();
333 if (params != null && params.doctrineOverride != null) {
334 doctrine = params.doctrineOverride;
335 }
336
337 List<String> skills = new ArrayList<String>(doctrine.getCommanderSkills());
338 if (skills.isEmpty()) return;
339
340 if (random.nextFloat() < doctrine.getCommanderSkillsShuffleProbability()) {
341 Collections.shuffle(skills, random);
342 }
343
344 stats.setSkipRefresh(true);
345
346 boolean debug = true;
347 debug = false;
348 if (debug) System.out.println("Generating commander skills, person level " + stats.getLevel() + ", skills: " + numSkills);
349 int picks = 0;
350 for (String skillId : skills) {
351 if (debug) System.out.println("Selected skill: [" + skillId + "]");
352 stats.setSkillLevel(skillId, 1);
353 picks++;
354 if (picks >= numSkills) {
355 break;
356 }
357 }
358 if (debug) System.out.println("Done generating commander skills\n");
359
360 stats.setSkipRefresh(false);
361 stats.refreshCharacterStatsEffects();
362 }
363
364
365
366
367
368}
369
370
371
372
373
374
375
376
377
378
379
380
static SettingsAPI getSettings()
Definition Global.java:51
void addCommanderAndOfficers(CampaignFleetAPI fleet, FleetParamsV3 params, Random random)
static void addCommanderSkills(PersonAPI commander, CampaignFleetAPI fleet, FleetParamsV3 params, int numSkills, Random random)