Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- AddCSLuaFile()
- ENT.Base = "base_nextbot"
- ENT.Spawnable = false
- ENT.backuptime = 0;
- ENT.liveframes = 0;
- ENT.aiframes = 0;
- ENT.lastdoortimeout = 0;
- ENT.fallvel = 0;
- ENT.strafevec = Vector(0, 0, 0);
- ENT.backupgoal = Vector(0, 0, 0);
- ENT.pathTime = 0;
- ENT.searchtime = 0;
- ENT.wakeuptime = 0;
- --Behavior information
- --When should we next update our behavior?
- ENT.nextbtime = 0.01;
- --Should we animate through BodyMove?
- ENT.useBodyMove = false;
- --Pathing and locomotion information
- ENT.goal = Vector(0, 0, 0);
- --Where are we trying to path to right now? Of type PathSegment
- --Such a big hack, but PathFollower's update is too limiting.
- ENT.pathGoal = nil;
- ENT.curNode = 0;
- --What should we face while moving? nil=current goal, other=that target is faced
- ENT.faceTarget = nil;
- --Vision information
- --When can we look for a new target?
- ENT.targetTime = 0.01;
- --Can we see our current target right now?
- ENT.canSeeTarget = false;
- --Where did we last see our target?
- ENT.lastSeenPos = Vector(0, 0, 0);
- --When can we next check if we can see the target?
- ENT.checkVisTime = 0.01;
- --For ambushing, should we look all around?
- ENT.ambushMode = false;
- --Spawn in animations
- local BEHAVIOR_SPAWN = 0;
- --Stand, looking for target. Patrol on occasion
- local BEHAVIOR_STAND = 1;
- --Looking for target while moving to patrol point
- local BEHAVIOR_PATROL = 2;
- --Running straight to the target
- local BEHAVIOR_CHASE = 3;
- --Attempting to pick up a health kit while tracking target
- local BEHAVIOR_FINDHEALTH = 4;
- --For ranged attackers, found and looking for a point to engage
- local BEHAVIOR_FINDENGAGE = 5;
- --For ranged attackers, at our engaging point and attempting to shoot the target
- local BEHAVIOR_ENGAGING = 6;
- --I lost sight of my target, go check out the last visibile point
- local BEHAVIOR_LOSTVIS = 7;
- --I cannot see my target at the new point, I'm going to patrol for them
- local BEHAVIOR_TARGETPATROL = 8;
- --I cannot see my target at the new point, I'm going to lay in ambush
- local BEHAVIOR_FINDAMBUSH = 9;
- --I have decided on an amubsh point, and will stay here until I find vis
- local BEHAVIOR_AMBUSH = 10;
- function ENT:Initialize()
- self:SetModel( "models/player/charple.mdl" );
- self:SetSolid(SOLID_BBOX);
- self:PhysicsInitShadow(false, false);
- --self:SetMaxHealth(40);
- self:SetHealth(40);
- --self:SetTarget(Entity(1));
- --wait a bit before doing a thing
- self.pathtime = CurTime() + 0.1;
- self.searchtime = CurTime() + 0.1;
- self:SetAIPhase(0);
- if (SERVER) then
- self:NewBehavior(BEHAVIOR_SPAWN);
- self.loco:SetMaxYawRate(180);
- self.loco:SetAcceleration(2400);
- self.loco:SetJumpHeight(58);
- self.loco:SetStepHeight(32);
- end
- end
- function ENT:Classify()
- return CLASS_ZOMBIE;
- end
- function ENT:SetupDataTables()
- self:NetworkVar( "Int", 0, "AIPhase" )
- self:NetworkVar( "Int", 0, "Behavior" )
- end
- function ENT:SetTarget(ent)
- self.target = ent;
- self.lastSeenPos = self.target:GetPos();
- end
- function ENT:InvalidateTarget()
- self.targetTime = 0.01;
- self.target = nil;
- end
- function ENT:GetTargetGoal()
- if (self.canSeeTarget) then
- --print("Going to target");
- return self.target;
- end
- --print("Going to last seen");
- return self.lastSeenPos;
- end
- function ENT:UpdateTargetVis()
- --tr = util.TraceLine({start = self:EyePos(), endpos = testnode.pos, mask = MASK_SOLID_BRUSHONLY});
- if (CurTime() < self.checkVisTime) then
- return;
- end
- if (self.target == nil) then
- return;
- end
- --Do a trace to our target
- local vis = self:Visible(self.target);
- if (vis) then
- print("I can see target");
- self.canSeeTarget = true;
- self.lastSeenPos = self.target:GetPos();
- else
- self.canSeeTarget = false;
- print("I cannot see you");
- end
- self.checkVisTime = CurTime() + 0.5;
- end
- ----------------------------------------------------
- -- ENT:FindEnemy()
- -- Returns true and sets our enemy if we find one
- ----------------------------------------------------
- function ENT:FindTarget()
- --Do we like our target?
- if (CurTime() < self.targetTime) then
- return;
- end
- local testent;
- -- Search around us for entities
- -- This can be done any way you want eg. ents.FindInCone() to replicate eyesight
- --print(self:GetForward().x .. " " .. self:GetForward().y .. " " .. self:GetForward().z);
- --local _ents = ents.FindInCone(self:GetPos() + Vector(0, 0, 32), self:GetForward(), 1000, 45); --this doesn't work?
- local _ents = ents.FindInSphere(self:GetPos(), 768);
- -- Here we loop through every entity the above search finds and see if it's the one we want
- for k, v in pairs( _ents ) do
- --print(v:GetClass());
- if (v:GetClass() == "npc_metropolice" || v:GetClass() == "npc_barney" || v:GetClass() == "npc_kleiner") then
- targetvec = v:GetPos() - self:GetPos();
- targetvec:Normalize();
- ldot = targetvec:Dot(self:GetForward())
- if (ldot > 0.70 || self.ambushMode) then
- testent = v;
- break;
- end
- elseif (v:GetClass() == "doom_npc_vile") then
- testent = v;
- break;
- --try targeting other CharPles
- elseif (v:GetClass() == "npc_ibbot" && v != self) then
- testent = v;
- break;
- --elseif ( v:IsPlayer() ) then
- -- if (self:GetAIPhase() == 0) then
- -- targetvec = self:GetPos() - v:GetPos();
- -- targetvec:Normalize();
- -- ldot = targetvec:Dot(v:GetForward())
- -- if (ldot > 0.9) then
- -- self:SetAIPhase(1);
- -- self:SetTarget( v )
- -- return true
- -- end
- -- end
- end
- end
- if (testent == nil) then
- --Keep our current target and just look again later
- --print("no target");
- self.targetTime = CurTime() + 0.2;
- return
- end
- --print("we found a dude");
- --Otherwise set the new entity
- self:SetTarget(testent);
- self.ambushMode = false;
- --Follow this target exclusively for 7 seconds
- self.targetTime = CurTime() + 7.0;
- end
- function ENT:BehaveAct()
- end
- function ENT:OnStuck()
- --self:SetVelocity(Vector(0, 0, 0));
- --efdata = EffectData();
- --efdata:SetOrigin(self:GetPos());
- --util.Effect("explosion", efdata);
- --util.BlastDamage(self, self, self:GetPos(), 150, 40);
- --self:TakeDamage(1000, self, self);
- --self:ClearStuck();
- end
- function ENT:OnContact(ent)
- if (ent:GetClass() == "player") then
- --ent:TakeDamage(1, self, self);
- --util.BlastDamage(self, self, self:GetPos(), 150, 40);
- --ent:TakeDamage(25, self, self);
- --ent:Ignite(5);
- --give a jolt
- --self:SetVelocity(Vector(0, 0, 200));
- --efdata = EffectData();
- --efdata:SetOrigin(self:GetPos());
- --util.Effect("explosion", efdata);
- --self:TakeDamage(1000, self, self);
- self.heh = true;
- elseif (ent:GetClass() == "npc_metropolice" || ent:GetClass() == "npc_ibbot") then
- ent:Ignite();
- ent:TakeDamage(1000, self, self);
- self:TakeDamage(1000, self, self);
- elseif (ent:GetClass() == "npc_barney") then
- ent:Ignite(100);
- self:SetVelocity(Vector(0, 0, 0));
- self:TakeDamage(1000, self, self);
- --also used as emergency situation if something not targeted pissed it off
- --but only if they aren't a player. ugh.
- elseif (ent:GetClass() == "npc_kleiner" || ent:GetClass() == "doom_npc_vile" || (ent == self.target && !ent:IsPlayer())) then
- ent:SetVelocity(ent:GetVelocity() + Vector(0, 0, 1000));
- self:SetVelocity(Vector(0, 0, 100));
- self:TakeDamage(1000, self, self);
- --cheap smashing!
- elseif ((ent:GetClass() == "func_breakable_surf" || ent:GetClass() == "prop_physics" || ent:GetClass() == "func_breakable")) then
- ent:TakeDamage(1000, self, self);
- end
- end
- function ENT:Think()
- if not SERVER then return end
- --print("I'm using my brain!");
- --if (self.loco:IsClimbingOrJumping() ) then
- --print("fuck it I'm trying to climb or jump")
- --end
- physObj = self:GetPhysicsObject();
- if (IsValid(physObj)) then
- pos = self:GetPos();
- ang = self:GetAngles();
- physObj:UpdateShadow(pos, ang, FrameTime());
- --physObj:UpdateShadow(pos, ang, 0);
- end
- return true
- end
- function ENT:Use(activator, caller, type, value)
- self.loco:Jump();
- end
- function ENT:OnLeaveGround(entity)
- if (!SERVER) then
- return
- end
- --self:StartActivity(ACT_HL2MP_JUMP_PASSIVE);
- end
- function ENT:OnInjured(dmginfo)
- attacker = dmginfo:GetAttacker();
- if (!IsValid(self.target) && IsValid(attacker)) then
- --print("okay, now I'm pissed!");
- --attacker:Ignite(5);
- if (CurTime() < self.targetTime) then
- --too soon for new target, but make it happen sooner
- self.targetTime = self.targetTime - 0.5;
- end
- if (CurTime() > self.targetTime) then --try again! we might be able to change target
- --can change now
- self.target = attacker;
- end
- --elseif (IsValid(attacker) && attacker:GetClass() == "player" && self:GetAIPhase() == 0 && SERVER) then
- --wake me up!
- --self:SetAIPhase(1);
- --self.target = attacker;
- end
- end
- function ENT:OnKilled( dmginfo )
- --self.axe:Remove();
- hook.Run( "OnNPCKilled", self, dmginfo:GetAttacker(), dmginfo:GetInflictor() )
- self:BecomeRagdoll( dmginfo )
- --super():OnKilled(dmginfo)
- end
- function ENT:BodyUpdate()
- --print("Am I updated?")
- --eh
- if (self.useBodyMove) then
- --MsgN("BodyMoveXY");
- self:BodyMoveXY();
- else
- --MsgN("FrameAdvance");
- self:FrameAdvance();
- end
- end
- function ENT:OnLandOnGround(ent)
- if (!SERVER) then
- return
- end
- --self:StartActivity(ACT_HL2MP_RUN_FIST);
- if (self.loco:IsStuck()) then
- --stupidly strafe some
- --self:SetVelocity(Vector(0, math.Rand(-100, 100), 0));
- end
- --print("hit ground at" .. self.fallvel);
- --if (self.fallvel <= -800) then
- -- self:TakeDamage(1000, self, self);
- --end
- self.fallvel = 0;
- --print("heh");
- self.heh2 = true;
- end
- --We don't use the coroutine, so don't do anything
- function ENT:BehaveStart()
- end
- --ugh, gmod
- --path computation function must be anonymous in order to use self...
- --split the messy function off into its own one
- function ENT:DoPathComputation(path, pos)
- --print("time to compute path!");
- path:Compute(self, pos, function(area, fromArea, ladder, elevator, length)
- --print("Doing path computation");
- if ( !IsValid( fromArea ) ) then
- // first area in path, no cost
- return 0
- else
- if ( !self.loco:IsAreaTraversable( area ) ) then
- // our locomotor says we can't move here
- --print("Rejected location");
- return -1
- end
- // compute distance traveled along path so far
- local dist = 0
- if ( IsValid( ladder ) ) then
- --print("rejecting a ladder");
- --dist = ladder:GetLength()
- --absolutely no ladders, we can't mount them
- return -1;
- elseif ( length > 0 ) then
- // optimization to avoid recomputing length
- dist = length
- else
- dist = ( area:GetCenter() - fromArea:GetCenter() ):GetLength()
- end
- local cost = dist + fromArea:GetCostSoFar()
- // check height change
- local deltaZ = fromArea:ComputeAdjacentConnectionHeightChange(area)
- --print("Evalulating height change of " .. deltaZ);
- if (deltaZ >= 24) then
- --need to check if the area is stairs
- --if its stairs, we can walk up it
- if (!area:HasAttributes(NAV_MESH_STAIRS) && !fromArea:HasAttributes(NAV_MESH_STAIRS)) then
- --print("Trying to climb too high!");
- return -1;
- --else
- --print("Successfully navigated stairs");
- end
- --if ( deltaZ >= self.loco:GetMaxJumpHeight() ) then
- --test
- --maybe we won't try to ledge clime when we can't goddamned ledge climb!
- --if ( deltaZ >= 24 ) then
- --// too high to reach
- --return -1
- --end
- --// jumping is slower than flat ground
- --local jumpPenalty = 5
- --cost = cost + jumpPenalty * dist
- --elseif ( deltaZ < -self.loco:GetDeathDropHeight() ) then
- -- too far to drop
- --return -1
- end
- return cost
- end
- end )
- end
- function ENT:UpdateGoal()
- if (CurTime() < self.pathTime) then
- --print("its not time");
- return;
- end;
- local dest;
- local targetvec;
- if (self.goal != nil) then
- if (isentity(self.goal) && IsValid(self.goal)) then
- --print("okay we have a target");
- dest = self.goal:GetPos();
- elseif (isvector(self.goal)) then
- --print("okay we have a point");
- dest = self.goal;
- end
- else
- --print("our goal is not valid");
- self.pathTime = CurTime() + 0.2;
- return;
- end
- local dist = self:DistanceToGoal();
- --print("self is " .. self:GetPos().x .. " " .. self:GetPos().y .. " " .. self:GetPos().z);
- --print("goal is " .. dest.x .. " " .. dest.y .. " " .. dest.z);
- if (dest != nil) then
- if (dist < 20) then
- --print("we're at the destination");
- self.pathTime = CurTime() + 0.2;
- return; --too short to do anything to
- else
- --print("time to find path!");
- self:PathToPoint(dest);
- self.path:MoveCursorToEnd();
- self.pathTime = CurTime() + 0.2;
- end
- else
- --print("dest is not valid?");
- end
- end
- function ENT:InvalidateGoal()
- self.pathTime = 0.01;
- end
- function ENT:GetGoalPos()
- if (self.goal != nil) then
- if (isentity(self.goal) && IsValid(self.goal)) then
- return self.goal:GetPos();
- elseif (isvector(self.goal)) then
- return self.goal;
- end
- end
- end
- function ENT:DistanceToGoal()
- local targetvec;
- if (self.goal != nil) then
- if (isentity(self.goal) && IsValid(self.goal)) then
- targetvec = self:GetPos() - self.goal:GetPos();
- elseif (isvector(self.goal)) then
- targetvec = self:GetPos() - self.goal;
- end
- if (targetvec) then
- return targetvec:Length();
- end
- end
- return 0;
- end
- function ENT:IsAtGoal()
- if (self.goal == nil) then
- return true;
- end
- local dist = self:DistanceToGoal()
- if (dist < 20) then
- return true;
- end
- return false;
- end
- function ENT:UpdatePathGoal()
- if (self.path == nil || !IsValid(self.path)) then
- return;
- end
- if (self:GetRangeSquaredTo(self.pathGoal.pos) < 1296) then -- 36 units
- local nodes = self.path:GetAllSegments();
- local node;
- local maxNodes = #nodes;
- if (self.curNode > maxNodes) then --we're out of nodes
- --Invalidate the path, we need to find a new one
- --print("We're beyond the last node");
- self:InvalidateGoal();
- return;
- end
- node = nodes[self.curNode];
- if (node == nil) then
- --print(self.curNode);
- --print("what the hell");
- return;
- end
- self.curNode = self.curNode + 1;
- local i = self.curNode;
- local testnode;
- local tr;
- --failed reactive pathfinding thing. Couldn't handle paths on multiple elevations
- --while (i <= maxNodes) do
- -- testnode = nodes[i];
- -- if (testnode.distanceFromStart - self.path:GetCursorPosition() > 3000) then
- -- --Too far away, don't try this node anymore
- -- break;
- -- end
- -- tr = util.TraceLine({start = self:EyePos(), endpos = testnode.pos, mask = MASK_SOLID_BRUSHONLY});
- -- --can't use this node if trace fell short
- -- if (tr.Fraction < 0.999) then
- -- break;
- -- end
- -- node = testnode;
- -- self.curNode = i;
- -- i = i + 1;
- --end
- self.pathGoal = node;
- --efdata = EffectData();
- --efdata:SetOrigin(self.pathGoal.pos);
- --util.Effect("explosion", efdata);
- --print("Updated curnode, now " .. self.curNode);
- end
- end
- function ENT:LocomotionUpdate(fInterval)
- local dist = self:DistanceToGoal();
- local lgoal;
- --Are we already at our goal?
- if (self:IsAtGoal()) then
- --print("at destination");
- return;
- end
- if (IsValid(self.path) && self.path:IsValid()) then --is our current path ready for use
- if (dist < 192) then
- --print("attempting to approach");
- if (IsValid(self.faceTarget)) then
- self.loco:FaceTowards(self.faceTarget:GetPos());
- else
- self.loco:FaceTowards(self:GetGoalPos());
- end
- self.loco:Approach(self:GetGoalPos(), 1.0);
- else
- --print("attempting to pathfind");
- --self.path:Update(self);
- --self.path:GetPositionOnPath(self.path:GetCursorPosition());
- --print(self.path:GetCursorPosition());
- --self.path:MoveCursorToEnd();
- --lgoal = self.path:GetCurrentGoal();
- --print("my goal is " .. lgoal.length .. " units along a path " .. self.path:GetLength() .. " long." );
- --print("(" .. self:GetPos().x .. "," .. self:GetPos().y .. "," .. self:GetPos().z .. ") (" .. lgoal.pos.x .. "," .. lgoal.pos.y .. "," .. lgoal.pos.z .. ")");
- --self.loco:Approach(self.path:GetCursorData().pos, 1.0);
- --self.loco:Approach(lgoal.pos, 1.0);
- --self.loco:Approach(self.path:GetPositionOnPath(lgoal.length), 1.0);
- if (IsValid(self.faceTarget)) then
- self.loco:FaceTowards(self.faceTarget:GetPos());
- else
- self.loco:FaceTowards(self.pathGoal.pos);
- end
- self.loco:Approach(self.pathGoal.pos, 1.0);
- --update?
- self.path:MoveCursorToClosestPosition(self:GetPos(), SEEK_ENTIRE_PATH);
- self.path:Draw();
- end
- --else
- --print("why is everything deciding to not be valid tonight");
- end
- end
- function ENT:PathToPoint(pos)
- --print("Repathing");
- if (pos == nil) then
- return;
- end
- self.path = Path("Follow")
- self.path:SetMinLookAheadDistance(300);
- self.path:SetGoalTolerance(20);
- self:DoPathComputation(self.path, pos);
- if (self.path != nil) then
- --print("we got a path");
- self.pathGoal = self.path:FirstSegment();
- end
- self.curNode = 1;
- end
- --------------------------------------------------------------------------------
- --New AI experiment
- --New New AI experiment
- --------------------------------------------------------------------------------
- function ENT:BehaveUpdate(fInterval)
- if SERVER then
- if (self.heh) then
- self:AddGesture(ACT_HL2MP_GESTURE_RANGE_ATTACK_FIST);
- self.heh = false;
- end
- if (self.heh2) then
- self:CurBehaviorActivity();
- self.heh2 = true;
- end
- self:FindTarget();
- self:UpdateTargetVis();
- self:CurBehaviorThink(fInterval);
- self:UpdateGoal();
- self:UpdatePathGoal();
- self:LocomotionUpdate(fInterval);
- if (CurTime() >= self.nextbstart) then
- self:CurBehaviorTimeout();
- end
- end
- end
- --Called to start a behavior
- function ENT:CurBehaviorStart()
- if (self:GetBehavior() == BEHAVIOR_SPAWN) then
- print("BEHAVIOR_SPAWN");
- self.nextbstart = CurTime() + self:SetSequence("zombie_slump_rise_01");
- self:ResetSequenceInfo();
- self:SetCycle(0);
- self:SetPlaybackRate(1.0);
- self.useBodyMove = false;
- self.goal = self:GetPos();
- elseif (self:GetBehavior() == BEHAVIOR_STAND) then
- print("BEHAVIOR_STAND");
- self:StartActivity(ACT_HL2MP_IDLE);
- --self:StartActivity(ACT_HL2MP_GESTURE_RANGE_ATTACK_FIST);
- self.nextbstart = CurTime() + 0.3;
- self.useBodyMove = false;
- self:InvalidateGoal();
- self.goal = self:GetPos();
- elseif (self:GetBehavior() == BEHAVIOR_PATROL) then
- print("BEHAVIOR_PATROL");
- self:StartActivity(ACT_HL2MP_WALK);
- self.nextbstart = CurTime() + 25.0;
- self.useBodyMove = true;
- --Find a point
- self.loco:SetDesiredSpeed(100);
- self:InvalidateGoal();
- local pos = self:FindRSpot("random", { type = 'hiding', radius = 1000 })
- if (pos != nil) then
- self.goal = pos;
- else
- self.goal = self:GetPos();
- end
- elseif (self:GetBehavior() == BEHAVIOR_CHASE) then
- print("BEHAVIOR_CHASE");
- self:StartActivity(ACT_HL2MP_RUN_FIST);
- self.nextbstart = CurTime() + 0.4;
- self.useBodyMove = true;
- --self:InvalidateGoal();
- self.loco:SetDesiredSpeed(200);
- self.faceTarget = nil; --face direction of travel
- if (self.target) then
- self.goal = self:GetTargetGoal();
- else
- self.goal = self:GetPos();
- end
- elseif (self:GetBehavior() == BEHAVIOR_FINDHEALTH) then
- print("BEHAVIOR_FINDHEALTH");
- self:StartActivity(ACT_HL2MP_RUN_FIST);
- self.nextbstart = CurTime() + 20.0;
- self.useBodyMove = true;
- self.loco:SetDesiredSpeed(200);
- --changing goal in the new goal function seems messy, but we have no other option right now...
- --this code is not as good as it seems
- --self:InvalidateGoal();
- if (IsValid(self.target)) then
- self.faceTarget = self.target;
- end
- elseif (self:GetBehavior() == BEHAVIOR_LOSTVIS) then
- print("BEHAVIOR_LOSTVIS");
- self:StartActivity(ACT_HL2MP_RUN_FIST);
- self.nextbstart = CurTime() + 0.4;
- self.useBodyMove = true;
- --self:InvalidateGoal();
- self.loco:SetDesiredSpeed(200);
- self.faceTarget = nil; --face direction of travel
- --Try to go to the point where we last saw our guy
- self.goal = self.lastSeenPos;
- elseif (self:GetBehavior() == BEHAVIOR_FINDAMBUSH) then
- print("BEHAVIOR_FINDAMBUSH");
- self:StartActivity(ACT_HL2MP_WALK);
- self.nextbstart = CurTime() + 25.0;
- self.useBodyMove = true;
- self.loco:SetDesiredSpeed(100);
- self.ambushMode = true;
- --Goal has been found in advance
- elseif (self:GetBehavior() == BEHAVIOR_AMBUSH) then
- print("BEHAVIOR_AMBUSH");
- self:StartActivity(ACT_HL2MP_IDLE);
- --self:StartActivity(ACT_HL2MP_GESTURE_RANGE_ATTACK_FIST);
- self.nextbstart = CurTime() + 0.3;
- self:InvalidateGoal();
- self.goal = self:GetPos();
- end
- end
- function ENT:NewBehavior(newb)
- self:SetBehavior(newb);
- self:CurBehaviorStart();
- self:CurBehaviorActivity();
- end
- --Called to update a behavior
- function ENT:CurBehaviorThink(fInterval)
- if(self:GetBehavior() == BEHAVIOR_SPAWN || self:GetBehavior() == BEHAVIOR_STAND) then
- --This is a bit of a hack, but it prevents problems
- self.goal = self:GetPos();
- elseif(self:GetBehavior() == BEHAVIOR_PATROL) then
- if (self:IsAtGoal()) then
- print("at goal?");
- self.nextbstart = 0.01; --End the current behavior
- elseif (self.target != nil) then
- print("We found a target wandering");
- self.nextbstart = 0.01;
- end
- elseif(self:GetBehavior() == BEHAVIOR_CHASE) then
- if (IsValid(self.target) == false) then --has our target died?
- print("Target died");
- self:InvalidateTarget(); --pick a new target sooner
- self.nextbstart = 0.01; --abort instantly
- end
- --This really isn't elegant, honestly. Bot shouldn't know if their target died or not.
- elseif(self:GetBehavior() == BEHAVIOR_LOSTVIS) then
- if (IsValid(self.target) == false) then --has our target died?
- print("Target died");
- self:InvalidateTarget(); --pick a new target sooner
- self.nextbstart = 0.01; --abort instantly
- end
- elseif(self:GetBehavior() == BEHAVIOR_FINDHEALTH) then
- if (self:IsAtGoal()) then
- print("I got a healthkit!");
- self:SetHealth(self:Health() + 25);
- self.goal:Remove();
- self.nextbstart = 0.01; --End the current behavior
- end
- elseif(self:GetBehavior() == BEHAVIOR_FINDAMBUSH) then
- if (self:IsAtGoal()) then
- print("at goal?");
- self.nextbstart = 0.01; --End the current behavior
- elseif (self.target != nil) then
- print("We found a target wandering");
- self.nextbstart = 0.01;
- end
- elseif(self:GetBehavior() == BEHAVIOR_AMBUSH) then
- --Stand in the hiding spot, waiting for something to come. Look around randomly
- --local angles = self:GetAngles();
- --angles.yaw = angles.yaw + math.Rand(-2, 2);
- --self:SetAngles(angles);
- --This is a bit of a hack, but it prevents problems
- self.goal = self:GetPos();
- end
- end
- --Called to switch from one behavior into another
- function ENT:CurBehaviorTimeout()
- if (self:GetBehavior() == BEHAVIOR_SPAWN) then
- self:NewBehavior(BEHAVIOR_STAND);
- elseif (self:GetBehavior() == BEHAVIOR_STAND) then
- if (self.target != nil) then
- self:InvalidateGoal();
- self:NewBehavior(BEHAVIOR_CHASE);
- else
- --Sometimes find a new spot to patrol
- if (math.Rand(0, 1) > 0.9) then
- self:NewBehavior(BEHAVIOR_PATROL);
- else
- self:NewBehavior(BEHAVIOR_STAND);
- end
- end
- elseif (self:GetBehavior() == BEHAVIOR_PATROL) then
- self:NewBehavior(BEHAVIOR_STAND);
- elseif (self:GetBehavior() == BEHAVIOR_CHASE) then
- if (IsValid(self.target) == false) then
- self:NewBehavior(BEHAVIOR_STAND);
- elseif (!self.canSeeTarget) then
- --We cannot see our target anymore, we need to switch our behavior
- self:NewBehavior(BEHAVIOR_LOSTVIS);
- elseif (self:Health() < 25) then
- local _ents = ents.FindInSphere( self:GetPos(), 1200 )
- local kit = nil
- for k, v in pairs( _ents ) do
- if (v:GetClass() == "item_healthkit") then
- kit = v;
- end
- end
- if (kit) then
- --try to find a medikit
- self:InvalidateGoal();
- self.goal = kit;
- self:NewBehavior(BEHAVIOR_FINDHEALTH);
- else
- --Go back to chasing, we're doomed
- self:NewBehavior(BEHAVIOR_CHASE);
- end
- else
- self:NewBehavior(BEHAVIOR_CHASE);
- end
- elseif (self:GetBehavior() == BEHAVIOR_FINDHEALTH) then
- self:InvalidateGoal();
- self:NewBehavior(BEHAVIOR_CHASE);
- elseif (self:GetBehavior() == BEHAVIOR_LOSTVIS) then
- if (self.canSeeTarget) then
- self:NewBehavior(BEHAVIOR_CHASE);
- elseif (self:IsAtGoal()) then --We haven't found our target yet, do something
- --Either enter patrol or ambush mode
- self:InvalidateTarget();
- if (math.Rand(0, 1) > 0.5) then
- self:NewBehavior(BEHAVIOR_STAND);
- else
- local spot = self:FindSpot( "random", { type = 'hiding', radius = 5000 } );
- if (spot) then
- print("Hiding!");
- self.goal = spot;
- self:NewBehavior(BEHAVIOR_FINDAMBUSH);
- else
- print("No spot, patrolling");
- self:NewBehavior(BEHAVIOR_STAND);
- end
- end
- else --Continue going
- self:NewBehavior(BEHAVIOR_LOSTVIS);
- end
- elseif (self:GetBehavior() == BEHAVIOR_FINDAMBUSH) then
- if (self.target) then
- self:NewBehavior(BEHAVIOR_CHASE);
- else
- self:NewBehavior(BEHAVIOR_AMBUSH);
- end
- elseif (self:GetBehavior() == BEHAVIOR_AMBUSH) then
- if (self.target != nil) then
- self:InvalidateGoal();
- self:NewBehavior(BEHAVIOR_CHASE);
- end
- end
- end
- --Called to figure out what hte primary animation we should be doing
- function ENT:CurBehaviorActivity()
- if (self:GetBehavior() == BEHAVIOR_PATROL) then
- return ACT_HL2MP_WALK;
- elseif (self:GetBehavior() == BEHAVIOR_CHASE || self:GetBehavior() == BEHAVIOR_FINDHEALTH) then
- return ACT_HL2MP_RUN_FIST;
- end
- return ACT_HL2MP_IDLE;
- end
- --TEMPORARY HACK
- --
- -- Name: NextBot:FindSpots
- -- Desc: Returns a table of hiding spots.
- -- Arg1: table|specs|This table should contain the search info.\n\n * 'type' - the type (either 'hiding')\n * 'pos' - the position to search.\n * 'radius' - the radius to search.\n * 'stepup' - the highest step to step up.\n * 'stepdown' - the highest we can step down without being hurt.
- -- Ret1: table|An unsorted table of tables containing\n * 'vector' - the position of the hiding spot\n * 'distance' - the distance to that position
- --
- function ENT:FindRSpots( tbl )
- local tbl = tbl or {}
- tbl.pos = tbl.pos or self:WorldSpaceCenter()
- tbl.radius = tbl.radius or 1000
- tbl.stepdown = tbl.stepdown or 20
- tbl.stepup = tbl.stepup or 20
- tbl.type = tbl.type or 'hiding'
- -- Use a path to find the length
- local path = Path( "Follow" )
- -- Find a bunch of areas within this distance
- local areas = navmesh.Find( tbl.pos, tbl.radius, tbl.stepdown, tbl.stepup )
- local found = {}
- -- In each area
- for _, area in pairs( areas ) do
- -- get the spots
- local spot
- spot = area:GetRandomPoint()
- path:Invalidate()
- path:Compute( self, spot, 1 ) -- TODO: This is bullshit - it's using 'self.pos' not tbl.pos
- table.insert( found, { vector = spot, distance = path:GetLength() } )
- end
- return found
- end
- --
- -- Name: NextBot:FindSpot
- -- Desc: Like FindSpots but only returns a vector
- -- Arg1: string|type|Either "random", "near", "far"
- -- Arg2: table|options|A table containing a bunch of tweakable options. See the function definition for more details
- -- Ret1: vector|If it finds a spot it will return a vector. If not it will return nil.
- --
- function ENT:FindRSpot( type, options )
- local spots = self:FindRSpots( options )
- if ( !spots || #spots == 0 ) then return end
- if ( type == "near" ) then
- table.SortByMember( spots, "distance", true )
- return spots[1].vector
- end
- if ( type == "far" ) then
- table.SortByMember( spots, "distance", false )
- return spots[1].vector
- end
- -- random
- return spots[ math.random( 1, #spots ) ].vector
- end
- --
- -- List the NPC as spawnable
- --
- list.Set( "NPC", "npc_ibbot_new", {
- Name = "Enhanced IB Bot",
- Class = "npc_ibbot_new",
- Category = "Nextbot"
- } )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement