Creating Animals

Earendel

Chieftain
Joined
Jun 26, 2014
Messages
12
I'm trying to create wild animals for a mod. I'm at the point where I can have animals of different types spawn around the map by adding them to the Barbarian PlayerID 63. This works to a certain extent, but has the following problems:

Animals will sit on and fortify barbarian camps and refuse to move. (The main problem I'm trying to solve).
Barbarian spawning is reduced or stopped completely depending on the number of nearby animals.
Animals have their names prefixed with 'Barbarian', i.e "Barbarian Lion" (minor issue).
Barbarians and animals coordinate attacks (minor issue).

My first thought was to use some sort of promotion for animal units that makes Barbarian Camps impassable, but since Barbarian Camps are improvement, not features, it doesn't look like there's a good way of doing that.

I've tried creating a new player for animals under PlayerID 62. This would have the advantage of the animals having a different icon color, could be prefixed with "Wild", and could optionally be set to fight with barbarians. This seems like a good solution, but my initial tests are not going well - the main problem being that the game crashes when trying to process the new player's turn. I've searched for other cases where people have achieved something similar, but there don't seem to be any success stories so I'm not sure if it's even possible.

Here's the code I'm using:
Code:
Game.AddPlayer(62, 3, 19);
print("Player 62 added");
local plot = Map.GetPlot( 5, 5 );
plot = plot:GetNearestLandPlot();
local unitId = GameInfo.Units.UNIT_BARBARIAN_WARRIOR.ID
Players[62]:InitUnit(unitId, plot:GetX(), plot:GetY());	
print("unit added");

So my first question - Is adding another barbarian-style player going to be achievable? If so, what do I need to do to prevent the player from crashing.

If not, can anyone think of some alternative options? The only other think I can think of is a script that swaps animals in barbarian camps with a human unit, but that's not a great solution and would look silly.
 
Creating a new barbarian player will require DLL changes. IIRC Pazyryk has done it in his mod, so you can ask him how to do it.
 
Yeah, we have Animals as player 62. But it requires dll work to add them as another "barb faction".

You are free to take/copy any work from our dll. It's all at my GitHub site in the EaDll directory. All changes are documented in EaModding.h; the ones you want are:

#define EA_ANIMAL_PLAYER // Add as player 62 (define ANIMAL_PLAYER, ANIMAL_TEAM enums); all spawning logic on Lua side
#define EA_ANIMAL_BEHAVIOR // Don't enter enemy borders; don't pillage trade route

And even after that, there is some Lua/SQL-side work to do.

---------------------------------

It may be possible to add them without dll work. But it would require a lot of experimentation. First off, Game.AddPlayer(62, 3, 19) won't work because 62 is a city state slot (I changed that in my dll) and you can't add city states after map generation. You can kill an existing city state at game start and then add them back later. I believe that you can add full civ players (id < 22) during the game. In either case, the player is considered dead and all units are removed if they have no city. However, there is a trait XML tag StaysAliveZeroCities that could be used to get around that for a full civ. So, maybe you could add them by sort of hijacking a full civ. But then what do you do about contact/diplo screen? In the end, it will be easier to learn dll modding...
 
Ok thanks. I guess I'll look into DLL modding...

I do find it weird that player type slots are preallocated by ranges. Has anyone looked into modifying the dlls so that players of types (Major, Minor, Barbarian) can be dynamically added to any slot?
 
It's only the barbarian player that's hardcoded at #63 [oh, and the first local human player at #0]. Otherwise, the minors follow the majors in the next available slot.

I was thinking a more dynamic system that allows for an arbitrary number of barbarian player types would be nice.
 
No, it's definitely hard coded. In CvDLLUtilDefines.:
Code:
// do not change this or have MAX_PLAYERS exceed it - this will require the engine to be rebuilt
#define REALLY_MAX_PLAYERS (80)
#define REALLY_MAX_TEAMS (80)

#define MAX_CIV_PLAYERS          (63)
#define MAX_CIV_TEAMS          (MAX_CIV_PLAYERS)

#define MAX_MAJOR_CIVS          (22)
#define MAX_MINOR_CIVS          (MAX_CIV_PLAYERS - MAX_MAJOR_CIVS)

#define MAX_PLAYERS           (MAX_CIV_PLAYERS + 1)
#define MAX_TEAMS           (MAX_PLAYERS)
#define BARBARIAN_PLAYER         ((PlayerTypes)MAX_CIV_PLAYERS)
#define BARBARIAN_TEAM          ((TeamTypes)MAX_CIV_TEAMS)
#define OBSERVER_TEAM           ((TeamTypes)(MAX_MAJOR_CIVS-1))  //This only works because observer mode is multiplayer/debug only                                  //and multiplayer only supports 12 max players                                  //(due to map player restrictions)


Here's that part in Éa. We always add code with precompiler defines so that the original is intact and we can compile mod code or original code by turning a particular define on/off. In this case, the define is EA_ANIMAL_PLAYER so the compiled code is blue:
Code:
// do not change this or have MAX_PLAYERS exceed it - this will require the engine to be rebuilt
[COLOR="Blue"]#define REALLY_MAX_PLAYERS (80)
#define REALLY_MAX_TEAMS (80)[/COLOR]

#ifdef EA_ANIMAL_PLAYER          // Paz - Add Animal player & team
[COLOR="Blue"]#define MAX_CIV_PLAYERS          (62)[/COLOR]
#else
#define MAX_CIV_PLAYERS          (63)
#endif
[COLOR="Blue"]#define MAX_CIV_TEAMS          (MAX_CIV_PLAYERS)

#define MAX_MAJOR_CIVS          (22)
#define MAX_MINOR_CIVS          (MAX_CIV_PLAYERS - MAX_MAJOR_CIVS)[/COLOR]

#ifdef EA_ANIMAL_PLAYER
[COLOR="Blue"]#define MAX_PLAYERS           (MAX_CIV_PLAYERS + 2)[/COLOR]
#else
#define MAX_PLAYERS           (MAX_CIV_PLAYERS + 1)
#endif

[COLOR="Blue"]#define MAX_TEAMS           (MAX_PLAYERS)[/COLOR]

#ifdef EA_ANIMAL_PLAYER
[COLOR="Blue"]#define ANIMAL_PLAYER          ((PlayerTypes)MAX_CIV_PLAYERS)
#define ANIMAL_TEAM           ((TeamTypes)MAX_CIV_TEAMS)
#define BARBARIAN_PLAYER         ((PlayerTypes)(MAX_CIV_PLAYERS + 1))
#define BARBARIAN_TEAM          ((TeamTypes)(MAX_CIV_TEAMS + 1))[/COLOR]
#else
#define BARBARIAN_PLAYER         ((PlayerTypes)MAX_CIV_PLAYERS)
#define BARBARIAN_TEAM          ((TeamTypes)MAX_CIV_TEAMS)
#endif


[COLOR="Blue"]#define OBSERVER_TEAM           ((TeamTypes)(MAX_MAJOR_CIVS-1))  //This only works because observer mode is multiplayer/debug only                                  //and multiplayer only supports 12 max players                                  //(due to map player restrictions)[/COLOR]
So I not only added ANIMAL_PLAYER, but I also shifted other numbers around to accommodate (e.g., reduced MAX_CIV_PLAYERS and MAX_MINOR_CIVS by 1).

This is only part of the modification though. I also changed isBarbarian() and code in 6 or so other places.
 
I guess I was remembering the Lua table I was generating.

I'm well aware of yours, but my C++ is weak and rusty, and I can't get my code to compile (then again, I was trying it with VS 2013, which I think might be introducing some problems). Anyway, based on yours, I was adding one animal player and up to 11 extra barb players/teams. How would I do it without an #ifdef that allows me to check greater-than/less-than (I was thinking I might just have to define 12 booleans):
Code:
// ...
#ifdef MOD_BARB_ENHANCED		// Paz - Add Animal player & team
#define MAX_CIV_PLAYERS			(63 - MOD_NUM_NEW_BARBS)
#else
#define MAX_CIV_PLAYERS			(63)
#endif

#define MAX_CIV_TEAMS			(MAX_CIV_PLAYERS)

#if defined(MOD_GLOBAL_MAX_MAJOR_CIVS)
#define MAX_PREGAME_MAJOR_CIVS	(22)
#define MAX_MAJOR_CIVS			MOD_GLOBAL_MAX_MAJOR_CIVS
#else
#define MAX_MAJOR_CIVS			(22)
#endif
#define MAX_MINOR_CIVS			(MAX_CIV_PLAYERS - MAX_MAJOR_CIVS)

#ifdef MOD_BARB_ENHANCED
#define MAX_PLAYERS			(MAX_CIV_PLAYERS + MOD_NUM_NEW_BARBS + 1)
#else
#define MAX_PLAYERS			(MAX_CIV_PLAYERS + 1)
#endif

#define MAX_TEAMS			(MAX_PLAYERS)

#ifdef MOD_BARB_ENHANCED
#define BARBARIAN_PLAYER		((PlayerTypes)(MAX_CIV_PLAYERS + MOD_NUM_NEW_BARBS))
#define BARBARIAN_TEAM			((TeamTypes)(MAX_CIV_TEAMS + MOD_NUM_NEW_BARBS))
#define ANIMAL_PLAYER			((PlayerTypes)MAX_CIV_PLAYERS + MOD_NUM_NEW_BARBS - 1)
#define ANIMAL_TEAM			((TeamTypes)MAX_CIV_TEAMS + MOD_NUM_NEW_BARBS - 1)
#define BARBARIAN_PLAYER2		((PlayerTypes)(MAX_CIV_PLAYERS + MOD_NUM_NEW_BARBS - 2))
#define BARBARIAN_TEAM2			((TeamTypes)(MAX_CIV_TEAMS + MOD_NUM_NEW_BARBS - 2))
#define BARBARIAN_PLAYER3		((PlayerTypes)(MAX_CIV_PLAYERS + MOD_NUM_NEW_BARBS - 3))
#define BARBARIAN_TEAM3			((TeamTypes)(MAX_CIV_TEAMS + MOD_NUM_NEW_BARBS - 3))
#define BARBARIAN_PLAYER4		((PlayerTypes)(MAX_CIV_PLAYERS + MOD_NUM_NEW_BARBS - 4))
#define BARBARIAN_TEAM4			((TeamTypes)(MAX_CIV_TEAMS + MOD_NUM_NEW_BARBS - 4))
#define BARBARIAN_PLAYER5		((PlayerTypes)(MAX_CIV_PLAYERS + MOD_NUM_NEW_BARBS - 5))
#define BARBARIAN_TEAM5			((TeamTypes)(MAX_CIV_TEAMS + MOD_NUM_NEW_BARBS - 5))
#define BARBARIAN_PLAYER6		((PlayerTypes)(MAX_CIV_PLAYERS + MOD_NUM_NEW_BARBS - 6))
#define BARBARIAN_TEAM6			((TeamTypes)(MAX_CIV_TEAMS + MOD_NUM_NEW_BARBS - 6))
#define BARBARIAN_PLAYER7		((PlayerTypes)(MAX_CIV_PLAYERS + MOD_NUM_NEW_BARBS - 7))
#define BARBARIAN_TEAM7			((TeamTypes)(MAX_CIV_TEAMS + MOD_NUM_NEW_BARBS - 7))
#define BARBARIAN_PLAYER8		((PlayerTypes)(MAX_CIV_PLAYERS + MOD_NUM_NEW_BARBS - 8))
#define BARBARIAN_TEAM8			((TeamTypes)(MAX_CIV_TEAMS + MOD_NUM_NEW_BARBS - 8))
#define BARBARIAN_PLAYER9		((PlayerTypes)(MAX_CIV_PLAYERS + MOD_NUM_NEW_BARBS - 9))
#define BARBARIAN_TEAM9			((TeamTypes)(MAX_CIV_TEAMS + MOD_NUM_NEW_BARBS - 9))
#define BARBARIAN_PLAYER10		((PlayerTypes)(MAX_CIV_PLAYERS + MOD_NUM_NEW_BARBS - 10))
#define BARBARIAN_TEAM10		((TeamTypes)(MAX_CIV_TEAMS + MOD_NUM_NEW_BARBS - 10))
#define BARBARIAN_PLAYER11		((PlayerTypes)(MAX_CIV_PLAYERS + MOD_NUM_NEW_BARBS - 11))
#define BARBARIAN_TEAM11		((TeamTypes)(MAX_CIV_TEAMS + MOD_NUM_NEW_BARBS - 11))
#define BARBARIAN_PLAYER12		((PlayerTypes)(MAX_CIV_PLAYERS + MOD_NUM_NEW_BARBS - 12))
#define BARBARIAN_TEAM12		((TeamTypes)(MAX_CIV_TEAMS + MOD_NUM_NEW_BARBS - 12))
#else
#define BARBARIAN_PLAYER		((PlayerTypes)MAX_CIV_PLAYERS)
#define BARBARIAN_TEAM			((TeamTypes)MAX_CIV_TEAMS)
#endif
// ...
 
It looks right. But makes my head hurt doing the math.

You would have to mod elsewhere in every case that BARBARIAN_PLAYER or BARBARIAN_TEAM appears in the code. It's in isBarbarian() I think, but not only there. Safest to also search for the number "63", since Firaxis sometimes likes to up the challenge level for modders with things like that. And then there is some XML-side work to do (every reference to barb player or leader).

I haven't messed with pushing full civ number up, so the MOD_GLOBAL_MAX_MAJOR_CIVS and MAX_PREGAME_MAJOR_CIVS aren't familiar to me.

It's not really C++ if I understand correctly. It's pre-compiler instructions which is kind of different. The precompiler will look through all code before compiling and, for example, replace the text "MAX_MAJOR_CIVS" with "22". Or literally delete text within #ifdef block that isn't defined. What you have after that is compiled as C++ code. (Keep in mind that some percentage of everything I say may be wrong, since I'm only learning this myself...)
 
The MOD_GLOBAL_MAX_MAJOR_CIVS stuff comes from the Community Patch, on which I've based my changes.

I've touched all of the places where you guys have made modifications for the animal player; I'm just having trouble with understanding the rest of Firaxis' spaghetti code.
 
So it's not working yet, but this is my first attempt at lua and C++ so that's not exactly surprising.

I'm not sure how to tell definitively whether the dll has compiled with the animal code correctly. In the lua file, calling Players[62]:CivilizationType() throws "attempt to call method 'CivilizationType' (a nil value)" so I think Players[62] is nil. From looking at the C++ it looks like the player should already have been initialised, but maybe Game.addPlayer() still needs to be called in the lua somewhere for the animal slot to become active?

Also, the lack of an animal player id could be down to a problem with the xml. If for some reason the animal Civ were not available then presumably setting up the animal player would fail. I have checked that ANIMAL_CIVILIZATION is set up in PostDefines, etc...

So am I just missing some lua setup, or do I need to start debugging the dll?

Thanks
 
Should be GetCivilizationType(). Error is telling you that there is no CivilizationType method, not that there is no Players[62].

With my dll changes you should now have Defines.ANIMAL_PLAYER (added by dll, not xml/sql). So check that in Fire Tuner. The actual player is added by dll PreGame changes, so no need for AddPlayer.

Also, there is some non-dll work to do:

1) add to Civilizations table.
I added them as CIVILIZATION_ANIMALS and made everything else match CIVILIZATION_BARBARIAN, including subtables. Give them LEADER_BARBARIAN (used by barbarians and minors anyway).

For Civilization_UnitClassOverrides I disallow ALL units the easy way:
Code:
INSERT INTO Civilization_UnitClassOverrides (CivilizationType, UnitType, UnitClassType)
SELECT 'CIVILIZATION_ANIMALS', NULL, Type FROM UnitClasses;
--only works if this code loads after UnitClasses table! To tell the truth, I don't think this subtable really matters though. For the real Barb player, it determines which units the dll should not spawn. But I'm about 90% sure that the dll isn't trying to spawn for this player anyway.


2) add to PostDefines
Code:
INSERT INTO PostDefines (Name, Key, "Table") VALUES
('ANIMALS_CIVILIZATION', 'CIVILIZATION_ANIMALS', 'Civilizations');
This is paralleling CIVILIZATION_ANIMALS barbs again. It creates a Defines.ANIMALS_CIVILIZATION, which (iirc) the dll uses to connect that particular civilization to that particular player. (Defines.ANIMAL_PLAYER is 62 but Defines.ANIMALS_CIVILIZATION is whatever it happens to be in your Civilizations table.)

3) Spawn away using Lua! (It's OK to spawn units listed in Civilization_UnitClassOverrides)
 
My xml set-up is a bit different. My animal civ type is CIVILIZATION_ANINMAL not CIVILIZATION_ANINMALS, but I don't think the problem is with the xml / database.

I've managed to confirm that Player 62 is not being added.
print(Players[63]:GetCivilizationType()) returns 19
print(Players[62]:GetCivilizationType()) returns -1

Also in the Fire Tuner Players tab there are 62 slots, which makes me think that EA_ANIMAL_PLAYER is not being set in the precompiler at all. I've got a feeling that my equivalent of your EaModding.h file is not being processed. It is included in the project under Header files, but do I need to register it somewhere as part of some kind of precompiler config?
 
I started exploring the code and found that I could add my .h file to CvGameCoreDLLPCH.h, which had an effect on the build. Apparent some stuff was missing from CvGlobals.h. I don't know why but when I search in EaDLL in GitHub that file won't appear in the results. Anyway, that version built and it works!

I've just read whoward69's post, so now I'm going to have to go back and clean up my new code.

Thanks for the help!
 
My xml set-up is a bit different. My animal civ type is CIVILIZATION_ANINMAL not CIVILIZATION_ANINMALS, but I don't think the problem is with the xml / database.
Yes, I understand that CIVILIZATION_ANIMAL is the Type in the Civilizations table, but there is nothing telling the dll to hook up this civilization to player 62.

Hence,
I've managed to confirm that Player 62 is not being added.
print(Players[63]:GetCivilizationType()) returns 19
print(Players[62]:GetCivilizationType()) returns -1
No, you're misinterpreting that. Player 62 is added. But its civilization type is -1 (when it probably should be 20 or something like that, depending on your Civilizations table).

I know it's confusing but it exactly parallels the barb civ. You have a civilization type called CIVILIZATION_BARBARIANS, but then you have a Defines called BARBARIANS_CIVILIZATION. (This is from memory, so check to make sure those S's are there or not.)

Defines.BARBARIAN_PLAYER = 63
GameInfo.Civilizations.CIVILIZATION_BARBARIANS.ID = ? (depends on your table)
Defines.BARBARIANS_CIVILIZATION = ? (same number as above)
[again, from memory and I'm not confident about my S's]


I don't know why but when I search in EaDLL in GitHub that file won't appear in the results.
Keep in mind that not all files are in CvGameCoreDLL_Expansion2. It confused the heck out of me, because they are all in the "CvGameCoreDLL_Expansion2 project" in VS, which looks like a directory. But a lot of stuff in the project is really outside of that directory.

I suspect you'll save yourself some pain later if you follow whoward69's guild to set up a mod.h file rather than using CvGameCoreDLLPCH.h.
 
Yes, I understand that CIVILIZATION_ANIMAL is the Type in the Civilizations table, but there is nothing telling the dll to hook up this civilization to player 62.

Hence,No, you're misinterpreting that. Player 62 is added. But its civilization type is -1 (when it probably should be 20 or something like that, depending on your Civilizations table).

I know it's confusing but it exactly parallels the barb civ. You have a civilization type called CIVILIZATION_BARBARIANS, but then you have a Defines called BARBARIANS_CIVILIZATION. (This is from memory, so check to make sure those S's are there or not.)

Defines.BARBARIAN_PLAYER = 63
GameInfo.Civilizations.CIVILIZATION_BARBARIANS.ID = ? (depends on your table)
Defines.BARBARIANS_CIVILIZATION = ? (same number as above)
[again, from memory and I'm not confident about my S's]
Ok, GetCivilizationType() may have been a bad example. At the time I also managed to verify that there were still 62 player slots at the time, so that animal player had not been added because the dll was not compiling with the animal changes. Now that the dll is working there are 61 slots left and GetCivilizationType() returns the correct id.

Keep in mind that not all files are in CvGameCoreDLL_Expansion2. It confused the heck out of me, because they are all in the "CvGameCoreDLL_Expansion2 project" in VS, which looks like a directory. But a lot of stuff in the project is really outside of that directory.

I suspect you'll save yourself some pain later if you follow whoward69's guild to set up a mod.h file rather than using CvGameCoreDLLPCH.h.
That was a bit confusing at first, but the main thing that made things difficult was that I was using this search to try and find all the animal changes:
https://github.com/Pazyryk/EaDLL/search?q=animal&ref=cmdform
but the search results didn't list the changes to this file for some reason:
https://github.com/Pazyryk/EaDLL/blob/Ea/CvGameCoreDLL_Expansion2/CvGlobals.h
Once I found that and figured out how to get my mod.h file included the rest was quite easy.

Thanks for the help.
 
That is weird. I haven't tried using search at the GitHub website and apparently it isn't very reliable.

In general, GitHub is pretty awesome however, once you get along in the learning curve a little bit. It would have allowed you to "switch branches" to the EaDll and then search it in your own VS from your own computer. Another example is that I was just able to switch to and generate 6 dlls from past versions to narrow down a particular (intermittent) bug that I introduced about 20 days ago. It's a one line command, then Build from VS.
 
Back
Top Bottom