Character Control Script

From Wolfire Games Wiki
Revision as of 09:52, 12 January 2018 by Merlyn (talk | contribs)
Jump to: navigation, search

AI overview

TODO: This page is a work in progress, and I intend to finish it sooner rather than later. I wanted to save my work to make sure it doesn't get lost

AI in Overgrowth is handled with character controllers.

A character controller is a set of responses to events/stimuli that tell the character what physically to do at a given instant in time. It's sort of like a cockpit for an airplane.

Everything a human can do is technically available to an AI implementation, if it's smart enough to figure out how to properly trigger the right action at the right time.

The AI in Overgrowth is implemented using a goal system, and through simulations of sensory input. It makes decisions on what to do at a given moment in time based on its current goal/subgoal, the character's current status, and observations on the environment (sights, sounds).

  • For the player, this control happens in a script Data/Scripts/playercontrol.as
  • For NPCs, this control happens in a script Data/Scripts/enemycontrol.as

There's a layer of logic below the character controller that dictates what the character is physically allowed to do. That logic triggers events and consequences based on what happens to the character (via the Controller Script Interface), and listens to the inputs from the character controller to direct the character's motion. That base character layer won't be covered here in detail, though we will talk about the events and control queries that are sent from that layer into the Controller Script Interface.

  • For both players and NPCs this logic is shared, in a script Data/Scripts/aschar.as

Controller Script Interface

The functions in this control script interface tell the character what to do. If you implement these functions, then you will control the character.

The rest of the details on this page just detail how we chose to drive these functions in the base character controller scripts. You can feel free to drive them in some other way as well.

Functions called from other scripts

These are called both directly (aschar.as) and via movement_object.Execute(...) (by the engine, other scripts, and character-to-character calls)

Data accessed from other scripts

These are accessed by aschar.as mostly. There's only one call to movement_object.Execute(...) that modifies this data, in the older (no longer used) tutorial level.

Goals

The Goal is the current primary motivation for the AI.

Whenever the character is updated, this is consulted first when making decisions on what to do, before the Sub Goal is checked.

List of Goals:

The inital Goal for all AI is Patrolling.

Patrol Goal

This patrol goal is when the character is on the alert, listening for sounds, looking for ally's bodies, and looking for awake enemies to fight.

The inital Goal for all AI is Patrolling, and this is the mode that they will fall back to when they're done in combat.

TODO: Is there a more compact/cleaner way to do this? Is the rest of this just too much detail?

When Patrol gets set:

  • When the character is first spawned
  • When the character is reset (through the void ResetMind(); function)
  • When the Goal was Attack and the chase target is neutralized, and there are no other targets to pick from
  • When the Goal was Struggle or Hold Still and the character was broken free or was released, while still awake
  • When debug keys are enabled, the C is toggled in the editor, the character was previously not passive, and the character is now being set to to passive, then they will also now be set to patrol
  • When void MindReceiveMessage(string msg); is called for the character, with the message "set_hostile false" (basically when this message is sent to the character with movement_object.RecieveMessage("..."))
  • When the Goal was Investigate
  • When the Goal was Get Help and somehow you no longer have an ally to seek help from (TODO: I don't think this was completely implemented, so it's not clear how this can happen without this goal being switched ahead of this check)
  • When void AIMovementObjectDeleted(int id); is called, and the player had a goal that was focused on the now-deleted character:
    • If the Goal was Get Help and the target ally being sought for help is deleted
    • If the Goal was Investigate and the target being investigated is deleted
    • If the Goal was Escort and the target being escorted is deleted
    • If the Goal was Attack and the chase target is deleted

How character behaviors are affected by Patrol:

  • int IsUnaware(); returns 1. Other checks are also made, but this always returns 1 if on patrol
  • int IsIdle(); returns 1, otherwise it returns 0
  • string GetIdleOverride(); returns a non-empty value only if the character is on patrol
  • vec3 GetTargetVelocity(); returns waypoint based patrol movement if the character is on patrol, and isn't currently being startled
  • WalkDir WantsToWalkBackwards(); returns WALK_BACKWARDS or STRAFE if the character is on patrol, they don't have a waypoint target, and they get pushed around. If they aren't on patrol, or they have a waypoint target, it returns FORWARDS
  • bool WantsReadyStance(); returns true if the character is not on patrol

How Patrol works:

  • Whenever you switch to Patrol from any other goal:
    • Clear the patrol path wait timer - immediately begin your patrol route (unless otherwise specified)


  • Whenever you switch from Patrol to any other goal:
    • If you were asleep (and you're not immediately choked) then you enter the Wake Up state (todo: Link to it? Doc those?) before switching goals


  • When your brain updates (at 30fps frequency for AI)
    • If you're passive or just spawned or asleep
      • Clear chase and look targets (so you don't pay attention to anyone)
    • Else
      • If you see a knocked out friendly body you haven't seen before
        • Yelp, and set Goal to Investigate Sub-Goal to Investigate Urgent
      • Else If you see an enemy who is awake, they're not ducking enough, they're not stationary + "invisible_when_stationary", and they're < 4 units from you
        • Look in the direction they're going to move to (follow them with your eyes)
        • Start incrementing onto the "seen" counter for that particular enemy (the counter is shared between all enemies. It fades over time)
          • "invisible_when_stationary" reduces the speed by 2/3
          • Inside your FOV is high (0.5 per tick), outside is lower, farther away is lower
          • If your goal is patrol, your senses are more dull, so you don't get a 3x alertness buff
      • If your "seen" counter > 0.5
      • If your "seen" counter > 1.0
        • Fully notice them - see next section
    • If you have a chase target and can see them (or are omniscient)
      • Add a history marker so you know where they are and are heading
    • Set "ai attacking" to false (so you don't automatically attack while on patrol, until at least after you see someone)
    • If you're not on the nav mesh right now, decrement your "give up on finding a new path" timeout
    • Set the head look target to the closest character you can find that you know about


  • When you see an enemy for long enough to notice them (fades over time), or you are hit by any attack or when you are hit by any thrown item (always if a wolf or dog, only if you live if you're not), or when you're set to be omniscient by script messages
    • Then some extra stuff happens for each of these events, such as taking damage, making noise, and ragdoll impacts. None of it is dependent on whether you are patrolling or not
    • void MindReceiveMessage(string msg); is called for the character, with the message "notice <target character id>" (when this message is sent to the character with movement_object.RecieveMessage("..."))
      • If the goal is currently Patrol, and the noticed target character is not static, and they're not on the same team
        • If you don't already know about the character
          • Startle, play the startled sound, and alert nearby allies with your noise
        • Set the goal to Attack


  • Whenever you take any damage, if your goal is Patrol, and if you're not set to passive
    • Set the goal to Investigate and set the sub-goal to Investigate Around
    • Set your nav position to the current spot, and clear your Investigate Target Id


  • When void MindReceiveMessage(string msg); is called for the character, with the message "nearby_sound <... lots of args...>" (when a nearby character makes a noise, and you're close enough to be checked)
    • If you're close enough to hear it (rabbits hear 2x as far, and the "Hearing Modifier" param scales the distance), and if the goal is currently Patrol, and you are not set to be passive, and you aren't in the middle of a just-spawned cool-down, and the the character who made the sound still exists, and you are not static, and you aren't knocked out, and you didn't create the noise yourself
      • If the noisy character is not awake, or if the noisy character is on the same team and you already have seen them at some point, and the sound is "foley" or "loudy foley" (not a yelp, etc)
        • Ignore the sound and skip the rest of this
      • If the sound is "foley" (not loud foley or other sound types) add 0.3 to suspicion Else add 1.0 to suspicion
      • If suspicion is > 1.0
        • Play the "suspicious" sound, and look in the direction of the sound (which is projected behind the character causing the collision) for 2 seconds
        • If the sound is of type "foley" or "loud foley"
        • Else if sound is of type "voice" and the character is awake
          • Play the "startle" sound, do the startle action (a longer one than normal), set the goal to Escort, and set the Escort Target Id to the sound creator
          • Play the "startle" sound, do the startle action, set the goal to Investigate and set the sub-goal to Investigate Urgent
        • Set the Investigate Target Id to -1 (clear it)


  • When void MindReceiveMessage(string msg); is called for the character, with the message "collided <target character id>" (when a character collides with this character)
    • If the goal is currently Patrol, and you are not set to be passive, and you aren't in the middle of a just-spawned cool-down, and the the colliding target character still exists and you are not static, and you aren't knocked out, and you didn't somehow collide with yourself
      • If the colliding target character is not awake, or if the colliding target character is on the same team and you already have seen them at some point
        • Ignore the sound and skip the rest of this
      • Add 0.3 to suspicion, since the collision "sound" is a normal foley sound
      • If suspicion is > 1.0
        • Play the "suspicious" sound, and look in the direction of the sound (which is projected behind the character causing the collision) for 2 seconds
        • Play the "startle" sound, set the goal to Investigate and set the sub-goal to Investigate Slow, and set the Investigate Target Id to -1 (clear it)

Attack Goal

TODO: Write this section

Investigate Goal

TODO: Write this section

Get Help Goal

TODO: Write this section

Escort Goal

TODO: Write this section

Get Weapon Goal

TODO: Write this section

Navigate Goal

TODO: Write this section

Struggle Goal

TODO: Write this section

Hold Still Goal

TODO: Write this section

Flee Goal

TODO: Write this section

Sub Goals

The Sub Goal is the current secondary motivation for the AI. Each goal has an entirely different set of sub-goals, and the sub-goals aren't really shared between goals. It isn't "a second goal", it is like a plan of action to solve the current goal.

Whenever the character is updated, this is consulted second when making decisions on what to do, after the current Goal is checked.

List of Sub Goals:

The inital Sub Goal for all AI is Wait And Attack.

Unknown Sub Goal

TODO: Write this section

Provoke Attack Sub Goal

TODO: Write this section

Avoid Jump Kick Sub Goal

TODO: Write this section

Knock Off Ledge Sub Goal

TODO: Write this section

Wait And Attack Sub Goal

TODO: Write this section

Rush And Attack Sub Goal

TODO: Write this section

Defend Sub Goal

TODO: Write this section

Surround Target Sub Goal

TODO: Write this section

Escape Surround Sub Goal

TODO: Write this section

Investigate Slow Sub Goal

TODO: Write this section

Investigate Urgent Sub Goal

TODO: Write this section

Investigate Body Sub Goal

TODO: Write this section

Investigate Around Sub Goal

TODO: Write this section

Investigate Attack Sub Goal

TODO: Write this section