Assigning AI to the same team breaks the mod

Vipermist

Chieftain
Joined
Aug 25, 2007
Messages
8
Placing AI players on teams in single player custom games or multiplayer games, causes the player to be unable to attack animals.

I could not find any mention of this problem in the FAQ, however it appears other players have noted the same problem in this thread

Are there plans to fix this? Or will this be forever a limitation of the mod?
 
What version you are using?

Its fixed in SVN most likely.

https://github.com/caveman2cosmos/Caveman2Cosmos/wiki/Using-SVN

I have tested this in both a v41 fresh installation standalone and also using the latest SVN version v41.1.3907 at the time of posting.

The problem is not fixed in the SVN version and is present in both versions.

To replicate, start any custom game with more than one AI player assigned to the same team. The player will be unable to attack animals.
 
Are you assigning these as a scenario? Never had this trouble in our game setups and one of our guys on discord tested and couldn't replicate...
 
Are you assigning these as a scenario? Never had this trouble in our game setups and one of our guys on discord tested and couldn't replicate...

No scenario, game setup is standard custom game, on the included c2c maps

When it happens will usually get messages that the green man is first to discover X landmark or first to navigate the world. Entering world builder diplomacy mode shows green man teams of beasts and predators are not at war with the player and occupy teams <40. usually teams 35-40.

In custom games with AI's all set on their own teams, all green man teams start at 40 and above and are at war with the player, and the mod functions normally. Thus, the problem with adding multiple ai to the same team in custom games breaks something.
 
No scenario, game setup is standard custom game, on the included c2c maps

When it happens will usually get messages that the green man is first to discover X landmark or first to navigate the world. Entering world builder diplomacy mode shows green man teams of beasts and predators are not at war with the player and occupy teams <40. usually teams 35-40.

In custom games with AI's all set on their own teams, all green man teams start at 40 and above and are at war with the player, and the mod functions normally. Thus, the problem with adding multiple ai to the same team in custom games breaks something.
What game options are you using?
 
No scenario, game setup is standard custom game, on the included c2c maps

When it happens will usually get messages that the green man is first to discover X landmark or first to navigate the world. Entering world builder diplomacy mode shows green man teams of beasts and predators are not at war with the player and occupy teams <40. usually teams 35-40.

In custom games with AI's all set on their own teams, all green man teams start at 40 and above and are at war with the player, and the mod functions normally. Thus, the problem with adding multiple ai to the same team in custom games breaks something.
If you set team numbers too high it would be easy to screw up the NPCs. Are you setting team numbers as low as they can go or at the upper end count?
 
Game setup options are all unchecked, though changing them has no effect on bug.
Example game setup

Example world builder diplomacy screen showing ai teaming problem causing animal factions to not be at war with player. I don't normally enter worldbuilder in game, but have done so here to show the problem.
 
Confirmed the problem exists and that it would have something to do with what you just said - the Beast team appears to not be 'at war' with the player team. The Predator team IS at war so understandably a quick check into the issue may not have seemed to replicate the problem.

This was a normal random map setup - I did make player 2 and 3 on the same team (team 2) and MAYBE that has something to do with it. @Toffer90 or @MattCA I don't have a lot of time to go digging into how this failure to set the teams at war properly took place, but there's been some tampering in those areas recently and I'm not as familiar with them as I was a number of years ago so I was hoping maybe one of you guys could use this save to take a look. My hunter in the east cannot attack the viper it's next to in this save so it's easily confirmed with this save but how the failure to setup takes place is probably more the mystery. This is just to show replicability.

Seems important enough to delay .5 release. I might have some time later today or tomorrow to take a deeper look but I think one of you might be more qualified.
 

Attachments

Ok, I figured it out, and it has been a problem all the way since we introduced more than one NPC team.
The team number assigned to the NPC's have not been the constant the rest of the code expects them to be, so for each extra player added to any team, the team index number of all the NPC's got offset by one.
E.g. if two players were on the same team, the beast NPC would get team index 39 and predator 40 while the code expects it to be 40 and 41.
If three players is added to the same team, then the beast team gets 38, predator 39, and prey 40.
If two teams have two players, then that's the same as if one team had three players.

This all happens when the NPC teams are initialized where it loops through the player indexes above 39 and initialize each of the NPC's, the thing is that for each player index it does this to get its team index:
"TeamTypes eTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getID();"
The game makes some assumptions about team offsets based on how many players have abandoned their natural team number, so the newly initilized NPC will have taken a team number that is lower than its player index by default.
 
Ok, I figured it out, and it has been a problem all the way since we introduced more than one NPC team.
The team number assigned to the NPC's have not been the constant the rest of the code expects them to be, so for each extra player added to any team, the team index number of all the NPC's got offset by one.
E.g. if two players were on the same team, the beast NPC would get team index 39 and predator 40 while the code expects it to be 40 and 41.
If three players is added to the same team, then the beast team gets 38, predator 39, and prey 40.
If two teams have two players, then that's the same as if one team had three players.

This all happens when the NPC teams are initialized where it loops through the player indexes above 39 and initialize each of the NPC's, the thing is that for each player index it does this to get its team index:
"TeamTypes eTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getID();"
The game makes some assumptions about team offsets based on how many players have abandoned their natural team number, so the newly initilized NPC will have taken a team number that is lower than its player index by default.
Hm.. I thought that when I worked on this, the NPC team numbers were just as explicitly assigned as the player numbers were... as in NPC1 = player # 40 and team #40. Rather than being assigned by any sort of derivation on game setup.

Anyhow, thanks for figuring this out! I figured it was something along these lines but thought team IDs were as explicitly enumerated as Player ones, even within the same few paragraphs of code where player IDs are assigned to NPCs.
 
Hm.. I thought that when I worked on this, the NPC team numbers were just as explicitly assigned as the player numbers were... as in NPC1 = player # 40 and team #40. Rather than being assigned by any sort of derivation on game setup.

Anyhow, thanks for figuring this out! I figured it was something along these lines but thought team IDs were as explicitly enumerated as Player ones, even within the same few paragraphs of code where player IDs are assigned to NPCs.
This is from the earliest github version
Code:
            else if (ePlayer == NPC1_PLAYER && GC.getDefineINT("NPC1_CIVILIZATION") != GC.getDefineINT("BARBARIAN_CIVILIZATION"))
            {
                eLeader = (LeaderHeadTypes)GC.getDefineINT("NPC1_LEADER");
                eCivilization = (CivilizationTypes)GC.getDefineINT("NPC1_CIVILIZATION");
                addPlayer(ePlayer, eLeader, eCivilization, false);
                GET_PLAYER(ePlayer).setNewPlayerAlive(true);
                TeamTypes eTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getID();
                GET_TEAM(eTeam).init(eTeam);
                GC.getInitCore().setTeam(ePlayer, eTeam);
            }
 
This is from the earliest github version
Code:
            else if (ePlayer == NPC1_PLAYER && GC.getDefineINT("NPC1_CIVILIZATION") != GC.getDefineINT("BARBARIAN_CIVILIZATION"))
            {
                eLeader = (LeaderHeadTypes)GC.getDefineINT("NPC1_LEADER");
                eCivilization = (CivilizationTypes)GC.getDefineINT("NPC1_CIVILIZATION");
                addPlayer(ePlayer, eLeader, eCivilization, false);
                GET_PLAYER(ePlayer).setNewPlayerAlive(true);
                TeamTypes eTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getID();
                GET_TEAM(eTeam).init(eTeam);
                GC.getInitCore().setTeam(ePlayer, eTeam);
            }
Yep.. that's some Bill refactoring stuff. They used to be explicit assigned. A line for each for player ID assignments and for Team ID assignments. I believe he felt that was not an elegant solution.
 
Yep.. that's some Bill refactoring stuff. They used to be explicit assigned. A line for each for player ID assignments and for Team ID assignments. I believe he felt that was not an elegant solution.
This is from rev 9444 (13. February 2017)
Code:
            else if (ePlayer == NPC1_PLAYER && GC.getDefineINT("NPC1_CIVILIZATION") != GC.getDefineINT("BARBARIAN_CIVILIZATION"))
            {
                eLeader = (LeaderHeadTypes)GC.getDefineINT("NPC1_LEADER");
                eCivilization = (CivilizationTypes)GC.getDefineINT("NPC1_CIVILIZATION");
                addPlayer(ePlayer, eLeader, eCivilization, false);
                GET_PLAYER(ePlayer).setNewPlayerAlive(true);
                TeamTypes eTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getID();
                GET_TEAM(eTeam).init(eTeam);
                GC.getInitCore().setTeam(ePlayer, eTeam);
            }
 
I wonder if it fixes scenario bug with revolutions and NPCs.
That is while playing on scenarios revolutions can bug out and use NPCs as real civilizations.
 
This is from rev 9444 (13. February 2017)
Code:
            else if (ePlayer == NPC1_PLAYER && GC.getDefineINT("NPC1_CIVILIZATION") != GC.getDefineINT("BARBARIAN_CIVILIZATION"))
            {
                eLeader = (LeaderHeadTypes)GC.getDefineINT("NPC1_LEADER");
                eCivilization = (CivilizationTypes)GC.getDefineINT("NPC1_CIVILIZATION");
                addPlayer(ePlayer, eLeader, eCivilization, false);
                GET_PLAYER(ePlayer).setNewPlayerAlive(true);
                TeamTypes eTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getID();
                GET_TEAM(eTeam).init(eTeam);
                GC.getInitCore().setTeam(ePlayer, eTeam);
            }
Maybe all this is in a different section that I'm assuming. I can't recall where Barb Player # and Team # was originally defined explicitly but when I was working on this before, there was a line of code to define each just as explicitly, and not in a loop at all. This may have been somewhere in a different portion when the game is initialized and even then may have redefined some that had previously been explicitly determined. Just now checking, that portion of code is in CvDefines:
Code:
#define MAX_PC_PLAYERS                                    (40)
#define MAX_PC_TEAMS                                    (40)
#define BEAST_PLAYER                                    ((PlayerTypes)40)
#define BEAST_TEAM                                        ((TeamTypes)40)
#define PREDATOR_PLAYER                                    ((PlayerTypes)41)
#define PREDATOR_TEAM                                    ((TeamTypes)41)
#define PREY_PLAYER                                        ((PlayerTypes)42)
#define PREY_TEAM                                        ((TeamTypes)42)
#define INSECT_PLAYER                                    ((PlayerTypes)43)
#define INSECT_TEAM                                        ((TeamTypes)43)
#define NPC4_PLAYER                                        ((PlayerTypes)44)
#define NPC4_TEAM                                        ((TeamTypes)44)
#define NPC3_PLAYER                                        ((PlayerTypes)45)
#define NPC3_TEAM                                        ((TeamTypes)45)
#define NPC2_PLAYER                                        ((PlayerTypes)46)
#define NPC2_TEAM                                        ((TeamTypes)46)
#define NPC1_PLAYER                                        ((PlayerTypes)47)
#define NPC1_TEAM                                        ((TeamTypes)47)
#define NPC0_PLAYER                                        ((PlayerTypes)48)
#define NPC0_TEAM                                        ((TeamTypes)48)
#define NEANDERTHAL_PLAYER                                ((PlayerTypes)49)
#define NEANDERTHAL_TEAM                                ((TeamTypes)49)
// Toffer - Barbarian player must be last, expected by the exe.
#define BARBARIAN_PLAYER                                ((PlayerTypes)50)
#define BARBARIAN_TEAM                                    ((TeamTypes)50)
#define MAX_PLAYERS                                        (51)
#define MAX_TEAMS                                        (51)
Interesting that its still there.

Are these then being redefined later by the code you found?
 
Those defines weren't used when the NPC players were assigned a team.
This is how I fixed the bug:

FROM
Code:
        else if (ePlayer == NPC0_PLAYER)
        {
            addPlayer(
                ePlayer, (LeaderHeadTypes)GC.getDefineINT("NPC0_LEADER"),
                (CivilizationTypes)GC.getDefineINT("NPC0_CIVILIZATION"), false
            );
            GET_PLAYER(ePlayer).setNewPlayerAlive(true);
            TeamTypes eTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getID();
            GET_TEAM(eTeam).init(eTeam);
            GC.getInitCore().setTeam(ePlayer, eTeam);
            GC.getInitCore().setHandicap(ePlayer, (HandicapTypes)GC.getDefineINT("BARBARIAN_HANDICAP"));
        }

TO
Code:
        else if (ePlayer == NPC0_PLAYER)
        {
            addPlayer(
                ePlayer, (LeaderHeadTypes)GC.getDefineINT("NPC0_LEADER"),
                (CivilizationTypes)GC.getDefineINT("NPC0_CIVILIZATION"), false
            );
            GET_PLAYER(ePlayer).setNewPlayerAlive(true);
            GET_TEAM(NPC0_TEAM).init(NPC0_TEAM);
            GC.getInitCore().setTeam(ePlayer, NPC0_TEAM);
            GC.getInitCore().setHandicap(ePlayer, (HandicapTypes)GC.getDefineINT("BARBARIAN_HANDICAP"));
        }
 
Cool! We are so lucky to have you onboard with this project Toffer! Thank you! I don't think I'd have figured that out in any reasonable expense of time at all. Your skills have grown truly vast in the last few yrs.
 
Back
Top Bottom