View Full Version : GP Tech Lightbulb Idea


ruff_hi
Jun 07, 2007, 11:11 PM
There are lots of discussions in SGs about which tech a GP will lightbulb. I'm thinking of putting in a screen that shows the current tech that each GP will lightbulb as well as the tech the GP will lightbulb if each of the possible researchable techs are known.

To do this, I need to be able to get the lightbulbable tech for each GP - is there such a function in civ python?

Zebra 9
Jun 08, 2007, 04:56 PM
I think so. I would have to check though. :hmm:

snipperrabbit!!
Jun 10, 2007, 11:44 AM
There's something that already shows what's is lightblubable for every GP in the utilities I think.

Zebra 9
Jun 11, 2007, 10:50 AM
If you have a unit object you can call the function getDiscoveryTech(). Now this returns the index of the tech, if you need more info gc.getTechInfo(Unit Object.getDiscoveryTech()).getDescription() will give the tech name (Bronze Working), and gc.getTechInfo(Unit Object.getDiscoveryTech()).getType() will return the tyoe tag (TECH_BRONZE_WORKING).:thumbsup:

ruff_hi
Jun 11, 2007, 12:59 PM
Great - that should get me the current tech, but I am also looking for the next tech (ie assuming I know the current lightbulbed one) as well as possible techs (assuming I know the techs that I could research). Does that mean I will need to add the tech, work out the getdiscoverytech, take the tech away?

Zebra 9
Jun 12, 2007, 11:51 AM
Yes. You would have to give the required techs then check and the remove the techs.

That's at least from what I know.

ruff_hi
Aug 04, 2007, 11:20 AM
http://img180.imageshack.us/img180/2186/lightbulbtechix0.jpg

The above is the sort of screen that I mean. Down the left are the various GPs that you can get. The first column of techs is the techs that each GP will lightbulb based on your current known techs. The techs across the top are the techs that you can research. The techs shown in the body of the table are the techs the GPs will lightbulb if you research the tech at the top of the column.

Example 1: Great Artist. This GP will currently lightbulb physics. If you research physics, he will lightbulb fission.

Example 2: Great Tycoon (that is the name of the art file for the merchant!) This GP will currently lightbulb writing. If you research writing, he will lightbulb liberalism. If you research steam power, he will lightbulb monotheism. If you research monotheism, he will lightbulb corporations.

Example 3: What can I do with this table in a real game? Say I just got a Great Prophet. He will currently lightbulb Hinduism. However, if I research Hinduism myself, he will lightbulb radio. That is an obviously great thing to do.

OR - say I want to lightbulb writing and I have a Great Scientist due in 15 rounds. What tech should I not research so that I can still lightbulb writing? Answer: don't research steam power, physics or monotheism.

Note: This is a mock up and the lightbulb techs as well as the research techs are not as per the tree or the actual tech GP preference. Anyone who gives me grief about the techs being all wrong can go stick their head in a pig.

EmperorFool
Aug 04, 2007, 06:53 PM
The information you seek is available. CvTechInfo has getFlavorValue(int) which will give you the value for each flavor (science, military, gold, culture, production). These map to the XML <flavor> tags. See this post (http://forums.civfanatics.com/showpost.php?p=5605375&postcount=51) for the XML details. In this post (http://forums.civfanatics.com/showpost.php?p=5606033&postcount=53) I wrote up briefly what I wanted to do, similar to your idea.

You'll have to experiment to see what int you pass in to getFlavorValue(int). I can't find a FlavorInfo with defines for FLAVOR_GOLD and such, but there aren't that many.

This would be a very helpful mod indeed!

ruff_hi
Aug 04, 2007, 11:07 PM
The information you seek is available. CvTechInfo has getFlavorValue(int) which will give you the value for each flavor (science, military, gold, culture, production). These map to the XML <flavor> tags. See this post (http://forums.civfanatics.com/showpost.php?p=5605375&postcount=51) for the XML details. In this post (http://forums.civfanatics.com/showpost.php?p=5606033&postcount=53) I wrote up briefly what I wanted to do, similar to your idea.I don't think I need to do this. I can use the code from above (quoted below) to get which tech the GP will lightbulb.

If you have a unit object you can call the function getDiscoveryTech(). Now this returns the index of the tech, if you need more info gc.getTechInfo(Unit Object.getDiscoveryTech()).getDescription() will give the tech name (Bronze Working), and gc.getTechInfo(Unit Object.getDiscoveryTech()).getType() will return the tyoe tag (TECH_BRONZE_WORKING).:thumbsup:

Once I have this, I think I need to do something like this to fill in the grid ...


for each great person
display great person icon # first column
display lightbulb.tech # second column
next great person

for each possible.research.tech
display possible.research.tech
add possible.research.tech to current list of known techs
for each great person
display lightbulb.tech # next column
next great person
remove possible.research.tech from current list of known techs
next possible.research.tech

EmperorFool
Aug 04, 2007, 11:11 PM
Are there any snags in adding/removing techs like that? What happens when you do that for a tech that the person has already researched 50%? Do they lose their progress? Is it remembered somewhere?

I suppose creating great people and removing them shouldn't have any consequences, so that part should be doable without problems.

Sounds good! :goodjob:

ruff_hi
Aug 04, 2007, 11:21 PM
Don't know and don't know. I am not sure if you can add a GP. I know that you can kill off a unit. However, do we need to do that? I am thinking that we can say something like ...

zTech = UnitObject(GREAT_SCIENTIST).getDiscoveryTech()

... which will give us the handle to the tech icon, etc.

EmperorFool
Aug 05, 2007, 02:13 AM
zTech = UnitObject(GREAT_SCIENTIST).getDiscoveryTech()

Right, but

UnitObject(GREAT_SCIENTIST)

is accomplished by creating a unit for the player, right? I don't think you can just create a unit not on the map. But as I said, there shouldn't be any ramifications to putting a great person onto the map temporarily, so long as it doesn't insist on posting a message to that effect.

ruff_hi
Aug 05, 2007, 11:40 AM
Here is the code I have so far - its not pretty but it works. I have just stuck it in the keyboard event of the autologger just for testing.


def onKbdEvent(self, argsList):
if (theKey == int(InputTypes.KB_T)
and self.eventMgr.bAlt):

for i in range(CyMap().numPlots()):
tPlot = CyMap().plot(CyMap().plotX(i),CyMap().plotY(i))
if (tPlot.isCity()
and tPlot.getOwner() == CyGame().getActivePlayer()):
pPlot = tPlot
i = CyMap().numPlots()

iPlayer = CyGame().getActivePlayer()
pPlayer = gc.getPlayer(iPlayer)

zNewUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_ART IST'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
zNewUnit.setName("BUG-TEMP-NAME")

zNewUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_MER CHANT'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
zNewUnit.setName("BUG-TEMP-NAME")

zNewUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_SCI ENTIST'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
zNewUnit.setName("BUG-TEMP-NAME")

zNewUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_ENG INEER'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
zNewUnit.setName("BUG-TEMP-NAME")

zNewUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_PRO PHET'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
zNewUnit.setName("BUG-TEMP-NAME")

i = 0
for j in range(pPlot.getNumUnits()):
pLoopUnit = CyInterface().getInterfacePlotUnit(pPlot, j - i)

tUnitType = pLoopUnit.getUnitType()
if (tUnitType == gc.getInfoTypeForString('UNIT_ARTIST')
or tUnitType == gc.getInfoTypeForString('UNIT_MERCHANT')
or tUnitType == gc.getInfoTypeForString('UNIT_SCIENTIST')
or tUnitType == gc.getInfoTypeForString('UNIT_ENGINEER')
or tUnitType == gc.getInfoTypeForString('UNIT_PROPHET')):
zTech = pLoopUnit.getDiscoveryTech()
zMsg = "TECH: %i" % zTech
CyInterface().addImmediateMessage(zMsg, "")

if (pLoopUnit.getNameNoDesc() == "BUG-TEMP-NAME"):
pLoopUnit.kill(False, pLoopUnit.getOwner())
i = i + 1
This finds a city owned by the player, creates the great people, gets their lightbulb tech preference and then deletes the great people.

All I need now is some code to cycle thru the techs that can be researched, add them 1 at a time, cycle thru the new lightbulb techs, remove the tech and repeat.

Oh - and put together the screen. I've been thinking, this might make a nice second tab to the F6 screen (tech tree).

EmperorFool
Aug 05, 2007, 12:24 PM
Instead of looping across the map, just use pPlayer.getCity(0) for the first city or pPlayer.getCapitalCity() for the capital. Mr. Micro Optimize at work, so just ignore me. :)

In any case, to address what you actually asked about, the ExoticForeignAdvisor has code that finds all the techs a player can research. That's probably a good place to start.

Ah, I see that there is pPlayer.canResearch(TechType, bTrade). Just pass in False for bTrade. So something like this:


lTechs = []
for iTech in range(gc.getNumTechInfos()):
lTechs.append(iTech)


I'm looking through the C++ code to find functions to add/remove techs from a player.

EmperorFool
Aug 05, 2007, 12:33 PM
Looking further, I notice that player's don't get techs -- teams do. So you need to get the player's team and then use


CyTeam.setHasTech(TechType eIndex, BOOL bNewValue, PlayerType ePlayer, BOOL bFirst, BOOL bAnnounce)


Probably want bFirst and bAnnounce to be False so the player doesn't get free GPs or see a message.

ruff_hi
Aug 05, 2007, 02:23 PM
Instead of looping across the map, just use pPlayer.getCity(0) for the first city or pPlayer.getCapitalCity() for the capital. Mr. Micro Optimize at work, so just ignore me. :)Yeah - well, I would have used the simple code IF I KNEW ABOUT IT!

Impaler[WrG]
Aug 05, 2007, 02:41 PM
You most deffintaly can NOT go about giving and removing Techs for the purposes of seeing what will be avalible as a result. It will trigger effects like founding religions, spawning Great people and the like, it would be completly unplayable.

i'd recomend you structure the logic around determining what additional techs become avalible based on what techs have the hypotheticaly aquired tech as a pre-requisite. Psudocode would look something like this


PresentlyKnownTechList = []
ResearchableTechList = []
OpenableTechsList = []
ConfirmedOpenedList = []

for (ResearchableTechList)
for (AllTechs)
if (ResearchableTech is prerequsite of any kind)
OpenableTechs append Tech
for (OpenableTechsList)
for (AllPrereqs of OpenbleTech)
if (Prereq is in PresentlyKnownTech or is ResearchTech)
ConfirmedOpenedList append Tech
for (ResearchableTechList - ResearchTech + ConfirmedOpenedList)
if (TechFlavor > Highestvalue)
Highestvalue = TechFlavor
LightBulb Tech = Tech

return Tech

EmperorFool
Aug 05, 2007, 02:42 PM
Are you familiar with the online API (http://civilization4.net/files/modding/PythonAPI_v160/)? It lists all the Python classes and their functions. While it's not ideal and lacks documentation (as it was just generated from C++ code I think), sometimes I can find what I want.

ruff_hi
Aug 05, 2007, 04:12 PM
Are you familiar with the online API (http://civilization4.net/files/modding/PythonAPI_v160/)? It lists all the Python classes and their functions. While it's not ideal and lacks documentation (as it was just generated from C++ code I think), sometimes I can find what I want.I have that bookmarked. It is useful if you know what you are looking for. Sort of like looking up a word in a dictionary to check the spelling but not being able to find it because you cannot spell the word.

ruff_hi
Aug 05, 2007, 09:59 PM
;5778903']You most deffintaly can NOT go about giving and removing Techs for the purposes of seeing what will be avalible as a result. It will trigger effects like founding religions, spawning Great people and the like, it would be completly unplayable.If this is true (and no reason to doubt it), then we might need to code up the 'tech to lightbulb' stuff from scratch. I don't think Imp's suggestion will be 100% accurate.

If we do need to code it up from scratch - how can I get the list of GP techs to lightbulb into python?

EmperorFool
Aug 06, 2007, 12:34 AM
I don't know if it's true or not. The function that gives techs to a team has a "bFirst" parameter, and I assume that if you set it to False you won't found a religion or get a great person. This function is what WorldBuilder uses. So an easy test would be to open up WB, give yourself Theology, and see if you found Christianity.

That being said, I still prefer doing it the way Impaler suggests (see my original post). The info is available via TechInfo and could be put into a data structure the first time the screen is opened and used from then on. Let me see what I can come up with. Creating the lists of preferences like those posted in the GPTechPref thread should be pretty easy. It's what to do with that list and the current known techs that is a little trickier.

EmperorFool
Aug 06, 2007, 07:33 AM
I've completed the first part. The following code builds a data structure that holds the various GP tech preferenes and the tech prerequisites. This should be enough for you to build the screen.

To use it, import it and create it.


import TechPrefs # or whatever you save the file as

tp = TechPrefs.TechPrefs()


Next, tell it to remove all known techs.


tp.removeKnownTechs()


With that done, you can get the next tech for each GP type. Here's an example for a Great Scientist. *


pTech = tp.getNextResearchableFlavorTech(TechPrefs.FLAVOR_ SCIENCE)
pInfo = pTech.getInfo()
# now use pInfo to get button and place on screen


Use the following method to get all the techs the player can research:


sCanResearch = tp.getResearchableTechs() # a set
for pTech in sCanResearch:
# use like pTech above


Finally, to test which tech a GP would give once another tech has been researched, use this:


for pTech in sCanResearch:
sTechs = set()
sTechs.add(pTech)
pNextTech = tp.getNextResearchableWithFlavorTech(iFlavor, sTechs)
# use as above


The code below has some samples for using the other methods in it. I tried to put in comments as much as possible, but I know some parts won't be entirely obvious.

Basically, the Tech class encapsulates a single technology along with references to which techs it requires and which it leads to. The TechPrefs class holds all the Techs and builds the lists of GP prefs. You'll access Techs using the single TechPrefs you create in interfaceScreen().

Let me know if there are other methods I could add to be helpful.

* Note that the great people types are hard coded, so you'll need to use the constants defined at the top of my file. Actually, CyUnitType does have a getFlavorValue(int) that returns 1 if the passed in constant is its flavor type. So you should get this:


pUnitInfo = gc.getUnitUnit(gc.getInfoTypeForString("UNIT_SCIENTIST"))
if (pUnitInfo.getFlavorValue(TechPrefs.FLAVOR_SCIENCE ) == 1):
# should be true


Anyway, here's the code. Enjoy!


## TechPrefs
## Builds ordered lists of techs as preferred by the various Great People
## Copyright 2007 EmperorFool @ civfanatics.com

from CvPythonExtensions import *

gc = CyGlobalContext()

# see gc.getNumFlavorTypes() and gc.getFlavorTypes()
# not available via gc.getInfoTypeForString(), thus the hard-coding here :(
FLAVOR_MILITARY = 0
FLAVOR_RELIGION = 1
FLAVOR_PRODUCTION = 2
FLAVOR_GOLD = 3
FLAVOR_SCIENCE = 4
FLAVOR_CULTURE = 5
FLAVOR_GROWTH = 6
FLAVOR_ESPIONAGE = 7

NUM_FLAVORS = gc.getNumFlavorTypes()
FLAVORS = [ "Military", "Religion", "Production",
"Gold", "Science", "Culture",
"Growth", "Espionage" ]

NUM_TECHS = gc.getNumTechInfos()
NUM_AND_PREREQS = gc.getDefineINT("NUM_AND_TECH_PREREQS")
NUM_OR_PREREQS = gc.getDefineINT("NUM_OR_TECH_PREREQS")

class TechPrefs:

def __init__(self):
self.mTechs = {}
self.lTechsByFlavor = []
for iFlavor in range(NUM_FLAVORS):
self.lTechsByFlavor.append([])

# build a list of all techs and a list of techs for each flavor
for iTech in range(NUM_TECHS):
pTechInfo = gc.getTechInfo(iTech)
pTech = self.getTech(iTech)
for iFlavor in range(NUM_FLAVORS):
iFlavorValue = pTechInfo.getFlavorValue(iFlavor)
if (iFlavorValue > 0):
pTech.setFlavorValue(iFlavor, iFlavorValue)
self.lTechsByFlavor[iFlavor].append((-iFlavorValue, iTech, pTech))
bHasFlavor = True

# hook up prereq techs
for i in range(NUM_AND_PREREQS):
pPrereqTech = pTechInfo.getPrereqAndTechs(i)
if (pPrereqTech != -1):
pTech.addAndPrereq(self.getTech(pPrereqTech))
for i in range(NUM_OR_PREREQS):
pPrereqTech = pTechInfo.getPrereqOrTechs(i)
if (pPrereqTech != -1):
pTech.addOrPrereq(self.getTech(pPrereqTech))

# sort each flavor's list of techs by decreasing preference: reverse flavor value, tech number
for iFlavor in range(NUM_FLAVORS):
lTechs = self.lTechsByFlavor[iFlavor]
## print "%s has %d techs" % (FLAVORS[iFlavor], len(lTechs))
lTechs.sort()
self.lTechsByFlavor[iFlavor] = [ pTech for _, _, pTech in lTechs ]

## print "---- Techs with Flavor ----"
## for pTech in self.mTechs.values():
## print "%2d: %s" % (pTech.iTech, pTech.getName())
## for iFlavor in range(NUM_FLAVORS):
## print "---- %d Techs with Flavor %s ----" % (len(self.lTechsByFlavor[iFlavor]), FLAVORS[iFlavor])
## for pTech in self.lTechsByFlavor[iFlavor]:
## print "%2d-%2d: %s" % (pTech.getFlavorValue(iFlavor), pTech.iTech, pTech.getName())

## pOptics = self.getTech(gc.getInfoTypeForString("TECH_OPTICS"))
## print pOptics
## pAstronomy = self.getTech(gc.getInfoTypeForString("TECH_ASTRONOMY"))
## print pAstronomy
## pPhysics = self.getTech(gc.getInfoTypeForString("TECH_PHYSICS"))
## print pPhysics
## pAstronomy.removeFromTree()
## print pOptics
## print pAstronomy
## print pPhysics
##
## self.removeKnownTechs()
## print self.getNextResearchableFlavorTech(FLAVOR_RELIGION )
## print self.getNextResearchableFlavorTech(FLAVOR_SCIENCE)
## print self.getNextResearchableFlavorTech(FLAVOR_GOLD)
## techs = set()
## techs.add(self.getTechStr("TECH_THE_WHEEL"))
## techs.add(self.getTechStr("TECH_MYSTICISM"))
## print self.getNextResearchableWithFlavorTech(FLAVOR_RELI GION, techs)
## print self.getNextResearchableWithFlavorTech(FLAVOR_SCIE NCE, techs)
## print self.getNextResearchableWithFlavorTech(FLAVOR_GOLD , techs)
## techs.add(self.getTechStr("TECH_IRON_WORKING"))
## techs.add(self.getTechStr("TECH_IRON_POTTERY"))
## techs.add(self.getTechStr("TECH_MEDITATION"))
## print self.getNextResearchableWithFlavorTech(FLAVOR_RELI GION, techs)
## print self.getNextResearchableWithFlavorTech(FLAVOR_SCIE NCE, techs)
## print self.getNextResearchableWithFlavorTech(FLAVOR_GOLD , techs)#


def getTech(self, iTech):
if iTech not in self.mTechs:
self.mTechs[iTech] = Tech(iTech)
return self.mTechs[iTech]

def getTechStr(self, sTech):
iTech = gc.getInfoTypeForString(sTech)
if iTech in self.mTechs:
return self.mTechs[iTech]
return None

def removeTech(self, iTech):
"""Removes the given tech, usually because it has been researched."""
if (iTech in self.mTechs):
pTech = self.mTechs[iTech]
del self.mTechs[iTech]
for iFlavor in range(NUM_FLAVORS):
if pTech in self.lTechsByFlavor[iFlavor]:
self.lTechsByFlavor[iFlavor].remove(pTech)
pTech.removeFromTree()

def removeKnownTechs(self):
"""Removes the techs known to the current team."""
pTeam = gc.getTeam(gc.getActivePlayer().getTeam())
for iTech in range(NUM_TECHS):
if (pTeam.isHasTech(iTech)):
self.removeTech(iTech)


def getResearchableTechs(self):
"""Returns a set of all techs that can be researched now."""
sCan = set()
for pTech in self.mTechs.values():
if (pTech.canResearch()):
sCan.add(pTech)
return sCan

def getResearchableWithTechs(self, sTechs):
"""Returns a set of all techs that can be researched once the given techs have been researched."""
sCan = set()
for pTech in self.mTechs.values():
if (pTech not in sTechs and pTech.canResearchWith(sTechs)):
sCan.add(pTech)
return sCan


def getNextFlavorTech(self, iFlavor):
"""Returns the next tech in the flavor's list or None."""
if (len(self.lTechsByFlavor[iFlavor]) > 0):
return self.lTechsByFlavor[iFlavor][0]
else:
return None

def getNextResearchableFlavorTech(self, iFlavor):
"""Returns the next tech in the flavor's list that is researchable now or None."""
for pTech in self.lTechsByFlavor[iFlavor]:
if (pTech.canResearch()):
return pTech
return None

def getNextResearchableWithFlavorTech(self, iFlavor, sTechs):
"""Returns the next tech in the flavor's list that is researchable once the given techs are researched or None."""
for pTech in self.lTechsByFlavor[iFlavor]:
if (pTech not in sTechs and pTech.canResearchWith(sTechs)):
return pTech
return None


def printFlavorTechs(self, iFlavor):
"""Prints the techs in the flavor's list."""
for pTech in self.lTechsByFlavor[iFlavor]:
print pTech

def printResearchableFlavorTechs(self, iFlavor):
"""Prints the techs in the flavor's list."""
for pTech in self.lTechsByFlavor[iFlavor]:
if pTech.canResearch():
print pTech

def printResearchableWithFlavorTechs(self, iFlavor, sTechs):
"""Prints the techs in the flavor's list."""
for pTech in self.lTechsByFlavor[iFlavor]:
if pTech.canResearchWith(sTechs):
print pTech


class Tech:

def __init__(self, iTech):
self.iTech = iTech
self.lFlavorValues = [0] * NUM_FLAVORS
self.lFlavorPref = [0] * NUM_FLAVORS
self.sAndPrereqs = set()
self.sOrPrereqs = set()
self.iNumAndPrereqs = 0
self.iNumOrPrereqs = 0
self.sLeadsTo = set()

def getID(self):
return self.iTech

def getInfo(self):
return gc.getTechInfo(self.iTech)

def getName(self):
return self.getInfo().getDescription()


def setFlavorValue(self, iFlavor, iValue):
self.lFlavorValues[iFlavor] = iValue

def getFlavorValue(self, iFlavor):
return self.lFlavorValues[iFlavor]

def setFlavorPref(self, iFlavor, iPref):
self.lFlavorPref[iFlavor] = iPref

def getFlavorPref(self, iFlavor):
return self.lFlavorPref[iFlavor]


def addAndPrereq(self, pTech):
if pTech not in self.sAndPrereqs:
self.iNumAndPrereqs += 1
self.sAndPrereqs.add(pTech)
pTech.addLeadsTo(self)

def addOrPrereq(self, pTech):
if pTech not in self.sOrPrereqs:
self.iNumOrPrereqs += 1
self.sOrPrereqs.add(pTech)
pTech.addLeadsTo(self)

def removePrereq(self, pTech):
self.sAndPrereqs.discard(pTech)
self.sOrPrereqs.discard(pTech)


def getNumTechsNeeded(self):
"""Returns the minimum number of techs that must be researched to be able to research this tech."""
return self.getNumAndTechsNeeded() + self.getNumOrTechsNeeded()

def getNumAndTechsNeeded(self):
return len(self.sAndPrereqs)

def getNumOrTechsNeeded(self):
if (len(self.sOrPrereqs) == 0 or len(self.sOrPrereqs) < self.iNumOrPrereqs):
return 0
return 1

def getTechsNeeded(self):
"""
Returns two sets of techs that are needed to make this tech researchable.

The first set are all missing And prereqs.
The second set is all Or prereqs or an empty set if at least one has already been researched.
"""
andSet = self.sAndPrereqs.copy()
if (len(self.sOrPrereqs) == 0 or len(self.sOrPrereqs) < self.iNumOrPrereqs):
orSet = set()
else:
orSet = self.sOrPrereqs.copy()
return andSet, orSet

def canResearch(self):
"""Returns True if this tech has met all And prereqs and at least one Or prereq."""
return self.getNumTechsNeeded() == 0

def canResearchWith(self, sTechs):
"""Returns True if this tech can be researched once the given tech(s) have been researched."""
if (len(sTechs) == 0):
return self.canResearch()
sAnds = self.sAndPrereqs.difference(sTechs)
sOrs = self.sOrPrereqs.difference(sTechs)
return (len(sOrs) == 0 or len(sOrs) < self.iNumOrPrereqs) and len(sAnds) == 0


def addLeadsTo(self, pTech):
self.sLeadsTo.add(pTech)

def removeLeadsTo(self, pTech):
self.sLeadsTo.discard(pTech)

def removeFromTree(self):
"""Removes this tech from the prereq lists of the techs it leads to and the leads to lists of its prereqs."""
for pTech in self.sAndPrereqs:
pTech.removeLeadsTo(self)
for pTech in self.sOrPrereqs:
pTech.removeLeadsTo(self)
for pTech in self.sLeadsTo:
pTech.removePrereq(self)


def __str__(self):
str = self.getName()
bFirst = True
if (len(self.sAndPrereqs) > 0 or len(self.sOrPrereqs) > 0):
str += " requires "
if (len(self.sAndPrereqs) > 0):
for pTech in self.sAndPrereqs:
if bFirst:
bFirst = False
else:
str += " and "
str += pTech.getName()
if (len(self.sOrPrereqs) > 0):
if not bFirst:
str += " and "
bFirst = True
for pTech in self.sOrPrereqs:
if bFirst:
bFirst = False
else:
str += " or "
str += pTech.getName()
if (len(self.sLeadsTo) > 0):
str += ", leads to "
bFirst = True
for pTech in self.sLeadsTo:
if bFirst:
bFirst = False
else:
str += ", "
str += pTech.getName()
return str

EmperorFool
Aug 06, 2007, 07:54 AM
An extension of your idea for the screen -- why make it too easy? ;) -- would be to turn each of the tech buttons along the top into a toggle button (CheckBoxGFC).

As you have described it now, for a given GP and tech it shows the tech that GP will pop if the column's tech is researched. The extension is that it would also assume all other "checked" techs along the top are researched.

Say you have possible techs A, B and C with techs X, Y and Z listed below them. X is what the GP will pop if you research A next.

I'm suggesting allowing the user to click on (and thus check) tech B at the top. Now X shows what the GP will pop if you research A and B and Z shows what the GP will pop if you research B and C. Y still only assumes you research X because neither A nor C are checked. Does that make sense?

In any case, the code I posted above will support this pretty easily. Both TechPrefs.getResearchableWithTechs(set) and TechPrefs.getNextResearchableWithFlavorTechs(int, set) accept a set of Techs rather than just a single tech (though you can pass in a set containing a single Tech as I demonstrated above).

This is obviously a bit trickier in the interface, but I think entirely doable once you get the original versional working. I'm really excited to see how this pans out.

Another idea would be to add more tech columns than you can currently research. You can find all the techs you could research if you were to research all the techs you can research now. Add those as columns and then grey them out unless you check the techs that are prereqs.

So for example if you haven't researched Mysticism yet, both Meditation and Polytheism would be additional disabled columns. If you check Mystycism at the top, those two columns enable and act as normal columns. You could theoretically put the whole list of techs on it, but that's probably too overwhelming.

ruff_hi
Aug 06, 2007, 10:18 AM
What about a screen with GPs and their associated lightbulb techs down the right and ALL techs to the left. Yellow highlighted techs are techs that you already have (and these drive the lightbulb'ed techs) and (some other colour highlight - red?) are techs that you can research. You can click on the red techs - turning them orange (mixture or yellow and red) and this will update the GPs lightbulb techs. Non coloured techs are techs that you cannot research yet. Clicking on a red tech updates some other techs to red too.

You could use large icons for the GPs and lightbulb techs and small icons for the grid of techs. If you have seen the tech grid in WB, then that is sort of what I am talking about.

ruff_hi
Aug 06, 2007, 10:21 AM
I've completed the first part. The following code builds a data structure that holds the various GP tech preferenes and the tech prerequisites. This should be enough for you to build the screen.Thx - screen building is stuff that I haven't done - will have to dig into another similar screen and steal the code.

EmperorFool
Aug 06, 2007, 11:24 AM
Here's another thought. On the existing tech screen you can already select multiple techs to research. How about either tagging the techs that the GPs will pop or putting the icons on the bottom (or somewhere else) of the screen?

It might be information overload, but it also might fit nicely. I'll have to load up a game later and see if that would even fit. Some techs will have multiple GPs on them, so it might be a tight squeeze. But it would probably be a lot easier than designing and coding a whole new screen.

ruff_hi
Aug 06, 2007, 11:32 AM
It might be information overload, but it also might fit nicely. I'll have to load up a game later and see if that would even fit. Some techs will have multiple GPs on them, so it might be a tight squeeze. But it would probably be a lot easier than designing and coding a whole new screen.

I'm thinking we add a tab to the current tech screen. F4 has multiple tabs, why not F6!

EmperorFool
Aug 07, 2007, 02:26 AM
I think a full tab would be great as you could show much more information as your sample screenshot shows. Until then, here's what I came up with. It adds a row of images at the bottom of the normal tech chooser screen, one block of three per GP type (5 types): GP image, tech the GP will pop now, and tech the GP would pop if you researched all the techs in your queue. The latter image updates as you click/shift-click on techs in the tree above.

Here (http://forums.civfanatics.com/uploads/111657/GP_Tech_Prefs_0.2.zip) is the ZIP containing the two files. TechPrefs.py goes into CustomAssets/Python and CvTechChooser.py goes into CustomAssets/Python/screens. It should give you a good sample of how to use TechPrefs in your screen and how to add the images.

Ket
Aug 07, 2007, 08:17 AM
I'm trying to do a cut of .04 of the unaltered that has some stuff added in.

This mod, being one of them... But what happens when I add the two files in
Is before Dawn Of Man, the Tech Screen Pops, and once its closed Dawn of Man is there and passing through dawn of man gives us a blank interface.

EmperorFool
Aug 07, 2007, 09:01 AM
Yes something funky is going on for me as well. I made some minor changes (locally only -- not in 0.4 of BUG or what I have posted above) to deal with Advanced Start and can't seem to get it to work at all. :(

Basically, it appears that all the screens are built when the game starts and then hidden. The TechChooser is throwing an exception, halting this process. The version I have posted here, however, was working when I posted it. Are you opening a game or starting a new game? If the latter, are you doing AdvStart?

Finally, can you try this mod with just the two files above? I'll do the same on my end. I've been :wallbash: for a while now with no luck. :(

EmperorFool
Aug 07, 2007, 09:08 AM
I just tested it with only the above two files in my CustomAssets, after clearing the cache, and it's borked. It was working so well when I uploaded it, I don't see how the same code could be failing now. :cry:

The whole import of TechPrefs is failing. None of its global variables are set when the module loads. I don't see how that's possible since the GC exists at that time. If I remove the TechChooser and import the module in the console, it works just fine. Any ideas?

Zebra 9
Aug 07, 2007, 09:27 AM
I have had trouble like this before. Somtimes the def __init__ function is called before the SDK has exposed everything to python and code like this "NUM_FLAVORS = gc.getNumFlavorTypes()" which is outside of the class can also cause this. If this is the problem you're having it could explain why it works from the console (The game has completely loaded).

EmperorFool
Aug 07, 2007, 09:30 AM
I found the error. L A M E is all I can say about this one. CyGlobalContext.getFlavorTypes() apparently halts execution if it's called where I have it in TechPrefs. No exception is generated -- the module just stops executing.

In TechPrefs replace it with 8


NUM_FLAVORS = 8


near the top of the file, and everything works. WTFever. Notice that gc.getNumTechInfos() works just fine a few lines down. :crazyeye:

I'll update the archive above once I've tested my changes to handle Advanced Start.

EmperorFool
Aug 07, 2007, 09:36 AM
code like this "NUM_FLAVORS = gc.getNumFlavorTypes()" which is outside of the class can also cause this.

Damn, I wish I had posted this an hour ago. I'm actually rather annoyed that I changed the file and then posted without testing the change. I'm not usually that careless. Apologies, Ket, for spinning you in circles.

The truly odd part is that the flavors are hard-coded (not defined in XML as far as I can tell) and not available via getInfoTypeForString(). Ah, I bet they are built from the techs, and the techs aren't processed until you call getNumTechInfos(). Total assumption, but good to know.

Normally I'd avoid executing code at the global level like this -- let alone use global variables -- but all the Firaxis code does it. Anyway, thanks Zebra9 for the info.

Zebra 9
Aug 07, 2007, 10:19 AM
You're welcome.:thumbsup:

EmperorFool
Aug 07, 2007, 11:00 AM
I've uploaded version 0.2 (http://forums.civfanatics.com/uploads/111657/GP_Tech_Prefs_0.2.zip) of this mod, working so far as I can tell.

It handles Advanced Start correctly and also displays a "N/A" button (like no state religion) when a GP can't research any techs.

Impaler[WrG]
Aug 07, 2007, 12:07 PM
Flavors are defined in XML on the GlobalTypes.xml directly under the XML directory

EmperorFool
Aug 07, 2007, 07:48 PM
Ah okay, that explains why they're not available through getInfoTypeForString. That and they dont have a FlavorInfo class I suppose. Thanks!

ruff_hi
Aug 08, 2007, 05:14 AM
Can you post the unadjusted techchooser file so that I can extract your changes and put it into Vanilla and Warlords?

EmperorFool
Aug 08, 2007, 08:25 AM
Check your email.

ruff_hi
Aug 08, 2007, 05:30 PM
Well - for what it is worth, here are the vanilla (http://forums.civfanatics.com/uploads/64034/LightBulbTechs-Vanilla.zip) and warlords (http://forums.civfanatics.com/uploads/64034/LightBulbTechs-Warlords.zip) versions. Both of them give me errors that I haven't been able to track down.


ERR: Python function showTechChooser failed, module CvScreensInterface
Traceback (most recent call last):
File "CvScreensInterface", line 612, in forceScreenUpdate
File "CvTechChooser", line 644, in updateTechRecords
IndexError
:
list index out of range

The code in question ...


def updateTechRecords (self, bForce):

# If we are the Pitboss, we don't want to put up an interface at all
if ( CyGame().isPitbossHost() ):
return

# Get the screen
screen = CyGInterfaceScreen( "TechChooser", CvScreenEnums.TECH_CHOOSER )

abChanged = []
bAnyChanged = 0

# Go through all the techs
for i in range(gc.getNumTechInfos()):

abChanged.append(0)

if ( gc.getTeam(gc.getPlayer(self.iCivSelected).getTeam ()).isHasTech(i) ):
if ( self.aiCurrentState[i] != CIV_HAS_TECH ):
self.aiCurrentState[i] = CIV_HAS_TECH
abChanged[i] = 1
bAnyChanged = 1
elif ( gc.getPlayer(self.iCivSelected).getCurrentResearch () == i ):
if ( self.aiCurrentState[i] != CIV_IS_RESEARCHING ):
self.aiCurrentState[i] = CIV_IS_RESEARCHING
abChanged[i] = 1
bAnyChanged = 1


I think the error has something to do with [i] being out of range but that makes no sense to me.

Note: the above warlords version might still have some advancestart code in it. I did remove it and got the same error, not sure if I correctly saved the version with that part removed.

EmperorFool
Aug 08, 2007, 08:03 PM
I kept getting this same error a lot. The problem is that the screen itself is created as the game is starting up, and something in interfaceScreen() is causing this to fail. Part of that code is creating the arrays that are accessed later in updateTechRecords().

To fix it on my end I had to make sure that the TechPrefs isn't created on the first call to interfaceScreen(). That's why I put create it in updateTechPrefs(). You have it the same way, so this shouldn't be the problem.

The arrays are created by placeTechs() which is called from interfaceScreen(). Look for a way that interfaceScreen() could fail before line 121. The only addition I see is


self.NO_TECH_ART = ArtFileMgr.getInterfaceArtInfo("INTERFACE_BUTTONS_CANCEL").getPath()


Does this art file exist in both Vanilla and Warlords? I pulled it from the religion screen, which should exist in both. When exactly does it fail? Starting a game? Loading one? Or do those work but it fails wben you hit F6? Do you end up with an empty TechChooser screen?

When I was having the problems, starting and loading both ended up with a blank TechChooser screen with only the Engineer button showing. I feel your pain. This was the worst part for me since there's no error message for what initially causes the arrays not to be created. I think the reason is because it's during preGameStart() that there are no error messages -- logging hasn't been enabled yet.

ruff_hi
Aug 08, 2007, 10:31 PM
When exactly does it fail? Starting a game? Loading one? Or do those work but it fails wben you hit F6? Do you end up with an empty TechChooser screen?Everything starts up ok but I get errors when I hit F6. The tech screen looks ok, but clicking on anything (except scroll bar and exit) gives me the errors again. I'll take a further look at the code and do my usual debugging route which is to comment everything out and slowly add stuff back in.

I feel your pain."I feel your pain." What sort of comment is that? Are we a mutual self help group? How about "The solution is ..." or "let me fix it". :D