Skip to content

Instantly share code, notes, and snippets.

@MiroslavR
Last active December 10, 2016 15:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MiroslavR/097bf13768bb07cbf13dfe9258fabf25 to your computer and use it in GitHub Desktop.
Save MiroslavR/097bf13768bb07cbf13dfe9258fabf25 to your computer and use it in GitHub Desktop.
Vanilla AI combat decision making
# This is a simplified version of the function that determines the next combat action (similar to OpenMW's prepareNextAction()).
# It's called regularly (on timer) during combat.
def shouldFlee(actor, target):
if actor.isWerewolf:
return False
if should use potion: # Unresearched. Fairly long code.
return False
fleeRating = getFleeRating(actor, target)
if fleeRating < 100.0:
fleeRating = 0.0
magicRating = getMagicCombatRating(actor, target)
weaponRating, weapon = getWeaponCombatRating(actor, target)
nextAction = None
if weaponsRating > max(magicRating, fleeRating):
nextAction = RANGED_FIGHT if weapon.type >= MARKSMAN_BOW else MELEE_FIGHT
# ...
# some spell setting code?
if fleeRating > max(weaponRating, magicRating):
return True
if fleeRating > 100.0:
# NOTE: No check whether target is player...
if actor is NPC and glob["PCKnownWerewolf"] != 0:
return True
if player.isWerewolf:
return True
if nextAction == None:
nextAction = FIST_FIGHT
elif nextAction != FIST_FIGHT:
return False
if actor has active SUMMON_X effect:
return True # fists + active summon -> flee
return False
def getFleeRating(actor, target):
if actor.something24 != 0: # Seems to be the number of objects that are obstructing the actor's movement. This prevents actors from continuing to run into walls while fleeing.
return 0.0
if actor.flee >= 100:
return actor.flee
rating = (1.0 - actor.healthPercentage) * gmst["fAIFleeHealthMult"] + actor.flee * gmst["fAIFleeFleeMult"]
if target.isWerewolf and actor.level < gmst["iWereWolfLevelToAttack"]:
rating = gmst["iWereWolfFleeMod"]
if rating != 0.0:
rating += (gmst["iFightDistanceBase"] - gmst["fFightDistanceMultiplier"] * distance(actor, target))
return rating
def getMagicCombatRating(actor, target):
if actor.currentMagicka == 0.0
return 0.0, None
if actor has no spells:
return 0.0, None
maxRating = 0.0
bestSpell = None
for spell in actor.spells:
if spell.type != REGULAR_SPELL: # skip powers, abilities etc.
continue
if spell has no HARMFUL effects: # MGEF flag 0x10
continue
if spell has UNREFLECTABLE effect: # MGEF flag 0x10000
continue
if target.isCurrentlyAffectedBy(spell):
continue
if spell.magickaCost > actor.currentMagicka:
continue
if spell has TARGET effect: # range type
if target.isSwimming:
mult = 0.0
else:
mult = gmst["fAIRangeMagicSpellMult"]
else:
mult = gmst["fAIMagicSpellMult"]
if spell.successChance(actor) * mult >= maxRating:
maxRating = spell.successChance(actor) * mult
bestSpell = spell
return maxRating, bestSpell
def getWeaponCombatRating(actor, target):
if actor is creature and not actor.crea.flags & WEAPON_AND_SHIELD:
return 0.0
bestDamageRating = 0.0
bestWeapon = None
weaponSkillsTable = [LONG_BLADE, AXE, SPEAR, SHORT_BLADE, MARKSMAN, BLUNT_WEAPON]
for i in range(len(weaponSkillsTable)):
skillMult = actor.skills[weaponSkillsTable[i]].totalValue * 0.01
for weapon in actor.inventory:
if weapon.type in [ARROW, BOLT]:
continue
if weaponTypeToSkill(item.weapon.type) == weaponSkillsTable[i]:
chopMult = 0.0
ammoDamage = 0.0
if weapon.type >= MARKSMAN_BOW: # bows, crossbows and thrown weapons
found = False
for ammo in actor.inventory:
if (ammo.type == ARROW and weapon.type == BOW) or (ammo.type == BOLT and weapon.type == CROSSBOW):
ammoDamage = ammo.chopMax
found = True
break
if found and actor is not swimming:
if actor.Z >= water level or (actor.height + actor.Z) * 0.7 <= water level: # NOTE: Bug? Should probably be (actor.Z + actor.height * 0.7).
chopMult = gmst["fAIRangeMeleeWeaponMult"]
else:
chopMult = gmst["fAIMeleeWeaponMult"]
chopRating = (weapon.chopMax + ammoDamage) * skillMult * chopMult
slashRating = weapon.slashMax * skillMult * gmst["fAIMeleeWeaponMult"]
thrustRating = weapon.thrustMax * skillMult * gmst["fAIMeleeWeaponMult"]
if weapon.type == MARKSMAN_THROWN and not actor.hasAnimationGroup("MarksmanThrown")
continue
if chopRating != 0.0 and chopRating >= bestDamageRating:
chopRating = bestDamageRating
bestWeapon = weapon
if slashRating != 0.0 and slashRating >= bestDamageRating:
slashRating = bestDamageRating
bestWeapon = weapon
if thrustRating != 0.0 and thrustRating >= bestDamageRating:
thrustRating = bestDamageRating
bestWeapon = weapon
if bestWeapon != None and bestWeapon.currentHealth > 0.0:
return actor.getArmorRating() * gmst["fAIMeleeArmorMult"] + bestDamageRating
return 0.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment