Sample SDK Code (basic)

Kael said:
Replyed in PM.

please always post every single bit of code on the forum, it is all veeery interesting you are an awesome developer !! If the solution is in python rather than in C++ then maybe open another Python-Topic ? ....
 
Rod said:
please always post every single bit of code on the forum, it is all veeery interesting you are an awesome developer !! If the solution is in python rather than in C++ then maybe open another Python-Topic ? ....

It wasnt code we just chatted about what he was seeing and why (as I recall).

As for example code here is a fairly minor change:

To keep buildings from listing units they are required to train that the civilization doesn't have access to

CvGameTextMgr.cpp
void CvGameTextMgr::setBuildingHelp(CvWString &szBuffer, BuildingTypes eBuilding, bool bCivilopediaText, bool bStrategyText, bool bTechChooserText, CvCity* pCity)

Code:
//FfH: Modified by Kael 11/02/2006
//	for (iI = 0; iI < GC.getNumUnitInfos(); iI++)
//	{
//		if (GC.getUnitInfo((UnitTypes)iI).getPrereqBuilding() == eBuilding)
//		{
//			swprintf(szFirstBuffer, L"%s%s", NEWLINE, gDLL->getText("TXT_KEY_BUILDING_REQUIRED_TO_TRAIN").c_str());
//			swprintf(szTempBuffer,  SETCOLR L"<link=literal>%s</link>" ENDCOLR , TEXT_COLOR("COLOR_UNIT_TEXT"), GC.getUnitInfo((UnitTypes)iI).getDescription());
//			setListHelp(szBuffer, szFirstBuffer, szTempBuffer, L", ", bFirst);
//			bFirst = false;
//		}
//	}
	UnitTypes eLoopUnit;
	for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
	{
		if (GC.getGameINLINE().getActivePlayer() != NO_PLAYER)
		{
			eLoopUnit = (UnitTypes)GC.getCivilizationInfo(GC.getGameINLINE().getActiveCivilizationType()).getCivilizationUnits(iI);
		}
		else
		{
			eLoopUnit = (UnitTypes)GC.getUnitClassInfo((UnitClassTypes)iI).getDefaultUnitIndex();
		}
		if (eLoopUnit != NO_UNIT)
		{
    		if (GC.getUnitInfo(eLoopUnit).getPrereqBuilding() == eBuilding)
            {
        		swprintf(szFirstBuffer, L"%s%s", NEWLINE, gDLL->getText("TXT_KEY_BUILDING_REQUIRED_TO_TRAIN").c_str());
                swprintf(szTempBuffer,  SETCOLR L"<link=literal>%s</link>" ENDCOLR , TEXT_COLOR("COLOR_UNIT_TEXT"), GC.getUnitInfo(eLoopUnit).getDescription());
                setListHelp(szBuffer, szFirstBuffer, szTempBuffer, L", ", bFirst);
                bFirst = false;
            }
		}
	}
//FfH: End Modify

What it does: All of the commented out code at the top (the lines preceeded with a "//") are the origional code. As you can tell it used to read through all of the available units and add every unit to the list that requires that building. The new code reads through the unit classes instead and only lists the units appropriate for that civ.

Why? Vanilla doesn't have many (or any?) building requirements for units so it isn't an issue. But if you were to require that Musketmen and Musketeer's both required the 'Musket Crafter' building you would see that the building is 'Required to train Musketmen and Musketeers' for every civ. With this change the French would see 'Required to train Musketeers' and all the other civs would see 'Required to train Musketmen'.

This became even more important if you have set a large amount of UU's as we have in FfH. In some cases there can be 10 different versions of a unit for different civs so these lists got very long.

Special Bonus Feature (no charge, limited time only, not valid in Mississippi): The entire source code for Fall from Heaven 2 is online and available at: http://forums.civfanatics.com/showthread.php?t=171398. Modders are welcome to use it as a base for their mods, or just dig into it for bits and pieces they can use, I just ask that you please credit the programmer if you use his code.
 
I was wondering if anyone could explain the flow of how the SDK and Python files interact. My understanding on this is very fuzzy at best. To be more specific:

1. how does a .py file reference say a function from the SDK (if they actually can)?

2. how are the .cpp files with Cv prefixes related to the .cpp files with Cy prefixes?

3. what exactly is a wrapper?

4. how does a .cpp file get data from a .py class (is this somehow related to the wrappers?)?

I greatly appreciate any input on these. I have been playing around with the files and sometimes think I understand what is going on, but other times end up confused.

Thanks
 
1. how does a .py file reference say a function from the SDK (if they actually can)?

The function has to be exposed to python in the SDK. If that is the case python can call the function on an object. For example pUnit.getDamage() is calling the SDK call getDamage on the pUnit object.

2. how are the .cpp files with Cv prefixes related to the .cpp files with Cy prefixes?

Cv are SDK functions, Cy are the SDK functions that are "exposed" to python.

3. what exactly is a wrapper?

Wrapper is a python class that creates a list of python functions that can be used on a unit. Modders find them handy to make an easy way to reference common functions.

4. how does a .cpp file get data from a .py class (is this somehow related to the wrappers?)?

It doesn't. Python makes calls into the SDK. The SDK doesn't get data from python. You can make a python call into the SDK that passes data (for example: pUnit.setDamage(10)). But you wouldn't write an SDK function that requested info from python.

Python never directly calls XML either. the SDk calls XML, and python requests the info from the SDK.
 
The function has to be exposed to python in the SDK. If that is the case python can call the function on an object. For example pUnit.getDamage() is calling the SDK call getDamage on the pUnit object.



Cv are SDK functions, Cy are the SDK functions that are "exposed" to python.



Wrapper is a python class that creates a list of python functions that can be used on a unit. Modders find them handy to make an easy way to reference common functions.



It doesn't. Python makes calls into the SDK. The SDK doesn't get data from python. You can make a python call into the SDK that passes data (for example: pUnit.setDamage(10)). But you wouldn't write an SDK function that requested info from python.

Python never directly calls XML either. the SDk calls XML, and python requests the info from the SDK.


Thank you, this clears things up greatly.
 
Bump :) :)
 
Here's a little bit of code that I wrote for Ambreville and Jeckel in the "OIL vs. MOVE" thread. (I just converted what Jeckel did in Python to C++.)

Code:
    // Oil movement restriction - Gaius Octavius
    if (m_pUnitInfo->isMechUnit())
    {
        if (GET_PLAYER(getOwnerINLINE()).getNumAvailableBonuses((BonusTypes)GC.getInfoTypeForString("BONUS_OIL")) < 1)
        {
            return false;
        }
    }

This goes in CvUnit.cpp under the CvUnit::canMove() function. It checks to see whether a civilization has oil, and if it does not, then all mechanized units (tanks, planes, modern ships...) cannot move.

This is far from perfect code, since there are so many little things you'd need to take into account, like the fact that ships can use oil OR uranium - this currently would keep them from moving with no oil even if you have uranium. But I think people will get some use out of it. :)
 
Actually, I don't think you want to use the numavailablebonuses function. use the "hasbonus" function because I think the other one is used for trading to find out if you have resources available to trade.
 
Actually, I don't think you want to use the numavailablebonuses function. use the "hasbonus" function because I think the other one is used for trading to find out if you have resources available to trade.

I think that's what he wanted. You see, if you use "hasbonus" and you only have one supply, and you trade it away, then numavailable returns zero. At least, that was my experience. It worked when only one oil source was connected, and if it used only the tradeable resources, then it would not have worked.
 
My very first SDK mod
This goes in CvUnit.cpp as part of void CvUnit::doCommand

Code:
void CvUnit::doCommand(CommandTypes eCommand, int iData1, int iData2)
{
	CvUnit* pUnit;
	//Master's Mod
	CvCity* pCity;
	int smallUnitClassList [] = {GC.getInfoTypeForString("UNITCLASS_SCOUT"), GC.getInfoTypeForString("UNITCLASS_SPY"), GC.getInfoTypeForString("UNITCLASS_EXPLORER"), GC.getInfoTypeForString("UNITCLASS_EXECUTIVE_1"), GC.getInfoTypeForString("UNITCLASS_EXECUTIVE_2"), GC.getInfoTypeForString("UNITCLASS_EXECUTIVE_3"), GC.getInfoTypeForString("UNITCLASS_EXECUTIVE_4"), GC.getInfoTypeForString("UNITCLASS_EXECUTIVE_5"), GC.getInfoTypeForString("UNITCLASS_EXECUTIVE_6"), GC.getInfoTypeForString("UNITCLASS_EXECUTIVE_7"), GC.getInfoTypeForString("UNITCLASS_JEWISH_MISSIONARY"), GC.getInfoTypeForString("UNITCLASS_CHRISTIAN_MISSIONARY"), GC.getInfoTypeForString("UNITCLASS_ISLAMIC_MISSIONARY"), GC.getInfoTypeForString("UNITCLASS_HINDU_MISSIONARY"), GC.getInfoTypeForString("UNITCLASS_BUDDHIST_MISSIONARY"), GC.getInfoTypeForString("UNITCLASS_CONFUCIAN_MISSIONARY"),GC.getInfoTypeForString("UNITCLASS_WORKBOAT")};
	int i = 0;
	bool small = false;
	//end Master's Mod
	bool bCycle;

	bCycle = false;

	FAssert(getOwnerINLINE() != NO_PLAYER);

	if (canDoCommand(eCommand, iData1, iData2))
	{
		switch (eCommand)
		{
		case COMMAND_PROMOTION:
			promote((PromotionTypes)iData1, iData2);
			break;

		case COMMAND_UPGRADE:
			upgrade((UnitTypes)iData1);
			bCycle = true;
			break;

		case COMMAND_AUTOMATE:
			automate((AutomateTypes)iData1);
			bCycle = true;
			break;

		case COMMAND_WAKE:
			getGroup()->setActivityType(ACTIVITY_AWAKE);
			break;

		case COMMAND_CANCEL:
			getGroup()->popMission();
			break;

		case COMMAND_CANCEL_ALL:
			getGroup()->clearMissionQueue();
			break;

		case COMMAND_STOP_AUTOMATION:
			getGroup()->setAutomateType(NO_AUTOMATE);
			break;

		case COMMAND_DELETE:
			scrap();
			//Master's Mod
			
			for (i=0; i<17; i++)
			{
				if (GC.getUnitInfo(getUnitType()).getUnitClassType() == smallUnitClassList [i])
				{
					small = true;
					continue;
				}
			}
					
			pCity = GC.getMapINLINE().findCity(getX_INLINE(), getY_INLINE(), getOwnerINLINE(), NO_TEAM, false, false);

			if ((pCity != NULL) && !small)
			{
				pCity->changePopulation(1);
			}
			
			//end Master's Mod
			bCycle = true;
			break;

What it does:
Fairly obvious, if you disband a unit inside a city it adds 1 population point.

Edit: added some more code to check whether the unit is what I have called a 'small' unitclass (i.e. being small enough that it would not be a significant population boost) also added code so that population is added to the nearest city rather than having to be inside a city (thanks to xienwolf for that piece of code)

It's really designed to go with a similar function which subtracts a pop point when a unit is built
 
But count the lines to do the same stuff in C++ and in python, and you will see the difference...

For clarification,


The traditional "Hello, World!" statement often used in programming demonstrations:
(And the newline character, since that is used often)
In c++:

Code:
#include <iostream.h>
using namespace std;
void main() //Some IDE's use int main()
{
cout << "Hello, World!\n";
}

In python (3.1):

Code:
print('Hello, World!\n')

Now, a calculator that will do basic addition:
In c++:

Code:
#include <iostream.h>
using namespace std;
void main()
{
int a, b, c;
cout << "Please enter two integers.\n";
cin >> a;
cin >> b;
c = a + b;
cout << "The sum of your numbers is " << c << '\n';
}

In python:

Code:
a = int(input('Enter an integer.'))
b = int(input('Enter another integer.'))
c = a + b
print('The sum of your numbers is ', c)

A calculator that will let you choose what operation to do:
in c++:

Code:
#include <iostream.h>
using namespace std;
void main()
int a, b, c, d;
cout << "Please enter two integers.\n";
cin >> a;
cin >> b;
cout << "Enter 1 to perform addition, 2 for subtraction, 3 for multiplication and 4 for division.\n";
cin >> c;
if(c == 1)
{
     d = a + b;
     cout << d << '\n';
}
if(c == 2)
{
     d = a - b;
     cout << d << '\n';
}
if(c == 3)
{
     d = a * b
     cout << d << '\n';
}
if(c == 4)
{
     d = a/b;
     cout << d << '\n';
}
}

In python:

Code:
a = int(input('Enter an integer'))
b = int(input('Enter another integer'))
c = int(input('What operation would you like to perform?  Enter 1 for addition, 2 for subtraction, 3 for multiplication, 4 for division.'))
if c == 1:
    d = a + b
    print(d)
if c == 2:
    d = a - b
    print(d)
if c == 3:
    d = a * b
    print(d)
if c == 4:
    d = a/b
    print(d)

Much shorter in Python.
 
Top Bottom