Skip to content

Commit

Permalink
Fixes issue Musashi1584#40
Browse files Browse the repository at this point in the history
* Dual Wield specs can become complementary to a primary Dual Wield spec even if they have bCantBeComplementary = true. Dual Wield specs can become Secondary specs if the non-Dual Wield primary spec uses the same inventory slots/weapons.
* Added mutually exclusive specs logic.
* Fixes for the Random Classes and Weapon Restrictions SWOs, added some weapon category icons.
  • Loading branch information
Iridar authored Apr 17, 2020
1 parent 9860652 commit a4c1e6a
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 90 deletions.
9 changes: 1 addition & 8 deletions XCOM2RPGOverhaul/Config/XComRPG.ini
Original file line number Diff line number Diff line change
Expand Up @@ -451,11 +451,4 @@ SpecializationMetaInfo = (bUseForRandomClasses=true, AllowedWeaponCategories=(gr
SpecializationMetaInfo = (bUseForRandomClasses=true, AllowedWeaponCategories=(gremlin), InventorySlots=(eInvSlot_SecondaryWeapon), bGremlin=true)

[Valkyrie X2UniversalSoldierClassInfo]
SpecializationMetaInfo = (bUseForRandomClasses=true, bUniversal=true)

; Iridar's Akimbo
[DP_GunFu X2UniversalSoldierClassInfo]
SpecializationMetaInfo = (bUseForRandomClasses=true, AllowedWeaponCategories=(pistol), InventorySlots=(eInvSlot_PrimaryWeapon), bDualWield=true, bCantBeComplementary=true, bShoot=true, iWeightPrimary=1)

[DP_Scrapper X2UniversalSoldierClassInfo]
SpecializationMetaInfo = (bUseForRandomClasses=true, AllowedWeaponCategories=(pistol), InventorySlots=(eInvSlot_PrimaryWeapon), bDualWield=true, bCantBeComplementary=true, bShoot=true, iWeightPrimary=1)
SpecializationMetaInfo = (bUseForRandomClasses=true, bUniversal=true)
Binary file modified XCOM2RPGOverhaul/Content/UILibrary_RPG.upk
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -195,59 +195,58 @@ static function array<int> GetSpecIndices_ForRandomClass(XComGameState_Unit Unit
}
}
}
SpecTemplate = ValidSpecTemplates[`SYNC_RAND_STATIC(ValidSpecTemplates.Length)];

SelectedSpecTemplates.AddItem(SpecTemplate);
ReturnArray.AddItem(class'X2SoldierClassTemplatePlugin'.static.GetSpecializationIndex(UnitState, SpecTemplate.Name));
Count--;
if (ValidSpecTemplates.Length > 0)
{
SpecTemplate = ValidSpecTemplates[`SYNC_RAND_STATIC(ValidSpecTemplates.Length)];

// Record specialization index as a unit value so it can be looked at in class'X2TemplateHelper_RPGOverhaul'.static.CanAddItemToInventory
UnitState.SetUnitFloatValue('PrimarySpecialization_Value', class'X2SoldierClassTemplatePlugin'.static.GetSpecializationIndex(UnitState, SpecTemplate.Name), eCleanup_Never);
`LOG("SELECTED Primary specialization: " @ SpecTemplate.Name,, 'RPG');
SelectedSpecTemplates.AddItem(SpecTemplate);
ReturnArray.AddItem(class'X2SoldierClassTemplatePlugin'.static.GetSpecializationIndex(UnitState, SpecTemplate.Name));
Count--;

// Add complementary specializations, if necessary
AddComplementarySpecializations(UnitState, SpecTemplate, ReturnArray, SelectedSpecTemplates, Count);
// Record specialization index as a unit value so it can be looked at in class'X2TemplateHelper_RPGOverhaul'.static.CanAddItemToInventory
UnitState.SetUnitFloatValue('PrimarySpecialization_Value', class'X2SoldierClassTemplatePlugin'.static.GetSpecializationIndex(UnitState, SpecTemplate.Name), eCleanup_Never);
`LOG("SELECTED Primary specialization: " @ SpecTemplate.Name,, 'RPG');

// Exit function early if necessary
if (Count <= 0) return ReturnArray;
// Add complementary specializations, if necessary
AddComplementarySpecializations(UnitState, SpecTemplate, ReturnArray, SelectedSpecTemplates, Count);

// Exit function early if necessary
if (Count <= 0) return ReturnArray;
}
else `LOG("There were no valid primary specs to choose from.",, 'RPG');

// ########################################################
// Select random specialization for secondary weapon
if (SelectedSpecTemplates[0].SpecializationMetaInfo.bDualWield)
{
`LOG("## Primary spec is Dual Wield, skipping Secondary Spec.",, 'RPG');
}
else
{
`LOG("## Selecting secondary specialization: " @ Count,, 'RPG');
ValidSpecTemplates.Length = 0;
foreach AllSpecTemplates(SpecTemplate)
{
// Skip specialization if it was already selected
if (ReturnArray.Find(class'X2SoldierClassTemplatePlugin'.static.GetSpecializationIndex(UnitState, SpecTemplate.Name)) != INDEX_NONE) continue;
`LOG("## Selecting secondary specialization: " @ Count,, 'RPG');
ValidSpecTemplates.Length = 0;
foreach AllSpecTemplates(SpecTemplate)
{
// Skip specialization if it was already selected
if (ReturnArray.Find(class'X2SoldierClassTemplatePlugin'.static.GetSpecializationIndex(UnitState, SpecTemplate.Name)) != INDEX_NONE) continue;

// Skip specialization if it's mutually exclusive with one of the selected ones.
bSkipSpec = false;
for (i = 0; i < SelectedSpecTemplates.Length; i++)
// Skip specialization if it's mutually exclusive with one of the selected ones.
bSkipSpec = false;
for (i = 0; i < SelectedSpecTemplates.Length; i++)
{
if (SelectedSpecTemplates[i].SpecializationMetaInfo.MutuallyExclusiveSpecs.Find(SpecTemplate.Name) != INDEX_NONE)
{
if (SelectedSpecTemplates[i].SpecializationMetaInfo.MutuallyExclusiveSpecs.Find(SpecTemplate.Name) != INDEX_NONE)
{
bSkipSpec = true;
break;
}
bSkipSpec = true;
break;
}
if (bSkipSpec) continue;
}
if (bSkipSpec) continue;

if (SpecTemplate.IsSecondaryWeaponSpecialization())
if (class'X2SoldierClassTemplatePlugin'.static.IsSpecializationValidToBeSecondary(SelectedSpecTemplates, SpecTemplate))
{
for (i = 0; i < SpecTemplate.SpecializationMetaInfo.iWeightSecondary; i++)
{
for (i = 0; i < SpecTemplate.SpecializationMetaInfo.iWeightSecondary; i++)
{
`LOG("Valid spec: " @ SpecTemplate.Name,, 'RPG');
ValidSpecTemplates.AddItem(SpecTemplate);
}
`LOG("Valid spec: " @ SpecTemplate.Name,, 'RPG');
ValidSpecTemplates.AddItem(SpecTemplate);
}
}
}
if (ValidSpecTemplates.Length > 0)
{
SpecTemplate = ValidSpecTemplates[`SYNC_RAND_STATIC(ValidSpecTemplates.Length)];

SelectedSpecTemplates.AddItem(SpecTemplate);
Expand All @@ -263,6 +262,8 @@ static function array<int> GetSpecIndices_ForRandomClass(XComGameState_Unit Unit
// Exit function early if necessary
if (Count <= 0) return ReturnArray;
}
else `LOG("There were no valid secondary specs to choose from.",, 'RPG');

// ########################################################
// Select several additional specializations that either complement already selected specializations, or are weapon agnostic.
`LOG("## Selecting additional specializations: " @ Count,, 'RPG');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,42 +163,39 @@ static function bool IsSpecializationValidToBeComplementary(array<X2UniversalSol
return false;
}

/*
static function bool IsSpecializationValidToBeSecondary(array<X2UniversalSoldierClassInfo> SelectedSpecTemplates, X2UniversalSoldierClassInfo SpecTemplate)
{
local X2UniversalSoldierClassInfo CycleSpecTemplate;
// Specialization cannot be used if it's missing meta information
// Or if it is explicitly forbidden from being complementary
if (!SpecTemplate.SpecializationMetaInfo.bUseForRandomClasses || SpecTemplate.SpecializationMetaInfo.bCantBeComplementary) return false;
// If the Spec Template is Universal, then it can Complement any other specialization just fine.
if (SpecTemplate.SpecializationMetaInfo.bUniversal) return true;
// If both the Primary Specailization and this Specialization are Dual Wielding, then just compare their weapon categories.
if (SelectedSpecTemplates[0] != none &&
SelectedSpecTemplates[0].SpecializationMetaInfo.bDualWield &&
SpecTemplate.SpecializationMetaInfo.bDualWield)
{
return DoSpecializationsUseTheSameWeapons(SelectedSpecTemplates[0], SpecTemplate);
}
// Otherwise, cycle through Specs that have already been selected.
foreach SelectedSpecTemplates(CycleSpecTemplate)
// Allow only specs with configured meta info.
if (SpecTemplate.SpecializationMetaInfo.bUseForRandomClasses)
{
// At least one of the selected specializations roughly does the same thing as this specialization, then this specialization can complement that one.
if (DoSpecializationsUseTheSameSlots(CycleSpecTemplate, SpecTemplate) &&
(DoSpecializationsUseTheSameWeapons(CycleSpecTemplate, SpecTemplate) ||
SpecTemplate.SpecializationMetaInfo.bShoot && CycleSpecTemplate.SpecializationMetaInfo.bShoot ||
SpecTemplate.SpecializationMetaInfo.bGremlin && CycleSpecTemplate.SpecializationMetaInfo.bGremlin ||
SpecTemplate.SpecializationMetaInfo.bPsionic && CycleSpecTemplate.SpecializationMetaInfo.bPsionic ||
SpecTemplate.SpecializationMetaInfo.bMelee && CycleSpecTemplate.SpecializationMetaInfo.bMelee))
if (SelectedSpecTemplates.Length > 0)
{
return true;
// Primary specs marked as Dual Wield do not get secondary specs.
if (SelectedSpecTemplates[0].SpecializationMetaInfo.bDualWield)
{
//`LOG(SpecTemplate.Name @ "is not valid, because primary spec is dual wield.",, 'RPG');
return false;
}

// If this spec is marked as Dual Wield spec, and it uses the same weapons as the primary spec,
if (SpecTemplate.SpecializationMetaInfo.bDualWield && DoSpecializationsUseTheSameWeapons(SelectedSpecTemplates[0], SpecTemplate))
{
// then this spec can become a secondary spec for it
// Looking up Secondary Weight allows to restrict specific specs from becoming secondary to dual wield specs.

if (SpecTemplate.SpecializationMetaInfo.iWeightSecondary <= 0) `LOG(SpecTemplate.Name @ "is not valid, because iWeightSecondary is zero or lower.",, 'RPG');
return SpecTemplate.SpecializationMetaInfo.iWeightSecondary > 0;
}
}
// If this spec is not a dual wield spec, then we only check if the spec itself is valid to be a secondary on its own.
//if (!SpecTemplate.IsSecondaryWeaponSpecialization())
//{
// `LOG(SpecTemplate.Name @ "is not valid, because it's not a secondary spec.",, 'RPG');
//}
return SpecTemplate.IsSecondaryWeaponSpecialization();
}
return false;
}
*/

static function bool DoSpecializationsUseTheSameSlots(X2UniversalSoldierClassInfo SpecTemplateA, X2UniversalSoldierClassInfo SpecTemplateB)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1180,37 +1180,51 @@ static function bool CanAddItemToInventory_WeaponRestrictions(out int bCanAddIte
// Perform the check ONLY if the item has not been forbidden by another mod already, if the soldier is an RPGO soldier, and only if we're looking at a weapon
if (DisabledReason == "" && UnitState.GetSoldierClassTemplateName() == 'UniversalSoldier' && WeaponTemplate != none && (Slot == eInvSlot_PrimaryWeapon || Slot == eInvSlot_SecondaryWeapon))
{
//`LOG("Begin check for unit:" @ UnitState.GetFullName() @ "slot:" @ Slot @ "weapon:" @ WeaponTemplate.DataName,, 'IRITEST');

// Check if the soldier's primary specs allow using this weapon in this slot.
PrimarySpecs = class'X2SoldierClassTemplatePlugin'.static.GetTrainedPrimaryWeaponSpecializations(UnitState);
foreach PrimarySpecs(PrimarySpec)
{
PrimarySpecTemplate = class'X2SoldierClassTemplatePlugin'.static.GetSpecializationTemplate(PrimarySpec);
// If soldier's primary specialization is a Dual Wield one, then look only at primary specialization for both Primary and Secondary weapons.
if (PrimarySpecTemplate.SpecializationMetaInfo.bDualWield || Slot == eInvSlot_PrimaryWeapon)
if (PrimarySpecTemplate != none)
{
bAllowed = PrimarySpecTemplate != none && PrimarySpecTemplate.IsWeaponAllowed(WeaponTemplate.InventorySlot, WeaponTemplate.WeaponCat);
//`LOG("Checking primary spec:" @ PrimarySpec.TemplateName,, 'IRITEST');

// Primary Spec allows using primary weapon based on their category, or both primary and secondary, if it's marked as Dual Wield spec.
bAllowed = PrimarySpecTemplate.IsWeaponAllowed(WeaponTemplate.WeaponCat) && (Slot == eInvSlot_PrimaryWeapon || PrimarySpecTemplate.SpecializationMetaInfo.bDualWield);
if (bAllowed)
{
//`LOG("It allows this weapon.",, 'IRITEST');
break;
}
}
else `LOG("Weapon Restrictions: ERROR, could not get Spec Template for spec:" @ PrimarySpec.TemplateName,, 'RPG');
}

// Check if the weapon is not allowed yet, check if the secondary specs allow it.
if (!bAllowed)
{
SecondarySpecs = class'X2SoldierClassTemplatePlugin'.static.GetTrainedSecondaryWeaponSpecializations(UnitState);
foreach SecondarySpecs(SecondarySpec)
{
//`LOG("Checking secondary spec:" @ SecondarySpec.TemplateName,, 'IRITEST');
SecondarySpecTemplate = class'X2SoldierClassTemplatePlugin'.static.GetSpecializationTemplate(SecondarySpec);
bAllowed = SecondarySpecTemplate != none && SecondarySpecTemplate.IsWeaponAllowed(WeaponTemplate.InventorySlot, WeaponTemplate.WeaponCat);

// Secondary Spec can allow only secondary weapons.
bAllowed = SecondarySpecTemplate != none && SecondarySpecTemplate.IsWeaponAllowed(WeaponTemplate.WeaponCat) && Slot == eInvSlot_SecondaryWeapon;
if (bAllowed)
{
//`LOG("It allows this weapon.",, 'IRITEST');
break;
}
}
}


if (bAllowed)
{
//`LOG("Weapon allowed.",, 'IRITEST');
// Weapon allowed
DisabledReason = "";
bCanAddItem = 1;
Expand All @@ -1220,6 +1234,7 @@ static function bool CanAddItemToInventory_WeaponRestrictions(out int bCanAddIte
}
else
{
//`LOG("Weapon NOT allowed.",, 'IRITEST');
// Weapon NOT Allowed
LocTag = XGParamTag(`XEXPANDCONTEXT.FindTag("XGParam"));
LocTag.StrValue0 = UnitState.GetSoldierClassTemplate().DisplayName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,9 @@ var localized string ClassSpecializationTitle;

var config SpecializationMetaInfoStruct SpecializationMetaInfo;

function bool IsWeaponAllowed(EInventorySlot Slot, name WeaponCat)
function bool IsWeaponAllowed(name WeaponCat)
{
if (SpecializationMetaInfo.bDualWield)
{
return SpecializationMetaInfo.AllowedWeaponCategories.Find(WeaponCat) != INDEX_NONE;
}
else return SpecializationMetaInfo.InventorySlots.Find(Slot) != INDEX_NONE && SpecializationMetaInfo.AllowedWeaponCategories.Find(WeaponCat) != INDEX_NONE;
return SpecializationMetaInfo.AllowedWeaponCategories.Find(WeaponCat) != INDEX_NONE;
}

function string GetClassSpecializationTitleWithMetaData()
Expand Down Expand Up @@ -106,18 +102,18 @@ function array<String> GetLocalizedWeaponCategories()
function bool IsPrimaryWeaponSpecialization()
{
// Specialization is valid to be soldier's Primary specialization only if has meta information set up, if it is valid for Primry Weapon slot, and only if it specifies some weapon categories it can unlock.
return SpecializationMetaInfo.AllowedWeaponCategories.Length > 0 && SpecializationMetaInfo.InventorySlots.Find(eInvSlot_PrimaryWeapon) != INDEX_NONE;
return SpecializationMetaInfo.iWeightPrimary > 0 && SpecializationMetaInfo.AllowedWeaponCategories.Length > 0 && SpecializationMetaInfo.InventorySlots.Find(eInvSlot_PrimaryWeapon) != INDEX_NONE;
}

function bool IsSecondaryWeaponSpecialization()
{
// Spec is valid to be secondary if it allows using specific weapons in the secondary slot OR if it's a valid primary spec that is also a Dual Wield spec
return SpecializationMetaInfo.AllowedWeaponCategories.Length > 0 && (SpecializationMetaInfo.InventorySlots.Find(eInvSlot_SecondaryWeapon) != INDEX_NONE || SpecializationMetaInfo.bDualWield && IsPrimaryWeaponSpecialization());
// Spec is valid to be secondary if it allows using specific weapons in the secondary slot
return SpecializationMetaInfo.iWeightSecondary > 0 && SpecializationMetaInfo.AllowedWeaponCategories.Length > 0 && SpecializationMetaInfo.InventorySlots.Find(eInvSlot_SecondaryWeapon) != INDEX_NONE;
}

function bool IsComplemtarySpecialization()
{
return (!SpecializationMetaInfo.bCantBeComplementary && SpecializationMetaInfo.bUniversal);
return SpecializationMetaInfo.iWeightComplementary > 0 && !SpecializationMetaInfo.bCantBeComplementary && SpecializationMetaInfo.bUniversal;
}
/*
{
Expand Down

0 comments on commit a4c1e6a

Please sign in to comment.