Research:NPC AI Behaviour: Difference between revisions

From OpenMW Wiki
Jump to navigation Jump to search
Line 1: Line 1:
==General behaviour==
===Decision scan===
{{Formula
|AI decision check, every 5.0 seconds
|The main point for actor behaviour selection
|
|{{StatusCol|orange|Early, incomplete research}}}}
====Weight functions====
<syntaxhighlight lang="python">
actionGreetWeighting :: (npc, actor) -> weight
if npc is creature: return 0
if actor is not player: return 0
if player is incapacitated: return 0
if player is invisible or player.chameleon >= 75: return 0
if npc.hello == 0: return 0
if npc.currentAIPackage not in { AIWander, AITravel, AIPursue }: return 0
x = iGreetDistanceMultiplier * npc.hello
dist = distance(npc, actor)
if npc has already greeted and dist > 2 * x:
    npc resets greeting flag
    npc.target = none
elif npc has not greeted and dist < x:
    npc.target = actor
    return 100
else:
    return 0
actionCrimeWeighting :: (npc, actor) -> weight
if npc is creature: return 0
if npc is incapacitated: return 0
if npc.alarm == 0: return 0
if not npc.isGuard: return 0
if actor is not player: return 0
if player is incapacitated: return 0
if player is invisible or player.chameleon >= 75: return 0
if player is resisting arrest: return 0
if bounty < iCrimeThreshold: return 0
weight = npc.alarm + 0.1 * bounty
if weight < 100: return 0
return weight
actionFightWeighting :: (npc, actor) -> weight
if actor is in combat with an npc's escort: return 100
if actor is in combat with an actor escorted by npc: return 100
fightTerm = npc.fight
if actor.isWerewolf or (actor is player and player.isKnownWerewolf): fightTerm += iWerewolfFightMod
dist = distance(npc, actor)    # ignores vertical distance if actor is flying or swimming
weight = iFightDistanceBase - fFightDistanceMultiplier * distance + fFightDispMult * (50 - disp)
if weight < 100: return 0
return weight
actionLookAtWeighting :: (npc, actor) -> weight
if actor is incapacitated: return 0
if actor is invisible or player.chameleon >= 75: return 0
if npc current AI package prevents proximity check: return 0    # unclear conditions
if distance(npc, actor) >= 640: return 0
if actor is an npc's escort: return 0
if actor is being escorted by npc: return 0
return 100
</syntaxhighlight>
====Actor scan====
<syntaxhighlight lang="python">
actionWeight = 0
selectedAction = none
for each actor in environment: # player is examined first
    if actor is dead: continue
    if distance(npc, actor) > 7168: continue
    # 5-10 more conditions
   
    greetWeight = 0
    crimeWeight = 0
    if actor is player:
        if npc meets condition x:    # under research
            greetWeight = actionGreetWeighting(npc, actor)
        if npc meets condition y:    # under research
            crimeWeight = actionCrimeWeighting(npc, actor)
    fightWeight = actionFightWeighting(npc, actor)
    lookAtWeight = actionLookAtWeighting(npc, actor)
   
    action = none
    if lookAtWeight > npc.actionWeight:
        npc.target = actor
        actionWeight = int(lookAtWeight)
        action = lookAt
    if greetWeight > npc.actionWeight:
        npc.target = actor
        actionWeight = int(greetWeight)
        action = greet
    if fightWeight > npc.actionWeight:
        npc.target = actor
        actionWeight = int(fightWeight)
        action = fight
    if crimeWeight > npc.actionWeight:
        npc.target = actor
        actionWeight = int(crimeWeight)
        action = uphold the law
   
    if actionWeight != 0 and action != none and npc.target != none:
        if some 20 page function: # includes expensive functions like line of sight and sneak
            npc.target = 0
            actionWeight = 0
            action = none
   
    if action != none: selectedAction = action
</syntaxhighlight>
====Comments====
With the lookAt action, the NPC looks at its target's head. The anim controller blends the head pose over time.
==Combat Behaviour==
==Combat Behaviour==



Revision as of 18:20, 23 July 2014

General behaviour

Decision scan

Actions affected AI decision check, every 5.0 seconds
Description The main point for actor behaviour selection
Implementation status
Analysis status Early, incomplete research

Weight functions

actionGreetWeighting :: (npc, actor) -> weight
if npc is creature: return 0
if actor is not player: return 0
if player is incapacitated: return 0
if player is invisible or player.chameleon >= 75: return 0
if npc.hello == 0: return 0
if npc.currentAIPackage not in { AIWander, AITravel, AIPursue }: return 0

x = iGreetDistanceMultiplier * npc.hello
dist = distance(npc, actor)
if npc has already greeted and dist > 2 * x:
    npc resets greeting flag
    npc.target = none
elif npc has not greeted and dist < x:
    npc.target = actor
    return 100
else:
    return 0


actionCrimeWeighting :: (npc, actor) -> weight
if npc is creature: return 0
if npc is incapacitated: return 0
if npc.alarm == 0: return 0
if not npc.isGuard: return 0
if actor is not player: return 0
if player is incapacitated: return 0
if player is invisible or player.chameleon >= 75: return 0
if player is resisting arrest: return 0
if bounty < iCrimeThreshold: return 0

weight = npc.alarm + 0.1 * bounty
if weight < 100: return 0
return weight


actionFightWeighting :: (npc, actor) -> weight
if actor is in combat with an npc's escort: return 100
if actor is in combat with an actor escorted by npc: return 100

fightTerm = npc.fight
if actor.isWerewolf or (actor is player and player.isKnownWerewolf): fightTerm += iWerewolfFightMod
dist = distance(npc, actor)     # ignores vertical distance if actor is flying or swimming

weight = iFightDistanceBase - fFightDistanceMultiplier * distance + fFightDispMult * (50 - disp)
if weight < 100: return 0
return weight


actionLookAtWeighting :: (npc, actor) -> weight
if actor is incapacitated: return 0
if actor is invisible or player.chameleon >= 75: return 0
if npc current AI package prevents proximity check: return 0    # unclear conditions
if distance(npc, actor) >= 640: return 0
if actor is an npc's escort: return 0
if actor is being escorted by npc: return 0
return 100

Actor scan

actionWeight = 0
selectedAction = none

for each actor in environment: # player is examined first
    if actor is dead: continue
    if distance(npc, actor) > 7168: continue
    # 5-10 more conditions
    
    greetWeight = 0
    crimeWeight = 0
    if actor is player:
        if npc meets condition x:    # under research
            greetWeight = actionGreetWeighting(npc, actor)
        if npc meets condition y:    # under research
            crimeWeight = actionCrimeWeighting(npc, actor)
    fightWeight = actionFightWeighting(npc, actor)
    lookAtWeight = actionLookAtWeighting(npc, actor)
    
    action = none
    if lookAtWeight > npc.actionWeight:
        npc.target = actor
        actionWeight = int(lookAtWeight)
        action = lookAt
    if greetWeight > npc.actionWeight:
        npc.target = actor
        actionWeight = int(greetWeight)
        action = greet
    if fightWeight > npc.actionWeight:
        npc.target = actor
        actionWeight = int(fightWeight)
        action = fight
    if crimeWeight > npc.actionWeight:
        npc.target = actor
        actionWeight = int(crimeWeight)
        action = uphold the law
    
    if actionWeight != 0 and action != none and npc.target != none:
        if some 20 page function: # includes expensive functions like line of sight and sneak
            npc.target = 0
            actionWeight = 0
            action = none
    
    if action != none: selectedAction = action

Comments

With the lookAt action, the NPC looks at its target's head. The anim controller blends the head pose over time.


Combat Behaviour

NPC Awareness Check

Actions affected NPC AI
Description
Implementation status implemented
Analysis status Verified

This check runs every 5 seconds for each NPC. It occurs whether you are sneaking or not, but isn't the same as the combat distance check.

Player side

if sneaking:
    sneakTerm = fSneakSkillMult * sneak + 0.2 * agility + 0.1 * luck + bootWeight * fSneakBootMult
else:
    sneakTerm = 0

fatigueTerm = fFatigueBase - fFatigueMult*(1 - normalisedFatigue)
where normalisedFatigue is a function of fatigue. empty fatigue bar -> 0.0, full fatigue bar -> 1.0
 
distTerm = fSneakDistanceBase + fSneakDistanceMultiplier*dist
x = sneakTerm * distTerm * fatigueTerm + chameleon (+ 100 if invisible)

NPC side

npcTerm = npcSneak + 0.2 * npcAgility + 0.1 * npcLuck - npcBlind
npcFatigueTerm = fFatigueBase - fFatigueMult * (1 - normalisedFatigue)
 
using NPC normalisedFatigue

if PC is behind NPC (180 degrees):
    y = npcTerm * npcFatigueTerm * fSneakNoViewMult
else:
    y = npcTerm * npcFatigueTerm * fSneakViewMult

Final check

target = x - y
roll 100, win if roll < target


Comments

Appears straightforward and bug-free. NPCs can take up to five seconds to notice you even if you are not sneaking. This function precedes the combat distance check. I have not identified if there is a line of sight check occuring before or after.

According to this formula, NPCs can still detect you when invisible? That seems wrong. Scrawl (talk) 00:25, 7 January 2014 (CET)

AI Tendencies

Fight

Affected by calm and frenzy spells.

Flee

Affected by rally and demoralize spells.

Alarm

Hello