If you've seen my code for hero retreats, you can tell it's one very big hack. If you haven't seen it, don't bother - it's ugly and outdated
I've rewritten it to make it much cleaner, but there are a couple SDK things that could really make it easier. I've implemented the following events, changes, and features and I'd like them to be implemented in the SDK:
onCombatInit: Called right after cannotAttack() but before onCombatLogCalc.
onCombatWithdraw: Called when a unit withdraws (or retreats).
onCombatDone: Called after onCombatResult or onCombatWithdraw.
onCombatLogCalc, onCombatLogHit: Attacker and defender added to arguments list.
onCombatResult: Attacker and defender also added to the arguments list. This wouldn't be necessary if CvUnit::isAttacking() and CvUnit::isDefending() worked. Please either fix this or add the attacker and defender arguments.
onAfterPlayerTurns: Supposedly called after moves are reset for a turn. I say supposedly since this actually happens at the very end of CvGame::doTurn(). For its purpose (changing moves before the turn starts) and with simultaneous moves enabled, this event seems to work just fine. But ideally, there should be an event right after CvPlayer::doTurnUnits() is called in the CvPlayer::setTurnActive() function.
cannotAttack: Should only be called once during a fight. Right now, if combat animations are enabled, cannotAttack is called again at the end of the animation but before onCombatResult fires.
Some way to toggle the display of combat messages. I have this implemented by overriding the default combat text strings with blank entries in XML, then manually display them with Python (and new text strings).
onCombatLogCalc, onCombatLogHit: This is something I couldn't find a workaround for. I can mess around with the mechanics of combat in onCombatLogHit, but these fire only during battles involving human players. They should also fire for battles between AI players. Although the SDK should be solely responsible for the combat mechanics, since Python is so much more customizable, I'd argue that at the very least, there should be reliable Python hooks to modify combat mechanics.
CvGameInterface.canWithdraw: This function is called when a unit is about to be dealt a lethal blow in combat. The unit can be either the attacker or the defender. This allows customization of withdrawal chances and behavior.
Custom data: I've been tinkering around with the *ScriptData functions to add custom data to objects. What I've done is simulate a dictionary on objects that custom data can be added to. This allows multiple custom values to be stored instead of just one string. I've finally gotten to a point where storing and modifying custom data is pretty efficient, but it would still be better for this to be implemented in the SDK. This is a low priority request.
CvSelectionGroup::generatePath, CvUnit::generatePath, (possibly more generatePath functions): This function just doesn't work since an int pointer can't be passed. Please fix this.
Combat odds: I will need to change the combat odds message. I know how to do it - port CvGameCoreUtils::getCombatOdds() to Python and modify it, then intercept the CyInterface().getHelpString() call in CvMainInterface.updateHelpStrings and modify the message. But I haven't done it yet, since it would be very time consuming. So I'm not sure what the best way a Python hook can be implemented for getCombatOdds(). Here's one possible approach: Allow the 4 calls to currCombatStr() and currFirepower() to be replaced by a call to a Python function that can return the 4 values by reference. Not sure if that's possible, but if Cy* objects can be modified in Python functions (which can be viewed as a type of return by reference), it should be possible here.
That's all the requests I can think of off the top of my head. I'll post more later whenever I encounter another obstacle.

onCombatInit: Called right after cannotAttack() but before onCombatLogCalc.
onCombatWithdraw: Called when a unit withdraws (or retreats).
onCombatDone: Called after onCombatResult or onCombatWithdraw.
onCombatLogCalc, onCombatLogHit: Attacker and defender added to arguments list.
onCombatResult: Attacker and defender also added to the arguments list. This wouldn't be necessary if CvUnit::isAttacking() and CvUnit::isDefending() worked. Please either fix this or add the attacker and defender arguments.
onAfterPlayerTurns: Supposedly called after moves are reset for a turn. I say supposedly since this actually happens at the very end of CvGame::doTurn(). For its purpose (changing moves before the turn starts) and with simultaneous moves enabled, this event seems to work just fine. But ideally, there should be an event right after CvPlayer::doTurnUnits() is called in the CvPlayer::setTurnActive() function.
cannotAttack: Should only be called once during a fight. Right now, if combat animations are enabled, cannotAttack is called again at the end of the animation but before onCombatResult fires.
Some way to toggle the display of combat messages. I have this implemented by overriding the default combat text strings with blank entries in XML, then manually display them with Python (and new text strings).
onCombatLogCalc, onCombatLogHit: This is something I couldn't find a workaround for. I can mess around with the mechanics of combat in onCombatLogHit, but these fire only during battles involving human players. They should also fire for battles between AI players. Although the SDK should be solely responsible for the combat mechanics, since Python is so much more customizable, I'd argue that at the very least, there should be reliable Python hooks to modify combat mechanics.
CvGameInterface.canWithdraw: This function is called when a unit is about to be dealt a lethal blow in combat. The unit can be either the attacker or the defender. This allows customization of withdrawal chances and behavior.
Custom data: I've been tinkering around with the *ScriptData functions to add custom data to objects. What I've done is simulate a dictionary on objects that custom data can be added to. This allows multiple custom values to be stored instead of just one string. I've finally gotten to a point where storing and modifying custom data is pretty efficient, but it would still be better for this to be implemented in the SDK. This is a low priority request.
CvSelectionGroup::generatePath, CvUnit::generatePath, (possibly more generatePath functions): This function just doesn't work since an int pointer can't be passed. Please fix this.
Combat odds: I will need to change the combat odds message. I know how to do it - port CvGameCoreUtils::getCombatOdds() to Python and modify it, then intercept the CyInterface().getHelpString() call in CvMainInterface.updateHelpStrings and modify the message. But I haven't done it yet, since it would be very time consuming. So I'm not sure what the best way a Python hook can be implemented for getCombatOdds(). Here's one possible approach: Allow the 4 calls to currCombatStr() and currFirepower() to be replaced by a call to a Python function that can return the 4 values by reference. Not sure if that's possible, but if Cy* objects can be modified in Python functions (which can be viewed as a type of return by reference), it should be possible here.
That's all the requests I can think of off the top of my head. I'll post more later whenever I encounter another obstacle.