Starsector API
Loading...
Searching...
No Matches
SkillsChangeOfficerEffect.java
Go to the documentation of this file.
1package com.fs.starfarer.api.characters;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.LinkedHashMap;
6import java.util.LinkedHashSet;
7import java.util.List;
8import java.util.Map;
9import java.util.Set;
10
11import com.fs.starfarer.api.Global;
12import com.fs.starfarer.api.characters.MutableCharacterStatsAPI.SkillLevelAPI;
13import com.fs.starfarer.api.fleet.FleetMemberAPI;
14import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
15import com.fs.starfarer.api.impl.campaign.ids.Stats;
16import com.fs.starfarer.api.impl.campaign.plog.OfficerSkillGainRecord;
17import com.fs.starfarer.api.impl.campaign.plog.PlaythroughLog;
18import com.fs.starfarer.api.ui.Alignment;
19import com.fs.starfarer.api.ui.ButtonAPI;
20import com.fs.starfarer.api.ui.TooltipMakerAPI;
21import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipCreator;
22import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipLocation;
23import com.fs.starfarer.api.util.Misc;
24
26
27 public static class OfficerEffectData {
28 OfficerDataAPI data;
29 int newLevel;
30 List<String> removeSkills = new ArrayList<String>();
31 List<String> removeElite = new ArrayList<String>();
32 FleetMemberAPI member;
33 boolean unusable = false;
34 boolean makeMercenary;
35
36 ButtonAPI buttonMerc;
37 ButtonAPI buttonOther;
38
39 public boolean hasChanges() {
40 return data.getPerson().getStats().getLevel() != newLevel || unusable ||
41 !removeSkills.isEmpty() || !removeElite.isEmpty() || makeMercenary;
42 }
43 }
44
45 public static class OfficerDataMap {
46 Map<PersonAPI, OfficerEffectData> map = new LinkedHashMap<PersonAPI, SkillsChangeOfficerEffect.OfficerEffectData>();
47 }
48 public void setMap(OfficerDataMap map, Map<String, Object> dataMap) {
49 String key = getClass().getSimpleName();
50 dataMap.put(key, map);
51 }
52 public OfficerDataMap getMap(Map<String, Object> dataMap) {
53 String key = getClass().getSimpleName();
54 OfficerDataMap map = (OfficerDataMap)dataMap.get(key);
55 if (map == null) {
56 map = new OfficerDataMap();
57 dataMap.put(key, map);
58 }
59 return map;
60 }
61
62
64 int bonus = (int) stats.getDynamic().getMod(Stats.OFFICER_MAX_LEVEL_MOD).computeEffective(0);
65 return (int) Global.getSettings().getFloat("officerMaxLevel") + bonus;
66 }
67
70 return (int) Global.getSettings().getFloat("officerMaxEliteSkills") + bonus;
71 }
72
73 public int getNumEliteSkills(PersonAPI person) {
74 int num = 0;
75 for (SkillLevelAPI sl : person.getStats().getSkillsCopy()) {
76 if (sl.getLevel() >= 2f) {
77 num++;
78 }
79 }
80 return num;
81 }
82
84 OfficerDataMap result = new OfficerDataMap();
85
86
87 int maxOfficersPre = (int) from.getOfficerNumber().getModifiedValue();
88 int maxOfficersPost = (int) to.getOfficerNumber().getModifiedValue();
89
90 Map<OfficerDataAPI, FleetMemberAPI> members = new LinkedHashMap<OfficerDataAPI, FleetMemberAPI>();
91 Set<OfficerDataAPI> unusable = new LinkedHashSet<OfficerDataAPI>();
92 if (maxOfficersPre > maxOfficersPost) {
93 int count = 0;
95 boolean merc = Misc.isMercenary(officer.getPerson());
96 if (!merc) {
97 count++;
98 }
99 if (count > maxOfficersPost && !merc) {
100 unusable.add(officer);
101 }
102 }
103 }
104
107 if (member.getCaptain() == officer.getPerson()) {
108 members.put(officer, member);
109 break;
110 }
111 }
112 }
113
114 int maxLevelPre = getMaxLevel(from);
115 int maxLevelPost = getMaxLevel(to);
116
117 int maxElitePre = getMaxEliteSkills(from);
118 int maxElitePost = getMaxEliteSkills(to);
119
121 PersonAPI person = data.getPerson();
122 MutableCharacterStatsAPI stats = person.getStats();
123
124 if (Misc.isMercenary(person)) {
125 continue;
126 }
127
129 if (pods) continue;
130
131 OfficerEffectData effect = new OfficerEffectData();
132 effect.data = data;
133 effect.newLevel = stats.getLevel();
134
135 if (members.containsKey(data)) {
136 effect.member = members.get(data);
137 }
138 if (unusable.contains(data)) {
139 effect.unusable = true;
140 }
141
142 if (stats.getLevel() > maxLevelPost && maxLevelPost < maxLevelPre) {
143 final List<String> skillsInOrderOfGain = new ArrayList<String>();
145 if (rec.getPersonId().equals(person.getId()) && !rec.isElite() &&
146 stats.getSkillLevel(rec.getSkillId()) > 0) {
147 skillsInOrderOfGain.add(rec.getSkillId());
148 }
149 }
150 Collections.reverse(skillsInOrderOfGain);
151
152 // if the skill records aren't present, assume it's a cryopods officer
153 // it can also be an officer from 0.95a before officer skill gain was tracked
154 // so, erring on the side of not nuking skills needlessly
155 int skillsToRemove = stats.getLevel() - maxLevelPost;
156 for (int i = 0; i < skillsToRemove && !skillsInOrderOfGain.isEmpty(); i++) {
157 effect.removeSkills.add(skillsInOrderOfGain.remove(0));
158 }
159 if (!effect.removeSkills.isEmpty()) {
160 effect.newLevel = maxLevelPost;
161 }
162 }
163
164 int numElite = getNumEliteSkills(person);
165 if (numElite > maxElitePost && maxElitePost < maxElitePre) {
166 final List<String> eliteInOrderOfGain = new ArrayList<String>();
168 if (rec.getPersonId().equals(person.getId()) && rec.isElite() &&
169 stats.getSkillLevel(rec.getSkillId()) >= 2 &&
170 !effect.removeSkills.contains(rec.getSkillId())) {
171 eliteInOrderOfGain.add(rec.getSkillId());
172 }
173 }
174 Collections.reverse(eliteInOrderOfGain);
175
176 int eliteToRemove = numElite - maxElitePost;
177 for (int i = 0; i < eliteToRemove && !eliteInOrderOfGain.isEmpty(); i++) {
178 effect.removeElite.add(eliteInOrderOfGain.remove(0));
179 }
180 }
181
182
183 if (effect.hasChanges()) {
184 result.map.put(person, effect);
185 }
186 }
187 return result;
188 }
189
190
191 @Override
193 return !getEffects(from, to).map.isEmpty();
194 }
195
196 @Override
197 public void printEffects(MutableCharacterStatsAPI from, MutableCharacterStatsAPI to, TooltipMakerAPI info, Map<String, Object> dataMap) {
198 super.prepare();
199
200 OfficerDataMap map = getEffects(from, to);
201 setMap(map, dataMap);
202
203 float bw = 470;
204 float bh = 25;
205 float pad = 3f;
206 float opad = 10f;
207
208
209 info.addSectionHeading("Officers", base, dark, Alignment.MID, 15f);
210
211 info.addPara("Officers whose skills that exceed the new limits will have their excess skills removed, or become mercenaries on a temporary contract. "
212 + "Officers that already had those kinds of skills when acquired will not be affected.", opad,
214 "excess skills removed");
215 float initPad = opad;
216 for (final OfficerEffectData data : map.map.values()) {
217 final PersonAPI person = data.data.getPerson();
218 String str = person.getRank() + " " + person.getNameString();
219 if (data.member == null) {
220 str += ", unassigned, will...";
221 } else {
222 str += ", commanding the " + data.member.getShipName() +
223 " (" + data.member.getHullSpec().getHullNameWithDashClass() + "), will...";
224 }
225
226 String mercText = "Become a mercenary, drawing higher pay on a new contract";
227 String otherText = "";
228
229 if (data.unusable && data.removeElite.isEmpty() && data.removeSkills.isEmpty()) {
230 if (data.member == null) {
231 otherText = "Become unable to be assigned command of a ship";
232 } else {
233 otherText = "Be relieved of command, and unable to be assigned command of a ship";
234 }
235 } else {
236 String part1 = "";
237 if (data.unusable) {
238 part1 = "Be relieved of command, and";
239 }
240 String part2 = "";
241 if (!data.removeElite.isEmpty() && data.removeSkills.isEmpty()) {
242 part2 = " lose excess elite skill effects";
243 } else if (data.removeElite.isEmpty() && !data.removeSkills.isEmpty()) {
244 part2 = " lose excess skills";
245 } else {
246 part2 = " lose excess skills and elite skill effects";
247 }
248 if (part1.isEmpty()) {
249 part2 = part2.trim();
250 otherText = Misc.ucFirst(part2);
251 } else {
252 otherText = part1 + part2;
253 }
254 }
255
256 info.addPara(str, opad + 5f);
257
258 float indent = 40f;
259 data.buttonOther = info.addAreaCheckbox(otherText, new Object(), base, dark, bright, bw, bh, opad, true);
260 data.buttonOther.getPosition().setXAlignOffset(indent);
261
263 public boolean isTooltipExpandable(Object tooltipParam) {
264 return false;
265 }
266 public float getTooltipWidth(Object tooltipParam) {
267 return 450;
268 }
269 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) {
270 float opad = 10f;
271 float pad = 3f;
272 tooltip.addPara("This officer has the following skills:", 0f);
273 tooltip.addSkillPanel(person, pad);
274
275 if (!data.removeSkills.isEmpty()) {
276 tooltip.addPara("They will lose the following skills:", opad);
278 for (String skillId : data.removeSkills) {
279 fake.getStats().setSkillLevel(skillId, person.getStats().getSkillLevel(skillId));
280 }
281 tooltip.addSkillPanel(fake, pad);
282 }
283 if (!data.removeElite.isEmpty()) {
284 tooltip.addPara("They will lose the elite effects of the following skills:", opad);
286 for (String skillId : data.removeElite) {
287 fake.getStats().setSkillLevel(skillId, person.getStats().getSkillLevel(skillId));
288 }
289 tooltip.addSkillPanel(fake, pad);
290 }
291 }
292 }, TooltipLocation.RIGHT);
293
294 //data.buttonMerc = info.addAreaCheckbox(mercText, new Object(), base, dark, bright, bw, bh, pad, true);
295 data.buttonMerc = info.addAreaCheckbox(mercText, new Object(), sBase, sDark, sBright, bw, bh, pad, true);
296 data.buttonOther.setChecked(true);
297 data.buttonMerc.setChecked(false);
298
300 public boolean isTooltipExpandable(Object tooltipParam) {
301 return false;
302 }
303 public float getTooltipWidth(Object tooltipParam) {
304 return 450;
305 }
306 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) {
307 int payPre = (int) Misc.getOfficerSalary(person, false);
308 int payPost = (int) Misc.getOfficerSalary(person, true);
309 int contractDur = (int) Global.getSettings().getFloat("officerMercContractDur");
310 tooltip.addPara("This officer's pay will be increased from %s to %s per month,"
311 + " and they will want to leave after %s days, when their contract expires.", 0f,
313 Misc.getDGSCredits(payPre),
314 Misc.getDGSCredits(payPost),
315 "" + contractDur
316 );
317 tooltip.addPara("Extending the contract beyond that will require a %s.", 10f,
318 Misc.getStoryOptionColor(), Misc.STORY + " point");
319 }
320 }, TooltipLocation.RIGHT);
321
322 info.addSpacer(0).getPosition().setXAlignOffset(-indent);
323 }
324 }
325
326 @Override
327 public void infoButtonPressed(ButtonAPI button, Object param, Map<String, Object> dataMap) {
328 OfficerDataMap map = getMap(dataMap);
329
330 for (OfficerEffectData data : map.map.values()) {
331 if (data.buttonMerc == button) {
332 data.makeMercenary = true;
333 data.buttonMerc.setChecked(true);
334 data.buttonOther.setChecked(false);
335 //System.out.println("Merc button, " + data.data.getPerson().getNameString());
336 } else if (data.buttonOther == button) {
337 data.makeMercenary = false;
338 data.buttonMerc.setChecked(false);
339 data.buttonOther.setChecked(true);
340 //System.out.println("Other button, " + data.data.getPerson().getNameString());
341 }
342 }
343 }
344
345 @Override
346 public void applyEffects(MutableCharacterStatsAPI from, MutableCharacterStatsAPI to, Map<String, Object> dataMap) {
347 OfficerDataMap map = getMap(dataMap);
348
349 for (OfficerEffectData data : map.map.values()) {
350 if (data.makeMercenary) {
351 Misc.setMercenary(data.data.getPerson(), true);
352 Misc.setMercHiredNow(data.data.getPerson());
353 } else {
354 if (data.unusable && data.member != null) {
355 data.member.setCaptain(Global.getFactory().createPerson());
356 }
357 MutableCharacterStatsAPI stats = data.data.getPerson().getStats();
358 for (String id : data.removeElite) {
359 stats.setSkillLevel(id, 1);
360 PlaythroughLog.getInstance().removeOfficerSkillRecord(data.data.getPerson().getId(), id, true);
361 }
362 for (String id : data.removeSkills) {
363 stats.setSkillLevel(id, 0);
364 PlaythroughLog.getInstance().removeOfficerSkillRecord(data.data.getPerson().getId(), id, false);
365 PlaythroughLog.getInstance().removeOfficerSkillRecord(data.data.getPerson().getId(), id, true);
366 }
367 if (data.newLevel != stats.getLevel()) {
368 stats.setLevel(data.newLevel);
369 }
370 }
371
372 }
373 }
374
375
376}
377
378
379
static SettingsAPI getSettings()
Definition Global.java:57
static FactoryAPI getFactory()
Definition Global.java:41
static SectorAPI getSector()
Definition Global.java:65
void infoButtonPressed(ButtonAPI button, Object param, Map< String, Object > dataMap)
void setMap(OfficerDataMap map, Map< String, Object > dataMap)
OfficerDataMap getEffects(MutableCharacterStatsAPI from, MutableCharacterStatsAPI to)
boolean hasEffects(MutableCharacterStatsAPI from, MutableCharacterStatsAPI to)
void printEffects(MutableCharacterStatsAPI from, MutableCharacterStatsAPI to, TooltipMakerAPI info, Map< String, Object > dataMap)
void applyEffects(MutableCharacterStatsAPI from, MutableCharacterStatsAPI to, Map< String, Object > dataMap)
float computeEffective(float baseValue)
static final String OFFICER_MAX_ELITE_SKILLS_MOD
Definition Stats.java:10
static final String OFFICER_MAX_LEVEL_MOD
Definition Stats.java:9
void removeOfficerSkillRecord(String personId, String skillId, boolean elite)
static Color getStoryOptionColor()
Definition Misc.java:782
static String getDGSCredits(float num)
Definition Misc.java:1390
static String ucFirst(String str)
Definition Misc.java:559
static boolean isMercenary(PersonAPI person)
Definition Misc.java:5454
static Color getNegativeHighlightColor()
Definition Misc.java:802
static Color getHighlightColor()
Definition Misc.java:792
static void setMercHiredNow(PersonAPI person)
Definition Misc.java:5459
static void setMercenary(PersonAPI person, boolean mercenary)
Definition Misc.java:5468
static float getOfficerSalary(PersonAPI officer)
Definition Misc.java:4416
List< OfficerDataAPI > getOfficersCopy()
List< FleetMemberAPI > getMembersListCopy()
MutableCharacterStatsAPI getStats()
void setChecked(boolean checked)
PositionAPI setXAlignOffset(float xAlignOffset)
void addTooltipToPrevious(TooltipCreator tc, TooltipLocation loc)
ButtonAPI addAreaCheckbox(String text, Object data, Color base, Color bg, Color bright, float width, float height, float pad)
LabelAPI addPara(String format, float pad, Color hl, String... highlights)
UIComponentAPI addSpacer(float height)
UIComponentAPI addSkillPanel(PersonAPI person, float pad)
LabelAPI addSectionHeading(String str, Alignment align, float pad)