Yoda Power
✫✫✫✫✫✫✫
- Joined
- Sep 24, 2002
- Messages
- 13,870
How do I restrict city building in desert, glacier and tundra, such that it is only possible near water? (Coast, river or oasis)
void CvDllTranslator::initializeTags(CvWString& szTagStartIcon, CvWString& szTagStartOur, CvWString& szTagStartCT, CvWString& szTagStartColor, CvWString& szTagStartLink, CvWString& szTagEndLink, CvWString& szEndLinkReplacement, std::map<std::wstring, CvWString>& aIconMap, std::map<std::wstring, CvWString>& aColorMap)
{
szTagStartIcon = L"[ICON_";
szTagStartOur = L"[OUR_";
szTagStartCT = L"[CT_";
szTagStartColor = L"[COLOR_";
szTagStartLink = L"[LINK";
szTagEndLink = L"[\\LINK";
szEndLinkReplacement = L"</link>";
//create icons map
aIconMap[L"[ICON_BULLET]"] = std::wstring(1, (wchar)gDLL->getSymbolID(BULLET_CHAR));
aIconMap[L"[ICON_HAPPY]"] = std::wstring(1, (wchar)gDLL->getSymbolID(HAPPY_CHAR));
aIconMap[L"[ICON_UNHAPPY]"] = std::wstring(1, (wchar)gDLL->getSymbolID(UNHAPPY_CHAR));
aIconMap[L"[ICON_HEALTHY]"] = std::wstring(1, (wchar)gDLL->getSymbolID(HEALTHY_CHAR));
aIconMap[L"[ICON_UNHEALTHY]"] = std::wstring(1, (wchar)gDLL->getSymbolID(UNHEALTHY_CHAR));
aIconMap[L"[ICON_STRENGTH]"] = std::wstring(1, (wchar)gDLL->getSymbolID(STRENGTH_CHAR));
aIconMap[L"[ICON_MOVES]"] = std::wstring(1, (wchar)gDLL->getSymbolID(MOVES_CHAR));
aIconMap[L"[ICON_RELIGION]"] = std::wstring(1, (wchar)gDLL->getSymbolID(RELIGION_CHAR));
aIconMap[L"[ICON_STAR]"] = std::wstring(1, (wchar)gDLL->getSymbolID(STAR_CHAR));
aIconMap[L"[ICON_SILVER_STAR]"] = std::wstring(1, (wchar)gDLL->getSymbolID(SILVER_STAR_CHAR));
aIconMap[L"[ICON_TRADE]"] = std::wstring(1, (wchar)gDLL->getSymbolID(TRADE_CHAR));
aIconMap[L"[ICON_DEFENSE]"] = std::wstring(1, (wchar)gDLL->getSymbolID(DEFENSE_CHAR));
aIconMap[L"[ICON_GREATPEOPLE]"] = std::wstring(1, (wchar)gDLL->getSymbolID(GREAT_PEOPLE_CHAR));
aIconMap[L"[ICON_BAD_GOLD]"] = std::wstring(1, (wchar)gDLL->getSymbolID(BAD_GOLD_CHAR));
aIconMap[L"[ICON_BAD_FOOD]"] = std::wstring(1, (wchar)gDLL->getSymbolID(BAD_FOOD_CHAR));
aIconMap[L"[ICON_EATENFOOD]"] = std::wstring(1, (wchar)gDLL->getSymbolID(EATEN_FOOD_CHAR));
aIconMap[L"[ICON_GOLDENAGE]"] = std::wstring(1, (wchar)gDLL->getSymbolID(GOLDEN_AGE_CHAR));
aIconMap[L"[ICON_ANGRYPOP]"] = std::wstring(1, (wchar)gDLL->getSymbolID(ANGRY_POP_CHAR));
aIconMap[L"[ICON_OPENBORDERS]"] = std::wstring(1, (wchar)gDLL->getSymbolID(OPEN_BORDERS_CHAR));
aIconMap[L"[ICON_DEFENSIVEPACT]"] = std::wstring(1, (wchar)gDLL->getSymbolID(DEFENSIVE_PACT_CHAR));
aIconMap[L"[ICON_MAP]"] = std::wstring(1, (wchar)gDLL->getSymbolID(MAP_CHAR));
aIconMap[L"[ICON_OCCUPATION]"] = std::wstring(1, (wchar)gDLL->getSymbolID(OCCUPATION_CHAR));
aIconMap[L"[ICON_POWER]"] = std::wstring(1, (wchar)gDLL->getSymbolID(POWER_CHAR));
aIconMap[L"[ICON_GOLD]"] = std::wstring(1, (wchar)GC.getCommerceInfo(COMMERCE_GOLD).getChar());
aIconMap[L"[ICON_RESEARCH]"] = std::wstring(1, (wchar)GC.getCommerceInfo(COMMERCE_RESEARCH).getChar());
aIconMap[L"[ICON_CULTURE]"] = std::wstring(1, (wchar)GC.getCommerceInfo(COMMERCE_CULTURE).getChar());
aIconMap[L"[ICON_ESPIONAGE]"] = std::wstring(1, (wchar)GC.getCommerceInfo(COMMERCE_ESPIONAGE).getChar());
aIconMap[L"[ICON_FOOD]"] = std::wstring(1, (wchar)GC.getYieldInfo(YIELD_FOOD).getChar());
aIconMap[L"[ICON_PRODUCTION]"] = std::wstring(1, (wchar)GC.getYieldInfo(YIELD_PRODUCTION).getChar());
aIconMap[L"[ICON_COMMERCE]"] = std::wstring(1, (wchar)GC.getYieldInfo(YIELD_COMMERCE).getChar());
//create color map
aColorMap[L"[COLOR_REVERT]"] = CvWString(L"</color>");
for(int i=0; i < GC.getNumColorInfos(); i++)
{
const NiColorA& color = GC.getColorInfo((ColorTypes) i).getColor();
CvWString colorType(GC.getColorInfo((ColorTypes) i).getType());
CvWString wideColorType;
wideColorType.Format(L"[%s]", colorType.GetCString());
CvWString colorOut;
colorOut.Format(L"<color=%i,%i,%i,%i>", (int) (color.r * 255), (int) (color.g * 255), (int) (color.b * 255), (int) (color.a * 255));
aColorMap[wideColorType.GetCString()] = colorOut;
}
}
Thanks! That's what I was looking forYou're probably looking for ICON_STAR / ICON_SILVER_STAR
Finally I had time to look into this. But where do I put it?I had to look into this. Python doesn't quite allow that.
If you are willing to accept the scaling from city size, then this is all the code you need:
- changeExtraHappiness can add an arbitrary amount of happiness or unhappiness to a city, but it's permanent.
- changeHappinessTimer can add a happiness bonus to a city for a certain length of time, but it only allows a flat +1
. The Wedding Feud and Great Beast events in BTS use this.
- changeConscriptAngerTimer and changeHurryAngerTimer can add temporary unhappiness to a city and it stacks, but it also scales with city size.
Code:def onUnitBuilt(argsList): pCity = argsList[0] pUnit = argsList[1] if pUnit.getUnitClass() == gc.getInfoTypeForString("UNITCLASS_XXX"): gamespeed = CyGame.getGameSpeedType() gameSpeedModifier = gc.getGameSpeedInfo(gamespeed).getGrowthPercent() iSlave = 8 * gameSpeedModifier / 100 pCity.changeConscriptAngerTimer(iSlave)
XXX is the unit class of the unit that you want to trigger the unhappiness addition when it is built. GrowthPercent is serving as the scaling factor. If you want to go strictly by turns you have to calculate the length of the game for both your current speed and normal speed.
Finally I had time to look into this. But where do I put it?![]()
from CvPythonExtensions import *
import CvUtil
gc = CyGlobalContext()
<?xml version="1.0" encoding="ISO-8859-1" ?>
<mod id="XXX">
<event type="XXX" function="onXXX"/>
</mod>
<load mod="XXX"/>
Well, that doesn't work.It's a process. First, an FYI: Python is not modular the way XML is. You are going to have to either edit existing Python files in the main mod Python folder or create new files in both the Python and Config folders.
The easy way to do this is to "piggyback" the Python code onto existing Python code in another file. Find the def onXXX function that you want to use somewhere in the existing Python files and copy the non-duplicated lines in your own code to that file (that is, everything after the argslist lines). This works better once you've already created your own files and are having multiple different things happening with the same trigger. AND has several different effects triggered by Wonders being built that all use the same onBuildingBuilt trigger in VokaryaWonders.py.
The better way is to create your own files. You need two new files and an edit to an existing one. First, you need a .py file in the Python folder. This is the starting point.
Make a file in a text editor with this, add your Python code at the end, and save it as a .py file in the Python folder. The gc line is a space-saver. You can type out CyGlobalContext() every time you use it, but gc saves keystrokes.Code:from CvPythonExtensions import * import CvUtil gc = CyGlobalContext()
Now you need to tell Civ4 how to use this file. For this, you need to create an .xml file in the Config folder. At a minimum, this file looks like this:
You will need one copy of the event line for each different Python function (onBuildingBuilt, onUnitBuilt, etc.) in your Python file. The function matches the trigger you use in your Python file and the event type matches that except it doesn't start with on.Code:<?xml version="1.0" encoding="ISO-8859-1" ?> <mod id="XXX"> <event type="XXX" function="onXXX"/> </mod>
The last step is to tell Civ4 to load the config xml file you created. To do this, you edit the init.xml file in the Config folder. There is a long list of load lines in that file. Somewhere in there, you put the line for your own mod:
where the XXX is the ID that you put in your xml file in the Config folder. That should do it.Code:<load mod="XXX"/>
Well, that doesn't work.
Here are my 3 files. Did I do something wrong?
No, it's still not working. No unhappiness from training Slaves. (In the test game the city trained 6 Slaves but nothing)I think the issue is your whitespace. Python is very sensitive to whitespace. (XML is not. All it needs is properly closed tags.) Each time you indent, you indent by one tab. Don't use spaces to indent.
This is how your Python code should look. Replace each <tab> with an actual tab.
def onUnitBuilt(argsList):
<tab>pCity = argsList[0]
<tab>pUnit = argsList[1]
<tab>if pUnit.getUnitClass() == gc.getInfoTypeForString("UNITCLASS_SLAVE"):
<tab><tab>gamespeed = CyGame.getGameSpeedType()
<tab><tab>gameSpeedModifier = gc.getGameSpeedInfo(gamespeed).getGrowthPercent()
<tab><tab>iSlave = 8 * gameSpeedModifier / 100
<tab><tab>pCity.changeConscriptAngerTimer(iSlave)
No, it's still not working. No unhappiness from training Slaves. (In the test game the city trained 6 Slaves but nothing)
EDIT:
I also tried tinkering with values in
iSlave = 8 * gameSpeedModifier / 100
by chenging 100 to 10, than to 1000, but nothing.
I did all the above but still see no effect. Not even python popups.Actually, I found two bugs. First of all, the config xml file (not init.xml) does need a module declaration. The second line should be something like this:
<mod id="UnhappyTest" module="UnhappyTest">
Second, in the Python file, CyGame needs to have empty parentheses () right after it and before the period.
Another thing that helps if you are trying to use Python is to enable Python popups. In the Civilization4.ini is a setting for HidePythonPopups. Set that to 0 and every time Python tries to do something and fails it will pop up a little screen telling you what the problem is. Understanding this information is another story, but it can help with debugging.
Okay, it's finally working!It should be UnitClassType. I got it to work but I was doing it fast and didn't quite remember what I did properly. These are the files that I was able to get to work, although I have it triggering on a Musketman.
Also, the Python popups are good for finding errors in syntax. If you try to call a function that doesn't exist, it is likely that nothing will happen and that doesn't generate popups.