public enum WeaponGroupConfig {
STANDARD, MISSILE, BROADSIDE, MISSILE_BROADSIDE, ALPHA_STRIKE
}
public static final Map weaponTypeOverride;
static {
Map weaponTypeOverrideTemp = new HashMap();
weaponTypeOverrideTemp.put("Missile",
createWeaponList(new String[]{
"annihilator", "annihilatorpod", "swarmer", "pilum",
"exigency_mmm", "exigency_mmm_f"
}));
weaponTypeOverrideTemp.put("No Aim",
createWeaponList(new String[]{
"swarmer", "pilum", "exigency_mmm", "exigency_mmm_f",
"exigency_drumlauncher"
}));
weaponTypeOverrideTemp.put("Anti-Fighter",
createWeaponList(new String[]{
"swarmer", "phasecl"
}));
weaponTypeOverrideTemp.put("Point Defense",
createWeaponList(new String[]{
"phasecl"
}));
weaponTypeOverrideTemp.put("Strike",
createWeaponList(new String[]{
"x"
}));
weaponTypeOverrideTemp.put("Assault",
createWeaponList(new String[]{
"annihilator", "annihilatorpod", "phasecl", "exigency_mmm",
"exigency_mmm_f"
}));
weaponTypeOverrideTemp.put("Close Support",
createWeaponList(new String[]{
"exigency_drumlauncher"
}));
weaponTypeOverrideTemp.put("Fire Support",
createWeaponList(new String[]{
"ssp_redeemer", "pilum"
}));
weaponTypeOverrideTemp.put("Special",
createWeaponList(new String[]{
"x"
}));
weaponTypeOverrideTemp.put("Low Flux", // Cannot be autodetected
createWeaponList(new String[]{
"lightmg", "lightdualmg", "lightmortar", "vulcan",
"fragbomb", "clusterbomb", "bomb", "flak",
"heavymg", "dualflak", "mininglaser", "pdlaser",
"taclaser", "lrpdlaser", "pdburst", "gravitonbeam",
"heavyburst", "guardian", "ssp_tpc", "annihilator",
"annihilatorpod", "swarmer", "phasecl", "pilum",
"exigency_mmm", "exigency_mmm_f"
}));
weaponTypeOverrideTemp.put("High Flux", // Cannot be autodetected
createWeaponList(new String[]{
"mjolnir", "miningblaster", "heavyblaster", "plasma",
"exigency_rr", "exigency_cigenrepeater", "exipirated_rr_p"
}));
weaponTypeOverrideTemp.put("Sustained Beam", // Cannot be autodetected
createWeaponList(new String[]{
"hil", "gravitonbeam", "lrpdlaser", "pdlaser",
"mininglaser", "phasebeam", "taclaser", "exigency_repulsor_beam",
"exigency_lightning_gun"
}));
weaponTypeOverrideTemp.put("Limited Ammo", // Cannot be autodetected
createWeaponList(new String[]{
"amblaster"
}));
weaponTypeOverrideTemp.put("Override", // Disables autodetection
createWeaponList(new String[]{
"ssp_redeemer", "annihilator", "annihilatorpod", "swarmer",
"phasecl", "pilum", "exigency_mmm", "exigency_mmm_f",
"exigency_drumlauncher"
}));
weaponTypeOverride = Collections.unmodifiableMap(weaponTypeOverrideTemp);
}
private static List<String> createWeaponList(String[] variants) {
return Collections.unmodifiableList(Arrays.asList(variants));
}
private static List<String> getWeaponList(String key) {
return (List<String>) weaponTypeOverride.get(key);
}
private static void integrateGroup(WeaponGroupSpec source, WeaponGroupSpec destination) {
for (String slot : source.getSlots()) {
destination.addSlot(slot);
}
destination.setType(source.getType());
destination.setAutofireOnByDefault(source.isAutofireOnByDefault());
}
public static void generateWeaponGroups(ShipVariantAPI variant, WeaponGroupConfig config) {
// Clean up the existing groups
// We go through this whole song and dance rather than just flushing and making new groups for reasons of maximum compatability
for (WeaponGroupSpec group : variant.getWeaponGroups()) {
WeaponGroupSpec clone = group.clone();
for (String slot : clone.getSlots()) {
group.removeSlot(slot);
}
}
// These are programmable variables passed from the WeaponGroupConfig
// This will change how the algorithm will behave
boolean splitMissiles = false;
boolean enforceSides = false;
boolean linkedStrike = false;
if (config == WeaponGroupConfig.MISSILE) {
splitMissiles = true;
} else if (config == WeaponGroupConfig.BROADSIDE) {
enforceSides = true;
} else if (config == WeaponGroupConfig.MISSILE_BROADSIDE) {
splitMissiles = true;
enforceSides = true;
} else if (config == WeaponGroupConfig.ALPHA_STRIKE) {
linkedStrike = true;
}
// Now we define all of the possible weapon groups that the variant can use
List<WeaponGroupSpec> groupList = new ArrayList();
WeaponGroupSpec PDGroup = new WeaponGroupSpec();
PDGroup.setType(WeaponGroupType.LINKED);
PDGroup.setAutofireOnByDefault(true);
groupList.add(PDGroup);
WeaponGroupSpec AFGroup = new WeaponGroupSpec();
AFGroup.setType(WeaponGroupType.LINKED);
AFGroup.setAutofireOnByDefault(true);
groupList.add(AFGroup);
WeaponGroupSpec AssGroup = new WeaponGroupSpec();
AssGroup.setType(WeaponGroupType.LINKED);
AssGroup.setAutofireOnByDefault(false);
groupList.add(AssGroup);
WeaponGroupSpec AssTGroup = new WeaponGroupSpec();
AssTGroup.setType(WeaponGroupType.LINKED);
AssTGroup.setAutofireOnByDefault(true);
groupList.add(AssTGroup);
WeaponGroupSpec CSGroup = new WeaponGroupSpec();
CSGroup.setType(WeaponGroupType.LINKED);
CSGroup.setAutofireOnByDefault(false);
groupList.add(CSGroup);
WeaponGroupSpec CSTGroup = new WeaponGroupSpec();
CSTGroup.setType(WeaponGroupType.LINKED);
CSTGroup.setAutofireOnByDefault(true);
groupList.add(CSTGroup);
WeaponGroupSpec SupGroup = new WeaponGroupSpec();
SupGroup.setType(WeaponGroupType.LINKED);
SupGroup.setAutofireOnByDefault(true);
groupList.add(SupGroup);
WeaponGroupSpec HvyGroup = new WeaponGroupSpec();
if (linkedStrike) {
HvyGroup.setType(WeaponGroupType.LINKED);
} else {
HvyGroup.setType(WeaponGroupType.ALTERNATING);
}
HvyGroup.setAutofireOnByDefault(false);
groupList.add(HvyGroup);
WeaponGroupSpec HvyTGroup = new WeaponGroupSpec();
if (linkedStrike) {
HvyTGroup.setType(WeaponGroupType.LINKED);
} else {
HvyTGroup.setType(WeaponGroupType.ALTERNATING);
}
HvyTGroup.setAutofireOnByDefault(false);
groupList.add(HvyTGroup);
WeaponGroupSpec BeaGroup = new WeaponGroupSpec();
BeaGroup.setType(WeaponGroupType.LINKED);
BeaGroup.setAutofireOnByDefault(false);
groupList.add(BeaGroup);
WeaponGroupSpec BeaTGroup = new WeaponGroupSpec();
BeaTGroup.setType(WeaponGroupType.LINKED);
BeaTGroup.setAutofireOnByDefault(false);
groupList.add(BeaTGroup);
WeaponGroupSpec StrGroup = new WeaponGroupSpec();
if (linkedStrike) {
StrGroup.setType(WeaponGroupType.LINKED);
} else {
StrGroup.setType(WeaponGroupType.ALTERNATING);
}
StrGroup.setAutofireOnByDefault(false);
groupList.add(StrGroup);
WeaponGroupSpec MisGroup = new WeaponGroupSpec();
if (linkedStrike) {
MisGroup.setType(WeaponGroupType.LINKED);
} else {
MisGroup.setType(WeaponGroupType.ALTERNATING);
}
MisGroup.setAutofireOnByDefault(false);
groupList.add(MisGroup);
WeaponGroupSpec SMisGroup = new WeaponGroupSpec();
SMisGroup.setType(WeaponGroupType.LINKED);
SMisGroup.setAutofireOnByDefault(true);
groupList.add(SMisGroup);
WeaponGroupSpec AMisGroup = new WeaponGroupSpec();
if (linkedStrike) {
AMisGroup.setType(WeaponGroupType.LINKED);
} else {
AMisGroup.setType(WeaponGroupType.ALTERNATING);
}
AMisGroup.setAutofireOnByDefault(false);
groupList.add(AMisGroup);
WeaponGroupSpec LGroup = new WeaponGroupSpec();
LGroup.setType(WeaponGroupType.LINKED);
LGroup.setAutofireOnByDefault(false);
groupList.add(LGroup);
WeaponGroupSpec RGroup = new WeaponGroupSpec();
RGroup.setType(WeaponGroupType.LINKED);
RGroup.setAutofireOnByDefault(false);
groupList.add(RGroup);
// This loops through all of the weapons and individually assigns them to initial groups
// Most weapon groupings are auto-detected based on AI hints, weapon data, and description strings
// The remaning groupings are enforced via overrides and configuration parameters
List<WeaponSlotAPI> slots = variant.getHullSpec().getAllWeaponSlotsCopy();
Collection<String> fittedSlots = variant.getFittedWeaponSlots();
for (String fittedSlot : fittedSlots) {
String weapon = variant.getWeaponId(fittedSlot);
String type = Global.getSettings().getDescription(weapon, Description.Type.WEAPON).getText2();
WeaponSlotAPI slot = null;
for (WeaponSlotAPI slott : slots) {
if (slott.getId().equals(fittedSlot)) {
slot = slott;
}
}
// These are the various weapon properties that are checked when making the groupings
boolean antiFighter = false;
boolean pointDefense = false;
boolean strike = false;
boolean noAim = false;
boolean assault = false;
boolean closeSupport = false;
boolean fireSupport = false;
boolean missile = false;
boolean lowFlux = false;
boolean highFlux = false;
boolean hardpoint = false;
boolean limitedAmmo = false;
boolean beam = false;
boolean special = false;
boolean front = false;
boolean back = false;
boolean left = false;
boolean right = false;
WeaponSize size = WeaponSize.SMALL;
if (slot != null) {
if (slot.isHardpoint() || slot.getArc() <= 5f) {
hardpoint = true;
}
if (slot.getAngle() <= 45f && slot.getAngle() >= -45f) {
front = true;
} else if (slot.getAngle() >= 45f && slot.getAngle() <= 135f) {
left = true;
} else if (slot.getAngle() > 135f || slot.getAngle() < -135f) {
back = true;
} else {
right = true;
}
size = slot.getSlotSize();
}
if (getWeaponList("Missile").contains(weapon)) {
missile = true;
}
if (getWeaponList("No Aim").contains(weapon)) {
noAim = true;
}
if (getWeaponList("Anti-Fighter").contains(weapon)) {
antiFighter = true;
}
if (getWeaponList("Point Defense").contains(weapon)) {
pointDefense = true;
}
if (getWeaponList("Strike").contains(weapon)) {
strike = true;
}
if (getWeaponList("Assault").contains(weapon)) {
assault = true;
}
if (getWeaponList("Close Support").contains(weapon)) {
closeSupport = true;
}
if (getWeaponList("Fire Support").contains(weapon)) {
fireSupport = true;
}
if (getWeaponList("Special").contains(weapon)) {
special = true;
}
if (getWeaponList("Low Flux").contains(weapon)) {
lowFlux = true;
}
if (getWeaponList("High Flux").contains(weapon)) {
highFlux = true;
}
if (getWeaponList("Sustained Beam").contains(weapon)) {
beam = true;
}
if (getWeaponList("Limited Ammo").contains(weapon)) {
limitedAmmo = true;
}
// If "Override" is set for the weapon, skip this auto-detection stuff and only use the overrides
// Weapons without "Override" set will use both auto-detected stuff and overrides
if (!getWeaponList("Override").contains(weapon)) {
EnumSet<AIHints> hints = variant.getWeaponSpec(fittedSlot).getAIHints();
for (AIHints hint : hints) {
if (hint == AIHints.ANTI_FTR) {
antiFighter = true;
}
if (hint == AIHints.PD || hint == AIHints.PD_ONLY) {
pointDefense = true;
}
if (hint == AIHints.STRIKE) {
strike = true;
}
if (hint == AIHints.DO_NOT_AIM || hint == AIHints.HEATSEEKER) {
noAim = true;
}
}
if (variant.getWeaponSpec(fittedSlot).getType() == WeaponType.MISSILE) {
missile = true;
lowFlux = true;
limitedAmmo = true;
}
if (type.equals("Anti-Fighter")) {
antiFighter = true;
}
if (type.equals("Point Defense")) {
pointDefense = true;
}
if (type.equals("Strike")) {
strike = true;
}
if (type.equals("Assault")) {
assault = true;
}
if (type.equals("Close Support")) {
closeSupport = true;
}
if (type.equals("Fire Support")) {
fireSupport = true;
}
if (type.equals("Special")) {
special = true;
}
}
// This is the logic for assigning weapons to general groups
// Make sure you know what you are doing before changing this stuff around
// The order of operations is important!
if (hardpoint && (highFlux || (size == WeaponSize.LARGE && (variant.getHullSize() == HullSize.FRIGATE || variant.getHullSize() == HullSize.DESTROYER || variant.getHullSize() == HullSize.DEFAULT)))) {
if (!beam) {
HvyGroup.addSlot(fittedSlot);
} else {
BeaGroup.addSlot(fittedSlot);
}
} else if (!hardpoint && (highFlux || (size == WeaponSize.LARGE && (variant.getHullSize() == HullSize.FRIGATE || variant.getHullSize() == HullSize.DESTROYER || variant.getHullSize() == HullSize.DEFAULT)))) {
if (!beam) {
HvyTGroup.addSlot(fittedSlot);
} else {
BeaTGroup.addSlot(fittedSlot);
}
} else if (missile && left && enforceSides && !noAim && !limitedAmmo) {
LGroup.addSlot(fittedSlot);
} else if (missile && right && enforceSides && !noAim && !limitedAmmo) {
RGroup.addSlot(fittedSlot);
} else if (missile && limitedAmmo && (noAim || !hardpoint) && !strike) {
MisGroup.addSlot(fittedSlot);
} else if (missile && !limitedAmmo && !strike) {
SMisGroup.addSlot(fittedSlot);
} else if (missile && limitedAmmo && !noAim && !strike) {
AMisGroup.addSlot(fittedSlot);
} else if (pointDefense && !hardpoint && !limitedAmmo) {
PDGroup.addSlot(fittedSlot);
} else if (antiFighter) {
AFGroup.addSlot(fittedSlot);
} else if (left && (hardpoint || enforceSides) && !lowFlux) {
LGroup.addSlot(fittedSlot);
} else if (right && (hardpoint || enforceSides) && !lowFlux) {
RGroup.addSlot(fittedSlot);
} else if (assault && hardpoint && !lowFlux) {
AssGroup.addSlot(fittedSlot);
} else if (assault && !hardpoint && !lowFlux) {
AssTGroup.addSlot(fittedSlot);
} else if (closeSupport && hardpoint && !lowFlux) {
CSGroup.addSlot(fittedSlot);
} else if (closeSupport && !hardpoint && !lowFlux) {
CSTGroup.addSlot(fittedSlot);
} else if (strike || limitedAmmo) {
if (!beam) {
StrGroup.addSlot(fittedSlot);
} else {
if (hardpoint) {
BeaGroup.addSlot(fittedSlot);
} else {
BeaTGroup.addSlot(fittedSlot);
}
}
} else if (fireSupport || lowFlux || special) {
SupGroup.addSlot(fittedSlot);
} else {
SupGroup.addSlot(fittedSlot); // This should always be at the end
}
}
// Now we consolidate the general weapon groups into five final weapon groups
// This is only done if the number of general weapon groups is greater than 5
// If Alex ever increases the maximum number of weapon groups, this number can be changed
int groupCount = 0;
for (WeaponGroupSpec group : groupList) {
if (group.getSlots().isEmpty()) {
groupCount++;
}
}
// This section is the most difficult part of the algorithm and can make or break large ships like the Onslaught
// Make sure you know what you are doing when adding logic here
// Do not pass weapons into a group that may have already become null; NetBeans should warn you about this
// Order of operations is extremely important
if (!AFGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!PDGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : AFGroup.getSlots()) {
PDGroup.addSlot(toAdd);
}
AFGroup = null;
}
} else {
AFGroup = null;
}
if (!AssGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!CSGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : AssGroup.getSlots()) {
CSGroup.addSlot(toAdd);
}
AssGroup = null;
}
} else {
AssGroup = null;
}
if (!splitMissiles) {
if (!AMisGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!MisGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : AMisGroup.getSlots()) {
MisGroup.addSlot(toAdd);
}
AMisGroup = null;
}
} else {
AMisGroup = null;
}
}
if (!PDGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!SupGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : PDGroup.getSlots()) {
SupGroup.addSlot(toAdd);
}
PDGroup = null;
}
} else {
PDGroup = null;
}
if (!splitMissiles) {
if (!SMisGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!AssTGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : SMisGroup.getSlots()) {
AssTGroup.addSlot(toAdd);
}
SMisGroup = null;
}
} else {
SMisGroup = null;
}
}
if (!AssTGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!CSTGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : AssTGroup.getSlots()) {
CSTGroup.addSlot(toAdd);
}
AssTGroup = null;
}
} else {
AssTGroup = null;
}
if (!BeaTGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!CSTGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : BeaTGroup.getSlots()) {
CSTGroup.addSlot(toAdd);
}
BeaTGroup = null;
}
} else {
BeaTGroup = null;
}
if (!BeaGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!CSGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : BeaGroup.getSlots()) {
CSGroup.addSlot(toAdd);
}
BeaGroup = null;
}
} else {
BeaGroup = null;
}
if (!enforceSides) {
if (!LGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!RGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : LGroup.getSlots()) {
RGroup.addSlot(toAdd);
}
LGroup = null;
}
} else {
LGroup = null;
}
}
if (!CSTGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!SupGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : CSTGroup.getSlots()) {
SupGroup.addSlot(toAdd);
}
CSTGroup = null;
}
} else {
CSTGroup = null;
}
if (!HvyTGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!HvyGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : HvyTGroup.getSlots()) {
HvyGroup.addSlot(toAdd);
}
HvyTGroup = null;
}
} else {
HvyTGroup = null;
}
if (!HvyGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!StrGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : HvyGroup.getSlots()) {
StrGroup.addSlot(toAdd);
}
HvyGroup = null;
}
} else {
HvyGroup = null;
}
if (splitMissiles && AMisGroup != null) {
if (!AMisGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!MisGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : AMisGroup.getSlots()) {
MisGroup.addSlot(toAdd);
}
AMisGroup = null;
}
} else {
AMisGroup = null;
}
}
if (splitMissiles && SMisGroup != null) {
if (!SMisGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!SupGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : SMisGroup.getSlots()) {
SupGroup.addSlot(toAdd);
}
SMisGroup = null;
}
} else {
SMisGroup = null;
}
}
if (enforceSides) {
if (!CSGroup.getSlots().isEmpty()) {
if (groupCount > 5) {
if (!SupGroup.getSlots().isEmpty()) {
groupCount--;
}
for (String toAdd : CSGroup.getSlots()) {
SupGroup.addSlot(toAdd);
}
CSGroup = null;
}
} else {
CSGroup = null;
}
} else {
if (CSGroup.getSlots().isEmpty()) {
CSGroup = null;
}
}
if (enforceSides && LGroup != null) {
if (LGroup.getSlots().isEmpty()) {
LGroup = null;
}
}
if (RGroup.getSlots().isEmpty()) {
RGroup = null;
}
if (StrGroup.getSlots().isEmpty()) {
StrGroup = null;
}
if (SupGroup.getSlots().isEmpty()) {
SupGroup = null;
}
if (MisGroup.getSlots().isEmpty()) {
MisGroup = null;
}
if (variant.getWeaponGroups().size() < 5) {
for (int i = variant.getWeaponGroups().size(); i < 5; i++) {
variant.addWeaponGroup(new WeaponGroupSpec());
}
}
// I didn't make a loop for this because the order in which you put these functions determines their order on the ship
int currentGroup = 0;
if (HvyGroup != null) {
integrateGroup(HvyGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (HvyTGroup != null) {
integrateGroup(HvyTGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (BeaGroup != null) {
integrateGroup(BeaGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (BeaTGroup != null) {
integrateGroup(BeaTGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (LGroup != null) {
integrateGroup(LGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (RGroup != null) {
integrateGroup(RGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (AssGroup != null) {
integrateGroup(AssGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (CSGroup != null) {
integrateGroup(CSGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (AssTGroup != null) {
integrateGroup(AssTGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (CSTGroup != null) {
integrateGroup(CSTGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (StrGroup != null) {
integrateGroup(StrGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (AMisGroup != null) {
integrateGroup(AMisGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (MisGroup != null) {
integrateGroup(MisGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (SMisGroup != null) {
integrateGroup(SMisGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (SupGroup != null) {
integrateGroup(SupGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (AFGroup != null) {
integrateGroup(AFGroup, variant.getGroup(currentGroup));
currentGroup++;
}
if (PDGroup != null) {
integrateGroup(PDGroup, variant.getGroup(currentGroup));
currentGroup++;
}
}