Preventing Religion Founding

OrionVeteran

Deity
Joined
Dec 25, 2003
Messages
2,443
Location
Newport News VA
Is there a simple way to prevent the founding of a new religion if you already have one?

1. If I already have a Holy City and I discover a tech that founds a religion, I want to prevent the founding of a new religion.

2. If I don't have a Holy City and I discover a tech that founds a religion, I want to found a new religion, as the game would normally.

Very Respectfully,

Orion Veteran :cool:
 
Hi there,

I'm trying to do a python script that do a very similar function, unfortunately I'm a beginner in Python and I would be glad to have the point of view of one of this Python veteran on the forum.

So far I have:

In CvCustomEventManager.py:

import CvEnhancedSpiritualEventManager

class CvCustomEventManager(CvEventManager.CvEventManager, object):

CustomEvents = {}

def __init__(self, *args, **kwargs):

super(CvCustomEventManager, self).__init__(*args, **kwargs)
# map the initial EventHandlerMap values into the new data structure
for eventType, eventHandler in self.EventHandlerMap.iteritems():
self.setEventHandler(eventType, eventHandler)


CvEnhancedSpiritualEventManager.CvEnhancedSpiritualEventManager(self)

In CvEnhancedSpiritualEventManager.py:

# EnhancedSpiritual
# CvEnhancedSpiritualEventManager

from CvPythonExtensions import *
import CvUtil
import CvEventManager
import sys

# --------- EnhancedSpiritual -------------
import EnhancedSpiritual
gc = CyGlobalContext()
EnhancedSpiritual = EnhancedSpiritual.EnhancedSpiritual()

###################################################
class CvEnhancedSpiritualEventManager(CvEventManager.CvEventManager):
def __init__(self, eventManager):
# initialize base class
eventManager.addEventHandler("ReligionFounded", self.onReligionFounded)


def onReligionFounded(self, argsList):
EnhancedSpiritual.onReligionFounded(argsList)

And EnhancedSpiritual.py

## Sid Meier's Civilization 4
## Copyright Firaxis Games 2005
##
## Enhanced Spiritual
##

from CvPythonExtensions import *
import CvUtil
import PyHelpers
import Popup as PyPopup

# globals
gc = CyGlobalContext()

class EnhancedSpiritual:
def onReligionFounded(self, argsList):
iReligion, iFounder = argsList
pPlayer = iFounder

CyInterface().addMessage(pPlayer.getID(),false,10,"pum")


So far I cannot even display the message "pum" when there is a new religion founded....

Please help me :cry:
 
@ orion: there is a gameutils callback to do this, but it will require some python. The callback doHolyCityTech is called when a tech with a religion is achieved, and you can control whether the related religion is founded. There is some discussion about it in this thread. You can see the original function in Assets/Python/CvGameUtils.py. I guess you would need to loop all the cities owned by this player, and loop all the religions to ask if this city is this religion's holy city. Maybe there is a more efficient way.

@ jack: do you have python exceptions and logging enabled? I guess you are using a nonstandard event manager, which is probably better than the way I did it. I don't recognize the code; but since you didn't say what error you are encountering I am guessing you don't have exceptions and logging enabled yet. This is critical for developing python.
 
@jack: In addition to enabling the logging as davidlallen suggests (more on that below), this jumps out at me:
Code:
iReligion, iFounder = argsList
pPlayer = iFounder
With the Firaxis style conventions, the prefix of variable names usually denote the type of the variable. While that doesn't really matter for code you write, it's less confusing if you also follow that convention and it makes understanding existing code easier too. iFounder is an integer (player ID number) while pPlayer is normally a pointer (i.e. a CyPlayer or other object). Setting pPlayer = iFounder is generally not something you want to do and since pPlayer is now an integer, pPlayer.getID() is certainly causing one of the errors you are encountering. Most likely you want to do one of the following:

Code:
pPlayer = gc.getPlayer(iFounder)  # return a CyPlayer object

pPlayer = PyPlayer(iFounder) # return a PyPlayer object

The type of object depends on what you want to do with it. CyPlayer is the parts of the SDK's CvPlayer object that are exposed to Python and PyPlayer is a Python-only helper that does some different useful stuff. Since all you are currently doing is printing a message, you don't even need a player object at all yet and can instead use iFounder in the addMessage call.

Type mismatches like this are exactly the kind of common errors that the Python logging is great at catching. Turn on all the various logging options in your Civilization 4.ini file and then check out the PythonErr.log file in your Civ4 Documents directory (default something like "<Document Folder>\My Games\<Game Name>\logs")
 
Hey,

Thx for your answers!

Enabling python error log is indeed a good start :)

I'm totally new to the world of python modding so... :(

Do you have any function to prevent a player from researching a specific tech (literaly deleting the tech from his tech tree)?
 
Once you have learned a little more about python you will easily be able to answer this type of question. With the original religion question, as well as this tech tree question, the way to research is to look into Assets/Python/CvGameUtils.py, and see what callbacks are available.

In this case you would want to explore canResearch and cannotResearch. This will prevent the AI from researching these techs. I assume it will prevent the player from researching them too. But it would not *remove* from the visual tech tree. I am not sure how the GUI would show up if the player were to click on a tech for which cannotResearch was returning true, hopefully something useful would display.
 
If cannotResearch return True, then the tech will be red on the Tech Advisor Screen and clicking on it will do nothing. If you don't want the diabled techs to show on the screen there are about 7 or 8 places in the CvTechChooser.py that you have to modify.
 
@ orion: there is a gameutils callback to do this, but it will require some python. The callback doHolyCityTech is called when a tech with a religion is achieved, and you can control whether the related religion is founded. There is some discussion about it in this thread. You can see the original function in Assets/Python/CvGameUtils.py. I guess you would need to loop all the cities owned by this player, and loop all the religions to ask if this city is this religion's holy city. Maybe there is a more efficient way.

Here is what I have so far:

Code:
def doHolyCityTech(self,argsList):
	eTeam = argsList[0]
	ePlayer = argsList[1]
	eTech = argsList[2]
	bFirst = argsList[3]		
		
	if gc.getPlayer( ePlayer ).isHuman( ):
		PyPlayer = gc.getPlayer(ePlayer)
			
		#Check if religious tech		
		bRTech = False
		if eTech == gc.getInfoTypeForString("TECH_MEDITATION"):
			iRelCheck = gc.getInfoTypeForString("RELIGION_BUDDHISM")
			bRTech = True
		elif eTech == gc.getInfoTypeForString("TECH_POLYTHEISM"):
			iRelCheck = gc.getInfoTypeForString("RELIGION_HINDUISM")
			bRTech = True
		elif eTech == gc.getInfoTypeForString("TECH_MONOTHEISM"):
			iRelCheck = gc.getInfoTypeForString("RELIGION_JUDAISM")
			bRTech = True
		elif eTech == gc.getInfoTypeForString("TECH_THEOLOGY"):
			iRelCheck = gc.getInfoTypeForString("RELIGION_CHRISTIANITY")
			bRTech = True
		elif eTech == gc.getInfoTypeForString("TECH_CODE_OF_LAWS"):
			iRelCheck = gc.getInfoTypeForString("RELIGION_CONFUCIANISM")
			bRTech = True
		elif eTech == gc.getInfoTypeForString("TECH_PHILOSOPHY"):
			iRelCheck = gc.getInfoTypeForString("RELIGION_TAOISM")
			bRTech = True
		elif eTech == gc.getInfoTypeForString("TECH_DIVINE_RIGHT"):
			iRelCheck = gc.getInfoTypeForString("RELIGION_ISLAM")
			bRTech = True

		if bRTech == True:			
			PlayerHasaHolyCity = False
		
			for pyCity in PyHelpers.PyPlayer(ePlayer).getCityList():
				for iReligionLoop in range(gc.getNumReligionInfos()):
					if pyCity.isHolyCityByType( iReligionLoop ):
						PlayerHasaHolyCity = True
						break
					
			if PlayerHasaHolyCity != -1 :
				# Returning true will stop Religion Popup from being dispalyed
				return True
			else:
				# Returning false allows the DLL to continue through it's normal process.
				return False
		
	else:
		return False

This code will prevent the popup to choose a religion from being dispalyed, but fails to stop a new religion from being founded. The game defaults back to picking a religion for you.

My Question is: What code can I insert to stop the new religion from being founded?
 
To make sure the function *can* do what you expect, temporarily replace the function with a one line version, "return true". Now do you see the effect of religions not being founded?

I am not sure why you are comparing a boolean value to -1. Instead of:

if PlayerHasaHolyCity != -1:

Try:

if not PlayerHasaHolyCity:

Or even better, replace the whole section with "return PlayerHasaHolyCity".

Finally, although it does not matter from the functionality standpoint, there is a better way to check the religions rather than hard coding all the religion and tech names. Instead of having a big if statement to find the prereqtech, loop over all the religions and for each one, call CvReligionInfo.getTechPrereq to get it.
 
To make sure the function *can* do what you expect, temporarily replace the function with a one line version, "return true". Now do you see the effect of religions not being founded?

I am not sure why you are comparing a boolean value to -1. Instead of:

if PlayerHasaHolyCity != -1:

Try:

if not PlayerHasaHolyCity:

Or even better, replace the whole section with "return PlayerHasaHolyCity".

Finally, although it does not matter from the functionality standpoint, there is a better way to check the religions rather than hard coding all the religion and tech names. Instead of having a big if statement to find the prereqtech, loop over all the religions and for each one, call CvReligionInfo.getTechPrereq to get it.

In my test scenario, I already have one religion founded and have a holy city. My goal is to stop the founding of a second religion at the discovery of the next religious tech; which is only one turn away for testing.

I removed all of my code and the results of a "True" value stops the popup that enables the selection of a religion. However, it does not stop a religion from being founded. The computer simply acts as if the checkbox for selecting of religions is not checked and founds the default religion for the tech I just discovered.

I made the change you suggested below and reversed the True, False values:

if not PlayerHasaHolyCity:

The code still works after the change and that line is now easier to read.

True will stop Religion Popup from being dispalyed
False allows the Popup to be dispalyed.

We can get to the more efficient loop code later. The code works OK to this point. What I need now is a way to stop the religion from being founded. Do you have a line of code to stop the founding?
 
to completely stop a relition founding, I believe you have to intercept doHolyCity as well. The following seemed to prevent founding a religion for me in a brief test (loaded up a save, bulbed Philosophy first, ended turn):

Code:
	def doHolyCity(self):
		return True

	def doHolyCityTech(self,argsList):
		eTeam = argsList[0]
		ePlayer = argsList[1]
		eTech = argsList[2]
		bFirst = argsList[3]
		return True
I'm not entirely sure what happens if doHolyCityTech() returns False while doHolyCity() returns True, but it's something for you to experiment with ;)
 
to completely stop a relition founding, I believe you have to intercept doHolyCity as well. The following seemed to prevent founding a religion for me in a brief test (loaded up a save, bulbed Philosophy first, ended turn):

Code:
	def doHolyCity(self):
		return True

	def doHolyCityTech(self,argsList):
		eTeam = argsList[0]
		ePlayer = argsList[1]
		eTech = argsList[2]
		bFirst = argsList[3]
		return True
I'm not entirely sure what happens if doHolyCityTech() returns False while doHolyCity() returns True, but it's something for you to experiment with ;)

My test confirmed your results -- no religion was founded only if both functions are set to True.

I should be able to work up a function to finish the job. :thanx:
 
Well, I figured it out and the code tested good.

Code:
def doLimitedRelionsCheck(self):				
	ePlayer = gc.getPlayer(CyGame().getActivePlayer()).getID()	
				
	if gc.getPlayer( ePlayer ).isHuman( ):
		PyPlayer = gc.getPlayer(ePlayer)			
		PlayerHasaHolyCity = False
			
		for pyCity in PyHelpers.PyPlayer(ePlayer).getCityList():
			for iReligionLoop in range(gc.getNumReligionInfos()):
				if pyCity.isHolyCityByType( iReligionLoop ):
					PlayerHasaHolyCity = True
					break
					
		if not PlayerHasaHolyCity:
			# Returning false allows the DLL to continue through it's normal process.				
			return False					
		else:	
			#Returning true will stop Religion Popup from being dispalyed	
			return True
					
	else:
		return False		
			
def doHolyCity(self):		
	MyRelionsCheck = self.doLimitedRelionsCheck()
		
	if not MyRelionsCheck:
		return False
	else:
		return True

def doHolyCityTech(self,argsList):
	eTeam = argsList[0]
	ePlayer = argsList[1]
	eTech = argsList[2]
	bFirst = argsList[3]		
	MyRelionsCheck = self.doLimitedRelionsCheck()
		
	if not MyRelionsCheck:
		return False
	else:
		return True

Note: To finish up, I will be transfering the doLimitedRelionsCheck code into doHolyCity.

I have one more question to ask: Is there a way to change a religious tech to allow any CIV to discover a religion, not just the first CIV?

Orion Veteran :cool:
 
Isn't that just an improvement on your existing function? Now it always prevents the religion from being founded. It seems you just want to put more "if" statements for whatever other conditions you want.
 
Isn't that just an improvement on your existing function? Now it always prevents the religion from being founded. It seems you just want to put more "if" statements for whatever other conditions you want.

It is a big improvement in code, but you are not correct when you say, "Now it always prevents the religion from being founded." That is just not true! If a Civ does not have a holycity, then a religion will be founded. Take another look.

Very Respectfully,

Orion Veteran :cool:
 
OK now I have improved the code to just the two functions that we are concerned about.

Code:
def doHolyCity(self):				
	ePlayer = gc.getPlayer(CyGame().getActivePlayer()).getID()
	PyPlayer = gc.getPlayer(ePlayer)			
	PlayerHasaHolyCity = False
			
	for pyCity in PyHelpers.PyPlayer(ePlayer).getCityList():
		for iReligionLoop in range(gc.getNumReligionInfos()):
			if pyCity.isHolyCityByType( iReligionLoop ):
				PlayerHasaHolyCity = True
				break
					
	if not PlayerHasaHolyCity:
		# Returning false will allow the religion to be founded
		return False					
	else:	
		# Returning true will stop the religion from being founded	
		return True
		
def doHolyCityTech(self,argsList):
	eTeam = argsList[0]
	ePlayer = argsList[1]
	eTech = argsList[2]
	bFirst = argsList[3]

	if gc.getPlayer( ePlayer ).isHuman( ):
		MyHolyCityCheck = self.doHolyCity()
		
		if not MyHolyCityCheck:
			# Returning false allows the Religion popup to be displayed
			return False
		else:
			# Returning true will stop Religion Popup from being dispalyed	
			return True			 
	else:
		return False

Now back to my question: Is there a way to change a religious tech to allow any CIV to discover a new religion, not just the first CIV, who discovered the tech?

Very Respectfully,

Orion Veteran :cool:
 
The SDK function CvTeam::setHasTech handles this and it specifically checks to see if you are first before running that section of the code. However, just before it does this check, it sends the techAcquired event to Python for information purposes.

So you might be able to hook techAcquired and then check if it's a religion-tech yourself, and then found the religion yourself in Python using CyPlayer.foundReligion().
 
The SDK function CvTeam::setHasTech handles this and it specifically checks to see if you are first before running that section of the code. However, just before it does this check, it sends the techAcquired event to Python for information purposes.

So you might be able to hook techAcquired and then check if it's a religion-tech yourself, and then found the religion yourself in Python using CyPlayer.foundReligion().

The hook of the SDK function is the trick. Any ideas how I might be able to bypass the SDK function? My idea is to create a function to determine if the Civ, who discovered the tech, is the first CIV or not.

Orion Veteran :cool:
 
All of the possible SDK bypasses are listed in the gameutils and eventmanager python files. I mentioned this in post 6 of this thread. In this case you want onTechAcquired from the event manager.
 
All of the possible SDK bypasses are listed in the gameutils and eventmanager python files. I mentioned this in post 6 of this thread. In this case you want onTechAcquired from the event manager.

I agree that I'm going to have to use onTechAcquired for this function. Assuming that, here is my approach:

1. If specific religious tech is discovered then
2. If player does not have a holy city then
3. If player is not the first CIV to discover the religious tech then
4. Run function to found a religion.

This would allow more than one Civ to found a religion upon discovering the same tech.

I know how to do items 1, 2 and 4 in Python. I did not see anything in gameutils or eventmanager that could help with number 3. How can I determine if Civ (iPlayer) is the first or not to discover a specific tech (iTechType)? Is there a line of Python code to do make that determination?

Respectfully,

Orion Veteran :cool:
 
Top Bottom