Skip to content

Commit

Permalink
Refactor event handling (#58)
Browse files Browse the repository at this point in the history
* Add platy settings

* Add event handler

* Remove rfc event manager

* Add screen handler

* Fix rebase

* Update BUG file

* Fix typo

* Remove data setup in bugdata

* Add handler for gamestart

* Update handlers

* Move components in root folder

* Remove class for name manager

* Remove class for province manager

* Remove class for rise and fall

* Remove class for AIWars

* Remove class for Barbs

* Remove class for Companies

* Remove class for Crusades

* Remove class for Mercenaries

* Remove class for Plague

* Remove class for Religions

* Remove class for Resources

* Remove class for Stability

* Remove class for UniquePowers

* Remove class for Victory

* Rename variable

* Add fct for message if player is human

* Apply handler for onCityAcquired

* Apply handler for onCityAcquiredAndKept

* Apply handler for onPreSave

* Apply handler for combatResult

* Fix pre save handler

* Fix city name manager

* Fix religion events

* Apply handler for onCityRazed

* Update export paths

* Apply handler for onCityBuilt

* Apply handler for onReligionFounded

* Apply handler for onBuildingBuilt

* Apply handler for onProjectBuilt

* Apply handler for onBeginGameTurn

* Clean unused variables

* Apply handler for onEndGameTurn

* Apply handler for onReligionSpread

* Apply handler for onFirstContact

* Apply handler for onUnitPromoted

* Apply handler for onUnitKilled

* Apply handler for onUnitLost

* Apply handler for onTechAcquired

* Apply handler for onPlayerChangeAllCivics

* Apply handler for onPlayerChangeStateReligion

* Apply handler for onUnitPillage

* Apply handler for onBeginPlayerTurn

* Fix show_if_human

* Fix buildingBuilt and playerchangeallcivics

* Simplify initUnit for belisarius

* Fix module calling

* Apply handler for onKbdEvent

* Rename

* Remove former RFCE handler

* Rename

* Apply handler for screen from CvEventManager

* Apply popup handler for custom events

* Fix loading

* Fix loading 2

* Fix playerChangeStateReligion

* Fix event popups

* Final fix of the game start

* Simplify calls of factories

* Add log

* Fix widget

* Fix bug in choice call
  • Loading branch information
VDuchauffour authored Sep 2, 2024
1 parent d7350c6 commit 5e76237
Show file tree
Hide file tree
Showing 64 changed files with 17,214 additions and 17,725 deletions.
293 changes: 293 additions & 0 deletions Assets/Python/AIWars.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
from CvPythonExtensions import *
from Consts import INDEPENDENT_CIVS
from Core import (
civilization,
civilizations,
get_data_from_upside_down_map,
get_scenario_start_turn,
is_independent_civ,
is_major_civ,
turn,
year,
plots,
)
from CoreTypes import Civ
from PyUtils import rand
from RFCUtils import (
getMaster,
getPlagueCountdown,
isAVassal,
minorCoreWars,
minorWars,
restorePeaceAI,
restorePeaceHuman,
)
from StoredData import data
from WarMapData import WARS_MAP
from Events import handler

gc = CyGlobalContext()

iMinIntervalEarly = 15
iMaxIntervalEarly = 30
iMinIntervalLate = 40
iMaxIntervalLate = 60
iThreshold = 100
iMinValue = 30


@handler("GameStart")
def setup():
iTurn = get_scenario_start_turn() # only check from the start turn of the scenario
data.iNextTurnAIWar = iTurn + rand(iMaxIntervalEarly - iMinIntervalEarly)


def getAttackingCivsArray(iCiv):
return data.lAttackingCivsArray[iCiv]


def setAttackingCivsArray(iCiv, iNewValue):
data.lAttackingCivsArray[iCiv] = iNewValue


@handler("BeginGameTurn")
def checkTurn(iGameTurn):
if iGameTurn > 20:
# Absinthe: automatically turn peace on between independent cities and all the major civs
turn_peace_human_mapper = {
Civ.INDEPENDENT: 9,
Civ.INDEPENDENT_2: 4,
Civ.INDEPENDENT_3: 14,
Civ.INDEPENDENT_4: 19,
}
for civ, value in turn_peace_human_mapper.items():
if iGameTurn % 20 == value:
restorePeaceHuman(civ)

turn_peace_ai_mapper = {
Civ.INDEPENDENT: 0,
Civ.INDEPENDENT_2: 9,
Civ.INDEPENDENT_3: 18,
Civ.INDEPENDENT_4: 27,
}
for civ, value in turn_peace_ai_mapper.items():
if iGameTurn % 36 == value:
restorePeaceAI(civ, False)

# Absinthe: automatically turn war on between independent cities and some AI major civs
# runned on the 2nd turn after restorePeaceAI()
turn_minor_wars_mapper = {
Civ.INDEPENDENT: 2,
Civ.INDEPENDENT_2: 11,
Civ.INDEPENDENT_3: 20,
Civ.INDEPENDENT_4: 29,
}
for civ, value in turn_minor_wars_mapper.items():
if iGameTurn % 36 == value:
minorWars(civ, iGameTurn)

# Absinthe: declare war sooner / more frequently if there is an Indy city inside the core area
# so the AI will declare war much sooner after an indy city appeared in it's core
turn_minor_core_wars_mapper = {
Civ.INDEPENDENT: 10,
Civ.INDEPENDENT_2: 7,
Civ.INDEPENDENT_3: 4,
Civ.INDEPENDENT_4: 1,
}
for civ, value in turn_minor_core_wars_mapper.items():
if iGameTurn % 12 == value:
minorCoreWars(civ, iGameTurn)

# Absinthe: Venice always seeks war with an Independent Ragusa - should help AI Venice significantly
if iGameTurn % 9 == 2:
pVenice = gc.getPlayer(Civ.VENECIA)
if pVenice.isAlive() and not pVenice.isHuman():
pRagusaPlot = gc.getMap().plot(64, 28)
if pRagusaPlot.isCity():
pRagusaCity = pRagusaPlot.getPlotCity()
iOwner = pRagusaCity.getOwner()
if is_independent_civ(iOwner):
# Absinthe: probably better to use declareWar instead of setAtWar
teamVenice = gc.getTeam(pVenice.getTeam())
teamVenice.declareWar(iOwner, False, WarPlanTypes.WARPLAN_LIMITED)

# Absinthe: Kingdom of Hungary should try to dominate Sisak/Zagreb if it's independent
if iGameTurn > year(1000) and iGameTurn % 7 == 3:
pHungary = gc.getPlayer(Civ.HUNGARY)
if pHungary.isAlive() and not pHungary.isHuman():
pZagrebPlot = gc.getMap().plot(62, 34)
if pZagrebPlot.isCity():
pZagrebCity = pZagrebPlot.getPlotCity()
iOwner = pZagrebCity.getOwner()
if is_independent_civ(iOwner):
# Absinthe: probably better to use declareWar instead of setAtWar
teamHungary = gc.getTeam(pHungary.getTeam())
teamHungary.declareWar(iOwner, False, WarPlanTypes.WARPLAN_LIMITED)

if iGameTurn == data.iNextTurnAIWar:
iMinInterval = iMinIntervalEarly
iMaxInterval = iMaxIntervalEarly

# skip if already in a world war
iCiv, iTargetCiv = pickCivs()
if 0 <= iTargetCiv <= civilizations().drop(Civ.BARBARIAN).len():
if iTargetCiv != Civ.POPE and iCiv != Civ.POPE and iCiv != iTargetCiv:
initWar(iCiv, iTargetCiv, iGameTurn, iMaxInterval, iMinInterval)
return
data.iNextTurnAIWar = iGameTurn + iMinInterval + rand(iMaxInterval - iMinInterval)


def pickCivs():
iCiv = chooseAttackingPlayer()
if iCiv != -1:
if is_major_civ(iCiv):
iTargetCiv = checkGrid(iCiv)
return (iCiv, iTargetCiv)
else:
return (-1, -1)


def initWar(iCiv, iTargetCiv, iGameTurn, iMaxInterval, iMinInterval):
# Absinthe: don't declare if couldn't do it otherwise
teamAgressor = gc.getTeam(gc.getPlayer(iCiv).getTeam())
if teamAgressor.canDeclareWar(iTargetCiv):
gc.getTeam(gc.getPlayer(iCiv).getTeam()).declareWar(iTargetCiv, True, -1)
data.iNextTurnAIWar = iGameTurn + iMinInterval + rand(iMaxInterval - iMinInterval)
# Absinthe: if not, next try will come the 1/2 of this time later
else:
data.iNextTurnAIWar = iGameTurn + (iMinInterval + rand(iMaxInterval - iMinInterval) / 2)


def chooseAttackingPlayer():
# finding max teams ever alive (countCivTeamsEverAlive() doesn't work as late human starting civ gets killed every turn)
iMaxCivs = civilizations().majors().len()
for i in civilizations().majors().ids():
j = civilizations().main().len() - i
if gc.getPlayer(j).isAlive():
iMaxCivs = j
break

if gc.getGame().countCivPlayersAlive() <= 2:
return -1
else:
iRndnum = rand(iMaxCivs)
iAlreadyAttacked = -100
iMin = 100
iCiv = -1
for i in range(iRndnum, iRndnum + iMaxCivs):
iLoopCiv = i % iMaxCivs
if gc.getPlayer(iLoopCiv).isAlive() and not gc.getPlayer(iLoopCiv).isHuman():
if (
getPlagueCountdown(iLoopCiv) >= -10 and getPlagueCountdown(iLoopCiv) <= 0
): # civ is not under plague or quit recently from it
iAlreadyAttacked = getAttackingCivsArray(iLoopCiv)
if isAVassal(iLoopCiv):
iAlreadyAttacked += 1 # less likely to attack
# check if a world war is already in place:
iNumAlreadyWar = 0
tLoopCiv = gc.getTeam(gc.getPlayer(iLoopCiv).getTeam())
for kLoopCiv in civilizations().majors().ids():
if tLoopCiv.isAtWar(kLoopCiv):
iNumAlreadyWar += 1
if iNumAlreadyWar >= 4:
iAlreadyAttacked += 2 # much less likely to attack
elif iNumAlreadyWar >= 2:
iAlreadyAttacked += 1 # less likely to attack

if iAlreadyAttacked < iMin:
iMin = iAlreadyAttacked
iCiv = iLoopCiv
if iAlreadyAttacked != -100:
setAttackingCivsArray(iCiv, iAlreadyAttacked + 1)
return iCiv
else:
return -1


def checkGrid(iCiv):
pCiv = gc.getPlayer(iCiv)
pTeam = gc.getTeam(pCiv.getTeam())
lTargetCivs = [0] * civilizations().drop(Civ.BARBARIAN).len()

# set alive civs to 1 to differentiate them from dead civs
for iLoopPlayer in civilizations().drop(Civ.BARBARIAN).ids():
if iLoopPlayer == iCiv:
continue
if pTeam.isAtWar(iLoopPlayer): # if already at war with iCiv then it remains 0
continue
pLoopPlayer = gc.getPlayer(iLoopPlayer)
iLoopTeam = pLoopPlayer.getTeam()
pLoopTeam = gc.getTeam(iLoopTeam)
if (
iLoopPlayer < civilizations().majors().len()
): # if master or vassal of iCiv then it remains 0
if pLoopTeam.isVassal(iCiv) or pTeam.isVassal(iLoopPlayer):
continue
if pLoopPlayer.isAlive() and pTeam.isHasMet(iLoopTeam):
lTargetCivs[iLoopPlayer] = 1

for plot in plots.all().not_owner(iCiv).not_owner(Civ.BARBARIAN).entities():
if lTargetCivs[plot.getOwner()] > 0:
iValue = get_data_from_upside_down_map(WARS_MAP, iCiv, plot)
if plot.getOwner() in INDEPENDENT_CIVS:
iValue /= 3
lTargetCivs[plot.getOwner()] += iValue

# normalization
iMaxTempValue = max(lTargetCivs)
if iMaxTempValue > 0:
for civ in civilizations().drop(Civ.BARBARIAN).ids():
if lTargetCivs[civ] > 0:
lTargetCivs[civ] = lTargetCivs[civ] * 500 / iMaxTempValue

for iLoopCiv in civilizations().drop(Civ.BARBARIAN).ids():
if iLoopCiv == iCiv:
continue

if lTargetCivs[iLoopCiv] <= 0:
continue

# add a random value
if lTargetCivs[iLoopCiv] <= iThreshold:
lTargetCivs[iLoopCiv] += rand(100)
else:
lTargetCivs[iLoopCiv] += rand(300)
# balanced with attitude
attitude = 2 * (pCiv.AI_getAttitude(iLoopCiv) - 2)
if attitude > 0:
lTargetCivs[iLoopCiv] /= attitude
# exploit plague
if (
getPlagueCountdown(iLoopCiv) > 0
or getPlagueCountdown(iLoopCiv) < -10
and not turn() <= civilization(iLoopCiv).date.birth + 20
):
lTargetCivs[iLoopCiv] *= 3
lTargetCivs[iLoopCiv] /= 2

# balanced with master's attitude
iMaster = getMaster(iCiv)
if iMaster != -1:
attitude = 2 * (gc.getPlayer(iMaster).AI_getAttitude(iLoopCiv) - 2)
if attitude > 0:
lTargetCivs[iLoopCiv] /= attitude

# if already at war
if not pTeam.isAtWar(iLoopCiv):
# consider peace counter
iCounter = min(7, max(1, pTeam.AI_getAtPeaceCounter(iLoopCiv)))
if iCounter <= 7:
lTargetCivs[iLoopCiv] *= 20 + 10 * iCounter
lTargetCivs[iLoopCiv] /= 100

# if under pact
if pTeam.isDefensivePact(iLoopCiv):
lTargetCivs[iLoopCiv] /= 4

# find max
iMaxValue = max(lTargetCivs)
iTargetCiv = lTargetCivs.index(iMaxValue)

if iMaxValue >= iMinValue:
return iTargetCiv
return -1
5 changes: 5 additions & 0 deletions Assets/Python/BUG/BugData.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@
from CvPythonExtensions import CyGame
import BugUtil
import cPickle as pickle
from StoredData import data


STORED_DATA = "doc_stored_data"

## Data Access


Expand Down Expand Up @@ -100,9 +103,11 @@ def onGameStart(argsList):

def onGameLoad(argsList):
initGameData().load()
data.update(getTable(STORED_DATA).data)


def save():
getTable(STORED_DATA).setData(data.__dict__)
getGameData().save()


Expand Down
Loading

0 comments on commit 5e76237

Please sign in to comment.