Self-aware units

whoward69

DLL Minion
Joined
May 30, 2011
Messages
8,699
Location
Near Portsmouth, UK
Scratching an itch!

So Hambril's mention of his dislike for loops within loops when checking units in his Follow and Cover mod got me to wondering if worker units could somehow be self-aware of their own movement and then use that knowledge to get the combat unit to follow them.

The results of scratching that itch are attached. The code is functional but incomplete (there is no UI to assign Escorts to Workers, or any persistance code for save/load of the game). But rather than ditch it (I'm not going to develop this further) I'll make it available to anybody that's interested.

Spoiler :
Code:
--
-- Workers, Escorts and Blockers
--
-- Workers do work, travel to their next assignment and do more work.  They may have an assigned Escort.
-- Escorts are assigned to a Worker, move as they move, fortify when they are working.  They may be being blocked.
-- Blockers are units that are stopping Escorts from entering a tile.
--
-- While we will keep with the terms "Worker" and "Escort" they are really just "Leader" and "Follower".
-- We could just as easily have a Settler follow a Warrior as a Warrior escort a Settler, and units may have multiple roles at the same time.
-- This makes more sense if you set a Great Admiral to follow a Destroyer which itself is following an embarked Settler,
-- or consider that an escort being blocked may itself be blocking another escort.
--

--
-- So what's going on ...
--
-- Firstly we have three primary classes - Worker, Escort and Blocker - inheriting from an (abstract) MoveAwareUnit base class.
-- Each of these wraps a Civ5 Unit object.  An individual Civ5 Unit may be all, any or none of these three classes.
--
-- Secondly we have three class managers - WorkerManager, EscortManager and BlockerManager - again inheriting from an (abstract) MoveAwareUnitManager class
-- The managers are responsible for "wrapping-up" the Civ5 Unit into the required class, in such a way that there is only ever one copy of the wrapped Unit.
-- In other words, if you call WorkerManger:Get() multiple times for the same Civ5 Unit (pWorker) you always get the same wrapper object.
--

--
-- So what do the primary classes do ...
--
-- worker = WorkerManager:Get(pWorker) pretty much does nothing useful on its own.
-- worker:AddEscort(pEscort) assigns the Civ5 Unit pEscort to guard pWorker.
-- Adding an escort makes the worker self-aware of it's movement, as the worker moves it tells its escort to move to the same plot.
-- The escort will attempt to move to the worker's plot, which it will either be able to do, or will be blocked by another unit.
-- If the escort is blocked, it wraps each blocking unit as a Blocker and adds itself to the list of unit(s) being blocked.
-- Blockers are also self-aware of their movement, so when they move away from the tile, they inform the escort(s) they are blocking that the tile is vacant.
--
-- The interesting methods are AddEscort() on Worker and MovedTo()/MoveTo() on all three classes
--

--
-- A note on GameEvents.UnitSetXY ...
--
-- The initial design added a movement listener for every MoveAwareUnit, which in an efficient system would be the nicely encapsulated way.
-- However, Civ 5 events are far from efficient, and receiving every movement step for every Civ 5 Unit into every MoveAwareUnit is grossly inefficient.
-- There is also the issue of apparently not being able to remove anonymous functions as event listeners (see http://forums.civfanatics.com/showthread.php?t=493645)
--
-- The new Civ 5 limitations aware design registers one GameEvents.UnitSetXY listener (in the MoveAwareUnitManager class)
-- that then distributes movement events directly to the associated MoveAwareUnit
-- Having a factory/manager understands it objects inner workings is NOT good OO design, but hey,
-- sometimes we have to make the "best" design decision and not the "correct" one!
--
 

Attachments

  • FollowMe.lua.txt
    17.2 KB · Views: 89
I will definitely include (with your permission) your logic or a version there of, on all the domain checks and embarked checks and such. Nice work. It's interesting being able to set something to follow something that is following something, and I want to explore that further. The idea that a civilian can escort a combat unit though I can't make sense out of. Only two can be on a square and one has to lead and one follow. Why ever let the civilian lead?
 
Why ever let the civilian lead?

Because that's what your workers do ;)

There is no reason for workers (civilians) to lead and combat troops to follow - it's just an unnecessary constraint. Any unit should be able to follow any other unit as far as the code is concerned. It's up to the modder to place meaning onto the movement "Guards follow and protect Workers" or ...

Switch to Great Generals/Admirals following units - how often have you moved a ship, attacked with it and then thought "Rats! I should have moved the Admiral up at the same time to get the 15% bonus". Set the Admiral to follow the Battleship and you'll never have that problem again ... "Admiral follows the flag-ship"
 
Sort of. Only combat units can be automated to start the behavior in my system. The workers will push the combat units in DoXY which I consider an unavoidable kludge, but other than that the combat units actively scan for nearby civilians to cover and actively move to cover them in DoTurn. If you removed the logic in DoXY the combat unit would be doing all the work, just lagging one turn :( But, you are right, it doesn't really matter. Except...

I will have to think about how to integrate this back into my interface... not all the units you talk about can be automated, so I'd be looking at having to add buttons in unitpanel... or an interface
redesign of some kind. Have you seen the interesting DLL work that S3rgeus was discussing in this thread?
 
Would be nice if Great Generals could be told to follow a unit. I'm not thinking so much about human player as AI: I kill dozens of these in a game (not much fun after about 4 or 5 of them). For GGs in particular, I often think that the game should have a "Join Unit" mechanism. The current system is just:
  1. Annoying for human player. I get no great feeling of strategy from driving these around and remembering to guard them.
  2. Another "feature" that the AI doesn't understand.
 
I've noticed several cases of modders extending existing classes like player. If overhead only kicks in when a button is clicked (to start the process) or when a unit is 'activated' by another unit, could all this be folded into unit itself? e.g. unit:AddEscort etc...

And would that take care of our factory manager understanding it's object's inner workings issue?
 
That's almost a religious discussion ;)

We can't create super-classes of the Lua objects (as they are only wrappers around / delegates for the C++ objects) so "black-box" OO design patterns suggest that's not good practice
 
That's almost a religious discussion ;)

We can't create super-classes of the Lua objects (as they are only wrappers around / delegates for the C++ objects) so "black-box" OO design patterns suggest that's not good practice

So we've got not good practice in either direction. I guess I think of ease of use, and creation, and maintenance at that point, since I'm the one that will have to maintain it. I'll just go with the style that feels most natural to me, unless you want to do some preaching on this religion of yours? :) :)
 
So we've got not good practice in either direction.

If you're referring to the comments in my FollowMe code, that's not solvable with either implementation in Lua, you'd need to go into the DLL for that
 
I guess I'm just trying to better understand what you meant by religious discussion. I am trying to decide how I want to move forward with this, and I like the feel/idea of just being able to say unit:blah, if it's just a half dozen of one six of the other thing. I know we are faking oo either way. But if there are major reasons for going one way or the other I'd like to know :)
 
I'll give you just the one major reason. You have no control whatsoever over what any other part of the system may also be doing with the Lua Unit class, so unless you are talking about a "total conversion" style mod (where you do have complete control) you cannot guarentee that nothing else will corrupt your extension and/or that your extension works correctly in the first place in all situations / edge cases. That is the whole purpose of delegates/wrappers in OO design, is to give you those guarentees.
 
Thanks :) We're talking design patterns. Awesome! That is something I've really wanted to get caught up and current on again. I hadn't considered doing a large(ish) project like this with design patterns. That will be very cool. I can even turn it into a Modder's Guide to Design Patterns in lua, tutorial when I'm done.

That's both the good and the bad of 4GLs. They let you get away with stuff.
 
Speaking of scratching an itch - this thought keeps dancing through my mind; magnets.

If each unit was aware not just of who it wanted to attach to, but how (what side of the hex - 0 being same hex)...

Why? Formations. The questions is are formations useful. I think so, I'm always trying to create a decent one manually.

Not exactly a "I want to scratch an itch so here is some completed complex code". Buy hey, design has value too!
 
Okay, I'm trying to figure out from a design pattern standpoint who, or what, would be aware of the formation. A unit can tell another unit what hex side it wishes to attach to, but to have it be aware of it's position in a formation and the next part of the formation it needs to build, I do not like.

Hmmmm... The next part of a formation it needs to build... there's something in that statement... that seems to call for some sort of unit manager class. That might be the ticket.
 
That creates too much duplicated logic for me. A single manager class can be formation aware, receive movement notices from units, and set their magnetism properties as needed. Okay, edited to add you can have multiple armies. Thus it would not be a single instance of the manager class. It would still be less then adding the logic to the unit itself, and more logical I think. The class doesn't really care if it's managing civilians, combat units, or some mix. It's just trying to match units to a pattern (formation).

It would have to communicate with other manager instances though, so it can inform them of the units it's interested in managing and avoid 'fighting' over them. All I need now is to write a simple (j/king) algorithm that turns a sparse distributed pattern of units into a tight formation of units in the minimal number of turns while co-operating with other algorithms trying to do the same thing, all in mountainous terrain while being attacked. Let's see Whoward link an existing post for that one! :p

I'm just going to keep editing this post, sort of a developers diary. That seems less like talking to myself.

If we assume that we are going to let all the pathing be done by Firaxes (badly, but done) then the manager class simply needs to decide what units to magnetize and how for a given formation. Still a lot of work, but much less work.

If I use one of Whowad's plot iterators, I can find nearby units for my command unit (oh yeah, while you where not in the loop I decided a formation needs a command unit, leader, whatever) to magnetize. It would make it's best guess at where to put the unit in the formation, and adjust as it finds new units. Once the formation is created, the magnetism of the units keep it alive. I still need to work out the UI, and looks up some good pattern/formation code to use as a start...

I think the UI is going to require a DLL mode which I can hopeful talk a certain someone into. Ideally we'd have a button that worked much like the build button (a popout button menu on unitpanel with choices for formation types). Since I already need this functionality for Follow and Cover it's double useful. Otherwise I'm going to have to get real creative and have a lot of headache doing some major rewrites on unitpanel.lua/xml and it's little friend.

The button would change to 'disband formation' when a formation is active. Or perhaps disband will simply be one of the actions in the new popout ''build" menu, since that would allow switching of formations without disbanding. I think I like that better.

Since we don't have the ability to select multiple units, the leader unit that starts the formation will have to choose between preset formation sizes. e.g. 5 man flying V. 4 man double row.

Smart placement within the formation of melee vs. range units would be a possibly nice feature also. Perhaps that should be built into the formation types. If the leader can't pick up enough units of the type it needs within range, we'll have to decide on behavior. There are a number of ways we could handle it; proceed with a partial formation; fail with an error message; wait and broadcast a request for more of a specific unit type... with the right information we could even 'grey out' certain formation types if the units weren't in range.
 
I've almost completed the first step of this, which is to integrate a system of leaders and followers based on the code provided by Whoward (though modified so don't blame him) into Follow and Cover. My intention is to drop unit panel and hook in a dll mod and then I will post the results. I won't 'go live' with them until I've gotten feedback and testing. The new model uses the concept/term magnetic/magnetizing, etc, to describe the mechanism by which units 'attract' each other to follow and cover. It also prepares me to work on an even more ambitious outgrowth of Follow and Cover; Armies and Formations.

C:\Program Files (x86)\Steam\SteamApps\common\Sid Meier's Civilization V\Assets\Units\UnitFormations.xml.... well now isn't that interesting.
 
Okay, finally circled back to this, as I think Follow and Cover can become a real hidden gem if I polish it enough :)

I have a design question. Right now (and arbitrarily as Whoward points out) we have workers and escorts, could just as easily be leaders and followers. In fact, in the code I am pulling together it is leader and follower.

However, workers it turns out have special behaviors I have to account for. Mainly, they run away when threatened and don't care if they have cover or not. Interestingly they will seek cover but not recognize they are already covered.

This means I've had to painfully micromanage workers (I can't automate them). At least, I can't automate them when they are under threat.

But it occurred to me that if I am going to put in all the code to automate a worker under threat I might as well just take the next step and take over worker automation completely. And after having discovered Whoward's Modular Unit Panel I was further enamored with the idea that I could spiff up workers a little while I was at it, so I added "build only roads" and "build for this city" build mods. This is what has been taking so long for the next release of Follow and Cover - it's a pretty major re-write.

The central design issue I face is how to meld this with the current FollowerManager (formerly WorkerManager).

I am tempted to split into two mods at this point. Follow and Cover, which does exactly that. And Workers Enhanced, which would include the other worker features. (remove panic from covered workers, add extra build options mentioned). My reasoning is this is strictly a worker issue. No other unit in the game is as complicated and it really mucks up the Follow and Cover code. Separating it cleans up things a bit.

One would not be dependent on the other, but they would work together naturally.

I'll probably put them together in a collection and recommend downloading/using them that way.

Anyway, this is the current design path I'm headed down, it seems sound, but feedback is welcome.

Update: Progress! Separating into two mods seems to have really cleared things up in my head.
 
Top Bottom