New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Split "Locomotor" from Mobile #14893
Conversation
b8320d7
to
b2d6818
Compare
I'd be open to try help if I can better understand the intent. Can you explain the desired relationship of Mobile, MobileInfo and MovementClassInfo in your design change here? |
Are you manually testing, or have you been running the "make test" flow? I'm seeing a bunch non-blocking load failures getting logged, when instantiating the new MovementClassInfo at load time. These might be happening upstream of your crashes. _Testing mods... OpenRA.Utility(1,1): Error: Actor type |
@PeterAntal I only migrated RA so far, so test errors in the other mods are to be expected. The idea here is to use a limited number of MovementClass (a different name like MovementType might be better, but this is what I went with, for now) traits residing on the world actor that cache their terrain speed and cost etc. at game start, which Mobile then only needs to access, instead of doing this for every actor with Mobile separately. This is meant to be the first step of #14830. See also the discussions on IRC (log) from yesterday and around 19-20 days ago, or try to get hold of @pchote or @chrisforbes here or on IRC for any additional technical questions (I understand the concept, but I'm not good at explaining the details). |
b2d6818
to
56745b8
Compare
Updated. There are still issues with the TS-specific stuff (at least with Tunnel-, Jumpjet- and SubterraneanConditions), but the other 3 mods now pass all tests and seem to work fine. |
Got it - so, to paraphrase, it sounds like the general aspiration is: I'll see if I can get context on what should be implementing traits of your new MovementClass. |
OpenRA.Mods.Common/Traits/Mobile.cs
Outdated
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, bool checkTransientActors = true) | ||
{ | ||
if (MovementCostForCell(world, cell) == int.MaxValue) | ||
var movementClassInfo = world.WorldActor.TraitsImplementing<MovementClass>().Single(mc => mc.Info.Class == MovementClass).Info; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The worldActor does not itself include MovementClass Trait, which the mobile instances are looking for.
Are you sure a trait on the world the right mechanism for obtaining an instance?
Seems like you want something of a instance management mechanism in relation to the world actor, to request instantiation of the MovementClass, if it doesn't already exist(the world probably shouldn't need to declare what specific Movement Class instances are needed).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OpenRA uses traits on the world to hold various types of global state, including palettes, faction definitions, and references for the in-game UI. Defining the movement types on the world also matches up with the original games (which called them "locomotors"), and makes it more explicit that these are things that should be limited in number.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But please do this as world.WorldActor.Info.TraitInfos<MovementClassInfo>().Single(mc => mc.Class == MovementClass)
in RulesetLoaded
instead of going via the actual world actor, and use the cached version here.
OpenRA.Mods.Common/Traits/Mobile.cs
Outdated
public readonly int WaitAverage = 5; | ||
|
||
public readonly int WaitSpread = 2; | ||
public readonly string MovementClass = "default"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to define a [MovementClassReference]
attribute that can be linted in the same way as actor, weapon, sequence, etc references.
OpenRA.Mods.Common/Traits/Mobile.cs
Outdated
|
||
public bool SharesCell = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be cleaner to use the IRulesetLoaded
to store a reference to the MovementClassInfo
, the same way that we do for weapons in ArmamentInfo
. This can then be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shifting this towards movementclass might imply the plumbing/possibility for multi-cell units which sounds out-of-scope on this run to me though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you mean. This is about code quality, not feature changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that aspect, but not all modders might follow us in that regard though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This variable holds a cached value of the SharesCell
defined on MovementClassInfo
. It is overwritten whenever CanEnterCell
is called, so any values set in the yaml are completely ignored.
I'm not really pleased with the idea to tie crushing abilities to the locomotor tbh. |
The ability to crush is one of the most important parameters in the pathing state. The pathfinder improvements can't happen without it moving. |
Well, Crushable is conditional already so I guess this can go that way then and maybe it's just me who's seeing issues atm. |
315b972
to
b8a9fcd
Compare
Updated, managed to work out all the initial kinks (even tunnels, jumpjets and subterranean units work now). I think this looks good enough now that I can at least remove the help request label. One thing I'd like to get feedback on, though: |
My preference is for |
I don't think what this does and what Locomotor in original games does is the same thing. This is more like the SpeedType. Not sure if is a good name, but i don't think Locomotor is good either. For reference, here is Locomotor's ModEnc page: https://www.modenc.renegadeprojects.com/Locomotors |
That may be the case right now, but there are at least two things to consider:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you mind splitting this up a bit further? These changes should go a long way towards allowing modders to define their own custom movement types without having to modify the common logic.
[RequireExplicitImplementation] | ||
public interface INotifyChangedCustomLayer | ||
{ | ||
void JumpjetLaunch(Actor self); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we please simplify this down to a single CustomLayerChanged(CustomMovementLayerType old, CustomMovementLayerType new)
?
public object Create(ActorInitializer init) { return new GrantConditionOnCustomLayer(init, this); } | ||
} | ||
|
||
public class GrantConditionOnCustomLayer : INotifyCreated, INotifyChangedCustomLayer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and then split this into separate traits for Tunnel/Subterranean/Jumpjet. You could define a single abstract class that is given the CustomLayerMovementType
to check in its ctor from the subclass. The CustomLayerChanged
method in each can then check this value and grant/revoke the condition when needed.
OpenRA.Mods.Common/Traits/Mobile.cs
Outdated
tunnelToken = conditionManager.RevokeCondition(self, tunnelToken); | ||
if (toCell.Layer == CustomMovementLayerType.Tunnel && fromCell.Layer != CustomMovementLayerType.Tunnel) | ||
foreach (var n in notifyCustomLayerChange) | ||
n.TunnelEnter(self); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of the logic here can then go away.
public readonly bool MoveIntoShroud = true; | ||
|
||
[Desc("Can this unit move underground?")] | ||
public readonly bool Subterranean = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please define subclasses for SubterraneanMovementClassInfo
and the other custom types?
The checks for if (mc.Subterranean)
can then be replaced by if (mc is SubterraneanMovementClassInfo)
or
var smc = mc as SubterraneanMovementClassInfo;
if (smc != null)
if the Subterranean specific info is needed.
80093a0
to
230a9ee
Compare
Updated. |
8a2c84f
to
208e068
Compare
Updated again. |
23e7d16
to
21dd914
Compare
Updated and rebased. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍 Any regressions that may be hiding inside here will hopefully be shaken out during playtesting.
void CheckLocomotors(ActorInfo actorInfo, Action<string> emitError, Ruleset rules, string locomotor) | ||
{ | ||
var worldActor = rules.Actors["world"]; | ||
var locomotorInfos = worldActor.TraitInfos<LocomotorInfo>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: You could pull this out of all the foreach loops probably, as this isn't changing during the lint run?
{ | ||
var passable = mi.GetMovementClass(world.Map.Rules.TileSet); | ||
if (!domainIndex.IsPassable(source, target, mi, (uint)passable)) | ||
if (!domainIndex.IsPassable(source, target, li)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neglectible: I'd be nice if this was combined into the if check above.
var grantNode = new MiniYamlNode("GrantConditionOnTunnelLayer", ""); | ||
grantNode.AddNode("Condition", tunnelConditionNode.Value.Value); | ||
addNodes.Add(grantNode); | ||
mobileNode.Value.Nodes.Remove(tunnelConditionNode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This applies to the entire file: You should use the respective Remove
and Add
methods instead of using .Value.Nodes
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, but only In places I was confident about (this rule was pretty much written by @pchote).
OpenRA.Mods.Common/Traits/Mobile.cs
Outdated
if (!string.IsNullOrEmpty(Info.SubterraneanTransitionSound)) | ||
Game.Sound.Play(SoundType.World, Info.SubterraneanTransitionSound); | ||
// The first time SetVisualPosition is called is in the constructor before creation, so we need a null check here as well | ||
if (notifyVisualPositionChanged == null || !notifyVisualPositionChanged.Any()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neglectible: I don't think the .Any
check is needed. When the collection is empty, we'll just loop zero times.
OpenRA.Mods.Common/Traits/Mobile.cs
Outdated
internal readonly TerrainInfo[] TerrainInfos; | ||
internal WorldMovementInfo(World world, MobileInfo info) | ||
var locomotorInfos = rules.Actors["world"].TraitInfos<LocomotorInfo>(); | ||
LocomotorInfo = locomotorInfos.SingleOrDefault(li => li.Name == Locomotor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The part below won't work. SingleOrDefault
will already crash when there are multiple locomotors with the same name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rewrote the entire check. SingleOrDefault didn't return a meaningful error, so I changed it to FirstOrDefault and check the Count later instead.
Needs a rebase. Looks good to me otherwise. 👍 / ✅ |
Add GrantConditionOn*Layer traits This allows to - drop some booleans from Locomotor - drop a good part of the subterranean- and jumpjet-specific code/hacks from Mobile - grant more than 1 condition per layer type (via multiple traits) - easily add more traits of this kind for other layers
Let IsPassable get that info from the locomotor instead.
Rebased and updated. |
This moves a lot of stuff from
Mobile
toLocomotor
world trait(s).The idea is to keep the number of locomotors limited and - in the future - cache/pre-calculate as many things as possible on them, to save performance and make further movement code refactors easier.