R.E.D. DLL for Brave New World

Gedemon

Modder
Super Moderator
Joined
Oct 4, 2004
Messages
12,825
Location
France
I've switched the development of my DLL mod to Brave New World only, as I don't have the capacity to debug the code for 3 versions of the game...

So this is the DLL that I will use for next version of R.E.D. WWII and Another History.

All features are OFF by default, they are activated using different methods:

Options (using XML, SQL, Lua or direct selection in any advanced setup screen)
  • GAMEOPTION_CAN_STACK_IN_CITY: allow illimited unit stacking in cities
  • GAMEOPTION_CAN_ENTER_FOREIGN_CITY: allow placement of units in friendly cities (with open border or ally relation for CS)
  • GAMEOPTION_REBASE_IN_FRIENDLY_CITY: allow rebasing of aircraft in friendly cities (with open border or ally relation for CS)
  • GAMEOPTION_CIVILIAN_MOVE_THROUGH: Civilian units can move through foreign units
  • GAMEOPTION_BEST_DEFENDER_BY_HEALTH: force the combat engine to pick the unit that will have the most Health left after the attack to defend a plot. If unchecked, the unit with the best fighting strength is used (even if it has only 1 HP left...)


Updates of new columns in the Units table (using XML or SQL)
  • <StackClass> : can be any string, each different class can stack with another class, but not units of the same class.
  • <MaxStack> : MaxStack: 0 (default) means use plots limit, -1 means no stacking limit, values above 0 means a specific plot limit for this unit type. Can be combined with StackClass.


Updates of new values in the Defines table (using XML or SQL)

City Stacking Base Limit for unit of same type (or <StackClass>) in a domain (0 = use PLOT_UNIT_LIMIT, -1 = unlimited)
  • CITY_LAND_UNIT_LIMIT (default = 0)
  • CITY_SEA_UNIT_LIMIT (default = 0)
  • CITY_AIR_UNIT_LIMIT (default = -1)

Change/Set/Get functions exposed to Lua (using Lua or source code)

See usage example in next post.
  • City:GetAirStackLimit()
  • City:SetAirStackLimit()
  • City:ChangeAirStackLimit()
  • City:GetLandStackLimit()
  • City:SetLandStackLimit()
  • City:ChangeLandStackLimit()
  • City:GetSeaStackLimit()
  • City:SetSeaStackLimit()
  • City:ChangeSeaStackLimit()


Note that there is an order of priority for the stacking rules:
- If you activate GAMEOPTION_CAN_STACK_IN_CITY, then the unit stacking class and city specific stacking value will have no effect, it will be unlimited stacking in cities for all.
- If units which have a specific <StackClass> are in a city, then the limit will be the higher value between the units <MaxStack> and the city limit for the units domain (air, sea, land)


To update the tables from another mod, just remember to set the References in the mod's properties so that R.E.D. DLL is loaded first.
Here's the mod ID for that use: 2896c6d4-0273-4527-813b-b9ab58f0b95e


List of added Game Events (to use in Lua)

  • GameEvents.CombatResult(iAttackingPlayer, iAttackingUnit, attackerDamage, attackerFinalDamage, attackerMaxHP, iDefendingPlayer, iDefendingUnit, defenderDamage, defenderFinalDamage, defenderMaxHP, iInterceptingPlayer, iInterceptingUnit, interceptorDamage, plotX, plotY)
  • GameEvents.CombatEnded : use the same parameters as CombatResult
  • GameEvents.MustAbortAttack : "TestAll" event, use the same parameters as CombatResult, can be used to abort an AI attack on a plot.
  • GameEvents.PushingMissionTo(iPlayer, iUnit, x, y, iMission)
  • GameEvents.TacticalAILaunchUnitAttack(iPlayer, iUnit, x, y)


Download available on CFC Database

Source code available on GitHub


You may want to also use my small UI fix for Unit Stacking to display all unit's flag when stacking:

ui_unitstacking_6V9.jpg
 
Excellent. The stacking looks like exactly what I need, so I'll probably move my mod over to this as I'm updating for BNW.

I only have a few other dll needs, so I'll just list them here in the hopes that they might be deemed sufficiently general for others to want too (or maybe they already exist):
  1. Actual invisibility (there are details to work out related to inadvertent stacking, but I don't think they are prohibitive in any way).
  2. Ability for specific units to enter foreign cities.
  3. Ability to get a unit object safely (for info in Lua) before it is eliminated by engine for any reason.
 
I've edited the first post to add the test version of my UI fix for Unit Stacking to display unit's flag when stacking, could be useful now :D
 
Without open border you mean ?
Yes.

But your question implies that this is possible now with open borders. I don't mean "pass through", I mean enter the city and do something, or just sit there. It was something that you could do in Civ4 easy enough. But in Civ5 we have to do everything (like religious conversion) outside of foreign cities rather than in them. This goes hand-in-hand with stacking because you have to alter that for this to make any sense.

The DLL prevents you from selecting a foreign city as a destination, and also (if you subvert that) kicks out foreign units between turns. It's possible to kludge around both these issues in Lua (as I have already done) but it's very messy.
 
It's possible now with open border and this DLL using GAMEOPTION_CAN_ENTER_FOREIGN_CITY.

I may add 2 tags in the unit table like <CanAlwaysEnterForeignCity> and <CanEnterForeignCity> to make it unit specific (ATM the option is a on/off switch for all land/sea units)
 
Not sure if suggestions are needed or wanted, but...

I sure wish that there was a method for persisting data (for mostly Lua modders) that didn't suck. We basically have two methods that rely either on ScriptData (serializing/deserializing text) or SaveGameDB (read/writing a DB). There are a variety of reasons why both of these suck, which I'll list if anyone wants me to.

Would it be possible for a dll mod to persist table data? It doesn't necessarily need to be recursive or handle complicated table structures (as TableSaverLoader). But it would be nice if it acted on Lua table data without need for explicit Load/Save commands. I guess what I'm asking for is something like a table pointer that could be associated with objects (units, plots, etc.). Lua data thus pointed to would be persisted through game exit/reload. But I guess you can't intercept saves on the dll side either, so this might be impossible. (Well, unless we used Lua magic tables that know when they have been changed.)

Only comes up for me because I'm now exceeding 2000 things to persist and DB writes are approaching 1 sec for autosaves.
 
I'd like to have that possibility (which means I don't think I can do it, else I would have tried... :o )
 
Example 1: Configure the units stacking feature using <StackClass>

In a SQL file with OnModActivated/UpdateDatabase Action
Spoiler :
Code:
/* Scout as a separate stacking class, will stack with civilian and military units */
UPDATE Units SET StackClass ='RECON' WHERE Type = 'UNIT_SCOUT';

/* Worker as a separate stacking class, will stack with settlers, great people and military units */
UPDATE Units SET StackClass ='WORKER' WHERE Type = 'UNIT_WORKER';

/* Unlimited stacking for Great People */
UPDATE Units SET MaxStack ='-1', StackClass ='GREAT_PEOPLE' WHERE Special = 'SPECIALUNIT_PEOPLE';

/* Ranged land units as support class (stack with other units) */
UPDATE Units SET StackClass ='RANGED' WHERE RangedCombat > 0 AND Domain = 'DOMAIN_LAND';


Example 2: Set limited and dynamic stacking in cities

The goal here is to have a stacking limit that can be raised by specific buildings in cities. In the example, aircraft are limited to 3 per cities, missiles are put in a stacking class to keep them unlimited, airbases raise the aircraft limit by 3 and airport by 5 more. The mechanism is also applied to land and sea units.

In a SQL file with OnModActivated/UpdateDatabase Action
Spoiler :
Code:
-----------------------------------------------
-- DLL Stacking setup
-----------------------------------------------

/* Unlimited stacking for Missiles */
UPDATE Units SET MaxStack ='-1', StackClass ='MISSILE' WHERE Special = 'SPECIALUNIT_MISSILE';

/* City initial stacking limits */
UPDATE Defines SET Value = 1		WHERE Name = 'CITY_LAND_UNIT_LIMIT';
UPDATE Defines SET Value = 1		WHERE Name = 'CITY_SEA_UNIT_LIMIT';
UPDATE Defines SET Value = 3		WHERE Name = 'CITY_AIR_UNIT_LIMIT';


-----------------------------------------------
-- Lua Stacking setup (won't do anything without the corresponding Lua functions)
-----------------------------------------------

/* Add new column for effect of buildings on number of units that can stack in a city */
ALTER TABLE Buildings ADD COLUMN AirStackChange integer DEFAULT '0';
ALTER TABLE Buildings ADD COLUMN LandStackChange integer DEFAULT '0';
ALTER TABLE Buildings ADD COLUMN SeaStackChange integer DEFAULT '0';

/* Land stacking */
UPDATE Buildings SET LandStackChange ='1' WHERE BuildingClass = 'BUILDINGCLASS_BARRACKS';
UPDATE Buildings SET LandStackChange ='1' WHERE BuildingClass = 'BUILDINGCLASS_CASTLE';
UPDATE Buildings SET LandStackChange ='1' WHERE BuildingClass = 'BUILDINGCLASS_HIMEJI_CASTLE';
UPDATE Buildings SET LandStackChange ='3' WHERE BuildingClass = 'BUILDINGCLASS_MILITARY_BASE';
UPDATE Buildings SET LandStackChange ='1' WHERE BuildingClass = 'BUILDINGCLASS_RED_FORT';

/* Air stacking */
UPDATE Buildings SET AirStackChange ='5' WHERE BuildingClass = 'BUILDINGCLASS_AIRPORT';
UPDATE Buildings SET AirStackChange ='3' WHERE BuildingClass = 'BUILDINGCLASS_MILITARY_BASE';

/* Sea stacking */
UPDATE Buildings SET SeaStackChange ='1' WHERE BuildingClass = 'BUILDINGCLASS_HARBOR';
UPDATE Buildings SET SeaStackChange ='1' WHERE BuildingClass = 'BUILDINGCLASS_SEAPORT';
UPDATE Buildings SET SeaStackChange ='2' WHERE BuildingClass = 'BUILDINGCLASS_MILITARY_BASE';

In a Lua file with InGameUIAddin Content
Spoiler :
Code:
function SetStackingLimitOnNewCity(iPlayer, x, y)
	print ("-------------------------------------")
	print ("Set Stacking limits in new city...")
	local player = Players[iPlayer]
	local city = GetPlot(x,y):GetPlotCity()
	SetCityStackingLimit(city)
end
GameEvents.PlayerCityFounded.Add(SetStackingLimitOnNewCity)

function SetCityStackingLimit(city)
	local airLimit = GameDefines.CITY_AIR_UNIT_LIMIT
	local seaLimit = GameDefines.CITY_SEA_UNIT_LIMIT
	local landLimit = GameDefines.CITY_LAND_UNIT_LIMIT
	for building in GameInfo.Buildings() do 
		if (city:GetNumBuilding(building.ID) > 0  ) then 
			airLimit = airLimit + building.AirStackChange
			seaLimit = seaLimit + building.SeaStackChange
			landLimit = landLimit + building.LandStackChange
		end
	end
	if airLimit > 0 then
		city:SetAirStackLimit(airLimit)
	end
	if seaLimit > 0 then
		city:SetSeaStackLimit(seaLimit)
	end
	if landLimit > 0 then
		city:SetLandStackLimit(landLimit)
	end
	print (" - " .. tostring(city:GetName()) .."		Air = " .. tostring(airLimit) ..", Sea = " .. tostring(seaLimit) ..", Land = " .. tostring(landLimit), bDebug)
end

function SetPlayerCitiesStackingLimit(iPlayer)

	local player = Players[iPlayer]
	if not player then
		return
	end
	
	if not player:IsEverAlive() then
		return
	end

	print ("Set Stacking limits in cities for " .. tostring(player:GetName()), bDebug)
	print("-------------------------------------", bDebug)

	for city in player:Cities() do
		SetCityStackingLimit(city)
	end
end
GameEvents.PlayerDoTurn.Add(SetPlayerCitiesStackingLimit)

function InitializeCitiesStackingLimit()
	for iPlayer = 0, GameDefines.MAX_PLAYERS-1 do
		SetPlayerCitiesStackingLimit(iPlayer)
	end
end
Events.SequenceGameInitComplete.Add(InitializeCitiesStackingLimit)
There is one limitation to that code: changes in stacking from buildings that are rushed take effect the next turn.
 
v.6 uploaded, examples of use added in the second post.

Change:
- Option to select the defending unit by health instead of strength on a plot
- Allow dynamic stacking limit in cities (see example #2)
- Add some GameEvents from the old RED DLL
 
Hi Gedemon,

Thank you, can I use your DLL in my mod?

To use it, I just import the new CvGameCore_Expansion2.DLL into my mod like before right?
 
Yes.

You can do as before, but since patch 1.0.3.70 it's recommended to use the OnGetDLLPath/SetDLLPath action from the mod's property tab.

You can also just set your mod's Dependency to include this one.
 
Hi Gedemon,

I tested your new DLL, but I encounter error. Here is my code:

Code:
function Aegis (iAttackingPlayer, iAttackingUnit, attackerDamage, attackerFinalDamage, attackerMaxHP, iDefendingPlayer, iDefendingUnit, defenderDamage, defenderFinalDamage, defenderMaxHP, iInterceptingPlayer, iInterceptingUnit, interceptorDamage, plotX, plotY)
	print("BEGIN Aegis");
	local aPlayer = Players[iAttackingPlayer];
	print("aPlayer", aPlayer);
	print("iAttackingPlayer", iAttackingPlayer);
	local aUnit = aPlayer:GetUnitByID(iAttackingUnit);
	print("aUnit", aUnit);
	local aType = aUnit:GetUnitType();
	print("aType", aType);
	local aName = GameInfo.Units[aType].Type;
	print("aName", aName);
	local aText = Locale.ConvertTextKey(GameInfo.Units[aType].Description);
	print("aText", aText);
	local aNuke = GameInfo.Units[aType].NukeDamageLevel;
	print("aNuke", aNuke);
	local p = Game.GetActivePlayer()
	local intercept = false
	
	if ( aName == "UNIT_SCUD" ) then
		print("Scud");
		local Mchance = 100;
		local diceroll = Map.Rand(100, "Missile Interception chance");
		if ( diceroll < Mchance ) then
			intercept = true;
			print("Intercept", intercept);
			aUnit:Kill();
			print("killed")
			return true
		end
	end
	
	print("END Aegis");

	return false
	
end
GameEvents.CombatResult.Add( Aegis )

First I used an infantry unit (named Liberation Army) to attack another unit. Both units are alive after combat. The code runs well:

Project_Aegis: BEGIN Aegis
Project_Aegis: aPlayer table: 13996378
Project_Aegis: iAttackingPlayer 0
Project_Aegis: aUnit table: 4956CBD8
Project_Aegis: aType 178
Project_Aegis: aName UNIT_LIBERATION_ARMY
Project_Aegis: aText Liberation Army
Project_Aegis: aNuke -1
Project_Aegis: END Aegis
CityBannerManager: CityBanner CombatBegin
CityBannerManager: CityBanner CombatEnd

Then I tried to use a missile (named Scud) to attack. I set the interception chance to 100% so that interception happens. The output:

Project_Aegis: BEGIN Aegis
Project_Aegis: aPlayer table: 13996378
Project_Aegis: iAttackingPlayer 0
Project_Aegis: aUnit table: 49F60B38
Project_Aegis: aType 186
Project_Aegis: aName UNIT_SCUD
Project_Aegis: aText Scud
Project_Aegis: aNuke -1
Project_Aegis: Scud
Project_Aegis: Intercept true
Project_Aegis: killed

Then the game is hang here.

Can give teach me how to solve this? Thank you.:)
 
try with GameEvents.MustAbortAttack (and add "return true" after killing the unit)
 
IIRC there is an optionnal boolean to delay the unit death, try with aUnit:Kill(true)
 
IIRC there is an optionnal boolean to delay the unit death, try with aUnit:Kill(true)

Thank Gedemon. MustAbortAttack and aUnit:Kill(true) work.
Project_Aegis: BEGIN Aegis
Project_Aegis: aPlayer table: 1341FD00
Project_Aegis: iAttackingPlayer 0
Project_Aegis: aUnit table: 141FDF18
Project_Aegis: aType 186
Project_Aegis: aName UNIT_SCUD
Project_Aegis: aText Scud
Project_Aegis: aNuke -1
Project_Aegis: Scud
Project_Aegis: Intercept true
Project_Aegis: killed
CityBannerManager: CityBanner CombatBegin
CityBannerManager: CityBanner CombatEnd

However, it takes around 10s to resolve the combat, i.e CityBannerManager: CityBanner CombatEnd appears 10s after CityBannerManager: CityBanner CombatBegin :rolleyes: which is quite slow...
 
thanks for the report, I'll check that when back home next week.
 
Anyone else having problems compiling this DLL? I try to do it w/o any changes and still get errrors. Step by step from anyone?
 
Updated to patch 1.0.3.144, uploaded on CFC database...

Hopefully it will fix the trade's screen bug.
 
Back
Top Bottom