Tech tree screen not even remotely accurate

GenericKen

Not at all suspicious
Joined
Sep 2, 2005
Messages
202
As we all know, the tech tree poster included in the game is incomprehensible, and the tech tree in the game is worthless. There are no arrows to flight though it requires two techs. There's no arrow to indicate that genetics requires refrigeration or that Fusion requires Fission, and there are a number of other missing arrows.

What's more, I find it absolutely inconcievable that there wasn't a single UI developer on the team who didn't think it neccessary to somehow graphically distinguish the OR technologies from the AND technologies.


I was snooping around the assets folder trying to find where the img used for the tech tree was to try to fix it, to no avail. Eventually, I gave up and searched the forums.

Turns out, the tech tree is actually generated by a python script called CvTechChooser. While this is pretty damn cool (and slow), it also defines the problem as a bug in either the drawArrows code or the XML that nobody felt like fixing for some reason (http://www.civfanatics.com/civ4/techtree/).


Given the distributed and consistant behavior of the problem, I suspect the bug is in the XML, which I've yet to locate; this is, of course, assuming that the tech-tree drawer uses different XML files than the actual gamecode and the civopedia.


I still think the inability to easily distinguish between AND arrows and OR arrows is ridiculous. Given that most people actually trace backward through a tech tree to see how to get to a specific goal tech, wouldn't the simplest, most intuitive, and most informative solution simply to have the or gates be forked, shared arrows?
 

Attachments

  • techtree standard.JPG
    techtree standard.JPG
    18.2 KB · Views: 465
Here's the python code in question. For the life of me, I can't figure out how each python script specifies which XML it's using.

# Will draw the arrows
def drawArrows (self):

global g_civSelected

screen = CyGInterfaceScreen( "TechChooser", CvScreenEnums.TECH_CHOOSER )

iLoop = 0
self.nWidgetCount = 0

ARROW_X = ArtFileMgr.getInterfaceArtInfo("ARROW_X").getPath()
ARROW_Y = ArtFileMgr.getInterfaceArtInfo("ARROW_Y").getPath()
ARROW_MXMY = ArtFileMgr.getInterfaceArtInfo("ARROW_MXMY").getPath()
ARROW_XY = ArtFileMgr.getInterfaceArtInfo("ARROW_XY").getPath()
ARROW_MXY = ArtFileMgr.getInterfaceArtInfo("ARROW_MXY").getPath()
ARROW_XMY = ArtFileMgr.getInterfaceArtInfo("ARROW_XMY").getPath()
ARROW_HEAD = ArtFileMgr.getInterfaceArtInfo("ARROW_HEAD").getPath()

for i in range(gc.getNumTechInfos()):

bFirst = 1

fX = (BOX_INCREMENT_WIDTH * PIXEL_INCREMENT) - 8

for j in range( gc.getDefineINT("NUM_AND_TECH_PREREQS") ):

eTech = gc.getTechInfo(i).getPrereqAndTechs(j)

if ( eTech > -1 ):

fX = fX - X_INCREMENT

iX = 30 + ( (gc.getTechInfo(i).getGridX() - 1) * ( ( BOX_INCREMENT_X_SPACING + BOX_INCREMENT_WIDTH ) * PIXEL_INCREMENT ) )
iY = ( gc.getTechInfo(i).getGridY() - 1 ) * ( BOX_INCREMENT_Y_SPACING * PIXEL_INCREMENT ) + 5

szTechPrereqID = "TechPrereqID" + str((i * 1000) + j)
screen.addDDSGFCAt( szTechPrereqID, "TechList", gc.getTechInfo(eTech).getButton(), iX + fX, iY + 6, TEXTURE_SIZE, TEXTURE_SIZE, WidgetTypes.WIDGET_HELP_TECH_PREPREQ, eTech, -1, False )

#szTechPrereqBorderID = "TechPrereqBorderID" + str((i * 1000) + j)
#screen.addDDSGFCAt( szTechPrereqBorderID, "TechList", ArtFileMgr.getInterfaceArtInfo("TECH_TREE_BUTTON_BORDER").getPath(), iX + fX + 4, iY + 22, 32, 32, WidgetTypes.WIDGET_HELP_TECH_PREPREQ, eTech, -1, False )

j = 0

for j in range( gc.getDefineINT("NUM_OR_TECH_PREREQS") ):

eTech = gc.getTechInfo(i).getPrereqOrTechs(j)

if ( eTech > -1 ):

iX = 24 + ( (gc.getTechInfo(eTech).getGridX() - 1) * ( ( BOX_INCREMENT_X_SPACING + BOX_INCREMENT_WIDTH ) * PIXEL_INCREMENT ) )
iY = ( gc.getTechInfo(eTech).getGridY() - 1 ) * ( BOX_INCREMENT_Y_SPACING * PIXEL_INCREMENT ) + 5

# j is the pre-req, i is the tech...
xDiff = gc.getTechInfo(i).getGridX() - gc.getTechInfo(eTech).getGridX()
yDiff = gc.getTechInfo(i).getGridY() - gc.getTechInfo(eTech).getGridY()

if (yDiff == 0):
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_X, iX + self.getXStart(), iY + self.getYStart(3), self.getWidth(xDiff), 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_HEAD, iX + self.getXStart() + self.getWidth(xDiff), iY + self.getYStart(3), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
elif (yDiff < 0):
if ( yDiff == -6 ):
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_X, iX + self.getXStart(), iY + self.getYStart(1), self.getWidth(xDiff) / 2, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_XY, iX + self.getXStart() + ( self.getWidth(xDiff) / 2 ), iY + self.getYStart(1), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_Y, iX + self.getXStart() + ( self.getWidth(xDiff) / 2 ), iY + self.getYStart(1) + 8 - self.getHeight(yDiff, 0), 8, self.getHeight(yDiff, 0) - 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_XMY, iX + self.getXStart() + ( self.getWidth(xDiff) / 2 ), iY + self.getYStart(1) - self.getHeight(yDiff, 0), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_X, iX + 8 + self.getXStart() + ( self.getWidth(xDiff) / 2 ), iY + self.getYStart(1) - self.getHeight(yDiff, 0), ( self.getWidth(xDiff) / 2 ) - 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_HEAD, iX + self.getXStart() + self.getWidth(xDiff), iY + self.getYStart(1) - self.getHeight(yDiff, 0), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
elif ( yDiff == -2 and xDiff == 2 ):
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_X, iX + self.getXStart(), iY + self.getYStart(2), self.getWidth(xDiff) * 5 / 6, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_XY, iX + self.getXStart() + ( self.getWidth(xDiff) * 5 / 6 ), iY + self.getYStart(2), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_Y, iX + self.getXStart() + ( self.getWidth(xDiff) * 5 / 6 ), iY + self.getYStart(2) + 8 - self.getHeight(yDiff, 3), 8, self.getHeight(yDiff, 3) - 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_XMY, iX + self.getXStart() + ( self.getWidth(xDiff) * 5 / 6 ), iY + self.getYStart(2) - self.getHeight(yDiff, 3), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_X, iX + 8 + self.getXStart() + ( self.getWidth(xDiff) * 5 / 6 ), iY + self.getYStart(2) - self.getHeight(yDiff, 3), ( self.getWidth(xDiff) / 6 ) - 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_HEAD, iX + self.getXStart() + self.getWidth(xDiff), iY + self.getYStart(2) - self.getHeight(yDiff, 3), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
else:
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_X, iX + self.getXStart(), iY + self.getYStart(2), self.getWidth(xDiff) / 2, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_XY, iX + self.getXStart() + ( self.getWidth(xDiff) / 2 ), iY + self.getYStart(2), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_Y, iX + self.getXStart() + ( self.getWidth(xDiff) / 2 ), iY + self.getYStart(2) + 8 - self.getHeight(yDiff, 3), 8, self.getHeight(yDiff, 3) - 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_XMY, iX + self.getXStart() + ( self.getWidth(xDiff) / 2 ), iY + self.getYStart(2) - self.getHeight(yDiff, 3), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_X, iX + 8 + self.getXStart() + ( self.getWidth(xDiff) / 2 ), iY + self.getYStart(2) - self.getHeight(yDiff, 3), ( self.getWidth(xDiff) / 2 ) - 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_HEAD, iX + self.getXStart() + self.getWidth(xDiff), iY + self.getYStart(2) - self.getHeight(yDiff, 3), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
elif (yDiff > 0):
if ( yDiff == 2 and xDiff == 2):
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_X, iX + self.getXStart(), iY + self.getYStart(4), self.getWidth(xDiff) / 6, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_MXMY, iX + self.getXStart() + ( self.getWidth(xDiff) / 6 ), iY + self.getYStart(4), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_Y, iX + self.getXStart() + ( self.getWidth(xDiff) / 6 ), iY + self.getYStart(4) + 8, 8, self.getHeight(yDiff, 3) - 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_MXY, iX + self.getXStart() + ( self.getWidth(xDiff) / 6 ), iY + self.getYStart(4) + self.getHeight(yDiff, 3), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_X, iX + 8 + self.getXStart() + ( self.getWidth(xDiff) / 6 ), iY + self.getYStart(4) + self.getHeight(yDiff, 3), ( self.getWidth(xDiff) * 5 / 6 ) - 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_HEAD, iX + self.getXStart() + self.getWidth(xDiff), iY + self.getYStart(4) + self.getHeight(yDiff, 3), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
elif ( yDiff == 4 and xDiff == 1):
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_X, iX + self.getXStart(), iY + self.getYStart(5), self.getWidth(xDiff) / 3, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_MXMY, iX + self.getXStart() + ( self.getWidth(xDiff) / 3 ), iY + self.getYStart(5), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_Y, iX + self.getXStart() + ( self.getWidth(xDiff) / 3 ), iY + self.getYStart(5) + 8, 8, self.getHeight(yDiff, 0) - 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_MXY, iX + self.getXStart() + ( self.getWidth(xDiff) / 3 ), iY + self.getYStart(5) + self.getHeight(yDiff, 0), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_X, iX + 8 + self.getXStart() + ( self.getWidth(xDiff) / 3 ), iY + self.getYStart(5) + self.getHeight(yDiff, 0), ( self.getWidth(xDiff) * 2 / 3 ) - 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_HEAD, iX + self.getXStart() + self.getWidth(xDiff), iY + self.getYStart(5) + self.getHeight(yDiff, 0), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
else:
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_X, iX + self.getXStart(), iY + self.getYStart(4), self.getWidth(xDiff) / 2, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_MXMY, iX + self.getXStart() + ( self.getWidth(xDiff) / 2 ), iY + self.getYStart(4), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_Y, iX + self.getXStart() + ( self.getWidth(xDiff) / 2 ), iY + self.getYStart(4) + 8, 8, self.getHeight(yDiff, 3) - 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_MXY, iX + self.getXStart() + ( self.getWidth(xDiff) / 2 ), iY + self.getYStart(4) + self.getHeight(yDiff, 3), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_X, iX + 8 + self.getXStart() + ( self.getWidth(xDiff) / 2 ), iY + self.getYStart(4) + self.getHeight(yDiff, 3), ( self.getWidth(xDiff) / 2 ) - 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )
screen.addDDSGFCAt( self.getNextWidgetName(), "TechList", ARROW_HEAD, iX + self.getXStart() + self.getWidth(xDiff), iY + self.getYStart(4) + self.getHeight(yDiff, 3), 8, 8, WidgetTypes.WIDGET_GENERAL, -1, -1, False )

return
 
Woah, I did not notice that...

So some techs get arrows, while others don't? Bronzeworking -> metalcasting is strange.
 
This seems to come up a lot. Absolute prerequisites for a tech are shown by an icon in the upper right corner. (Wheel, in the image above.) Optional prerequisites (one or the other) are shown by the arrows. You always need at least one of the optional prerequisites, but you don't need them all.

It is confusing, but it all fits once you know how it works.
 
In addition, clicking on a tech gives you the cheapest path to it.
 
warpstorm said:
In addition, clicking on a tech gives you the cheapest path to it.
Not always. Start a new game with a civ that does not have Meditation or Mining. Go into the tech tree screen and click on Monotheism. It selects Mining as the first tech followed by Masonry, then Mysticism, then Polythesim, and finally Monotheism. Masonry requires either Mining or Mysticism, but not both.

This is the only case that I have noticed but there may be others.
 
What do you know, you are right.
 
The poster that has techs, civics, and promotions?
It is fine to me.
The lack of units and buildings though is annoying.
Why can't you do a colored version like the poster?
Unless that is what you mean by incomprehensible.
You way isn't too bad except I don't
remember all the icons.
Plus it can be annoying when they are far into the past.
At least the poster I can see all at once.

P.S. The link doesn't seem to work.
Is it the the tree that the game defaults while playing?
That is, well, usless.
 
omg the icons! Thank you. I was just going to agree with OP.

For example try to manually steer for railroads without know the tech tree or that icon trick.
 
The poster that has techs, civics, and promotions?
It is fine to me.

I hate it.

After getting into the Industrial Age and not being able to build rails because I'd missed some little dead end MA tech that was a prereq (which had a prereq in AA), I took the poster out and studied it.

While the poster shows the arrows, it has none of the necessary prereqs like the in-game tech tree has. It's a nice, basic FYI, but you can't use it to say "I want TECH, and I'll get to it this way" because you'll get within one tech and then you'll have to back track.
 
How about this one: In the civilopedia future tech requires composites and genetics. The tech advisor indicates it requires only genetics. On the poster future tech requires fiber optics and composites. I don't know which is correct since I haven't gotten to that point yet.

I found this out while creating my own tech chart which shows the ands and ors as well as other points of interest for the techs.
 
rainmaker said:
How about this one: In the civilopedia future tech requires composites and genetics. The tech advisor indicates it requires only genetics. On the poster future tech requires fiber optics and composites. I don't know which is correct since I haven't gotten to that point yet.

I found this out while creating my own tech chart which shows the ands and ors as well as other points of interest for the techs.

:confused: You notice the icon in the upper right corner that everyone's talking about? That's composites.

Req
 
Renata said:
This seems to come up a lot. Absolute prerequisites for a tech are shown by an icon in the upper right corner. (Wheel, in the image above.) Optional prerequisites (one or the other) are shown by the arrows. You always need at least one of the optional prerequisites, but you don't need them all.

It is confusing, but it all fits once you know how it works.

This does not work for a lot of the techs. Basically all the techs with only TWO mandatory prerequisites or those with ONE LONE mandatory prerequisite.

Really, the whole thing is just confusing since you tend to think that ARROWS are mandatory while the ICONS would not be.

ED: for clarity

Req
 
Basically all the techs with only TWO mandatory prerequisites or those with ONE LONE mandatory prerequisite.

I don't understand what you're trying to say here.
 
By the way, you don't have to memorize all of the icons. Just mouse over the icon and it tells you what tech it's talking about.
 
Requies said:
Absolute prerequisites are NOT ALWAYS shown by the icon.
You are partially right. If there is only exactly 1 arrow leading to the tech and tech only have 1 prerequisite then you are right. Otherwise then mandatory required techs are ALWAYS displayed by an icon in top right corner.
 
Back
Top Bottom