Éa dll modding

Pazyryk

Deity
Joined
Jun 13, 2008
Messages
3,584
Well, I compiled and tested my own CvGameCore_Expansion2.dll, so now it's time to get down to business...

I thought I'd lay out my dll plans so folks could give feedback. This is for my total conversion mod, Éa, so the dll itself is not meant to be useful to other modders, though I'm happy if the code is. I'll probably hard-code a few changes, particularly regarding civilian unit stacking. The only constraint is that I need to be able to test the dll in autoplay in an otherwise unmodded BNW game.

I'm new to C++, but program in a few different scripting languages. The model I'm following is Gedemon's RED, using his excellent GitHub page (have other dll modders done anything like this?). I'll eventually set up a similar page for Éa's dll. I've listed all the critical needs for the mod below. I'm most interested in starting with the civilian movement constraints. Breaking civilian 1upt is critical for the mod, but I also think it is an utterly pointless and downright annoying feature in base Civ5.

I've color coded by my current estimate of difficulty: Easy, Medium or Hard. Blue = Done!

Part 1: Civilizations
  • Add table Civilization_Traits that acts in conjunction with Leader_Traits. Works! Civs can have traits too!
  • Change civilization type in-game. Using method below.
  • Change leader type in-game. Using method below.
  • Control of civ meeting and contact. Via two new GameEvents below.
  • New column in Civilizations: FallbackCityListCivType. Needed because mod has >100 Civs, but uses primarily 3 very long city lists (1 per race). It's "fallback" so civ-specific city names will be used if provided.
  • Modify research rate for Techs via GameEvents accumulator. Easy I guess because I can imitate the current city number effect.

New GameEvents:
GameEvents.CanMeetTeam(iTeam, iTeam2) Prevents any contact; war state can't be changed.
GameEvents.CanContactMajorTeam(iTeam, iTeam2) Prevents most per turn interactions among AI civs (e.g., trade) and leader popup for human; DoW is possible (by unit move for human) but peace deal is not.
GameEvents.TechResearchMod(iPlayer, techID) Accumulator, so different effects can add/subtract. Expects signed integer, which is interpreted as percent research modifier.

New Lua methods:
void player:ChangeCivilizationType(civID) Runs PreGame.SetCivilization() and a full Traits reset
void player:ChangeLeaderType(leaderID) Runs PreGame.SetCivilization(), player:SetPersonalityType(), and a full Traits reset


Part 2: Civilian Stacking and Great People Changes

  • --Hard-coded rule changes for civilian stacking:
  • All civilians can stack without limit on land, but only one can build on a plot at a time. --Done!
  • Civilians can stack with units of other civs if not at war. --Done!
  • Civilians can enter foreign cities if not at war --Done!
    --GP-specific stuff
  • GPs can enter a plot with an enemy unit with a GameEvents check. --complicated capture issues
  • GPs can enter an enemy city with a GameEvents check. --as above
  • GPs can convert between non-combat and combat role by new Lua method
  • GPs can stack with combat units even if they are combat units themselves (and both units can attack from same plot)

New GameEvents:
GameEvents.GPCanOccupyPlotWithEnemyUnit(iPlayer, iUnit, iEnemyPlayer, iEnemyUnit)
GameEvents.GPCanEnterForeignCityAtWar(iPlayer, iUnit, iCityOwner, iCity)

New Lua methods:
bool unit:CanEnterCity(city)
void unit:ChangeUnitCombat(iCombatStr, iRangedStr, iUnitCombat) --to make a GP into combat unit, then back to civilian with args 0, 0 ,-1.


Part 3: Miscellaneous
  • Bypass leader scenes (mod supplies own images/framing via standard UI lua/xml modding) I'm 60% done with this but it is nighmareishly hard. Most communication between Dll and Lua is via UI (which we don't have) and that is where the leader scene gets triggered. So I have to rebuild all those lines of communication with new Lua methods (Lua->Dll) and GameEvents (Dll->Lua).
  • Rivers allow internal city connection with a tech.
  • GameEvents.OnUnitCapture(iCapturePlayer, iOriginalOwner, iUnitTypeID). Return of false prevents capture from happening.
 
Civilian stacking was insanely easy...

Spoiler :
Code:
bool CvGameQueries::AreUnitsSameType(UnitTypes eFirstUnitType, UnitTypes eSecondUnitType)
{
	CvUnitEntry* pkFirstUnitInfo = GC.getUnitInfo(eFirstUnitType);
	CvUnitEntry* pkSecondUnitInfo = GC.getUnitInfo(eSecondUnitType);

	if(pkFirstUnitInfo == NULL || pkSecondUnitInfo == NULL)
		return false;

	int eFirstDomain = pkFirstUnitInfo->GetDomainType();
	int eSecondDomain = pkSecondUnitInfo->GetDomainType();

	// antonjs: Added for Smoky Skies scenario. 
	// If unit is DOMAIN_HOVER, its effective domain is a wildcard, equal to the other unit's domain. This prevents HOVER units from disobeying 1UPT.
	if (eFirstDomain == DOMAIN_HOVER && eSecondDomain == DOMAIN_HOVER)
	{
		// Already a match
	}
	else if (eFirstDomain == DOMAIN_HOVER)
	{
		eFirstDomain = eSecondDomain; // Make it a match
	}
	else if (eSecondDomain == DOMAIN_HOVER)
	{
		eSecondDomain = eFirstDomain; // Make it a match
	}

	// Must be in the same domain
	if(eFirstDomain == eSecondDomain)
	{
		// Conversely air units can always stack
		if(eFirstDomain == DOMAIN_AIR)
		{
			return false;
		}

		bool bUnit1Combat = false;
		bool bUnit2Combat = false;

		// Unit 1 is a combat unit?
		if(pkFirstUnitInfo->GetCombat() > 0 || pkFirstUnitInfo->GetRange() > 0)
		{
			bUnit1Combat = true;
		}

		[COLOR="RoyalBlue"]// <<<<< Paz allow civilian stacking
		if(!bUnit1Combat)
		{
			return false;
		}
		// Paz >>>>>[/COLOR]

		// Unit 2 is a combat unit?
		if(pkSecondUnitInfo->GetCombat() > 0 || pkSecondUnitInfo->GetRange() > 0)
		{
			bUnit2Combat = true;
		}

		// Looped unit matches combat or non-combat type?
		if(bUnit1Combat == bUnit2Combat)
		{
			// Unit is the same domain & combat type, so we have a match
			return true;
		}
	}

	return false;
}
Works for human and the AI seems to be handling it fine, at least in my first autoplay session. Saw a couple AI workers in a city (hiding from barbs) and some missionaries briefly stacked (both went on their way the next turn). I'll have to run more autoplay to believe it is totally safe. Gedemon had issues with embarked units stacking, but CvUnit::AreUnitsOfSameType checks for either unit embarked (returning true) before it calls CvGameQueries::AreUnitsSameType.

Oh, and workers can't "stack build" on a single plot as I predicted above. To be complete I'd need some red UI text to tell you why, although it should be instantly obvious.
 
OK, this probably falls under the "I'm new to C++" category of questions, but...

I keep seeing what are clearly being used as constants all over the place, like BARBARIAN_PLAYER or DOMAIN_HOVER, but I can never find where these are set. What am I missing here?
 
CvGameCoreSource\CvGameCoreDLLUtil\include

specifically CvDLLUtilDefines.h and CvEnums.h

You can always double click the "word", right click, and select "Go to definition"
 
Excellent! So I changed above to...
Code:
		// Paz mod
		#if defined(EA_CIVILIAN_RULES)
		if(!bUnit1Combat)
		{
			return false;
		}
		#endif
and defined EA_CIVILIAN_RULES in a new header file. What's really nice is that I can turn on/off code everywhere at once (there will be other code changes for this) or even debug different code variants with different values of EA_CIVILIAN_RULES. Thanks!
 
I'm new to C++, but program in a few different scripting languages. The model I'm following is Gedemon's RED, using his excellent GitHub page (have other dll modders done anything like this?).

I've set up a Github repository for my Wheel of Time mod as well, if you wanted to refer to anything else. It branches for my Custom Missions/Notifications modcomp and for SiegeMod as well, but the master branch (which is the Wheel of Time stuff) includes all changes from those two other mods in addition to its own. Hopefully I'll have done some stuff that's helpful for you since we're both working on total conversion fantasy mods.

EDIT: From a quick glance, you may be able to control teams meeting through the CvTeam::makeHasMet function. Have the function early-out if the lua check fails. I'm not sure if this will interact well with the graphics DLL triggering on screen menus though. (Looks like it might, since there's a gDLL call at the end of that function that looks like it would trigger all the leader scenes and such.)
 
Thanks S3rgeus, I'll take a look at your Github repository.

I played around with blocking CvTeam::meet (return on a GameEvent just before it calls makeHasMet ). Works perfectly except you can't start a war, even by unit movement. The way I'd want it to work is that you can still start a war with unit movement but never end it (until/unless contact re-established). Alternatively, I can allow makeHasMet to occur, but block AI interaction with other AIs and CSs (easy) and human interaction with AI (easy in concept, but requires alteration at ~100 places) and CSs (haven't looked into that).


Anyway, my civilian rules changes has already resulted in a mod: The No More Civilian Traffic Jams (NMCTJs) Mod. Breaking civilian 1upt was very easy. Allowing them to enter plots with foreign units and cities was harder (about 6 changes including in AStar) but not too hard.
 
Top Bottom