POI rework & AI

Discussion in 'Suggestions' started by Exacute, May 21, 2017.

  1. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    1,050
    Likes Received:
    756
    POI rework & AI

    This thread consists of two main themes; Reworking POI-mechanics, and reworking AI.
    The two have been mixed together, because of how strong they are tied together.

    The concept is to improve the experience of POI raiding, and overall AI, aswell as provide additional tools to scenario & POI creators.

    First up: POIs.
    I suggest implementing an alarm-tier to a POI
    -One global
    -One with distance from the trigger. (adjustable like a spawner, range-wise)
    These would have several states: Idle, Suspicious, Alerted, Lockdown
    Idle would be the basic state for the POI. Everyone is "Minding their own bussiness".
    Suspicious would be the state for when they suspect an intruder (defined and used in AI section).
    Alerted would be the state when there is a confirmed intruder.
    Lockdown would be the "shoot first, ask questions later" state. Essentially fortify, and lockdown sections.

    The alarm-tier would be "trickle down". Ie. if global is triggered, it would affect all AI. If distance is triggered, it would affect any AI inside that range.
    The alarm tiers should have a natural cooldown of sorts. For instance drop down a tier, after n-minutes of not being triggered again.

    Alarm tiers would essentially be fixed signals, that would always be present, and triggerable, like normal sensor logic, but naturally tiered.

    I suggest implementing the following meta-parts, to the signal logic.
    -Trigger Chapter ; Ability to set a chapter to (active/discovered/activatable/completed). ; This would allow for chapters to be rewealed/activated/.. when entering a POI. The completed flag is a little obsolete, when the PDA have checks for signals, but it might be a nice utility thing to have.
    -Check Chapter ; Check the status of a chapter (completed/discovered/activatable/active)
    (For advanced uses, there could be similar for Tasks, and even Actions)
    -Trigger alarm-tier ; two types: Set alarm to a specific tier, or trigger the alarm tier to increase or decrease.

    I suggest making a tweak to sensor blocks, allowing their area to be highlighted, if using night-vision. (ability to see the trigger-range of sensors for the player).

    I suggest implementing a camera of sorts, that essentially work like a sensor block, but with a cone-range. Also ideally able to give it a bite of a circle to patrol. (ie. from 90°, move 180°) ; Giving the player an alternative to "always being detected", that is avoidable using smarts..
    I suggest making a similar block to the camera, that would have a larger radius, and look for HV/SV/CV (definable), and trigger signals based on that. ; being able to detect incoming threats.

    I suggest making the alarm-tiers a "signal" aswell, that can trigger additional mechanics inside the base.
    (By using the signals from these, one could activate spawners, lock doors, etc.. )

    Now that the groundwork is laid, the second part: AI.
    This is structured, so it can be used for tiered programming. Essentially there's basic states, that are the lowest-level, and the rest would be programmable by anyone.

    Meta functionalities:
    Implement several types to the player.
    -Noiselevel (Essentially a radius around the player, it would be detected by others) ; This would be influenced on approach style. Firing a weapon increases it dramatically temporarily, running increases it some. Sneaking decreases it. Basic is "don't do anything but walking". Interacting with objects increases it (for instance opening crate/door, etc)
    -Visibilitylevel (Essentially how easy it is to spot the player, radius) ; How visible is the player to others? This would be increased by running, and by using a vehicle for instance. Decreased by sneaking. If making light, it would be increased.
    -SmellLevel (Essentially how smelly the player are). This could be increased by certain foods, while canned might be neutral, etc. Might be decreased by armor types.

    Visiblitylevel requires a LoS to the target.
    SmellLevel requires a "LoS", where there's no-solid blocks from the mob to the target. (For instance, if the room is airtight, the target would not be detected, if not within the room, but if not, it would be, unless target is in an airtight room)
    NoiseLevel passes through anything. Might have a modifier based on blocktypes (Concrete halves sound, wood barely affects it, non-solids doesn't affect it), etc..

    Basic pretext:
    When spawning, a mob will be given a script to follow.
    When spawning, a mob will have attributes, that are a modifer to the default noise, and visible level of a player (for instance, zirax might have a bad smell, and the modifier might be 0.5, hence almost impossible to detect player by sent. Creatures might have a 2 modifier, doubling the radius of which they detect it). Similar modifiers for visibilitylevel, and noiselevel.
    When spawning, a mob will have a smell, noise, visibility attribute.
    The mob would have a max-amount of ammunitien.
    The mob would have a count of ammunition. (for instance 30 bullets)
    The mob would have a max-amount of healing items.
    The mob would have a series of weapons (for instance "claw", "fang" or "knife","plasmarifle")

    Basic functionalities:
    NB: Range is intended to be a radius, with center of the caller.
    -Trigger nearest lever(Range) ; Would allow the mob to trigger an alarm tier, or signal, from a nearby lever, within range.
    -Trigger nearby mobs(Range,alarm-state) ; Would essentialy be a "call for help", or similar warning originating from this mob, to any inside the range.
    -Move to nearby cover(Range) ; Would attempt to take cover behind a wall, or another suitable blocking-object, within range.
    -Move to nearby(Type,Range,Gravity,Distance) ; Would attempt to move with gravity(Speed essentially. Positive: towards, negative: away from) a suitable of Type(Friendly/[Specific(for instance another zirax with plasma weapon)]/Hostile), ending up in about the given Distance, from the target, within given range.
    -Reload() ; Would reload the weapon
    -Patrol(range,type) ; Would make the mob patrol in a range. The type of patrol could be "detailed", Truly looking hard, or similar, defining how the patrol would look, and how the mob moves. Type also defines wether mob should walk through doors, when patrolling, or skip them.
    -Heal() ; Would make the mob use a healthpack, if any.
    -AttackType(type,weapon) ; Would make the mob use a type of attack (range/melee most likely). And with which weapon.
    -PlaySound(sound,radius) ; This would make the mob play a sound in a radius.
    -YieldLoot([]) ; Array with loot and amount.
    -Say/yell ; Possibly a way to communicate.
    -SetNewHome() ; Would make the mob "think", it spawned here. This would affect the center that the mob patrols from
    -GoToOfType(type,radius) ; Would make the mob go to the nearest of type(For instance door, constructor, etc)
    -ReturnHome() ; Would make the mob go to its spawnpoint.
    -Goto(x,y,z) ; Go to these coordinates. Obviously respecting non-passables.
    -FollowNearest(Type,radius,manner) ; Would make this mob follow the nearest of this type, within range, in a given manner(Stick close, some sort of formation, stalk).
    -MenuItemAppend(title,function()) ; Will append this option to the menu presented by the npc (friendly). If function is specified, it will be formatted, clickable, and clear the menu, replacing it with whatever happens in that function. If no function is specified, it will append it as text.
    This is mostly for use to make conversations, and give interactionoptions. For instance you could speak with this mob, and have it turn hostile. Have a long conversation with it. Use it as an actual menu. Or have it activate and unlock a PDA chapter. Or open the trades, if it offers any. etc. A myriad of options.

    Information:
    -GetTypesOfWeapons() ; Would return a list of weapons the mob have.
    -GetAmountOfHealthKits() ; Returns how many healthitems are left
    -GetAmountOfAmmo() ; Returns how much ammunition is left.
    -CombatTime() ; Returns time mob have been in combat
    -GetAlarmState() ; Get current alarm state of mob.
    -GetPatrolState() ; Is mob patrolling? if so, how?
    -GetMoveState() ; Is mob moving?
    -GetLastMoveState() ; Get last movement type
    -GetCoverState() ; Is mob in cover?
    -GetCurrentHealth() ; Would return the % of health the mob have.
    -GetNearestHostile() ; Returns coordinates of nearest hostile
    -GetNearestHostileSettlement() ; Returns coordinates of nearest hostile settlement
    -GetFriendliesInRange() ; Returns a list of friendlies that are nearby, with coordinates, and range. ("zirax","2"[meters],"1337,12,900")
    -GetSpawnersCoordinates() ; Returns the coordinates of the spawner this npc originated from
    -GetCurrentCoordinates() ; Get current coordinates of the npc
    -GetAllActivePlayers() ; Returns a list of names with all current active players
    -GetCoordinatesOfTarget(input) ; Get coordinates of either a generic 'player' (closest), a specific player (by name), an npc (either generic, or by name, which returns closest of type), block (for instance 'AutomaticDoor' : closest-of-type)

    Triggers:
    OnSpawned() ; Triggers when the mob is 'created'
    OnSpotHostile() ; Triggered if the mob is inside the visibility radius, and have LoS to the player.
    OnHearHostile() ; Triggered if the mob is inside the noise radius.
    OnSmellHostile() ; Triggered if the mob is inside smelling radius.
    OnEngageHostile() ; Triggered if the mob is engaged in hostile activity (being shot at/being hit/taking damage/..)
    OnReload() ; When the mob is out of ammo, and would reload.
    OnDisengageHostile() ; When not in combat for a certain amount of time, this would trigger.
    AlertLevelChange() ; Would trigger when the alarm level for the zone changes.
    OnOutOfHealthKits() ; Would trigger when mob have depleted amount of healthkits
    OnOutOfAmmunition() ; Would trigger when mob have no ammunition left
    OnDeath() ; Would trigger if death. Most likely be used to yield loot, and play a death-ratle.
    OnMovementFinished() ; Would trigger when current movement
    OnHealth() ; Would trigger when health% changes.
    OnNearbyDeath() ; Would trigger if the mob can see a friendly die.
    OnTakingDamage() ; Would trigger when the mob takes damage from any source.
    OnInteractedWith() ; Would trigger when the player is speaking with it (for friendly npcs)
    OnUnableToReach() ; If the npc is unable to reach it's "waypoint". Should return what its waypoint was, and ideally, if it was a player, npc, machine, or blankspace. (this would trigger if pathing fails)

    I think these would allow for some rather powerfull mob scripting.
    I suggest implementing it in LUA, and make it a part of the Extras folder.
    When a mob is spawned, its spawner would dictate which to use.

    The end goal is, to give unique experiences, to supplement the basic behaviour, that the game gives, hence making the AI end up in a better spot. Also allowing more focus on the basic mechanics, rather than how they are used.
    Ideally the AI scripts should end up on steam aswell ;)
     
    #1
    Last edited: Aug 26, 2017
  2. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    1,050
    Likes Received:
    756
    Here's an old example of how some WOW-based engines have implemented NPC scripting. (In LUA)

    Code:
    local self=getfenv(1)
    function OnCombat(unit,_,mAggro)
        self[tostring(unit)] = {
            void_blast = math.random(5,10),
            dark_shell = math.random(20,25),
            isHeroic = (mAggro:IsPlayer() and mAggro:IsHeroic() )
        }
        local allies = unit:GetInRangeFriends()
        for _,v in pairs(allies) do
            if(v:GetDistanceYards(unit) < 50 and string.find(v:GetName(),"Ethereal") ~= nil) then
                v:AttackReaction(mAggro,1,0)
            end
        end
        local say_text = math.random(3)
        if(say_text == 1) then
            unit:PlaySoundToSet(10561)
            unit:MonsterYell("I will feed on your soul.")
        elseif(say_text == 2) then
            unit:PlaySoundToSet(10562)
            unit:MonsterYell("So... full of life!")
        else
            unit:PlaySoundToSet(10563)
            unit:MonsterYell("Do not... resist.")
        end
        unit:RegisterAIUpdateEvent(1000)
    end
    function OnWipe(unit)
        unit:RemoveAIUpdateEvent()
        self[tostring(unit)] = nil
    end
    function OnTargetKill(unit)
        local say_text = math.random()
        if(say_text) then
            unit:MonsterYell("Yes! I am... empowered!")
            unit:PlaySoundToSet(10564)
        else
            unit:MonsterYell("More... I must have more!")
            unit:PlaySoundToSet(10565)
        end
    end
    function OnDeath(unit)
        unit:MonsterYell("To the void... once... more..")
        unit:PlaySoundToSet(10566)
    end
    function AIUpdate(unit)
        if(unit:IsCasting() ) then return end
        if(unit:GetNextTarget() == nil) then
            unit:WipeThreatList()
        end
        local ref = self[tostring(unit)]
        ref.void_blast = ref.void_blast -1
        ref.dark_shell = ref.dark_shell - 1
      
        if(ref.dark_shell <= 0) then
            unit:BossRaidEmote(unit:GetName().." casts dark shell!")
            if(ref.isHeroic) then
                unit:FullCastSpell(38759)
            else
                unit:FullCastSpell(32358)
            end
            ref.dark_shell = math.random(20,30)+11 -- the +11 is for the cast time and the aura duration.
        elseif(ref.void_blast <= 0) then
            for i = 1,5 do
                unit:RegisterLuaEvent(VoidBlast_Protocol,i*1000,1)
            end
            ref.void_blast = math.random(10,20)
            ref.dark_shell = ref.dark_shell+5 -- delay dark shell event hax!
        end
    end
    function VoidBlast_Protocol(unit)
        local target = unit:GetRandomEnemy()
        if(self[tostring(unit)].isHeroic) then
            unit:FullCastSpellOnTarget(38760,target)
        else
            unit:FullCastSpellOnTarget(32325,target)
        end
    end
    RegisterUnitEvent(18341,1,OnCombat)
    RegisterUnitEvent(18341,2,OnWipe)
    RegisterUnitEvent(18341,3,OnTargetKill)
    RegisterUnitEvent(18341,4,OnDeath)
    RegisterUnitEvent(18341,21,AIUpdate)
    Obviously this is created for a different sort of game. But the basic concept might be used for inspiration?
    Some of the major differences is, how many more mobs there are in that game, and that they are usually pre-spawned. (ie. not from spawners), (18341 is the ID of a specific NPC in the above)

    Also it's a great example of how easy it would be to write "custom AI", given this approach. I'd highly recommend dedicating the scripting to an actual language, to allow for variables, loops, and other logic-driven mechanics.
    LUA is suggested as it have a very low entry-level; And is fairly easyTM to implement.
     
    #2
    Last edited: May 21, 2017
    campcraftyinc and VISION305 like this.
  3. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    1,050
    Likes Received:
    756
    A few final words:
    This obviously opens up for a myriad of other stuff, like rebalancing of npc stats, what is cover? How can they utilize cover? How will a mob handle a specific weapon, among others.
    Furthermore this could easily be expanded with new stuff, like "usethrowing" for different kind of envoirenmental modifiers, such as smoke, fire, gas, etc etc.. Aswell as being able to deploy covers, and all kind of other crazyness.
    Overall, I do believe this could *hugely* benefit the combat system.

    There's ofcourse also a myriad of uses for these, for non-alien, such as base defenders, doing various tasks, etc. Heck, it could even be used to go crazy (if they could craft and place), to make the NPCs expand their own POIs or settle new ;)

    Basically I see it as a snowball; Once rolling, it will get dramatically larger, and more usefull over time, and add a ton of crazyness to the game.
     
    #3
    VISION305 likes this.
  4. VISION305

    VISION305 Captain

    Joined:
    Dec 17, 2016
    Messages:
    917
    Likes Received:
    980
    such a well thought out post. I love it.
     
    #4
    Exacute likes this.
  5. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    1,050
    Likes Received:
    756
    A little extra detail, that also would make a huge impact in combination : Emotes.
    While it requires some uniformity of the models (that they all have the ability to perform a certain emote, for the most part anyway).

    This could be something like "sit","dance","Interact","shoot","bow", etc.

    These can be used together with this, as calls, (such as setEmote("interact")), which will lead to npcs looking more natural in place.

    This could furthermore be combined with the ability to create "permanent" mobs, that does not respawn (unless POI respawn, that is).
    (Basically saved to the blueprint)
    These could be a supplement to spawners, and a more natural way to create a POI.
    While spawners do have purpose, pre-spawned "feels more right" for the most part.
     
    #5
  6. [BP]wakkytabbaky

    Joined:
    Jun 1, 2017
    Messages:
    26
    Likes Received:
    5
    next in 8.0 alarm tiers for POI's compliments of Exacute
     
    #6
  7. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    1,050
    Likes Received:
    756
  8. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    1,050
    Likes Received:
    756

Share This Page