raystuttgart
Civ4Col Modder
How is it solved in "Further Age of Discovery"? Does it check every time a unit moves? I am not sure this concept is what I was thinking.
Age of Further Discovery handles this generally just the same as I am suggesting.

-> At the End of Turn, when logic is looping through the cities.
Here the main code for this feature in Age of Further Discovery:
Spoiler :
Code:
void CvCity::doTurn()
{
PROFILE_FUNC();
CvPlot* pLoopPlot;
int iI;
if (!isBombarded())
{
changeDefenseDamage(-(GC.getDefineINT("CITY_DEFENSE_DAMAGE_HEAL_RATE")));
}
setLastDefenseDamage(getDefenseDamage());
setBombarded(false);
// PatchMod: City bombard START
[COLOR="Red"]doBombard();[/COLOR]
// PatchMod: City bombard END
AI_doTurn();
bool bAllowNoProduction = !doCheckProduction();
doSpecialists();
doYields();
doGrowth();
doCulture();
doPlotCulture(false, getOwnerINLINE(), getCultureRate());
doProduction(bAllowNoProduction);
doDecay();
doMissionaries();
doRebelSentiment();
if (!isDisorder())
{
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
pLoopPlot = getCityIndexPlot(iI);
if (pLoopPlot != NULL)
{
if (pLoopPlot->getWorkingCity() == this)
{
if (pLoopPlot->isBeingWorked())
{
pLoopPlot->doImprovement();
}
}
}
}
}
if (getCultureUpdateTimer() > 0)
{
changeCultureUpdateTimer(-1);
}
if (getOccupationTimer() > 0)
{
changeOccupationTimer(-1);
}
for (uint i = 0; i < m_aPopulationUnits.size(); ++i)
{
m_aPopulationUnits[i]->doTurn();
}
// ONEVENT - Do turn
gDLL->getEventReporterIFace()->cityDoTurn(this, getOwnerINLINE());
}
Spoiler :
Code:
// PatchMod: City bombard START
void CvCity::doBombard()
{
if (isHasBuilding((BuildingTypes)GC.getDefineINT("BUILDING_FORT")) || isHasBuilding((BuildingTypes)GC.getDefineINT("BUILDING_FORTRESS")))
{
CvWString szBuffer;
CvPlot* pPlot = plot();
CvPlot* pAdjacentPlot = NULL;
CvUnit* pBombUnit = NULL;
CvUnit* pLoopUnit = NULL;
CvUnit* pLoopUnit2 = NULL;
bool bCanBomb = false;
CLLNode<IDInfo>* pUnitNode = NULL;
CLLNode<IDInfo>* pUnitNode2 = NULL;
int iDamage = 0;
pUnitNode = pPlot->headUnitNode();
while (pUnitNode)
{
pLoopUnit = ::getUnit(pUnitNode->m_data);
pUnitNode = plot()->nextUnitNode(pUnitNode);
iDamage = 0;
if (pLoopUnit->bombardRate() > 0 && pLoopUnit->getOwnerINLINE() == getOwnerINLINE())
{
//bCanBomb = false;
//if (pLoopUnit->getFortifyTurns() > 0)
{
bCanBomb = true;
pBombUnit = pLoopUnit;
}
if (bCanBomb && iDamage == 0)
{
for (int iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
{
pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));
if (pAdjacentPlot != NULL && iDamage == 0)
{
if (pAdjacentPlot->isWater())
{
pUnitNode2 = pAdjacentPlot->headUnitNode();
while (pUnitNode2)
{
pLoopUnit2 = ::getUnit(pUnitNode2->m_data);
pUnitNode2 = pAdjacentPlot->nextUnitNode(pUnitNode2);
if (getTeam() != pLoopUnit2->getTeam() && GET_TEAM(getTeam()).isAtWar(pLoopUnit2->getTeam()) && !pLoopUnit2->isCargo())
{
int randNum = GC.getGameINLINE().getSorenRandNum(100, " ");
if (randNum < 30)
{
iDamage = pLoopUnit2->maxHitPoints() * pBombUnit->bombardRate() / 100;
}
else if (randNum < 35)
{
iDamage = pLoopUnit2->maxHitPoints();
}
else
{
iDamage = 0;
}
pLoopUnit2->changeDamage(iDamage, pBombUnit);
if (iDamage > 0)
{
if (pLoopUnit2->isDead())
{
int iExperience = pLoopUnit2->attackXPValue();
iExperience = ((iExperience * pLoopUnit2->currCombatStr(plot(), pBombUnit)) / pBombUnit->currCombatStr(NULL, NULL));
iExperience = range(iExperience, GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), GC.getDefineINT("MAX_EXPERIENCE_PER_COMBAT"));
pBombUnit->changeExperience(iExperience, pLoopUnit2->maxXPValue(), true, plot()->getOwnerINLINE() == pBombUnit->getOwnerINLINE(), true);
szBuffer = gDLL->getText("TXT_KEY_FORTBOMB_YOU_SUNK");
gDLL->getInterfaceIFace()->addMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_CIVIC_ADOPT", MESSAGE_TYPE_MINOR_EVENT, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_FORTBOMB_THEM_SUNK");
gDLL->getInterfaceIFace()->addMessage(pLoopUnit2->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_CIVIC_ADOPT", MESSAGE_TYPE_MINOR_EVENT, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pLoopUnit2->getX_INLINE(), pLoopUnit2->getY_INLINE(), true, true);
}
else
{
szBuffer = gDLL->getText("TXT_KEY_FORTBOMB_YOU_HIT", iDamage);
gDLL->getInterfaceIFace()->addMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_CIVIC_ADOPT", MESSAGE_TYPE_MINOR_EVENT, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_FORTBOMB_THEM_HIT", iDamage);
gDLL->getInterfaceIFace()->addMessage(pLoopUnit2->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_CIVIC_ADOPT", MESSAGE_TYPE_MINOR_EVENT, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pLoopUnit2->getX_INLINE(), pLoopUnit2->getY_INLINE(), true, true);
}
}
else
{
szBuffer = gDLL->getText("TXT_KEY_FORTBOMB_YOU_MISS");
gDLL->getInterfaceIFace()->addMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_CIVIC_ADOPT", MESSAGE_TYPE_MINOR_EVENT, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_GREEN"), getX_INLINE(), getY_INLINE(), true, true);
szBuffer = gDLL->getText("TXT_KEY_FORTBOMB_THEM_MISS");
gDLL->getInterfaceIFace()->addMessage(pLoopUnit2->getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_CIVIC_ADOPT", MESSAGE_TYPE_MINOR_EVENT, NULL, (ColorTypes)GC.getInfoTypeForString("COLOR_RED"), pLoopUnit2->getX_INLINE(), pLoopUnit2->getY_INLINE(), true, true);
}
if (pAdjacentPlot->isActiveVisible(false))
{
// Bombard entity mission
CvMissionDefinition kDefiniton;
kDefiniton.setMissionTime(GC.getMissionInfo(MISSION_BOMBARD).getTime() * gDLL->getSecsPerTurn());
kDefiniton.setMissionType(MISSION_BOMBARD);
kDefiniton.setPlot(pAdjacentPlot);
kDefiniton.setUnit(BATTLE_UNIT_ATTACKER, pBombUnit);
kDefiniton.setUnit(BATTLE_UNIT_DEFENDER, pLoopUnit2);
gDLL->getEntityIFace()->AddMission(&kDefiniton);
}
if (pLoopUnit2->isDead())
{
pLoopUnit2->kill(false);
}
}
if (iDamage > 0)
{
pUnitNode2 = NULL;
}
}
}
}
}
}
}
}
}
return;
}
// PatchMod: City bombard END
I do hope "Firing when Enemy moves by a city" isnt forgotten. That was the main feature idea I had.
Sorry, I do not want to do that.

(Performance and Implementation Efforts.)
I would have to check at every move a unit does !
(With my suggested solution I can really simply loop through the cities at end of turn.)
Does it really make such a big difference if it is "End of Turn Next to City" or "While Passing City" ?
(My suggested solution is easier to implement and also more performant ...)
In many cases (especially for military on land) the result would be just the same.

Please let us not make this feature too complicated.

There are a few things I would like:
1. Keep efforts for implementation and also risk of bugs low.
(To be honest, this feature is only a "Nice-To-Have" for me.)
2. Ensure, that it does not destroy balance.
(AI should not loose too many units on this feature because it does not know how to handle like a human player does.)
3. Performant logic, so game turns do not get unnecessarily longer.