HOW TO add unit Oil move cost, Oil Reserve counter and Oil produced each turn counter

Duke176

Warlord
Joined
Oct 19, 2006
Messages
241
Location
Turin - Italy
TITTLE SHOULD BE (pls change if possible) "HOW TO add unit Oil move cost, Oil Reserve counter and Oil produced each turn counter" - thx.


All right after my long studying and help recived I've reach a goal vs SDK and python difficoulties.
Here is a (for what I've understood) Tutorial about how I made it.
Let's start.
(to understand all the contents look at my project link in signature)

Files we are gonna change are:

XML:
Civ4UnitInfos.xml
Civ4UnitSchema.xml
GlobalDefines.xml
Civ4ImprovementInfos.xml
Civ4ImprovementSchema.xml

Python:
.../screens/CvMainInterface.py

SDK:
CvGameTextMgr.cpp
CvGameTextMgr.h
CvInfos.cpp
CvInfos.h
CvPlayer.cpp
CvPlayer.h
CvUnit.cpp
CvUnit.h
CyGameTextMgr.cpp
CyGameTextMgr.h
CyGameTextMgrInterface.cpp
CyInfoInterface1.cpp
CyInfoInterface2.cpp
CyPlayer.cpp
CyPlayer.h
CyPlayerInterface1.cpp
CyUnit.cpp
CyUnit.h
CyUnitInfoInterface.cpp

Bit long right?
You will see step by step won’t be so hard.

---------- PART I ------------
HOW TO ADD OIL CONSUMPTION FOR UNIT MOVEMENT

XML:

First we should start saying the Game how much unit will consume with their movements.

Civ4UnitInfos.xml
Each Unit have mainly the same Bold or Integer Infos so look for there lines and insert the new integer value you desire (I called it “iOilMoveCost”)
Code:
			<iMinAreaSize>-1</iMinAreaSize>
			<iMoves>2</iMoves>
<!-- MIO OIL COST -->
			<iOilMoveCost>1</iOilMoveCost>
<!-- end -->			
			<iAirRange>0</iAirRange>
			<iNukeRange>-1</iNukeRange>
Repeat it as many times you prefer, I mean for all the units you desire to have oil consumption.
Than we should tell the game what is this new tag it finds in XML files UnitInfos so get

Civ4UnitSchema.xml
Exactly in the same position (I don&#8217;t think it changes in another place but it&#8217;s to be sure)
Code:
	<ElementType name="iMinAreaSize" content="textOnly" dt:type="int"/>
	<ElementType name="iMoves" content="textOnly" dt:type="int"/>
<!-- COSTO PER MOVE MOD -->
	<ElementType name="iOilMoveCost" content="textOnly" dt:type="int"/>
<!-- end -->
	<ElementType name="iAirRange" content="textOnly" dt:type="int"/>
	<ElementType name="iNukeRange" content="textOnly" dt:type="int"/>
Now the game knows it&#8217;s an integer value written with text symbols (so numbers of pad)
Now let&#8217;s start to interact with CPP to apply this modification.

SDK:
We start with CvInfos.cpp and CvInfos.h

In CvInfos.cpp look for &#8220;iMoves&#8221; &#8211; this way you will see how the programmers have implemented in this file the number of plot one unit can move.
Now let&#8217;s add our code and set our &#8220;iOilMoveCost&#8221; = 0 to defoult.
Code:
//======================================================================================================
//					CvUnitInfo
//======================================================================================================

//------------------------------------------------------------------------------------------------------
//
//  FUNCTION:   CvUnitInfo()
//
//  PURPOSE :   Default constructor
//
//------------------------------------------------------------------------------------------------------
CvUnitInfo::CvUnitInfo() :
m_iAIWeight(0),
m_iProductionCost(0),
m_iHurryCostModifier(0),
m_iMinAreaSize(0),
m_iMoves(0),
//----------------------- MIO OIL COST ------------------
m_iOilMoveCost(0),
//-------------------------- end -------------------------------
m_iAirRange(0),
m_iNukeRange(0),

Look further and tell the game what will be the function that will recall the number taken from XML tag and stored here in the 1st part of this file code
Code:
int CvUnitInfo::getMoves() const
{
	return m_iMoves;
}

//------------------------- MIO OIL COST ------------------------
int CvUnitInfo::getOilMoveCost() const
{
	return m_iOilMoveCost;
}
//--------------------------- end --------------------------------------

int CvUnitInfo::getAirRange() const
{
	return m_iAirRange;
}
As you see we tell the game that the function of the group &#8220;CvUnitInfo&#8221; &#8211; &#8220;getOilMoveCost()&#8221; will return the value of &#8220;m_iOilMoveCost&#8221;.
Now go on and look for streams data savers (dunno if in English is correct &#8211; but I&#8217;m not a programmer so sorry).

WHERE IS READ
Code:
	stream->Read(&m_iMinAreaSize);
	stream->Read(&m_iMoves);
//--------------- MIO OIL COST -------------------------
	stream->Read(&m_iOilMoveCost);
//--------------------- end ---------------------------------
	stream->Read(&m_iAirRange);
	stream->Read(&m_iNukeRange);

WHERE IS WRITE
Code:
	stream->Write(m_iMinAreaSize);
	stream->Write(m_iMoves);
//--------------------- MIO OIL COST -------------------------
	stream->Write(m_iOilMoveCost);
//------------------------------ end -------------------------------
	stream->Write(m_iAirRange);
	stream->Write(m_iNukeRange);

Now the most important part for this. Explain the game where he can find the tag we need to full the empty m_iOilMoveCost(0) with the value stored there and explain always the game how did you called that tag.

Code:
	pXML->GetChildXmlValByName(&m_iMinAreaSize, "iMinAreaSize");
	pXML->GetChildXmlValByName(&m_iMoves, "iMoves");
//------------------------- MIO OIL COST -----------------------------------
	pXML->GetChildXmlValByName(&m_iOilMoveCost, "iOilMoveCost");
//------------------------------------- end ---------------------------------------
	pXML->GetChildXmlValByName(&m_iAirRange, "iAirRange");
	pXML->GetChildXmlValByName(&m_iNukeRange, "iNukeRange");
You see? GetChild-XML-Value-assigned to the tag that have name-(&#8220;iOilMoveCost&#8221;, we used in XML, and is related with m_iOilMoveCost, we decleared before in this file). I hope you understood ask me if not.

Pass to CvInfo.h
Dunno exacly the meaning of what we write here but remeber always that X.ccp is always realted with X.h (if both exists), so if you add something in X.cpp you will have to change also X.h most of the times.

Again look for &#8220;iMoves&#8221; you will find and add this &#8211; here we are saying to the game which functions are stored and used in X.CPP and which kind of functions are INT/BOOL/VOID:
Code:
	DllExport int getMinAreaSize() const;				// Exposed to Python
	DllExport int getMoves() const;				// Exposed to Python
//---------------- MIO OIL COST -------------------------
	DllExport int getOilMoveCost() const;    // Exposed to Python
//--------------------- end -------------------------------------
	DllExport int getAirRange() const;				// Exposed to Python
	DllExport int getNukeRange() const;				// Exposed to Python

Go on

Code:
	int m_iMinAreaSize;
	int m_iMoves;
//--------------- MIO OIL COST --------------------
	int m_iOilMoveCost;
//-------------------- end ------------------------------
	int m_iAirRange;
	int m_iNukeRange;

So now the game knows what are the parts we added inside CvUnit.cpp.

now we will modify CvUnit.cpp and will tell the game how to use those finctions and members we decleared before in game.

Look for &#8220;int CvUnit::Move...&#8221; and add before:
Code:
void CvUnit::attack(CvPlot* pPlot, bool bQuick)
{
	FAssert(canMoveInto(pPlot, true));
	FAssert(getCombatTimer() == 0);

	setAttackPlot(pPlot);

	updateCombat(bQuick);
}


//---------------------------------- MIO OIL COST ------------------------------------ I get this here to decleare the function that recalls the row after
int CvUnit::oilMoveCost() const
{
	return GC.getUnitInfo(getUnitType()).getOilMoveCost();
}
//------------------------------------------- end -----------------------------


void CvUnit::move(CvPlot* pPlot, bool bShow)
{
	FeatureTypes eFeature;
	EffectTypes eEffect;

All right core of our mod.
We want the game knows where to go to get the values token from XML assigning a value to iMoveOilCost each time we move a Unit.

Explain:

Int CvUnit::oilMoveCost() cost
We create this function that we call &#8220;oilMoveCost()&#8221; in group of functions called &#8220;CvUnit&#8221; and we tell the game it will return a number &#8220;int&#8221;.

return GC.getUnitInfo(getUnitType()).getOilMoveCost();
All right &#8220;return&#8221; needed in INT functions &#8211; here we say to the game which will the number at the end of the function, return it&#8217;s always at the last row of the int functions.
Than we use structural functions of the game.
&#8220;GC.get&#8221; &#8211; go and get the function we need in another group of functions (so when we will compile the .cpp and .h files the game will look for that group in the big .dll file)

&#8220;.getUnitInfo&#8221; &#8211; the name of group of functions the game should look for

&#8220;(getUnitType())&#8221; &#8211; another game already existing function; here it becomes an argument of the GC.get... function and return the unit type that is selecet

&#8220;.getOilMoveCost();&#8221; &#8211; we tell the game which function should get from the group we directed it to. Now you see? &#8220;getOilMoveCost&#8221; is the one we created to recall the value m_iOilMoveCost in CvInfos.cpp.

Now the game knows which value is &#8220;iOilMoveCost&#8221; now we should use it.
We will sneak into CvUnit::Move(...) function the one ofter our &#8220;oilMoveCost()&#8221; and will insert a little part of code.
Code:
void CvUnit::move(CvPlot* pPlot, bool bShow)
{
	FeatureTypes eFeature;
	EffectTypes eEffect;

	FAssert(canMoveOrAttackInto(pPlot) || isMadeAttack());

	changeMoves(pPlot->movementCost(this, plot()));

	setXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true, (bShow && pPlot->isVisibleToWatchingHuman()));

	if (getOwnerINLINE() == GC.getGameINLINE().getActivePlayer())
	{
		if (!(pPlot->isOwned()))
		{
			//spawn birds if trees present - JW
			eFeature = pPlot->getFeatureType();

			if (eFeature != NO_FEATURE)
			{
				if (GC.getASyncRand().get(100) < GC.getFeatureInfo(eFeature).getEffectProbability())
				{
					eEffect = (EffectTypes)GC.getInfoTypeForString(GC.getFeatureInfo(eFeature).getEffectType());
					gDLL->getEngineIFace()->TriggerEffect(eEffect, pPlot->getPoint(), (float)(GC.getASyncRand().get(360)));
					gDLL->getInterfaceIFace()->playGeneralSound("AS3D_UN_BIRDS_SCATTER", pPlot->getPoint());
				}
			}
		}
	}

	gDLL->getEventReporterIFace()->unitMove(pPlot, this);
//------------------------------- MIO OIL COST ------------------------------
	GET_PLAYER(getOwnerINLINE()).changeOilReserve(-(oilMoveCost()));
//------------------------------------- end ----------------------------------------
}

What we added?
&#8220;GET_PLAYER&#8221; &#8211; we say the game get Players who are playing this match (all AI too)

&#8220;(getOwnerINLINE())&#8221; &#8211; get the owner of that unit (&#8220;setXY(pPlot->getX_INLINE(), pPlot->getY_INLINE(), true, true, (bShow && pPlot->isVisibleToWatchingHuman()));&#8221; i think is here where in MOVE function the game get the unit the player choosed but I could be wrong&#8221;)

&#8220;.changeOilReserve(-(oilMoveCost()));&#8221; &#8211; here we complite and tell the game to get from the CvPlayer group of functions (with GET_PLAYER I think we recall all those functions) the function called &#8220;changeOilReserver(....)&#8221; and to subtract &#8220;(-(......))));&#8221; the value we get with our function (the one we creted before this one) &#8220;oilMoveCost()&#8221;.
ChangeOilReserve( int iChange) will be the function we will use to modify OIL RESERVE counter. You could change it with &#8220;ChangeGold&#8221; it does the same thing with gold. So oyu can check if your mod works by looking at Gold subtraction each move of unit.


Last part now.
We should also remember the game to check if the Reserve of OIL we have are enough to make the unit moving. So we will look further in this file and will look for
&#8220;bool CvUnit::canMove() const&#8221; and we will add this:
Code:
int CvUnit::movesLeft() const
{
	return max(0, (maxMoves() - getMoves()));
}


bool CvUnit::canMove() const
{
//----------------------------- MIO OIL COST --------------------------------- look if the cost of movement in oil ammount is higher than total ammount of OIL
	if (GET_PLAYER(getOwnerINLINE()).getOilReserve() < oilMoveCost())
	{
		return false;
	}
//------------------------------------------------- end ------------------------------
	return (getMoves() < maxMoves());
}


bool CvUnit::hasMoved()	const
{
	return (getMoves() > 0);
}

Here before the game checks if the unit has units left we should see if there&#8217;s enough OIL to move it. It&#8217;s a bool function because it returns YES or NO not a number.
So again:
&#8220;if (GET_PLAYER(getOwnerINLINE())&#8221; &#8211; again here we get the owner of unit, the functions of CvPlayer

&#8220;.getOilReserve() < oilMoveCost())&#8221; &#8211; now in spite of &#8220;ChangeOilReserve&#8221; we use &#8220;getOilReserve()&#8221; we will create if later and will return the Oil Reserves we have (you can change it with &#8220;getGold()&#8221; to see if it&#8217;s working) and look if your RESERVES are < (lower) than &#8220;oilMoveCost()&#8221; &#8211; the function we created and returns the value taken from XML.

Now one more step.
Pass to CvUnit.h
Now we decleare the functions we created in corrispondent CvUnit.cpp.
As before we decleare it near &#8220;CanMove&#8221;...
Code:
	DllExport int movesLeft() const;																			// Exposed to Python			
	DllExport bool canMove() const;																				// Exposed to Python			
	DllExport bool hasMoved() const;																			// Exposed to Python			

//----------------------------------- MIO OIL COST -----------------------------------

	DllExport int oilMoveCost() const;                            
// Exposed to Python

//--------------------------------------end------------------------------------------------- 		
	
	DllExport int airRange() const;																				// Exposed to Python			
	DllExport int nukeRange() const;

Now we pass to CyUnit.cpp
Dunno exactly what to do here but look for &#8220;Move&#8221; again and add:
Code:
bool CyUnit::hasMoved()
{
	return m_pUnit ? m_pUnit->hasMoved() : false;
}

//---------------------------------- MIO OIL COST ---------------------------------
int CyUnit::oilMoveCost()
{
	return m_pUnit ? m_pUnit->oilMoveCost() : -1;
}
//------------------------------------- end ----------------------------------------------

int CyUnit::airRange()
{
	return m_pUnit ? m_pUnit->airRange() : -1;
}

Now we move to CyUnit.h
We decleare also here our new function.

Code:
	bool canMove();
	bool hasMoved();
//------------------------------ MIO OIL COST -----------------------
	int oilMoveCost();
//------------------------------------- end -------------------------------
	int airRange();
	int nukeRange();

Last move to CyUnitInterface1.cpp
Here we define what we made before, so we define our new function.
Code:
		.def("canMove", &CyUnit::canMove, "bool ()")
		.def("hasMoved", &CyUnit::hasMoved, "bool ()")
//------------------------------------- MIO OIL COST --------------------------------------------------
		.def("oilMoveCost", &CyUnit::oilMoveCost, "int ()")
//--------------------------------------------- end ----------------------------------------------------------
		.def("airRange", &CyUnit::airRange, "int ()")
		.def("nukeRange", &CyUnit::nukeRange, "int ()")

As done before we say to the game that our new funtion is an Integer.

Easy no?
Not truly.
Here it stops 1st part. Hope you understood, I&#8217;m not a programmer so I try to make ppl understand with not techincal speaking.
Sorry for mistakes and faces appearing.
Soon part TWO: HOW TO ADD OIL RESERVER COUNTER.
 
I don't wanna sound like a billy begger or anything but do you know how to make a code for oil production/consumption.

-i.e.
1. A civilization consumes 10 gallons of oil per turn.
2. Each city needs 5 gallons of oil.
3. A single oil reserve holds about 500 gallons.
 
allright what's wrong Kt.
This code do a part of what is needed to make units consume some oil each time they move of 1 plot and subtract it from an ammount - oil reserve - you produce each turn.
All clear? So it's writed.
 
I can't test the AI effects right now as my map is still unfinished (cities are all pop 1) but I know that there is no problem with the human units. The good thing is that no roads to the oil resource also stop units in their tracks so, for example, in a ww2 scenario if the Allies or Axis wanted to stop oil production they can also do several bomber runs on the resource tile and it would also cripple the enemy for a couple of turns until workers can rebuild.

I will try and setup a small map with 2 cities and oil next to each one and see how the AI handles the resource management.

Has anyone tested the diplomacy system with the oil counter? Does the AI offer oil access to civs with no access to the resource. This could answer the potential issue of having Sweden, for example, offering iron to Germany and so on if the counter system was expanded.
 
Duke176, are you ever going to finish your tutorial and let us know how to add an Oil reserve counter? :confused:




:nuke: Cheers, Thorgrimm :nuke:
 
I'll do during these holidays sorry :)

Lol, no problem. I began to add it in a variant called Anti-Matter instead of oil and came to the part to add the counter and saw it was not complete. :D

A couple of questions, where do you put the modded .cpp and .h files at so it does not bother the vanilla game.

Can the resource production be tied to a building? For example Anti-Matter tied to a Anti-Matter production facility.




:nuke: Cheers, Thorgrimm :nuke:
 
for the modded .cpp and .h file should be compiled.

and yes you can link the production of a resource to a certain building, the game will check throw the cities id there's that building and will return a number you will multiply for the constant you decided for the production.

es. nuclear plant - electricty prod.
you decide each plant prod. 100 GWatt - so you have 5 plants and the game will multiplay 5 * 100 = 500GW of production.
 
for the modded .cpp and .h file should be compiled.

and yes you can link the production of a resource to a certain building, the game will check throw the cities id there's that building and will return a number you will multiply for the constant you decided for the production.

es. nuclear plant - electricty prod.
you decide each plant prod. 100 GWatt - so you have 5 plants and the game will multiplay 5 * 100 = 500GW of production.

So in theory you could use your idea and have each building require some sort of energy source to run each turn? If that is possible it would be outstanding!

Thanks for the information. It helps a lot. :D



:nuke: Cheers, Thorgrimm :nuke:
 
I hope you will finish Your Tutorial!

I like it a lot, the idea as well as how you explained it, even for a newbie it is very easy to understand. I learned more from you then from some other Tutorials!

But my Marine Problem I still couldn't solve... *sigh
But got some new ideas, Thanks Duke
 
Just look at Grey Fox's fuel modcomp. It's easily combined with the Oil Move Cost from this tutorial, and it creates quite a powerful combo. ;)
 
Back
Top Bottom