Skip to content

Commit

Permalink
Added M3G_UMIN ability to target world tile.
Browse files Browse the repository at this point in the history
  • Loading branch information
Epicguru committed Aug 17, 2020
1 parent 2ce896d commit 493a2db
Show file tree
Hide file tree
Showing 16 changed files with 1,382 additions and 331 deletions.
8 changes: 7 additions & 1 deletion Languages/English/Keyed/Keys.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@
<AA.NotRunningInfo>Not running: {0}</AA.NotRunningInfo> <!-- For example, 'Not running: reason' -->

<!-- M3G_UMIN -->
<AA.MegStrikeLocation>Strike location</AA.MegStrikeLocation>
<AA.MegStrikeLocation>Strike local position</AA.MegStrikeLocation>
<AA.MegStrikeWorld>Strike world position</AA.MegStrikeWorld>
<AA.MegStrikeLocationDesc>Fires the M3G_UMIN at a specific position on this map.</AA.MegStrikeLocationDesc>
<AA.MegStrikeWorldDesc>Fires the M3G_UMIN at a specific position in the world.</AA.MegStrikeWorldDesc>
<AA.MegCancelStrike>Cancel strike</AA.MegCancelStrike>
<AA.MegCancelStrikeDesc>Cancels the M3G_UMIN strike.\nAll charged power is discarded. Only available during charging phase.</AA.MegCancelStrikeDesc>
<AA.MegCoolingDown>Weapon is cooling down ({0} left).</AA.MegCoolingDown> <!-- {0} is time period, such as '16 hours' or '5 days' -->
Expand All @@ -76,6 +78,10 @@
<AA.MegBlockedByRoof>Blocked by roof.</AA.MegBlockedByRoof>
<AA.MegReadyToFire>Ready to fire.</AA.MegReadyToFire>
<AA.MegCooldown>Cooldown: {0}</AA.MegCooldown> <!-- {0} is time period, such as '16 hours' or '5 days' -->
<AA.MegInvalidWorldTarget>Invalid target position.</AA.MegInvalidWorldTarget>
<AA.MegOutOfRange>Target is out of range.</AA.MegOutOfRange>
<AA.MegNeedsMap>Target map is not loaded. You must load the map before firing at it. You should attack it with a caravan, for example.</AA.MegNeedsMap>
<AA.MegCantTargetSelf>Can't target self: to target a position on the home map, use the 'Strike local position' option instead.</AA.MegCantTargetSelf>

<!-- Particle accelerator -->
<AA.PAProducing>Producing {0} canisters per day.\n{1} hours until next antimatter canister.</AA.PAProducing>
Expand Down
215 changes: 162 additions & 53 deletions Source/AntimatterAnnihilation/Buildings/Building_Megumin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using RimWorld.Planet;
using UnityEngine;
using Verse;
Expand All @@ -29,7 +30,7 @@ public class Building_Megumin : Building, IConditionalGlower
public static int EXPLOSION_DAMAGE = 50;
public static float EXPLOSION_PEN = 0.7f;
public static float CHARGE_WATT_DAYS = 600 * 5; // Requires 5 fully-powered batteries to charge (semi-instantly). Otherwise it will take longer depending on power production.
public static int WORLD_MAP_RANGE = 90;
public static int WORLD_MAP_RANGE = 130;

public bool ShouldBeGlowingNow
{
Expand Down Expand Up @@ -112,13 +113,29 @@ protected set
}
public int CooldownTicks;
public int PoweringUpTicks;
public Map TargetMap
{
get
{
if (globalTarget.IsValid && globalTarget.Map != null)
{
// The target map is in the global target.
return globalTarget.Map;
}

// If there is no global target, the target must be this map.
return this.Map;
}
}

private IntVec3 lastKnownThingLoc; // Used to prevent bugs where target is destroyed and laser does not spawn.
private bool isChargingUp;
private UpBeam beam;
private LocalTargetInfo localTarget;
private LocalTargetInfo localTarget = LocalTargetInfo.Invalid;
private GlobalTargetInfo globalTarget = GlobalTargetInfo.Invalid;
private Sustainer soundSustainer;
private ParticleSystem chargeEffect;
private Map globalTargetMapLastKnown;

public override void SpawnSetup(Map map, bool respawningAfterLoad)
{
Expand Down Expand Up @@ -160,18 +177,21 @@ public override IEnumerable<Gizmo> GetGizmos()
cmd.icon = ContentFinder<Texture2D>.Get("UI/Commands/Attack", true);
cmd.hotKey = KeyBindingDefOf.Misc4;
cmd.targetingParams = new TargetingParameters() {canTargetBuildings = true, canTargetLocations = true, canTargetPawns = true, canTargetAnimals = true };
cmd.onTargetSelected = StartAttackSequence;
cmd.onTargetSelected = (local) =>
{
StartAttackSequence(GlobalTargetInfo.Invalid, local);
};

// Attack on world map.
Command_Action cmd2 = new Command_Action();
cmd2.icon = Content.GlobalStrikeIcon;
cmd2.defaultLabel = "AA.MegStrikeWorld".Translate();
cmd2.defaultDesc = "AA.MegStrikeWorldDesc".Translate();
cmd2.action = () =>
{
Log.Message("Starting world targeting...");
Find.WorldSelector.ClearSelection();
CameraJumper.TryJump(CameraJumper.GetWorldTarget(this));
Find.WorldTargeter.BeginTargeting(ChoseWorldTarget, false, Content.AutoAttackIcon, true, delegate
Find.WorldTargeter.BeginTargeting(OnChoseWorldTarget, false, Content.AutoAttackIcon, true, delegate
{
GenDraw.DrawWorldRadiusRing(this.Map.Tile, WORLD_MAP_RANGE);
});
Expand Down Expand Up @@ -235,65 +255,93 @@ public override IEnumerable<Gizmo> GetGizmos()
}
}

private bool ChoseWorldTarget(GlobalTargetInfo target)
private bool OnChoseWorldTarget(GlobalTargetInfo target)
{
//Building_Railgun.<> c__DisplayClass14_0 CS$<> 8__locals1 = new Building_Railgun.<> c__DisplayClass14_0();
//CS$<> 8__locals1.<> 4__this = this;
//if (!target.IsValid)
//{
// Messages.Message("MessageRailgunTargetInvalid".Translate(), MessageTypeDefOf.RejectInput, true);
// return false;
//}
//if (Find.WorldGrid.TraversalDistanceBetween(base.Map.Tile, target.Tile, true, 2147483647) > this.WorldRange)
//{
// Messages.Message("MessageTargetBeyondMaximumRange".Translate(), this, MessageTypeDefOf.RejectInput, true);
// return false;
//}
//MapParent mapParent = target.WorldObject as MapParent;
//if (mapParent == null || !mapParent.HasMap)
//{
// Messages.Message("MessageRailgunNeedsMap".Translate(), MessageTypeDefOf.RejectInput, true);
// return false;
//}
//if (mapParent.Map == base.Map)
//{
// Messages.Message("MessageRailgunCantTargetMyMap".Translate(), MessageTypeDefOf.RejectInput, true);
// return false;
//}
//CS$<> 8__locals1.myMap = base.Map;
//CS$<> 8__locals1.map = mapParent.Map;
//Current.Game.CurrentMap = CS$<> 8__locals1.map;
//Find.Targeter.BeginTargeting(Building_Railgun.ForFireMission(), delegate (LocalTargetInfo x)
//{
// foreach (Building_Railgun building_Railgun in CS$<> 8__locals1.<> 4__this.selectedRailguns)

// {
// building_Railgun.FireMission(CS$<> 8__locals1.map.Tile, x, CS$<> 8__locals1.map.uniqueID);
// }
//}, null, new Action(CS$<>8__locals1.<ChoseWorldTarget>g__ActionWhenFinished|0), Building_Railgun.FireMissionTex);
Messages.Message("Not working yet.", MessageTypeDefOf.RejectInput, true);
// Invalid target.
if (!target.IsValid)
{
Messages.Message("AA.MegInvalidWorldTarget".Translate(), MessageTypeDefOf.RejectInput);
return false;
}

// Out of range.
if (Find.WorldGrid.TraversalDistanceBetween(base.Map.Tile, target.Tile) > WORLD_MAP_RANGE)
{
Messages.Message("AA.MegOutOfRange".Translate(), this, MessageTypeDefOf.RejectInput);
return false;
}

// Make sure that the map is generated. Cannot fire into a null map.
MapParent mapParent = target.WorldObject as MapParent;
if (mapParent == null || !mapParent.HasMap)
{
Messages.Message("AA.MegNeedsMap".Translate(), MessageTypeDefOf.RejectInput);
return false;
}

// Can't attack own map.
var targetMap = mapParent.Map;
if (targetMap == this.Map)
{
Messages.Message("AA.MegCantTargetSelf".Translate(), MessageTypeDefOf.RejectInput);
return false;
}

// Allow targeting everything.
var targParams = new TargetingParameters
{
canTargetPawns = true,
canTargetBuildings = true,
canTargetLocations = true,
canTargetAnimals = true
};

Current.Game.CurrentMap = targetMap;
Find.Targeter.BeginTargeting(targParams, (localTarg) =>
{
GlobalTargetInfo globalTargetInfo = localTarg.ToGlobalTargetInfo(targetMap);
StartAttackSequence(globalTargetInfo, LocalTargetInfo.Invalid);
});

//Messages.Message("Not working yet.", MessageTypeDefOf.RejectInput, true);
return true;
}

public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish)
public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish)
{
base.DeSpawn(mode);

beam?.Dispose();
beam = null;
}

private void StartAttackSequence(LocalTargetInfo target)
private void StartAttackSequence(GlobalTargetInfo global, LocalTargetInfo local)
{
if (!target.IsValid)
if (!local.IsValid && !global.IsValid)
{
Log.Error($"Tried to start M3G_UMIN attack with invalid target(s): Local {local}, Global {global}.");
return;
}
if (local.IsValid && global.IsValid)
{
Log.Error($"Tried to start M3G_UMIN attack with invalid target {target}.");
Log.Error("Passed in valid global and local targets to attack sequence. This is not valid. Must be one or the other.");
return;
}

// Set local target.
this.localTarget = target;
lastKnownThingLoc = target.Cell;
if (local.IsValid)
{
Log.Message("Starting local attack.");
this.localTarget = local;
lastKnownThingLoc = local.Cell;
}
//Set global target.
if (global.IsValid)
{
Log.Message("Starting global attack.");
this.globalTarget = global;
lastKnownThingLoc = global.Cell;
}

// Enter the charging phase.
IsChargingUp = true;
Expand All @@ -320,26 +368,68 @@ private void StartPowerUpSequence()

private void StartRealAttack()
{
bool cast = true;
if (globalTarget.IsValid)
{
Log.Message($"Using global target to create local target. ({globalTarget})");
if (globalTarget.Map == null)
{
if(globalTargetMapLastKnown != null)
{
Log.Warning($"Global target has null map (most likely missing pawn target). Falling back to last known map ({globalTargetMapLastKnown}).");
localTarget = new LocalTargetInfo(lastKnownThingLoc);
globalTarget = new GlobalTargetInfo(lastKnownThingLoc, globalTargetMapLastKnown);
}
else
{
Log.Error("Global target is valid, but has null map. Last known map is also null. Probably caused by map being unloaded prematurely, or immediately after the updated that added this feature. Strike cancelled to avoid damage to wrong map.");
cast = false;
}
}
else
{
// Convert to local target. Note that Thing may be non-null be destroyed, that is handled below.
localTarget = globalTarget.HasThing ? new LocalTargetInfo(globalTarget.Thing) : new LocalTargetInfo(globalTarget.Cell);
}
}
// Make sure that the local target is completely valid: it is possible that thing is destroyed and Target.IsValid is still true.
if (localTarget.HasThing && localTarget.ThingDestroyed)
{
Log.Warning($"M3G_UMIN appears to have been targeting a Thing but that Thing was destroyed. Using last known location: {lastKnownThingLoc}");
localTarget = new LocalTargetInfo(lastKnownThingLoc);
}

// Spawn sky beam of death.
AttackVerb.TryStartCastOn(localTarget);
if (cast)
{
bool worked = AttackVerb.TryStartCastOn(localTarget);
if (!worked)
{
Log.Error("Megumin verb cast failed!");
cast = false;
}
}

if (!cast)
{
// Cast failed. Stop be beam effect immediately.
OnStrikeEnd(null);
}


chargeEffect?.Stop(true, ParticleSystemStopBehavior.StopEmitting);

// Put on cooldown.
CooldownTicks = COOLDOWN_TICKS;

// Delete target position.
localTarget = null;
lastKnownThingLoc = IntVec3.Zero;
localTarget = LocalTargetInfo.Invalid;
globalTarget = GlobalTargetInfo.Invalid;
lastKnownThingLoc = IntVec3.Invalid;
globalTargetMapLastKnown = null;

// Spawn a solar flare event on the map that it was fired from.
if (DoSolarFlare)
if (DoSolarFlare && cast)
{
IncidentParms param = new IncidentParms();
param.forced = true;
Expand Down Expand Up @@ -412,7 +502,8 @@ public override string GetInspectString()
public override void ExposeData()
{
base.ExposeData();
Scribe_TargetInfo.Look(ref localTarget, "localTarget");
Scribe_TargetInfo.Look(ref localTarget, "localTarget", LocalTargetInfo.Invalid);
Scribe_TargetInfo.Look(ref globalTarget, "globalTarget", GlobalTargetInfo.Invalid);
Scribe_Values.Look(ref CooldownTicks, "cooldownTicks");
Scribe_Values.Look(ref PoweringUpTicks, "powerUpTicks");
Scribe_Values.Look(ref isChargingUp, "isChargingUp");
Expand All @@ -437,7 +528,16 @@ public override void Tick()
{
base.Tick();

if (localTarget != null && localTarget.HasThing && !localTarget.ThingDestroyed)
// Save the last known global target map. This is necessary for when targeting a pawn that is destroyed in a foreign map.
// Note that this isn't a perfect fix: this isn't saved, so reloading the game will still cause a 'fake strike' where the laser will not spawn.
if (globalTarget.IsValid && globalTarget.Map != null)
{
globalTargetMapLastKnown = globalTarget.Map;
}

if (globalTarget.HasThing && !globalTarget.ThingDestroyed)
lastKnownThingLoc = globalTarget.Cell;
else if (localTarget.HasThing && !localTarget.ThingDestroyed)
lastKnownThingLoc = localTarget.Cell;

beam?.Tick();
Expand Down Expand Up @@ -491,6 +591,15 @@ public override void Tick()

if (this.Spawned && !this.Destroyed)
this.GunComp?.verbTracker?.VerbsTick();

if(this.chargeEffect != null)
{
bool isActive = chargeEffect.gameObject.activeSelf;
bool shouldBeActive = Find.CurrentMap == this.Map;

if(isActive != shouldBeActive)
chargeEffect.gameObject.SetActive(shouldBeActive);
}
}

/// <summary>
Expand Down
3 changes: 2 additions & 1 deletion Source/AntimatterAnnihilation/Content.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static class Content
public static Texture2D Expand, Collapse;
public static Texture2D PowerNetGraph;
public static Texture2D PowerLevelLow, PowerLevelMedium, PowerLevelHigh;
public static Texture2D AutoAttackIcon, CancelIcon, ArrowIcon;
public static Texture2D AutoAttackIcon, CancelIcon, ArrowIcon, GlobalStrikeIcon;

static Content()
{
Expand All @@ -40,6 +40,7 @@ static Content()
AutoAttackIcon = ContentFinder<Texture2D>.Get("AntimatterAnnihilation/UI/AutoAttackIcon");
CancelIcon = ContentFinder<Texture2D>.Get("AntimatterAnnihilation/UI/CancelIcon");
ArrowIcon = ContentFinder<Texture2D>.Get("AntimatterAnnihilation/UI/ArrowIcon");
GlobalStrikeIcon = ContentFinder<Texture2D>.Get("AntimatterAnnihilation/UI/GlobalStrikeIcon");

ModCore.Trace("Loaded content.");
}
Expand Down
Loading

0 comments on commit 493a2db

Please sign in to comment.