Jump to content

Formation Standards (Total War-style flags over battalions)


Recommended Posts

OeoOkdL.png

Standards appearing over battalions looks really nice and do help with gameplay. Now, I wonder how to make them civ-specific. I tried using the waypoint/rally point flag hack in the actor, which names variants based on civ codes ("athen", "spart", "cart", etc.). It works for the waypoint actor (obviously), but not for my formation standard actor. In the above screenshot, you see that the standards pick random variants instead of the civ-specific one. Both flags should be the "athen" variant, but they are not. Also, a new flag variant is chosen each time the formation is created. 

Here is the waypoint flag code, which works:

<?xml version="1.0" encoding="utf-8"?>
<actor version="1">
  <castshadow/>
  <float/>
  <group>
    <variant frequency="100">
      <animations>
        <animation file="mechanical/waypoint_flag_idle.dae" name="Idle" speed="30"/>
      </animations>
      <mesh>props/waypoint_flag.dae</mesh>
    </variant>
  </group>
  <group>
    <variant name="hele">
      <textures>
        <texture file="props/banner_greek.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="pers">
      <textures>
        <texture file="props/banner_persian.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="celt">
      <textures>
        <texture file="props/banner_celt.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="cart">
      <textures>
        <texture file="props/banner_carthage.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="iber">
      <textures>
        <texture file="props/banner_iberians.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="scyth">
      <textures>
        <texture file="props/banner_scythians.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="xion">
      <textures>
        <texture file="props/banner_scythians.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="rome">
      <textures>
        <texture file="props/banner_romans.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="imp">
      <textures>
        <texture file="props/banner_romans.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="spart">
      <textures>
        <texture file="props/banner_spartans.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="mace">
      <textures>
        <texture file="props/banner_macedonians.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="athen">
      <textures>
        <texture file="props/banner_greek.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="brit">
      <textures>
        <texture file="props/banner_celt.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="gaul">
      <textures>
        <texture file="props/banner_celt.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="maur">
      <textures>
        <texture file="props/banner_mauryas.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="ptol">
      <textures>
        <texture file="props/banner_ptolemies.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="sele">
      <textures>
        <texture file="props/banner_seleucids.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="theb">
      <textures>
        <texture file="props/banner_thebans.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="epir">
      <textures>
        <texture file="props/banner_epirotes.png" name="baseTex"/>
      </textures>
    </variant>
    <variant name="han">
      <textures>
	    <texture file="props/banner_chinese.png" name="baseTex"/>
	  </textures>
    </variant>
    <variant name="kush">
      <textures>
	    <texture file="props/banner_kushites.png" name="baseTex"/>
	  </textures>
    </variant>
    <variant name="noba">
      <textures>
	    <texture file="props/banner_kushites.png" name="baseTex"/>
	  </textures>
    </variant>
    <variant name="sueb">
      <textures>
	    <texture file="props/banner_germans.png" name="baseTex"/>
	  </textures>
    </variant>
    <variant name="goth">
      <textures>
	    <texture file="props/banner_norse.png" name="baseTex"/>
	  </textures>
    </variant>
    <variant name="zapo">
      <textures>
	    <texture file="props/banner_maya.png" name="baseTex"/>
	  </textures>
    </variant>
  </group>
  <material>basic_trans.xml</material>
</actor>

 

Here is the Standard code, which does not work:

 

<?xml version="1.0" encoding="utf-8"?>
<actor version="1">
  <castshadow/>
  <float/>
  <group>
    <variant frequency="100">
      <mesh>props/standards/formation_pole.dae</mesh>
      <textures>
        <texture file="props/kart_standard.png" name="baseTex"/>
        <texture file="props/kart_standard_norm.png" name="normTex"/>
        <texture file="props/kart_standard_spec.png" name="specTex"/>
      </textures>
    </variant>
  </group>
  <group>
    <variant name="athen">
      <props>
        <prop actor="props/units/standards/formation_flag_athen.xml" attachpoint="root"/>
      </props>
    </variant>
    <variant name="cart">
      <props>
        <prop actor="props/units/standards/formation_flag_cart.xml" attachpoint="root"/>
      </props>
    </variant>
    <variant name="spart">
      <props>
        <prop actor="props/units/standards/formation_flag_spart.xml" attachpoint="root"/>
      </props>
    </variant>
  </group>
  <material>no_trans_parallax_spec.xml</material>
</actor>

formation_flag_athen:

<?xml version="1.0" encoding="utf-8"?>
<actor version="1">
  <castshadow/>
  <group>
    <variant>
      <mesh>props/standards/formation_flag.dae</mesh>
      <textures>
        <texture file="props/standards/athen_infantry_1.png" name="baseTex"/>
      </textures>
    </variant>
  </group>
  <material>player_trans.xml</material>
</actor>
  • Like 5
Link to comment
Share on other sites

Won't work with a single actor with civ variants inside (as shown in the op).

Tried it with civ-specific actors and making new formations for Athen, as a test. That all works fine, until I realize all the hacking I'll have to do:

  • A flag actor for every civ (had to do that anyway, no problem).
  • Redundant formations for every civ, so that the civ-specific flag actors show up. errr
  • Making sure those formations show up correctly for each unit. So, use a mixin for each civ to pull the right civ-specific formations, then edit the parent template lines of every unit to grab the civ-specific mixin. Editing each civ.json file to list the right civ-specific formations.
  • Then try to fix the very common edge case where you have mercenaries that aren't of the player's civ. Redundant formation icons could show up in the formation UI panel. No idea how to prevent this. If it wasn't for this last bullet, I would proceed, but...

All of the above is an inelegant hack. More elegant is to edit Formations.js so that formations take the civ of the player, so we can just use 1 actor as originally planned (similar to how rally points use the waypoint flag actor with civ variants inside it). I don't see that happening unless the base game wants to add these too. 

Edited by wowgetoffyourcellphone
Link to comment
Share on other sites

for a26 yes.

Commands.js L1654:

			const cmpVisual = Engine.QueryInterface(formationEnt, IID_Visual);
			if (cmpVisual) {
				const civ = QueryPlayerIDInterface(player).GetCiv();
				cmpVisual.SetVariant("animationVariant", civ);
			}

Formation.js LoadFormation function:
 

Formation.prototype.LoadFormation = function(newTemplate)
{
	const newFormation = ChangeEntityTemplate(this.entity, newTemplate);
	let cmpVisual = Engine.QueryInterface(newFormation, IID_Visual);
	if (cmpVisual)
	{
		const cmpNewOwnership = Engine.QueryInterface(newFormation, IID_Ownership);
		const player = cmpNewOwnership.GetOwner();
		const civ = QueryPlayerIDInterface(player).GetCiv();
		cmpVisual.SetVariant("animationVariant", civ);
	}
	return Engine.QueryInterface(newFormation, IID_UnitAI);
};

 

template_formation.xml Commands.js Formation.js

  • Thanks 1
Link to comment
Share on other sites

10 hours ago, Silier said:

for a26 yes.

Commands.js L1654:

			const cmpVisual = Engine.QueryInterface(formationEnt, IID_Visual);
			if (cmpVisual) {
				const civ = QueryPlayerIDInterface(player).GetCiv();
				cmpVisual.SetVariant("animationVariant", civ);
			}

Formation.js LoadFormation function:
 

Formation.prototype.LoadFormation = function(newTemplate)
{
	const newFormation = ChangeEntityTemplate(this.entity, newTemplate);
	let cmpVisual = Engine.QueryInterface(newFormation, IID_Visual);
	if (cmpVisual)
	{
		const cmpNewOwnership = Engine.QueryInterface(newFormation, IID_Ownership);
		const player = cmpNewOwnership.GetOwner();
		const civ = QueryPlayerIDInterface(player).GetCiv();
		cmpVisual.SetVariant("animationVariant", civ);
	}
	return Engine.QueryInterface(newFormation, IID_UnitAI);
};

 

template_formation.xml 2 kB · 0 downloads Commands.js 61 kB · 0 downloads Formation.js 32 kB · 0 downloads

Thank you for this. There's 1 issue:

The first time the formation is created, it chooses a variation randomly, but when choosing a different formation with those same troops it chooses the correct one.

 

Also, this error when playing a game. Happens when formationed units temporarily disband the formation when constructing or grabbing a treasure and then reform the formation afterward:

Spoiler

ERROR: Error in timer on entity 10888, IID105, function TimerHandler: TypeError: cmpFormationUnitAI.GetTargetPositions()[0] is undefined Formation.prototype.MoveMembersIntoFormation@simulation/components/Formation.js:534:61 Timer@simulation/components/UnitAI.js:1011:19 FSM.prototype.ProcessMessage@globalscripts/FSM.js:265:17 UnitAI.prototype.TimerHandler@simulation/components/UnitAI.js:4210:15 Timer.prototype.OnUpdate@simulation/components/Timer.js:139:44

 

Edited by wowgetoffyourcellphone
Link to comment
Share on other sites

8 hours ago, wowgetoffyourcellphone said:

The first time the formation is created, it chooses a variation randomly, but when choosing a different formation with those same troops it chooses the correct one

Also this is strange because Commands file should take care about this case.

Link to comment
Share on other sites

I am also using just those lines of code that you added/changed in the js files, and not the entire file. Mods can do this by using a suffix to the file name (in my case, "_DE") without having to maintain the entire file (some of the component files are quite large). I will try by using the entire files and see if that fixes things?

Link to comment
Share on other sites

ERROR: JavaScript error: simulation/components/Commands.js line 1858 SetGlobal "GetFormationRequirements" called multiple times @simulation/components/Commands.js:1858:8 launchGame@gui/gamesettings/GameSettings.js:128:11 launchGame@gui/gamesetup/Controllers/GameSettingsController.js:273:18 onPress@gui/gamesetup/Pages/GameSetupPage/Panels/Buttons/StartGameButton.js:60:52

Link to comment
Share on other sites

6 hours ago, Silier said:

Did you use the suffix thing?

This shouldn't be the case of you use the whole file as is.

I used formations.js and commands.js as-is and I still get the original issue (incorrect flag when first formed, correct flag chosen thereafter), and the error code:

ERROR: Error in timer on entity 10888, IID105, function TimerHandler: TypeError: cmpFormationUnitAI.GetTargetPositions()[0] is undefined Formation.prototype.MoveMembersIntoFormation@simulation/components/Formation.js:534:61 Timer@simulation/components/UnitAI.js:1011:19 FSM.prototype.ProcessMessage@globalscripts/FSM.js:265:17 UnitAI.prototype.TimerHandler@simulation/components/UnitAI.js:4210:15 Timer.prototype.OnUpdate@simulation/components/Timer.js:139:44

Link to comment
Share on other sites

8 hours ago, wowgetoffyourcellphone said:

I used formations.js and commands.js as-is and I still get the original issue (incorrect flag when first formed, correct flag chosen thereafter), and the error code:

ERROR: Error in timer on entity 10888, IID105, function TimerHandler: TypeError: cmpFormationUnitAI.GetTargetPositions()[0] is undefined Formation.prototype.MoveMembersIntoFormation@simulation/components/Formation.js:534:61 Timer@simulation/components/UnitAI.js:1011:19 FSM.prototype.ProcessMessage@globalscripts/FSM.js:265:17 UnitAI.prototype.TimerHandler@simulation/components/UnitAI.js:4210:15 Timer.prototype.OnUpdate@simulation/components/Timer.js:139:44

This sounds _a lot_ like https://code.wildfiregames.com/D4294 is applied?

  • Thanks 1
Link to comment
Share on other sites

On 17/01/2022 at 9:46 PM, wowgetoffyourcellphone said:

I used formations.js and commands.js as-is and I still get the original issue (incorrect flag when first formed, correct flag chosen thereafter), and the error code:

You need to put whole Commands.js under simulation/helpers

  • Thanks 1
Link to comment
Share on other sites

To simulate a combat, in theory, as in Total War, not the entire formation would be disorganized, only the first units of each formation would fight. they would have to move little bite  but without disorder.

 

This is how it works in total war. In 0 A.D it continues as a classic RTS and it is still very buggy On many occasions, under certain circumstances, for example mixing it with civilian units or giving orders to build (etc).

I speak about what is already in the svn.

 

Link to comment
Share on other sites

  • 2 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...