TechShare and Projects

vincentz

Programmer
Joined
Feb 4, 2009
Messages
3,614
Location
Denmark
In my mod I have several Projects that uses TechShare (same as Internet Project) but instead uses 8/6/4 civs known (Internet uses 2).

This obviously doesnt work well with lots of civs. In my current 34 civ game I just got 15+ techs from building the early National Library Project (8 civs known).

So my thought was to
a) change to % known of total met civs (80%, 60%, 40%, 20%)
not sure it would make sense. open for suggestions...
b) not give all techs at once, but 1 every 5 turn, scaled at game speed.

However... Looking up TechShare in the Dll gives 50+ results, and being the code noob I am, I can't seem to find the right place to add.

So once again, I plead for help.

TIA
Vincentz :D
 
a) change to % known of total met civs (80%, 60%, 40%, 20%)
That would make it beneficial to meet as few civs as possible. I get what you are thinking, but say you only met one civ, then you would get all the techs he has. If you are unlucky enough to encounter another one, then you lose getting techs from any of those two unless they both have the same tech. This would put some strategy into NOT exploring and I don't think that is what you want.

b) not give all techs at once, but 1 every 5 turn, scaled at game speed.
That's actually a good idea. FreeCiv has something like that where Leonardo's workshop can upgrade one unit each turn, just one. That made it less powerful than the one, which upgrades all units in the same turn as it is finished, but also more realistic.

being the code noob I am, I can't seem to find the right place to add.
:popcorn:

So once again, I plead for help.
Well if you would share your source code, then I might look at it this time. I never managed to locate it the last time you had a DLL question.
 
Here is the gamecore. I hope everything is there.

Maybe its fine with the 8/6/4/2 civs, though if map is less than 8 civs first project will be pretty useless. Though if it was 80% and there was 30 civs on map then it needed to be 24 of AI met that had the tech, and that wouldnt make any sense either...
Going lower in % would skew it on small maps. f.ex. 40% of 5 civs would only be 2 civ, which is what Internet does atm, and shouldn't be what a Classic Era project should do.

Then there could be the OR which would prolly be a mess but... :
If (50% met of total civs OR 8 AI met) knows tech then give tech (National Library)
If (40% met of total civs OR 6 AI met) knows tech then give tech (Encyclopedia)
If (30% met of total civs OR 4 AI met) knows tech then give tech (Communication Network)
If (20% met of total civs OR 2 AI met) knows tech then give tech (The Internet)

Ie : map have 14 civ. Nat Library build (50%/8).
6 civs met who have tech. No tech is given. 7 civs met who have tech. Tech is given (50%)
Ie : map have >14 civ. Nat Library build (50%/8).
7 civs met who have tech. No tech is given. 8 civs met who have tech. Tech is given (8 civs)

But then again. In my current game I just got 15+ techs (on a 34 civ map) when I build the National Library, and that would still be that way with the OR.

Maybe... just maybe...:
If (80% met of total civs knows tech) OR (8 AI met AND 40% met of total civs knows tech) knows tech then give tech

Ie : map have 5 civ. Nat Library build (80%/8 & 40%) = 4 civs met who have tech. Tech is given (80%)
Ie : map have 10 civ. Nat Library build (80%/8 & 40%) = 8 civs met who have tech. Tech is given (80%/8 & 40%)
Ie : map have 20 civ. Nat Library build (80%/8 & 40%) = 8 civs met who have tech. Tech is given (8 & 40%)
Ie : map have 30 civ. Nat Library build (80%/8 & 40%) = 12 civs met who have tech. Tech is given (8 & 40%)

Though while it wouldn't really scale from 10-20 civs on map, it would still take into account if less than 8 civs on map and scale relatively better with lots of civs on map...

A 3rd way could be some hyperbole function. Dono about that though...

Sorry for thinking out loud and make an otherwise short and simple post to a TL: DR mess ;)

I think the most important part is to not give all techs at once, but give 1 per 2 turns at Normal Speed and scaled at gamespeed (iResearchPercent ?)

Btw. do you need the whole mod?
 
I feel like a magnet for modding issues right now. I looked at the code for 4 mods in the last few days, so I figured why not add another one to the madness :crazyeye:

Generally speaking I'm writing long names for functions and variables. I haven't really considered those and feel free to come up with something better.

I found a place to enter code about a percentage of players.

Code:
void CvTeam::updateTechShare(TechTypes eTech)
{
	...
	iBestShare = MAX_INT;

	for (iI = 0; iI < MAX_TEAMS; iI++)
	{
		if (isTechShare(iI))
		{
			iBestShare = std::min(iBestShare, (iI + 1));
		}
	}

	[COLOR="Red"][B]// your code here[/B][/COLOR]

	if (iBestShare != MAX_INT)

The new code should contain something like:
Code:
if (getPercentageTechShareThreshold() != MAX_INT)
{
	int iThreshold = getPercentageTechShareThreshold();
	iThreshold *= getNumAliveOtherTeamsNoBarbarians();
	iThreshold /= 100;
	if (iThreshold < iBestShare)
	{
		iBestShare = iThreshold;
	}
}
getNumAliveOtherTeamsNoBarbariansshould be straitforward to code. Just loop and check conditions and count.

getPercentageTechShareThreshold on the other hand is a bit tricky. Somehow you have to cache the lowest score. Either you cache with a list or you simply cache an int. Whenever a new building is added, you check if the building has a score higher than 0 and lower than the present cache. If a building is removed and the building has the same score as the cache, you set the cache to MAX_INT and loop all cities and in each city you loop all present buildings and each building lowers the score if it provides a score, which is lower than the one in the cache.

The loop city approach is kind of slow, but on the other hand I would assume losing a city, which provides the bonus will not happen multiple times each turn, which mean performance in this case isn't that important.


Regarding restricting number of techs stolen each turn
Add m_iNumTechsStolenThisTurn to CvTeam.

Change the code like this:
Code:
void CvTeam::updateTechShare(TechTypes eTech)
{
	[B]if (iNumTechsStolenThisTurn == -1)
	{
		return;
	}[/B]
...
		if (iCount >= iBestShare)
		{
			setHasTech(eTech, true, NO_PLAYER, true, true);
			[B]iNumTechsStolenThisTurn++;
			if (some random based on iNumTechsStolenThisTurn)
			{
				iNumTechsStolenThisTurn = -1; // no more techs this turn
			}[/B]
		}

In CvTeam::doTurn()
Code:
int iOld = iNumTechsStolenThisTurn;
iNumTechsStolenThisTurn  = 0;
if (iOld == -1)
{
	// the team failed to finish the steal tech loop last turn. Continue this loop
	updateTechShare();
}
This way you have a random change to jam the steal loop and this risk increase based on the number of techs the team has stolen this turn. With the right code, you will get 1 or 2 techs each turn, rarely (but possible) more than that. You could even set if iNumTechsStolenThisTurn == something, then iNumTechsStolenThisTurn = -1 to ensure that there is a max and you will not one out of 20000 get 50 techs in a single turn.


I haven't written a single line of code to test any of this, but I think this approach will get the job done.
 
I really liked the "Random"'ness factor, so I tried to keep it nice and simple :

If Tech is researchable then there is a RandomChance of getting it
PHP:
	if (!canResearch(eTech))
	{
		return;
	}
If RandomChance(100) > iTechShare *10 then give tech
PHP:
if ((iCount >= iBestShare) && (GC.getGameINLINE().getSorenRandNum(100, "Project Tech Sharing") > getTechShare() *10))


PHP:
void CvTeam::updateTechShare(TechTypes eTech)
{
	int iBestShare;
	int iCount;
	int iI;

	if (isHasTech(eTech))
	{
		return;
	}
	
// Vincentz TechShare
	if (!canResearch(eTech))
	{
		return;
	}
// Vincentz TechShare end
	
	iBestShare = MAX_INT;

	for (iI = 0; iI < MAX_TEAMS; iI++)
	{
		if (isTechShare(iI))
		{
			iBestShare = std::min(iBestShare, (iI + 1));
		}
	}

	if (iBestShare != MAX_INT)
	{
		iCount = 0;

		for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
		{
			if (GET_TEAM((TeamTypes)iI).isAlive())
			{
				if (GET_TEAM((TeamTypes)iI).isHasTech(eTech))
				{
					if (isHasMet((TeamTypes)iI))
					{
						FAssertMsg(iI != getID(), "iI is not expected to be equal with getID()");
						iCount++;
					}
				}
			}
		}

//		if (iCount >= iBestShare) Vincentz TechShare
		if ((iCount >= iBestShare) && (GC.getGameINLINE().getSorenRandNum(100, "Project Tech Sharing") > getTechShare() *10))
// Vincentz TechShare end		
		{
			setHasTech(eTech, true, NO_PLAYER, true, true);
		}
	}
}

However when compiling :
1>CvTeam.cpp(5201) : error C3861: 'canResearch': identifier not found, even with argument-dependent lookup
1>CvTeam.cpp(5237) : error C3861: 'getTechShare': identifier not found, even with argument-dependent lookup

Both functions should exist :confused:
 
1>CvTeam.cpp(5201) : error C3861: 'canResearch': identifier not found, even with argument-dependent lookup
1>CvTeam.cpp(5237) : error C3861: 'getTechShare': identifier not found, even with argument-dependent lookup

Are these both defined in CvTeam.h and implemented in CvTeam.cpp?

I presume that you have created these new functions yourself as they are not present in the standard BTS code.
 
They exists in the code :
in CvPlayer.cpp
PHP:
bool CvPlayer::canStealTech(PlayerTypes eTarget, TechTypes eTech) const
{
	if (GET_TEAM(GET_PLAYER(eTarget).getTeam()).isHasTech(eTech))
	{
		if (canResearch(eTech))
		{
			return true;
		}
	}
and in CvInfos.cpp
PHP:
int CvProjectInfo::getTechShare() const
{
	return m_iTechShare; 
}

However none of them are in CvTeam.

How do I add them to CvTeam.cpp?
 
You don't...

You can get access to the appropriate CvPlayer object via the CvTeam::getLeaderId() function:

PHP:
// Vincentz TechShare
    if (!GET_PLAYER(getLeaderID()).canResearch(eTech))
    {
        return;
    }
// Vincentz TechShare end

What are you trying to achieve with the call to getTechShare() as this only applies when you are dealing with Projects? It looks like you are expecting it to be a number between 1 and 10 as it is used to compare against a random number between 1 & 100.

I am also unsure what the iBestValue variable is supposed to represent. As your code stands it is the ID + 1 of the team with the smallest ID that has the tech. This is then compared against the number of teams you have met that have the tech. I'm not sure I follow the logic of what that is telling you.

If, for instance, you are playing a game with 12 teams it may be only one team that will techShare, but depending on whether this is the 2nd team in the internal list of teams or the 12th you could get a different result. Then say that there are 3 teams you have met who have the tech, in scenario 1 you have the random change of receiving the tech (iCount = 3 is >= iBestValue = 2 (1 + 1), in scenario 2 you don't (iCount = 3 is < iBestValue = 12 (11 + 1). That's not really random as the team order is fixed at the start of the game.
 
CvPlayer::canResearch()
CvProjectInfo::getTechShare()

No wonder it can't find it when you write the code in a CvTeam function. Besides I don't think you would want the result of that code anyway, at least not rejecting based on canResearch().

The problem is that you reject tech 7 because you can't research it. Then you gain tech 10. Now you can research tech 7, but you already ignored it because you could not research it when you looked at it.

PHP:
//        if (iCount >= iBestShare) Vincentz TechShare
        if ((iCount >= iBestShare) && (GC.getGameINLINE().getSorenRandNum(100, "Project Tech Sharing") > getTechShare() *10))
// Vincentz TechShare end
iBestShare is presumably what you want when you write getTechShare(). It contain the lowest number of civs, which has to have the tech in question before you gain it.

I'm not sure I like that random function though. If you set TechShare to 10, you have 100-100% chance of getting a tech, which renders this feature rather useless. If it is 4, then the chance of getting a tech is 100-40 = 60%. That is 60% for each tech, which can be quite a high number of techs during a single turn.
 
I am also unsure what the iBestValue variable is supposed to represent. As your code stands it is the ID + 1 of the team with the smallest ID that has the tech. This is then compared against the number of teams you have met that have the tech. I'm not sure I follow the logic of what that is telling you.
From what I can tell from an earlier study of the code, building a project, which sets a TechShare, it gives +1 to an element in an array of length MAX_TEAMS.
The loop is designed to locate the lowest index, which has a value higher than 0. I'm not completely happy with that code and particularly the confusing looping/naming. Also once a value has been found, the loop could stop. However the code quality of that part isn't the issue here and it appears to work.

iBestValue will contain the number of civs, which needs to have a certain tech before you can get it. You also have to have contact with those.
 
Thanks guys :D

I'm really wandering around like a 3 year old here.

If I understand the code correctly (by testing only the following : if ((iCount >= iBestShare) && (GC.getGameINLINE().getSorenRandNum(100, "Project Tech Sharing") > 50)) ) it will give a tech 50% of the time, which is definitely an improvement over the 100% it used to be.
But it gave techs that was not researchable, as it was used to give 100%, so I added the check (!canResearch(eTech)) to see if tech was even possible to get. This should also work as a "narrower path", so the amount given would be less (might be wrong) as it only checked the 50% that was researchable.

The getTechShare() should be the number from the Projects <iTechShare>6</iTechShare> which are numbers between 8 (National Library, Classical Era) to 2 (Internet, Modern Era).

By using this, the chance of getting a tech (which is researchable) through the Projects would increase for the later projects. (20% for National Library (100-8x10), and 80% for Internet (100-2x10)).
I might add a second randomizer to lessen the chance though, but first I wanted this to work.

Edit : btw, is there any easy way to inform what the values are ingame? sort of a "Hello World, your value for IBestShare is 6"?


Double Edit : Thanks guys :D It seems to be working. I started a Renaissance start, gave myself The Internet, and took away Medieval and Classical techs (I was then 56 techs behind). When I made contact to the second AI I got 8 techs and when I met the 3rd I got 11 techs. All of which I could research, so no odd skipping.
Loading and doing a different turn made a different result.

I ended up with this, though gonna do some testing. It is definitely an improvement :D
PHP:
		if ((iCount >= iBestShare) && (GC.getGameINLINE().getSorenRandNum(10, "Project Tech Sharing") > iBestShare))
 
The problem is that you reject tech 7 because you can't research it. Then you gain tech 10. Now you can research tech 7, but you already ignored it because you could not research it when you looked at it.
There is another function CvPlayer::canEverResearch() that might get around the issue Night mentions above. I haven't looked into this in detail though, so it may be the wrong thing to be doing.

is there any easy way to inform what the values are ingame?
The usual way is to compile a debug dll and then set a breakpoint in the code. You can then see all the variables and step through the code one line at a time to see how each variable changes.
 
It does appear to work quite nicely, though I changed the canresearch to the last line :
PHP:
		if ((iCount >= iBestShare) && (GC.getGameINLINE().getSorenRandNum(10, "Project Tech Sharing") > iBestShare) && (!GET_PLAYER(getLeaderID()).canResearch(eTech)))

It does (with my VERY limited testing) seems like it does the initial check (through all techs) when Project is being build and when meeting new civs. Later it only gives the techs that have just been researched. This would ofcourse make perfectly sense when Projects were giving 100% of techs possible.
I tested with Encyclopedia Civilization (<iTechShare>6</iTechShare>) and gave me 17 less techs than all others. I got only 2 techs which was nice. Later I got a tech now and then, but no overkill, so I guess mission accomplished :D

It would be nice though to add a line, sort of "You have constructed a XXX in YYY, Work has now begun on ZZZ", but saying :
"The ProjectXXX have gathered information regarding TechYYY." or something...
 
Back
Top Bottom