diff --git a/OpenRA.Mods.Cnc/Activities/Infiltrate.cs b/OpenRA.Mods.Cnc/Activities/Infiltrate.cs index 05cb5bf719..724998b3f1 100644 --- a/OpenRA.Mods.Cnc/Activities/Infiltrate.cs +++ b/OpenRA.Mods.Cnc/Activities/Infiltrate.cs @@ -64,9 +64,6 @@ namespace OpenRA.Mods.Cnc.Activities foreach (var t in targetActor.TraitsImplementing()) t.Infiltrated(targetActor, self, infiltrates.Info.Types); - var exp = self.Owner.PlayerActor.TraitOrDefault(); - exp?.GiveExperience(infiltrates.Info.PlayerExperience); - if (!string.IsNullOrEmpty(infiltrates.Info.Notification)) Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", infiltrates.Info.Notification, self.Owner.Faction.InternalName); diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForCash.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForCash.cs index e4b92f56db..30d7e9d5e3 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForCash.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForCash.cs @@ -33,6 +33,12 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Maximum amount of funds which will be stolen.")] public readonly int Maximum = int.MaxValue; + [Desc("Experience to grant to the infiltrating player.")] + public readonly int PlayerExperience = 0; + + [Desc("Experience to grant to the infiltrating player based on cash stolen.")] + public readonly int PlayerExperiencePercentage = 0; + [NotificationReference("Speech")] [Desc("Sound the victim will hear when they get robbed.")] public readonly string InfiltratedNotification = null; @@ -74,6 +80,8 @@ namespace OpenRA.Mods.Cnc.Traits targetResources.TakeCash(toTake); spyResources.GiveCash(toGive); + infiltrator.Owner.PlayerActor.TraitOrDefault()?.GiveExperience(info.PlayerExperience + toTake * info.PlayerExperiencePercentage / 100); + if (info.InfiltratedNotification != null) Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.InfiltratedNotification, self.Owner.Faction.InternalName); diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForDecoration.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForDecoration.cs index eccca9e999..f402259e44 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForDecoration.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForDecoration.cs @@ -24,6 +24,9 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("The `TargetTypes` from `Targetable` that are allowed to enter.")] public readonly BitSet Types = default; + [Desc("Experience to grant to the infiltrating player.")] + public readonly int PlayerExperience = 0; + public override object Create(ActorInitializer init) { return new InfiltrateForDecoration(init.Self, this); } } @@ -43,6 +46,8 @@ namespace OpenRA.Mods.Cnc.Traits if (!info.Types.Overlaps(types)) return; + infiltrator.Owner.PlayerActor.TraitOrDefault()?.GiveExperience(info.PlayerExperience); + infiltrators.Add(infiltrator.Owner); } diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForExploration.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForExploration.cs index cd6cb7bf71..23e8037bcc 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForExploration.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForExploration.cs @@ -22,6 +22,9 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("The `TargetTypes` from `Targetable` that are allowed to enter.")] public readonly BitSet Types = default; + [Desc("Experience to grant to the infiltrating player.")] + public readonly int PlayerExperience = 0; + [NotificationReference("Speech")] [Desc("Sound the victim will hear when they get sabotaged.")] public readonly string InfiltratedNotification = null; @@ -62,6 +65,8 @@ namespace OpenRA.Mods.Cnc.Traits TextNotificationsManager.AddTransientLine(info.InfiltratedTextNotification, self.Owner); TextNotificationsManager.AddTransientLine(info.InfiltrationTextNotification, infiltrator.Owner); + infiltrator.Owner.PlayerActor.TraitOrDefault()?.GiveExperience(info.PlayerExperience); + infiltrator.Owner.Shroud.Explore(self.Owner.Shroud); var preventReset = self.Owner.PlayerActor.TraitsImplementing() .Any(p => p.PreventShroudReset(self)); diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForPowerOutage.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForPowerOutage.cs index 81ab3756be..90f42e5c58 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForPowerOutage.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForPowerOutage.cs @@ -23,6 +23,9 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Measured in ticks.")] public readonly int Duration = 500; + [Desc("Experience to grant to the infiltrating player.")] + public readonly int PlayerExperience = 0; + [NotificationReference("Speech")] [Desc("Sound the victim will hear when they get sabotaged.")] public readonly string InfiltratedNotification = null; @@ -65,6 +68,8 @@ namespace OpenRA.Mods.Cnc.Traits TextNotificationsManager.AddTransientLine(info.InfiltratedTextNotification, self.Owner); TextNotificationsManager.AddTransientLine(info.InfiltrationTextNotification, infiltrator.Owner); + infiltrator.Owner.PlayerActor.TraitOrDefault()?.GiveExperience(info.PlayerExperience); + playerPower.TriggerPowerOutage(info.Duration); } diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPower.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPower.cs index 8d0b1b815e..db51bbd950 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPower.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPower.cs @@ -24,6 +24,9 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("The `TargetTypes` from `Targetable` that are allowed to enter.")] public readonly BitSet Types = default; + [Desc("Experience to grant to the infiltrating player.")] + public readonly int PlayerExperience = 0; + [NotificationReference("Speech")] [Desc("Sound the victim will hear when technology gets stolen.")] public readonly string InfiltratedNotification = null; @@ -64,6 +67,8 @@ namespace OpenRA.Mods.Cnc.Traits TextNotificationsManager.AddTransientLine(info.InfiltratedTextNotification, self.Owner); TextNotificationsManager.AddTransientLine(info.InfiltrationTextNotification, infiltrator.Owner); + infiltrator.Owner.PlayerActor.TraitOrDefault()?.GiveExperience(info.PlayerExperience); + infiltrator.World.AddFrameEndTask(w => w.CreateActor(info.Proxy, new TypeDictionary { new OwnerInit(infiltrator.Owner) diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPowerReset.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPowerReset.cs index bae7980a37..14d21e3ad9 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPowerReset.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPowerReset.cs @@ -25,6 +25,9 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Sound the victim will hear when they get sabotaged.")] public readonly string InfiltratedNotification = null; + [Desc("Experience to grant to the infiltrating player.")] + public readonly int PlayerExperience = 0; + [Desc("Text notification the victim will see when they get sabotaged.")] public readonly string InfiltratedTextNotification = null; @@ -61,6 +64,8 @@ namespace OpenRA.Mods.Cnc.Traits TextNotificationsManager.AddTransientLine(info.InfiltratedTextNotification, self.Owner); TextNotificationsManager.AddTransientLine(info.InfiltrationTextNotification, infiltrator.Owner); + infiltrator.Owner.PlayerActor.TraitOrDefault()?.GiveExperience(info.PlayerExperience); + var manager = self.Owner.PlayerActor.Trait(); var powers = manager.GetPowersForActor(self).Where(sp => !sp.Disabled); foreach (var power in powers) diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForTransform.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForTransform.cs index b5bce8512a..b07969c132 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForTransform.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForTransform.cs @@ -28,6 +28,9 @@ namespace OpenRA.Mods.Cnc.Traits public readonly bool SkipMakeAnims = true; + [Desc("Experience to grant to the infiltrating player.")] + public readonly int PlayerExperience = 0; + [Desc("The `TargetTypes` from `Targetable` that are allowed to enter.")] public readonly BitSet Types = default; @@ -61,6 +64,8 @@ namespace OpenRA.Mods.Cnc.Traits if (facing != null) transform.Facing = facing.Facing; + infiltrator.Owner.PlayerActor.TraitOrDefault()?.GiveExperience(info.PlayerExperience); + self.QueueActivity(false, transform); } } diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs index 6ccd29e299..613188646d 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs @@ -44,9 +44,6 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Text notification to display when a target is infiltrated.")] public readonly string TextNotification = null; - [Desc("Experience to grant to the infiltrating player.")] - public readonly int PlayerExperience = 0; - [CursorReference] [Desc("Cursor to display when able to infiltrate the target actor.")] public readonly string EnterCursor = "enter"; diff --git a/OpenRA.Mods.Common/UpdateRules/Rules/20210321/RemoveExperienceFromInfiltrates.cs b/OpenRA.Mods.Common/UpdateRules/Rules/20210321/RemoveExperienceFromInfiltrates.cs new file mode 100644 index 0000000000..9d66ef62ec --- /dev/null +++ b/OpenRA.Mods.Common/UpdateRules/Rules/20210321/RemoveExperienceFromInfiltrates.cs @@ -0,0 +1,48 @@ +#region Copyright & License Information +/* + * Copyright (c) The OpenRA Developers and Contributors + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System.Collections.Generic; + +namespace OpenRA.Mods.Common.UpdateRules.Rules +{ + public class RemoveExperienceFromInfiltrates : UpdateRule + { + public override string Name => "Removes PlayerExperience property from Infiltrates."; + + public override string Description => "Infiltrates property PlayerExperience was removed, it was replaced by adding PlayerExperience to all InfiltrateFor* Traits."; + + readonly List locations = new(); + + public override IEnumerable AfterUpdate(ModData modData) + { + if (locations.Count > 0) + yield return "The 'PlayerExperience' fields have been removed from the 'Infiltrates' trait\n" + + "and added to InfiltrateFor* traits. If you want to keep 'PlayerExperience' you will\n" + + "need to add it to each of the InfiltrateFor* traits. Properties removed from:\n" + + UpdateUtils.FormatMessageList(locations); + + locations.Clear(); + } + + public override IEnumerable UpdateActorNode(ModData modData, MiniYamlNode actorNode) + { + var removed = false; + foreach (var node in actorNode.ChildrenMatching("Infiltrates")) + if (node.RemoveNodes("PlayerExperience") > 0) + removed = true; + + if (removed) + locations.Add($"{actorNode.Key} ({actorNode.Location.Filename})"); + + yield break; + } + } +} diff --git a/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs b/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs index 4d71409702..f339fb2828 100644 --- a/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs +++ b/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs @@ -98,6 +98,7 @@ namespace OpenRA.Mods.Common.UpdateRules new RemoveSequenceHasEmbeddedPalette(), new RenameContrailWidth(), new RemoveNegativeSequenceLength(), + new RemoveExperienceFromInfiltrates(), }) }; diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index b840cca23f..978add4fab 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -1289,6 +1289,7 @@ Condition: power-outage InfiltrateForPowerOutage: Types: SpyInfiltrate + PlayerExperience: 10 Power: RequiresCondition: !disabled diff --git a/mods/ra/rules/disable-player-experience.yaml b/mods/ra/rules/disable-player-experience.yaml index 74b9bd07e9..0f79b7ef98 100644 --- a/mods/ra/rules/disable-player-experience.yaml +++ b/mods/ra/rules/disable-player-experience.yaml @@ -10,19 +10,9 @@ E6: Captures: PlayerExperience: 0 -SPY: - Infiltrates: - PlayerExperience: 0 - -MECH: - Captures: - PlayerExperience: 0 - THF: Captures: PlayerExperience: 0 - Infiltrates: - PlayerExperience: 0 SPEN: RepairsUnits: @@ -39,3 +29,64 @@ FIX: TRUK: DeliversCash: PlayerExperience: 0 + +^InfiltratableFake: + InfiltrateForDecoration: + PlayerExperience: 0 + +MSLO: + InfiltrateForSupportPowerReset: + PlayerExperience: 0 + +SPEN: + InfiltrateForSupportPower: + PlayerExperience: 0 + +SYRD: + InfiltrateForSupportPower: + PlayerExperience: 0 + +IRON: + InfiltrateForSupportPowerReset: + PlayerExperience: 0 + +DOME: + InfiltrateForExploration: + PlayerExperience: 0 + +ATEK: + InfiltrateForSupportPowerReset: + PlayerExperience: 0 + +WEAP: + InfiltrateForSupportPower: + PlayerExperience: 0 + +PROC: + InfiltrateForCash: + PlayerExperience: 0 + PlayerExperiencePercentage: 0 + +HPAD: + InfiltrateForSupportPower: + PlayerExperience: 0 + +AFLD: + InfiltrateForSupportPower: + PlayerExperience: 0 + +POWR: + InfiltrateForPowerOutage: + PlayerExperience: 0 + +APWR: + InfiltrateForPowerOutage: + PlayerExperience: 0 + +BARR: + InfiltrateForSupportPower: + PlayerExperience: 0 + +TENT: + InfiltrateForSupportPower: + PlayerExperience: 0 diff --git a/mods/ra/rules/infantry.yaml b/mods/ra/rules/infantry.yaml index 7bc5d2e32d..bad73c0746 100644 --- a/mods/ra/rules/infantry.yaml +++ b/mods/ra/rules/infantry.yaml @@ -351,7 +351,6 @@ SPY: Types: SpyInfiltrate Notification: BuildingInfiltrated TextNotification: Building infiltrated. - PlayerExperience: 10 AutoTarget: InitialStance: HoldFire InitialStanceAI: HoldFire @@ -542,7 +541,6 @@ MECH: Types: Husk ValidRelationships: Ally EnterCursor: goldwrench - PlayerExperience: 10 WithInfantryBody: IdleSequences: idle DefaultAttackSequence: repair @@ -648,7 +646,6 @@ THF: Types: ThiefInfiltrate Notification: BuildingInfiltrated TextNotification: Building infiltrated. - PlayerExperience: 10 Voiced: VoiceSet: ThiefVoice -TakeCover: diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml index 55be962783..df5a338bda 100644 --- a/mods/ra/rules/structures.yaml +++ b/mods/ra/rules/structures.yaml @@ -65,6 +65,7 @@ MSLO: SupportPowerChargeBar: InfiltrateForSupportPowerReset: Types: SpyInfiltrate + PlayerExperience: 10 Targetable: TargetTypes: GroundActor, C4, DetonateAttack, Structure, SpyInfiltrate Power: @@ -128,6 +129,7 @@ SPEN: InfiltrateForSupportPower: Proxy: powerproxy.sonarpulse Types: SpyInfiltrate + PlayerExperience: 10 Valued: Cost: 800 Tooltip: @@ -272,6 +274,7 @@ SYRD: InfiltrateForSupportPower: Proxy: powerproxy.sonarpulse Types: SpyInfiltrate + PlayerExperience: 10 Buildable: Queue: Building BuildPaletteOrder: 40 @@ -451,6 +454,7 @@ IRON: SupportPowerChargeBar: InfiltrateForSupportPowerReset: Types: SpyInfiltrate + PlayerExperience: 10 Targetable: TargetTypes: GroundActor, C4, DetonateAttack, Structure, SpyInfiltrate Power: @@ -547,6 +551,7 @@ PDOX: SupportPowerChargeBar: InfiltrateForSupportPowerReset: Types: SpyInfiltrate + PlayerExperience: 10 Targetable: TargetTypes: GroundActor, C4, DetonateAttack, Structure, SpyInfiltrate Power: @@ -693,6 +698,7 @@ DOME: RequiresCondition: !jammed && !disabled InfiltrateForExploration: Types: SpyInfiltrate + PlayerExperience: 10 Power: Amount: -40 ProvidesPrerequisite@buildingname: @@ -1016,6 +1022,7 @@ ATEK: SupportPowerChargeBar: InfiltrateForSupportPowerReset: Types: SpyInfiltrate + PlayerExperience: 10 Targetable: TargetTypes: GroundActor, C4, DetonateAttack, Structure, SpyInfiltrate Power: @@ -1129,6 +1136,7 @@ WEAP: InfiltrateForSupportPower: Proxy: vehicles.upgraded Types: SpyInfiltrate + PlayerExperience: 10 -ActorPreviewPlaceBuildingPreview: SequencePlaceBuildingPreview: Sequence: place @@ -1291,6 +1299,8 @@ PROC: Facing: 256 InfiltrateForCash: Percentage: 50 + PlayerExperience: 5 + PlayerExperiencePercentage: 1 Types: SpyInfiltrate, ThiefInfiltrate InfiltratedNotification: CreditsStolen InfiltratedTextNotification: Credits stolen. @@ -1354,6 +1364,8 @@ SILO: Range: 4c0 InfiltrateForCash: Percentage: 50 + PlayerExperience: 5 + PlayerExperiencePercentage: 1 Types: ThiefInfiltrate InfiltratedNotification: CreditsStolen InfiltratedTextNotification: Credits stolen. @@ -1464,6 +1476,7 @@ HPAD: InfiltrateForSupportPower: Proxy: aircraft.upgraded Types: SpyInfiltrate + PlayerExperience: 10 AFLD: Inherits: ^Building @@ -1613,6 +1626,7 @@ AFLD: InfiltrateForSupportPower: Proxy: aircraft.upgraded Types: SpyInfiltrate + PlayerExperience: 10 WithResupplyAnimation: RequiresCondition: !build-incomplete @@ -1826,6 +1840,7 @@ BARR: InfiltrateForSupportPower: Proxy: barracks.upgraded Types: SpyInfiltrate + PlayerExperience: 10 Targetable: TargetTypes: GroundActor, C4, DetonateAttack, Structure, SpyInfiltrate @@ -1999,6 +2014,7 @@ TENT: InfiltrateForSupportPower: Proxy: barracks.upgraded Types: SpyInfiltrate + PlayerExperience: 10 Targetable: TargetTypes: GroundActor, C4, DetonateAttack, Structure, SpyInfiltrate diff --git a/mods/ts/rules/gdi-structures.yaml b/mods/ts/rules/gdi-structures.yaml index 1ad859eb0d..519c7634a4 100644 --- a/mods/ts/rules/gdi-structures.yaml +++ b/mods/ts/rules/gdi-structures.yaml @@ -457,6 +457,7 @@ GARADR: ProvidesRadar: RequiresCondition: !disabled && !empdisable InfiltrateForExploration: + PlayerExperience: 10 DetectCloaked: Range: 10c0 RenderDetectionCircle: diff --git a/mods/ts/rules/nod-structures.yaml b/mods/ts/rules/nod-structures.yaml index 219348ef17..6986b94333 100644 --- a/mods/ts/rules/nod-structures.yaml +++ b/mods/ts/rules/nod-structures.yaml @@ -336,6 +336,7 @@ NARADR: ProvidesRadar: RequiresCondition: !disabled && !empdisable InfiltrateForExploration: + PlayerExperience: 10 DetectCloaked: Range: 10c0 RenderDetectionCircle: