[Lua] Writing a terrain cost system? (solved)

Irkalla

ENTP POWWWEEEEEER
Joined
Sep 25, 2012
Messages
1,009
Location
Way down around Vicksburg
I want a specific unit to have to ignore terrain movement penalties when entering any tile adjacent to river.

What I figure I'll do is give the unit a promotion that makes the unit ignore terrain movement penalties, that is the effect of the scout promotion. Then, with Lua, I'll wrtie a little system to re-implement terrain costs on anything except River tiles. This should give the desired effect as mentioned above.

I feel like this is the most painless way to go about it. What is the actual mechanism by which I can change a unit's terrain cost or perhaps take movement points from it?

Better yet, if anyone can think of a way that I might achieve this without messing up the move range display when moving the unit, I would most appreciate it.
 
The WOTAW scenario had a civ called the Hittites which possessed that form of movement as a UA. I don't know a lot about how the modding works, but perhaps you could look at that code to determine a unit-specific implementation.
 
The WOTAW scenario had a civ called the Hittites which possessed that form of movement as a UA. I don't know a lot about how the modding works, but perhaps you could look at that code to determine a unit-specific implementation.

It was the Sumerians and it, for whatever pants on head reason, didn't give a promotion. I guess they considered the ability to be too situational to factor it into military strength checks by the AI.
 
http://modiki.civfanatics.com/index.php/Plot_(Civ5_Type)

I would try this way:
1. GameEvents.UnitSetXY
2. If unit has promotion (fake promotion, doing nothing)
2.25 Check if unit owner is making turn
2.5 RiverFunPlot[unit] ~= nil
2.75 If distance between RiverFunPlot[unit] and plot is 1 then
3. If plot is river then
IsRiver() [?]
4. If plot.movementcost(unit, RiverFunPlot[unit]) > 60 then
4.5 If RiverFunMC[unit] > 60 then
unit.ChangeMove(+ or - [don't remember when it adds] RiverFunMC[unit] - 60 - unit:GetMoves())
5. RiverFunPlot[unit] = plot. RiverFunMC[unit] = unit:GetMoves() (it should be done if condition 2 is met)
+ Store position and moves at game loaded, iterator through all players then units.
+ Store moves and current player on PlayerDoTurn

Iirc, one movement is 60 in LUA. You can check it by printing unit:GetMoves().
 
Actually we thought of something a bit different. Well, Leugi did anyway.

Instead of "Unit pays only 1 movement cost to enter any tile adjacent to river," it is now "Unit ignores terrain cost when moving along rivers."

The only thing that really changes is that if you enter a river plot from a non river plot, then you pay full cost. However, you can still cross rivers and move along them at 1 cost per plot unless there's a road. I believe there's a precedent for this, being that you have to pay full cost to enter a road or railroad connection, but once you're on it you have modded costs.

I believe this is a reasonable plan of action. We could do the custom terrain cost system. We believe it to be achievable. However, we believe the following option to be more technically feasible, less demanding of system resources that could be better used, and to not require that we track anything between saves:

Add a promotion to the unit that describes what the unit does (Descriptor.) With Lua, on UnitSetXY, check if the unit has this promotion and the unit's current position (presumably this fires after setting XY, so we get the tile it moved into...) is a plot that returns true if passed to IsRiver() and if it does not already have the following promotion, then we give the unit a custom Ignore Terrain Penalties promotion (Actor.) This promotion is invisible. If it's not a River plot and the unit has the Descriptor promotion and hidden Actor promotion, then we remove the Actor promotion and deduct the proper terrain cost.

This way of doing it also has a hidden advantage: AI calculations regarding military strength of another civilization rely, in part, on how many promotions a unit has. This makes the unit appear stronger if it's on river, which makes sense considering it has a higher mobility on rivers.
 
it is now "Unit ignores terrain cost when moving along rivers."
Well, it doesn't truely matter.

1. I don't know about the way to make it without messing up with the move range display. With LUA, AI might be like "I will totally cross this hill and escape! ... and I am dead.".
2. Main function it seems you should use is: plot.MovementCost(Unit unit, Plot fromPlot)
2.5 "fromPlot" is the hardest part.
3. Rest is upon your fantasy. You can make it ignore terrain cost while moving along river, but not crossing it, both, etc.
4. You might need some code against unintentional movement (citadels, city trading, open borders ends, etc.)
 
Well, it doesn't truely matter.

1. I don't know about the way to make it without messing up with the move range display. With LUA, AI might be like "I will totally cross this hill and escape! ... and I am dead.".
2. Main function it seems you should use is: plot.MovementCost(Unit unit, Plot fromPlot)
2.5 "fromPlot" is the hardest part.
3. Rest is upon your fantasy. You can make it ignore terrain cost while moving along river, but not crossing it, both, etc.
4. You might need some code against unintentional movement (citadels, city trading, open borders ends, etc.)

Are you suggesting that I hook into that movement cost method? Because you can only hook into Events, GameEvents, or LuaEvents.
 
Nope, obviously you are using GameEvents.UnitSetXY, it is followed by several "if" (IsHasPromotion(), IsRiver(), IsHasPromotion()). It's all easy staff.

Then you have situation that your unit is "leaving" river. You are removing promotion, but unit already move there with reduced movement cost.

Code:
GameEvents.UnitSetXY.Add(function(player, unit, x, y)
local cUnit = Players[player]:GetUnitByID(unit);
	if cUnit:IsHasPromotion( FakeRiverLord ) then
		if Map.GetPlot(x,y) ~= nil then
			local cPlot = Map.GetPlot(x,y);
			if cPlot:IsRiver() then
				if not cUnit:IsHasPromotion( IgnoreRiverLord ) then
					cUnit:SetHasPromotion( IgnoreRiverLord ,true);
				end
			else
				RivLordEnd( cUnit, cPlot )
			end
		end
	end
end)

function RivLordFun( cUnit, cPlot )
	if cUnit:IsHasPromotion( IgnoreRiverLord ) then
		cUnit:SetHasPromotion( IgnoreRiverLord, false);
		if cUnit:GetMoves() > 0 then
			???
 
Nope, obviously you are using GameEvents.UnitSetXY, it is followed by several "if" (IsHasPromotion(), IsRiver(), IsHasPromotion()). It's all easy staff.

Then you have situation that your unit is "leaving" river. You are removing promotion, but unit already move there with reduced movement cost.

Code:
GameEvents.UnitSetXY.Add(function(player, unit, x, y)
local cUnit = Players[player]:GetUnitByID(unit);
	if cUnit:IsHasPromotion( FakeRiverLord ) then
		if Map.GetPlot(x,y) ~= nil then
			local cPlot = Map.GetPlot(x,y);
			if cPlot:IsRiver() then
				if not cUnit:IsHasPromotion( IgnoreRiverLord ) then
					cUnit:SetHasPromotion( IgnoreRiverLord ,true);
				end
			else
				RivLordEnd( cUnit, cPlot )
			end
		end
	end
end)

function RivLordFun( cUnit, cPlot )
	if cUnit:IsHasPromotion( IgnoreRiverLord ) then
		cUnit:SetHasPromotion( IgnoreRiverLord, false);
		if cUnit:GetMoves() > 0 then
			???



This was my solution to the issue. I had to take 2 moves instead of setting moves to zero, because there exists a scenario where the promotion can wind up on mounted units and even helicopter. I'll have to add a special case for helicopter.

Code:
-- Promotion_Vulture_Rivermovement
-- Author: Irkalla
-- DateCreated: 8/9/2013 10:45:59 PM
--------------------------------------------------------------

GameEvents.UnitSetXY.Add( function( iPlayerID, iUnitID, iX, iY )
	local pPlot		= Map.GetPlot( iX, iY );
	local pPlayer		= Players[ iPlayerID ];
	local pUnit		= pPlayer:GetUnitByID( iUnitID );
	local promotionHot	= GameInfo.UnitPromotions.PROMOTION_VULTURE_RIVERMOVEMENT_II.ID;
	local promotionCold	= GameInfo.UnitPromotions.PROMOTION_VULTURE_RIVERMOVEMENT.ID;

	if pPlot:IsRiver() and 
	pUnit:IsHasPromotion( promotionCold ) then
		pUnit:SetHasPromotion( promotionCold, false );
		pUnit:SetHasPromotion( promotionHot, true );
	end

	if ( not pPlot:IsRiver() ) and
	pUnit:IsHasPromotion( promotionHot ) then
		pUnit:SetHasPromotion( promotionHot, false );
		pUnit:SetHasPromotion( promotionCold, true );
		
		if pPlot:IsRoughGround() then
			pUnit:ChangeMoves( -2 )
		end

	end

end )
 
It doesn't even consider that unit can move through road.

I don't think it needs to. I'll give it a test, though. I think you might be right, though. The promotion itself should not affect road travel, but the rough terrain part would. However, it does cost to enter road. I'll have to check if the last plot was road. Perhaps LastMissionPlot?
 
Back
Top Bottom