mirror of
https://github.com/FalloutCollaborationProject/FCP-Tools.git
synced 2026-07-27 16:59:37 -07:00
Enlist: handle shuttles & hostile goodwill
Apply goodwill penalties and notify player when enlisting makes other factions hostile to the enlisted faction; new localization keys added for the letter. Add FCP_Core reference to the Enlist project. Replace fragile reflection logic for custom transport ships with direct type access: load shuttle defs via FactionModExtension, create shuttle Thing/CompTransporter, transfer caravan cargo to the shuttle, create TravellingTransporters with proper fields and add its transporter info; fall back to drop pods on failure. Also change texture lookup to reportFailure:false with BaseContent.BadTex fallback and update GetTransportPodsFloatMenuOptionsAt to accept an optional launchAction. Updated compiled assembly (FCP_Enlist.dll).
This commit is contained in:
Binary file not shown.
@@ -29,4 +29,8 @@
|
||||
<FCP_WeaponReq_WrongXenotype>Requires xenotype: {0}</FCP_WeaponReq_WrongXenotype>
|
||||
<FCP_WeaponReq_RequiresApparel>Requires apparel: {0}</FCP_WeaponReq_RequiresApparel>
|
||||
|
||||
<!-- Enlistment - Hostile Factions -->
|
||||
<FCP_Enemy_Of_Enlist_Faction>New Enemy</FCP_Enemy_Of_Enlist_Faction>
|
||||
<FCP_Enemy_Of_Enlist_Faction_Desc>By enlisting with {ENLISTED_name}, you have become an enemy of {HOSTILE_name}. They are now hostile toward your colony.</FCP_Enemy_Of_Enlist_Faction_Desc>
|
||||
|
||||
</LanguageData>
|
||||
@@ -36,4 +36,11 @@
|
||||
<Publicize Include="Assembly-CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="FCP_Core">
|
||||
<HintPath>..\..\1.6\Assemblies\FCP_Core.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -54,6 +54,27 @@ public class FactionEnlistWorker
|
||||
factionOptions.factionsStorages[def] = factionStorage;
|
||||
}
|
||||
def.enlistedSoundDef?.PlayOneShotOnCamera();
|
||||
|
||||
Faction player = Faction.OfPlayer;
|
||||
foreach (Faction faction in Find.FactionManager.AllFactionsListForReading)
|
||||
{
|
||||
if (faction == toEnlist || faction == player || faction.defeated || faction.IsPlayer) continue;
|
||||
|
||||
FactionRelation rel = toEnlist.RelationWith(faction, false);
|
||||
if (rel == null || rel.kind != FactionRelationKind.Hostile) continue;
|
||||
if (faction.HostileTo(player)) continue;
|
||||
|
||||
int goodwill = faction.GoodwillWith(player);
|
||||
if (goodwill > -75)
|
||||
{
|
||||
faction.TryAffectGoodwillWith(player, -75 - goodwill, false, true);
|
||||
}
|
||||
|
||||
Find.LetterStack.ReceiveLetter(
|
||||
"FCP_Enemy_Of_Enlist_Faction".Translate(),
|
||||
"FCP_Enemy_Of_Enlist_Faction_Desc".Translate(toEnlist.Named("ENLISTED"), faction.Named("HOSTILE")),
|
||||
LetterDefOf.NegativeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using FCP.Core.Shuttles;
|
||||
|
||||
namespace FCP.Enlist;
|
||||
|
||||
@@ -493,7 +494,7 @@ public class WorldObjectCompEnlist : WorldObjectComp
|
||||
{
|
||||
defaultLabel = optionDef.shuttleServiceLabelKey.Translate(faction.Named("FACTION")),
|
||||
defaultDesc = optionDef.shuttleServiceDescKey.Translate(faction.Named("FACTION")),
|
||||
icon = ContentFinder<Texture2D>.Get(optionDef.shuttleServiceButtonIconTexPath),
|
||||
icon = ContentFinder<Texture2D>.Get(optionDef.shuttleServiceButtonIconTexPath, reportFailure: false) ?? BaseContent.BadTex,
|
||||
action = delegate
|
||||
{
|
||||
StartChoosingShuttleDestination(caravan, optionDef);
|
||||
@@ -692,7 +693,7 @@ public class WorldObjectCompEnlist : WorldObjectComp
|
||||
Messages.Message("TransportPodDestinationBeyondMaximumRange".Translate(), MessageTypeDefOf.RejectInput, historical: false);
|
||||
return false;
|
||||
}
|
||||
IEnumerable<FloatMenuOption> source = GetTransportPodsFloatMenuOptionsAt(target.Tile, caravan);
|
||||
IEnumerable<FloatMenuOption> source = GetTransportPodsFloatMenuOptionsAt(target.Tile, caravan, launchAction);
|
||||
if (!source.Any())
|
||||
{
|
||||
if (Find.World.Impassable(target.Tile))
|
||||
@@ -783,92 +784,63 @@ public class WorldObjectCompEnlist : WorldObjectComp
|
||||
}
|
||||
ExtractMoneyFromCaravan(caravan, curFactionEnlistOptionsDef.shuttleServiceCost, curFactionEnlistOptionsDef);
|
||||
|
||||
// Try to get custom shuttle from faction using reflection
|
||||
TransportShipDef transportShipDef = null;
|
||||
|
||||
if (parent.Faction?.def?.modExtensions != null)
|
||||
// Get custom shuttle from faction extension - direct type access (no reflection)
|
||||
FactionModExtension factionExtension = parent.Faction?.def?.GetModExtension<FactionModExtension>();
|
||||
TransportShipDef transportShipDef = factionExtension?.transportShipDef;
|
||||
|
||||
if (transportShipDef != null && transportShipDef.worldObject != null)
|
||||
{
|
||||
foreach (var extension in parent.Faction.def.modExtensions)
|
||||
// Create the shuttle thing - it's a Building with CompTransporter, not an ActiveTransporter
|
||||
Thing shuttleThing = ThingMaker.MakeThing(transportShipDef.shipThing);
|
||||
CompTransporter compTransporter = shuttleThing.TryGetComp<CompTransporter>();
|
||||
|
||||
if (compTransporter == null)
|
||||
{
|
||||
if (extension.GetType().Name == "FactionModExtension")
|
||||
{
|
||||
var transportShipDefField = extension.GetType().GetField("transportShipDef");
|
||||
if (transportShipDefField != null)
|
||||
{
|
||||
transportShipDef = transportShipDefField.GetValue(extension) as TransportShipDef;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Log.Error($"[FCP Enlist] Shuttle thing {shuttleThing.def.defName} has no CompTransporter!");
|
||||
TryLaunch(destinationTile, arrivalAction, caravan);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Thing transportThing;
|
||||
ThingDef skyfallerDef;
|
||||
WorldObjectDef worldObjectDef;
|
||||
|
||||
if (transportShipDef != null)
|
||||
{
|
||||
// Use custom shuttle
|
||||
transportThing = ThingMaker.MakeThing(transportShipDef.shipThing);
|
||||
skyfallerDef = transportShipDef.leavingSkyfaller;
|
||||
worldObjectDef = transportShipDef.worldObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fall back to drop pods
|
||||
transportThing = ThingMaker.MakeThing(ThingDefOf.ActiveDropPod);
|
||||
skyfallerDef = ThingDefOf.DropPodLeaving;
|
||||
worldObjectDef = WorldObjectDefOf.TravellingTransporters;
|
||||
}
|
||||
|
||||
// Load the transport
|
||||
CompTransporter compTransporter = transportThing.TryGetComp<CompTransporter>();
|
||||
if (compTransporter != null)
|
||||
{
|
||||
compTransporter.GetDirectlyHeldThings().TryAddRangeOrTransfer(caravan.GetDirectlyHeldThings(), canMergeWithExistingStacks: true, destroyLeftover: true);
|
||||
}
|
||||
|
||||
// Create the skyfaller
|
||||
Skyfaller skyfaller = (Skyfaller)SkyfallerMaker.MakeSkyfaller(skyfallerDef, transportThing);
|
||||
if (skyfaller is FlyShipLeaving flyShip)
|
||||
{
|
||||
flyShip.groupID = 1;
|
||||
flyShip.destinationTile = destinationTile;
|
||||
flyShip.arrivalAction = arrivalAction;
|
||||
flyShip.worldObjectDef = worldObjectDef;
|
||||
}
|
||||
|
||||
// Create traveling world object
|
||||
WorldObject travelingPods = WorldObjectMaker.MakeWorldObject(worldObjectDef);
|
||||
travelingPods.Tile = base.parent.Tile;
|
||||
travelingPods.SetFaction(Faction.OfPlayer);
|
||||
|
||||
// Set destination using reflection since we don't know the exact type
|
||||
var destTileField = travelingPods.GetType().GetField("destinationTile");
|
||||
if (destTileField != null)
|
||||
{
|
||||
destTileField.SetValue(travelingPods, destinationTile);
|
||||
}
|
||||
|
||||
var arrivalActionField = travelingPods.GetType().GetField("arrivalAction");
|
||||
if (arrivalActionField != null)
|
||||
{
|
||||
arrivalActionField.SetValue(travelingPods, arrivalAction);
|
||||
}
|
||||
|
||||
Find.WorldObjects.Add(travelingPods);
|
||||
|
||||
// Add pods using reflection
|
||||
if (compTransporter != null)
|
||||
{
|
||||
var addPodMethod = travelingPods.GetType().GetMethod("AddPod");
|
||||
if (addPodMethod != null)
|
||||
|
||||
// Transfer caravan contents to shuttle's transporter
|
||||
compTransporter.GetDirectlyHeldThings().TryAddRangeOrTransfer(
|
||||
caravan.GetDirectlyHeldThings(),
|
||||
canMergeWithExistingStacks: true,
|
||||
destroyLeftover: true);
|
||||
|
||||
// Create the traveling world object - cast to TravellingTransporters for direct property access
|
||||
TravellingTransporters travelingShuttle = (TravellingTransporters)WorldObjectMaker.MakeWorldObject(transportShipDef.worldObject);
|
||||
travelingShuttle.Tile = parent.Tile;
|
||||
travelingShuttle.SetFaction(Faction.OfPlayer);
|
||||
travelingShuttle.destinationTile = destinationTile;
|
||||
|
||||
// Use custom arrival action that forces map loading to show shuttle landing
|
||||
travelingShuttle.arrivalAction = arrivalAction ?? new TransportersArrivalAction_FormCaravan();
|
||||
// Set the transport ship def (still need reflection for this private field)
|
||||
var transportShipField = typeof(TravellingTransporters).GetField("transportShip",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (transportShipField != null)
|
||||
{
|
||||
addPodMethod.Invoke(travelingPods, new object[] { compTransporter.GetDirectlyHeldThings().ToList(), true });
|
||||
transportShipField.SetValue(travelingShuttle, transportShipDef);
|
||||
}
|
||||
|
||||
Find.WorldObjects.Add(travelingShuttle);
|
||||
|
||||
// Wrap the cargo in ActiveTransporterInfo for AddTransporter
|
||||
ActiveTransporterInfo transporterInfo = new ActiveTransporterInfo();
|
||||
transporterInfo.innerContainer.TryAddRangeOrTransfer(
|
||||
compTransporter.GetDirectlyHeldThings(),
|
||||
canMergeWithExistingStacks: true,
|
||||
destroyLeftover: false);
|
||||
|
||||
travelingShuttle.AddTransporter(transporterInfo, true);
|
||||
|
||||
caravan.Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
caravan.Destroy();
|
||||
|
||||
// Fallback to drop pods
|
||||
Log.Warning("[FCP Enlist] Failed to launch shuttle, falling back to drop pods");
|
||||
TryLaunch(destinationTile, arrivalAction, caravan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -901,15 +873,19 @@ public class WorldObjectCompEnlist : WorldObjectComp
|
||||
}
|
||||
}
|
||||
}
|
||||
private IEnumerable<FloatMenuOption> GetTransportPodsFloatMenuOptionsAt(int tile, Caravan caravan)
|
||||
private IEnumerable<FloatMenuOption> GetTransportPodsFloatMenuOptionsAt(int tile, Caravan caravan, Action<int, TransportersArrivalAction, Caravan> launchAction = null)
|
||||
{
|
||||
// Default to TryLaunch if no launchAction specified (for drop pods)
|
||||
if (launchAction == null)
|
||||
launchAction = TryLaunch;
|
||||
|
||||
bool anything = false;
|
||||
if (!Find.World.Impassable(tile) && !Find.WorldObjects.AnySettlementBaseAt(tile) && !Find.WorldObjects.AnySiteAt(tile))
|
||||
{
|
||||
anything = true;
|
||||
yield return new FloatMenuOption("FormCaravanHere".Translate(), delegate
|
||||
{
|
||||
TryLaunch(tile, new TransportersArrivalAction_FormCaravan(), caravan);
|
||||
launchAction(tile, new TransportersArrivalAction_FormCaravan(), caravan);
|
||||
});
|
||||
}
|
||||
//List<WorldObject> worldObjects = Find.WorldObjects.AllWorldObjects;
|
||||
@@ -928,7 +904,7 @@ public class WorldObjectCompEnlist : WorldObjectComp
|
||||
{
|
||||
yield return new FloatMenuOption("TransportPodsContentsWillBeLost".Translate(), delegate
|
||||
{
|
||||
TryLaunch(tile, null, caravan);
|
||||
launchAction(tile, null, caravan);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user