WIP New MTF AI

#1
Replace the FindPath function in MapSystem.bb with this, as it currently has a bug that generates an invalid path when more than 20 waypoints need to be traversed to reach a destination:

Code: Select all

Function FindPath(n.NPCs, x#, y#, z#)
	
	DebugLog "findpath: "+n\NPCtype
	
	Local temp%, dist#, dist2#
	Local xtemp#, ytemp#, ztemp#
	
	Local w.WayPoints, StartPoint.WayPoints, EndPoint.WayPoints	
	
	Local StartX% = Floor(EntityX(n\Collider,True) / 8.0 + 0.5), StartZ% = Floor(EntityZ(n\Collider,True) / 8.0 + 0.5)
	;If StartX < 0 Or StartX > MapWidth Then Return 2
	;If StartZ < 0 Or StartZ > MapWidth Then Return 2
	
	Local EndX% = Floor(x / 8.0 + 0.5), EndZ% = Floor(z / 8.0 + 0.5)
	;If EndX < 0 Or EndX > MapWidth Then Return 2
	;If EndZ < 0 Or EndZ > MapWidth Then Return 2
	
	Local CurrX, CurrZ
	
	;pathstatus = 0, ei ole etsitty reittiä
	;pathstatus = 1, reitti löydetty
	;pathstatus = 2, reittiä ei ole olemassa	
	
	For w.WayPoints = Each WayPoints 
		w\state = 0
		w\Fcost = 0
		w\Gcost = 0
		w\Hcost = 0
	Next
	
	n\PathStatus = 0
	n\PathLocation = 0
	For i = 0 To 19
		n\Path[i] = Null
	Next
	
	Local pvt = CreatePivot()
	PositionEntity(pvt, x,y,z, True)	
	
	temp = CreatePivot()
	PositionEntity(temp, EntityX(n\Collider,True), EntityY(n\Collider,True)+0.15, EntityZ(n\Collider,True))
	
	;käytetään aloituspisteenä waypointia, joka on lähimpänä loppupistettä ja joka on näkyvissä
	dist = 400.0
	For w.WayPoints = Each WayPoints
		xtemp = EntityX(w\obj,True)-EntityX(temp,True)
		;If xtemp < 8.0 Then
		ztemp = EntityZ(w\obj,True)-EntityZ(temp,True)
			;If ztemp < 8.0 Then 
		ytemp = EntityY(w\obj,True)-EntityY(temp,True)
				;If ytemp < 8.0 Then 
		dist2# = (xtemp*xtemp)+(ytemp*ytemp)+(ztemp*ztemp)
		If dist2 < dist Then ; And EntityVisible(w\obj, temp)
			dist = dist2
			StartPoint = w
		EndIf
				;EndIf
			;EndIf
		;EndIf
	Next
	DebugLog "DIST: "+dist
	
	FreeEntity temp
	
	If StartPoint = Null Then Return 2
	StartPoint\state = 1		
	
	;If EndPoint = Null Then 
	EndPoint = Null
	dist# = 400.0
	For w.WayPoints = Each WayPoints
		xtemp = EntityX(pvt,True)-EntityX(w\obj,True)
		;If xtemp =< 8.0 Then
		ztemp = EntityZ(pvt,True)-EntityZ(w\obj,True)
			;If ztemp =< 8 Then
		ytemp = EntityY(pvt,True)-EntityY(w\obj,True)
		dist2# = (xtemp*xtemp)+(ytemp*ytemp)+(ztemp*ztemp)
		
		If dist2 < dist Then ; And EntityVisible(w\obj, pvt)
			dist = dist2
			EndPoint = w
		EndIf				
			;EndIf
		;EndIf
	Next
	;EndIf
	
	FreeEntity pvt
	
	If EndPoint = StartPoint Then 
		If dist < 0.4 Then
			Return 0
		Else
			n\Path[0]=EndPoint
			Return 1					
		EndIf
	EndIf
	If EndPoint = Null Then Return 2
	
	;aloitus- ja lopetuspisteet löydetty, aletaan etsiä reittiä
	
	Repeat 
		
		temp% = False
		smallest.WayPoints = Null
		dist# = 10000.0
		For w.WayPoints = Each WayPoints
			If w\state = 1 Then
				temp = True
				If (w\Fcost) < dist Then 
					dist = w\Fcost
					smallest = w
				EndIf
			EndIf
		Next
		
		If smallest <> Null Then
			
			w = smallest
			w\state = 2
			
			For i = 0 To 4
				If w\connected[i]<>Null Then 
					If w\connected[i]\state < 2 Then 
						
						If w\connected[i]\state=1 Then ;open list
							gtemp# = w\Gcost+w\dist[i]
							If n\NPCtype = NPCtypeMTF Then 
								If w\connected[i]\door = Null Then gtemp = gtemp + 0.5
							EndIf
							If gtemp < w\connected[i]\Gcost Then ;parempi reitti -> overwrite
								w\connected[i]\Gcost = gtemp
								w\connected[i]\Fcost = w\connected[i]\Gcost + w\connected[i]\Hcost
								w\connected[i]\parent = w
							EndIf
						Else
							w\connected[i]\Hcost# = Abs(EntityX(w\connected[i]\obj,True)-EntityX(EndPoint\obj,True))+Abs(EntityZ(w\connected[i]\obj,True)-EntityZ(EndPoint\obj,True))
							gtemp# = w\Gcost+w\dist[i]
							If n\NPCtype = NPCtypeMTF Then 
								If w\connected[i]\door = Null Then gtemp = gtemp + 0.5
							EndIf
							w\connected[i]\Gcost = gtemp
							w\connected[i]\Fcost = w\Gcost+w\Hcost 
							w\connected[i]\parent = w
							w\connected[i]\state=1
						EndIf				
					EndIf
					
				EndIf
			Next
		Else ;open listiltä ei löytynyt mitään
			If EndPoint\state > 0 Then 
				StartPoint\parent = Null
				EndPoint\state = 2
				Exit
			EndIf
		EndIf
		
		If EndPoint\state > 0 Then 
			StartPoint\parent = Null
			EndPoint\state = 2
			Exit
		EndIf
		
	Until temp = False
	
	If EndPoint\state > 0 Then
		
		currpoint.waypoints = EndPoint
		twentiethpoint.waypoints = EndPoint
		
		length = 0
		Repeat
			length = length +1
			currpoint = currpoint\parent
			If length>20 Then
				twentiethpoint = twentiethpoint\parent
			EndIf
		Until currpoint = Null
		
		currpoint.waypoints = EndPoint
		While twentiethpoint<>Null
			length=Min(length-1,19)
			;DebugLog "LENGTH "+length
			twentiethpoint = twentiethpoint\parent
			n\Path[length] = twentiethpoint
		Wend
		
		Return 1
		;RuntimeError length
;		For i = 0 To (length-1)
;			temp =False
;			If length < 20 Then
;				n\Path[length-1-i] = currpoint.WayPoints
;			Else
;				If i < 20 Then
;					n\Path[20-1-i] = w.WayPoints
;				Else
;					;Return 1
;				EndIf
;			EndIf
;			
;			If currpoint = StartPoint Then Return 1
;			
;			If currpoint\parent <> Null Then
;				currpoint = currpoint\parent
;			Else
;				Exit
;			EndIf
;			
;		Next
		
	Else
		
		DebugLog "FUNCTION FindPath() - reittiä ei löytynyt"
		Return 2 ;reittiä määränpäähän ei löytynyt
		
	EndIf
	
End Function
Replace the NPCs type in NPCs.bb with this:

Code: Select all

Type NPCs
	Field obj%, obj2%, obj3%, obj4%, Collider%
	Field NPCtype%, ID%
	Field DropSpeed#, Gravity%
	Field State#, State2#, State3#, PrevState%
	Field MakingNoise%
	
	Field Frame#
	
	Field Angle#
	Field Sound%, SoundChn%, SoundTimer#
	Field Sound2%, SoundChn2%
	
	Field Speed#, CurrSpeed#
	
	Field texture$
	
	Field Idle#
	
	Field Reload#
	
	Field LastSeen%, LastDist#
	
	Field PrevX#, PrevY#, PrevZ#
	
	Field Target.NPCs, TargetID%
	Field EnemyX#, EnemyY#, EnemyZ#
	
	Field Path.WayPoints[20], PathStatus%, PathTimer#, PathLocation%
	
	Field NVX#,NVY#,NVZ#,NVName$
	
	Field GravityMult# = 1.0
	Field MaxGravity# = 0.2
	
	Field MTFVariant%
	Field MTFLeader.NPCs
	Field IsDead%
	Field BlinkTimer#
	Field IgnorePlayer%
End Type
Add this code somewhere in NPCs.bb:

Code: Select all

Function OtherNPCSeesMeNPC%(me.NPCs,other.NPCs)
	If other\BlinkTimer<=0.0 Then Return False
	If EntityDistance(other\Collider,me\Collider)<4.0 Then
		If Abs(DeltaYaw(other\Collider,me\Collider))<40.0 Then
			Return True
		EndIf
	EndIf
	Return False
End Function

Function MeNPCSeesPlayer%(me.NPCs)
	If me\BlinkTimer<=0.0 Then Return False
	If EntityDistance(Collider,me\Collider)<4.0 Then
		If Abs(DeltaYaw(me\Collider,Collider))<40.0 Then
			Return True
		EndIf
	EndIf
	Return False
End Function

Function UpdateMTFUnit(n.NPCs)
	;[Block]
	If n\NPCtype<>NPCtypeMTF Then
		Local realType$ = ""
		Select n\NPCtype
			Case NPCtype173
				realType = "173"
			Case NPCtypeOldMan
				realType = "106"
			Case NPCtypeGuard
				realType = "guard"
			Case NPCtypeD
				realType = "d"
			Case NPCtype372
				realType = "372"
			Case NPCtypeApache
				realType = "apache"
			Case NPCtype096
				realType = "096"
			Case NPCtype049
				realType = "049"
			Case NPCtypeZombie
				realType = "zombie"
			Case NPCtype5131
				realType = "513-1"
			Case NPCtypeTentacle
				realType = "tentacle"
			Case NPCtype860
				realType = "860"
			Case NPCtype939
				realType = "939"
			Case NPCtype066
				realType = "066"
			Case NPCtype178
				realType = "178"
			Case NPCtypePdPlane
				realType = "PDPlane"
			Case NPCtype966
				realType = "966"
			Case NPCtype1048a
				realType = "1048-A"
		End Select
		RuntimeError "Called UpdateMTFUnit on "+realType
	EndIf
	;[End Block]
	
	Local x#,y#,z#
	Local r.Rooms
	Local prevDist#,newDist#
	Local n2.NPCs
	
	n\MaxGravity = 0.03
	n\BlinkTimer=n\BlinkTimer-FPSfactor
	If n\BlinkTimer<=-5.0 Then n\BlinkTimer = 70.0*Rnd(10.0,15.0)
	
	If n\IsDead Then
		n\BlinkTimer = -1.0
		SetNPCFrame(n, 532)
		Return
	EndIf
	
	For d.Doors = Each Doors
		If (EntityDistance(d\frameobj,n\Collider)<1.2) Then
			If (Abs(DeltaYaw(n\Collider,d\frameobj))<45.0) Then
				d\open = True
				d\templocked = True
			EndIf
		EndIf
		;should add something to stop mtfs from opening doors into the void
	Next
	
	DebugLog n\State
	
	If n\Idle>0.0 Then
		n\Idle=n\Idle-FPSfactor
		If n\Idle<=0.0 Then n\Idle = 0.0
	Else
		Select Int(n\State) ;what is this MTF doing
			Case 0 ;wandering around
				;[Block]
				n\Speed = 0.015
				If n\PathTimer<=0.0 Then ;update path
					If n\MTFLeader<>Null Then ;i'll follow the leader
						n\PathStatus = FindPath(n,EntityX(n\MTFLeader\Collider,True),EntityY(n\MTFLeader\Collider,True)+0.1,EntityZ(n\MTFLeader\Collider,True)) ;whatever you say boss
					Else ;i am the leader
						For r = Each Rooms
							If ((Abs(r\x-EntityX(n\Collider,True))>12.0) Or (Abs(r\z-EntityZ(n\Collider,True))>12.0)) And (Rand(1,Max(4-Int(Abs(r\z-EntityZ(n\Collider,True)/8.0)),2))=1) Then
								x = r\x
								y = 0.5
								z = r\z
								DebugLog r\roomtemplate\Name
								Exit
							EndIf
						Next
						n\PathStatus = FindPath(n,x,y,z) ;we're going to this room for no particular reason
					EndIf
					If n\PathStatus = 1 Then
						While n\Path[n\PathLocation]=Null
							If n\PathLocation>19 Then Exit
							n\PathLocation=n\PathLocation+1
						Wend
						If n\PathLocation<19 Then
							If (n\Path[n\PathLocation]<>Null) And (n\Path[n\PathLocation+1]<>Null) Then
								If Abs(DeltaYaw(n\Collider,n\Path[n\PathLocation]\obj))>Abs(DeltaYaw(n\Collider,n\Path[n\PathLocation+1]\obj)) Then
									n\PathLocation=n\PathLocation+1
								EndIf
							EndIf
						EndIf
					EndIf
					n\PathTimer = 70.0 * Rnd(6.0,10.0) ;search again after 6-10 seconds
				ElseIf (n\PathTimer<=70.0 * 2.5) And (n\MTFLeader=Null) Then
					n\PathTimer=n\PathTimer-FPSfactor
					n\CurrSpeed = 0.0
					If Rand(1,35)=1 Then
						RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
					EndIf
					n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
					RotateEntity n\obj,-90.0,n\Angle,0.0,True
				Else
					If n\PathStatus=2 Then
						n\PathTimer=n\PathTimer-(FPSfactor*2.0) ;timer goes down fast
						n\CurrSpeed = 0.0
						If Rand(1,35)=1 Then
							RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
						EndIf
						n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
						RotateEntity n\obj,-90.0,n\Angle,0.0,True
					ElseIf n\PathStatus=1 Then
						If n\Path[n\PathLocation]=Null Then
							If n\PathLocation > 19 Then 
								n\PathLocation = 0 : n\PathStatus = 0
							Else
								n\PathLocation = n\PathLocation + 1
							EndIf
						Else
							prevDist# = EntityDistance(n\Collider,n\Path[n\PathLocation]\obj)
							
							PointEntity n\Collider,n\Path[n\PathLocation]\obj
							RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
							n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
							RotateEntity n\obj,-90.0,n\Angle,0.0,True
							
							n\CurrSpeed = CurveValue(n\Speed,n\CurrSpeed,20.0)
							;MoveEntity n\Collider, 0, 0, n\CurrSpeed * FPSfactor
							TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, True
							AnimateNPC(n,488, 522, n\CurrSpeed*26)
							
							newDist# = EntityDistance(n\Collider,n\Path[n\PathLocation]\obj)
								
							If (newDist<0.2) Or ((prevDist<newDist) And (prevDist<1.0)) Then
								n\PathLocation=n\PathLocation+1
							EndIf
						EndIf
						n\PathTimer=n\PathTimer-FPSfactor ;timer goes down slow
					Else
						n\PathTimer=n\PathTimer-(FPSfactor*2.0) ;timer goes down fast
						If n\MTFLeader = Null Then
							If Rand(1,35)=1 Then
								RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
							EndIf
							n\CurrSpeed = 0.0
						ElseIf EntityDistance(n\Collider,n\MTFLeader\Collider)>1.0 Then
							PointEntity n\Collider,n\MTFLeader\Collider
							RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
							
							n\CurrSpeed = CurveValue(n\Speed,n\CurrSpeed,20.0)
							TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, True
							AnimateNPC(n,488, 522, n\CurrSpeed*26)
						Else
							If Rand(1,35)=1 Then
								RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
							EndIf
							n\CurrSpeed = 0.0
						EndIf
						n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
						RotateEntity n\obj,-90.0,n\Angle,0.0,True
					EndIf
				EndIf
				
				If EntityVisible(n\Collider,Camera) And MeNPCSeesPlayer(n) Then
					n\State = 1
					n\EnemyX = EntityX(Collider,True)
					n\EnemyY = EntityY(Collider,True)
					n\EnemyZ = EntityZ(Collider,True)
					n\State2 = 70.0*15.0 ;give up after 15 seconds
					n\PathTimer=0.0
					n\PathStatus=0
				EndIf
				
				If EntityVisible(n\Collider,Curr173\Collider) And OtherNPCSeesMeNPC(Curr173,n) And (Curr173\Idle<2) Then
					n\State = 2
					n\EnemyX = EntityX(Curr173\Collider,True)
					n\EnemyY = EntityY(Curr173\Collider,True)
					n\EnemyZ = EntityZ(Curr173\Collider,True)
					n\State2 = 70.0*15.0 ;give up after 15 seconds
					n\State3 = 0.0
					n\PathTimer=0.0
					n\PathStatus=0
				EndIf
				;[End Block]
			Case 1 ;searching for player
				;[Block]
				n\Speed = 0.015
				n\State2=n\State2-FPSfactor
				If EntityVisible(n\Collider,Camera) And MeNPCSeesPlayer(n) Then
					n\EnemyX = EntityX(Collider,True)
					n\EnemyY = EntityY(Collider,True)
					n\EnemyZ = EntityZ(Collider,True)
					n\State2 = 70.0*15.0 ;give up after 15 seconds
					n\PathTimer=0.0
					n\PathStatus=0
					
					For n2.NPCs = Each NPCs
						If n2<>n Then
							If (n2\NPCtype = NPCtypeMTF) And (n2\State<2) Then
								n2\State = 1
								n2\EnemyX = EntityX(Collider,True)
								n2\EnemyY = EntityY(Collider,True)
								n2\EnemyZ = EntityZ(Collider,True)
								n2\State2=n2\State2-(FPSfactor*4) ;timer goes down really fast
							EndIf
						EndIf
					Next
					
					If Distance(EntityX(n\Collider,True),EntityZ(n\Collider,True),n\EnemyX,n\EnemyZ)<0.2 Then
;						n\CurrSpeed = 0.0
;						n\State2=n\State2-FPSfactor
;						If Rand(1,30)=1 Then
;							x = Rnd(-6.0,6.0)
;							z = Rnd(-6.0,6.0)
;							
;							n\EnemyX = n\EnemyX+x
;							n\EnemyY = EntityY(n\Collider,True)
;							n\EnemyZ = n\EnemyZ+z
;						EndIf
					Else
						PositionEntity n\obj,n\EnemyX,n\EnemyY,n\EnemyZ,True
						PointEntity n\Collider,n\obj
						RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
						n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
						RotateEntity n\obj,-90.0,n\Angle,0.0,True
						
						n\CurrSpeed = CurveValue(n\Speed,n\CurrSpeed,20.0)
						TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, True
						AnimateNPC(n,488, 522, n\CurrSpeed*26)
					EndIf
				Else
					If n\PathTimer<=0.0 Then ;update path
						n\PathStatus = FindPath(n,n\EnemyX,n\EnemyY+0.1,n\EnemyZ)
						n\PathTimer = 70.0 * Rnd(6.0,10.0) ;search again after 6 seconds
					ElseIf n\PathTimer<=70.0 * 2.5 Then
						n\PathTimer=n\PathTimer-FPSfactor
						n\CurrSpeed = 0.0
						If Rand(1,35)=1 Then
							RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
						EndIf
						n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
						RotateEntity n\obj,-90.0,n\Angle,0.0,True
					Else
						If n\PathStatus=2 Then
							n\PathTimer=n\PathTimer-(FPSfactor*2.0) ;timer goes down fast
							n\CurrSpeed = 0.0
							If Rand(1,35)=1 Then
								RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
							EndIf
							n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
							RotateEntity n\obj,-90.0,n\Angle,0.0,True
						ElseIf n\PathStatus=1 Then
							If n\Path[n\PathLocation]=Null Then
								If n\PathLocation > 19 Then 
									n\PathLocation = 0 : n\PathStatus = 0
								Else
									n\PathLocation = n\PathLocation + 1
								EndIf
							Else
								prevDist# = EntityDistance(n\Collider,n\Path[n\PathLocation]\obj)
								
								PointEntity n\Collider,n\Path[n\PathLocation]\obj
								RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
								n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
								RotateEntity n\obj,-90.0,n\Angle,0.0,True
								
								n\CurrSpeed = CurveValue(n\Speed,n\CurrSpeed,20.0)
								
								TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, True
								AnimateNPC(n,488, 522, n\CurrSpeed*26)
								
								newDist# = EntityDistance(n\Collider,n\Path[n\PathLocation]\obj)
								
								If (newDist<0.2) Or ((prevDist<newDist) And (prevDist<1.0)) Then
									n\PathLocation=n\PathLocation+1
								EndIf
							EndIf
							n\PathTimer=n\PathTimer-FPSfactor ;timer goes down slow
						Else
							PositionEntity n\obj,n\EnemyX,n\EnemyY,n\EnemyZ,True
							If (Distance(EntityX(n\Collider,True),EntityZ(n\Collider,True),n\EnemyX,n\EnemyZ)<0.2) Or (Not EntityVisible(n\obj,n\Collider)) Then
								If Rand(1,35)=1 Then
									RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
								EndIf
								If Rand(1,35)=1 Then
									For wp.Waypoints = Each Waypoints
										If (EntityDistance(wp\obj,n\Collider)<6.0) And (Rand(1,3)=1) Then
											n\EnemyX = EntityX(wp\obj,True)
											n\EnemyY = EntityY(wp\obj,True)
											n\EnemyZ = EntityZ(wp\obj,True)
											n\PathTimer = 0.0
											Exit
										EndIf
									Next
								EndIf
								n\PathTimer=n\PathTimer-FPSfactor ;timer goes down slow
							Else
								PointEntity n\Collider,n\obj
								RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
								n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
								RotateEntity n\obj,-90.0,n\Angle,0.0,True
								
								n\CurrSpeed = CurveValue(n\Speed,n\CurrSpeed,20.0)
								TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, True
								AnimateNPC(n,488, 522, n\CurrSpeed*26)
							EndIf
						EndIf
					EndIf
				EndIf
				
				If n\State2<=0.0 Then
					n\State = 0
				EndIf
				
				If EntityVisible(n\Collider,Curr173\Collider) And OtherNPCSeesMeNPC(Curr173,n) And (Curr173\Idle<2) Then
					n\State = 2
					n\EnemyX = EntityX(Curr173\Collider,True)
					n\EnemyY = EntityY(Curr173\Collider,True)
					n\EnemyZ = EntityZ(Curr173\Collider,True)
					n\State2 = 70.0*15.0 ;give up after 15 seconds
					n\State3 = 0.0
					n\PathTimer=0.0
					n\PathStatus=0
				EndIf
				
				;DebugLog Distance(EntityX(n\Collider,True),EntityZ(n\Collider,True),n\EnemyX,n\EnemyZ)
				
				;[End Block]
			Case 2 ;searching for/looking at 173
				;[Block]
				If Curr173\Idle = 2 Then
					n\State = 0
				Else
					For n2.NPCs = Each NPCs
						If n2<>n Then
							If n2\NPCtype = NPCtypeMTF Then
								n2\State = 2
							EndIf
						EndIf
					Next
					If EntityVisible(n\Collider,Curr173\Collider) Then
						n\State2 = 70.0*15.0
						n\PathTimer = 0.0
						dist# = 1.0
						If n\MTFLeader<>Null Then dist = 2.0
						If Distance(EntityX(n\Collider,True),EntityZ(n\Collider,True),EntityX(Curr173\Collider,True),EntityZ(Curr173\Collider,True))<dist Then
							If n\MTFLeader = Null Then
								n\State3=n\State3+FPSfactor
								DebugLog "CONTAINING 173: "+n\State3
								If n\State3>=70.0*10.0 Then
									Curr173\Idle = 2
								EndIf
							EndIf
						Else
							PositionEntity n\obj,EntityX(Curr173\Collider,True),EntityY(Curr173\Collider,True),EntityZ(Curr173\Collider,True),True
							PointEntity n\Collider,n\obj
							RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
							n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
							RotateEntity n\obj,-90.0,n\Angle,0.0,True
							
							n\CurrSpeed = CurveValue(n\Speed,n\CurrSpeed,20.0)
							TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, True
							AnimateNPC(n,488, 522, n\CurrSpeed*26)
						EndIf
					Else
						If n\PathTimer<=0.0 Then ;update path
							n\PathStatus = FindPath(n,EntityX(Curr173\Collider,True),EntityY(Curr173\Collider,True)+0.1,EntityZ(Curr173\Collider,True))
							n\PathTimer = 70.0 * Rnd(6.0,10.0) ;search again after 6 seconds
						ElseIf n\PathTimer<=70.0 * 2.5 Then
							n\PathTimer=n\PathTimer-FPSfactor
							n\CurrSpeed = 0.0
							If Rand(1,35)=1 Then
								RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
							EndIf
							n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
							RotateEntity n\obj,-90.0,n\Angle,0.0,True
						Else
							If n\PathStatus=2 Then
								n\PathTimer=n\PathTimer-(FPSfactor*2.0) ;timer goes down fast
								n\CurrSpeed = 0.0
								If Rand(1,35)=1 Then
									RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
								EndIf
								n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
								RotateEntity n\obj,-90.0,n\Angle,0.0,True
							ElseIf n\PathStatus=1 Then
								If n\Path[n\PathLocation]=Null Then
									If n\PathLocation > 19 Then 
										n\PathLocation = 0 : n\PathStatus = 0
									Else
										n\PathLocation = n\PathLocation + 1
									EndIf
								Else
									prevDist# = EntityDistance(n\Collider,n\Path[n\PathLocation]\obj)
									
									PointEntity n\Collider,n\Path[n\PathLocation]\obj
									RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
									n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
									RotateEntity n\obj,-90.0,n\Angle,0.0,True
									
									n\CurrSpeed = CurveValue(n\Speed,n\CurrSpeed,20.0)
									
									TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, True
									AnimateNPC(n,488, 522, n\CurrSpeed*26)
									
									newDist# = EntityDistance(n\Collider,n\Path[n\PathLocation]\obj)
									
									If (newDist<0.2) Or ((prevDist<newDist) And (prevDist<1.0)) Then
										n\PathLocation=n\PathLocation+1
									EndIf
								EndIf
								n\PathTimer=n\PathTimer-FPSfactor ;timer goes down slow
							Else
								n\PathTimer=n\PathTimer-(FPSfactor*2.0) ;timer goes down fast
								n\CurrSpeed = 0.0
								If Rand(1,35)=1 Then
									RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
								EndIf
								n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
								RotateEntity n\obj,-90.0,n\Angle,0.0,True
							EndIf
						EndIf
					EndIf
				EndIf
				;[End Block]
		End Select
		
		If n\MTFLeader<>Null Then
			If EntityDistance(n\Collider,n\MTFLeader\Collider)<0.7 Then
				PointEntity n\Collider,n\MTFLeader\Collider
				RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
				n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
				RotateEntity n\obj,-90.0,n\Angle,0.0,True
				
				TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)-45)* 0.01 * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)-45)* 0.01 * FPSfactor, True
			EndIf
		Else
			For n2.NPCs = Each NPCs
				If (EntityDistance(n\Collider,n2\Collider)<0.7) And (Abs(DeltaYaw(n\Collider,n2\Collider))<80.0) Then
					TranslateEntity n2\Collider, Cos(EntityYaw(n\Collider,True)+90)* 0.01 * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90)* 0.01 * FPSfactor, True
				EndIf
			Next
		EndIf
		
		PositionEntity n\obj,EntityX(n\Collider,True),EntityY(n\Collider,True)-0.1,EntityZ(n\Collider,True),True
	EndIf
End Function
And then replace NPCtypeMTF's case in UpdateNPCs with a call to UpdateMTFUnit. Also add some code to 173 so it can target, kill, and get stopped by MTFs, using OtherNPCSeesMeNPC.

These MTFs are much better at containing 173 than the ones currently ingame (they ignore the player properly now). One MTF unit is a leader, while the others will follow the leader around. They have actual blink timers. They have to face the player to detect them, same with 173. They are usually trying to get to the LCZ as soon as possible. The leader can push other MTFs out of their way, to prevent them from getting stuck in any weird places (this isn't fully functional yet).

This code still needs support for voice lines (the MTFs are mute) and shooting the player (they just follow you). They also need to be able to detect, run away from, and get killed by 106.

Re: WIP New MTF AI

#2
juanjpro wrote:Replace the FindPath function in MapSystem.bb with this, as it currently has a bug that generates an invalid path when more than 20 waypoints need to be traversed to reach a destination:
Spoiler

Code: Select all

Function FindPath(n.NPCs, x#, y#, z#)
	
	DebugLog "findpath: "+n\NPCtype
	
	Local temp%, dist#, dist2#
	Local xtemp#, ytemp#, ztemp#
	
	Local w.WayPoints, StartPoint.WayPoints, EndPoint.WayPoints	
	
	Local StartX% = Floor(EntityX(n\Collider,True) / 8.0 + 0.5), StartZ% = Floor(EntityZ(n\Collider,True) / 8.0 + 0.5)
	;If StartX < 0 Or StartX > MapWidth Then Return 2
	;If StartZ < 0 Or StartZ > MapWidth Then Return 2
	
	Local EndX% = Floor(x / 8.0 + 0.5), EndZ% = Floor(z / 8.0 + 0.5)
	;If EndX < 0 Or EndX > MapWidth Then Return 2
	;If EndZ < 0 Or EndZ > MapWidth Then Return 2
	
	Local CurrX, CurrZ
	
	;pathstatus = 0, ei ole etsitty reittiä
	;pathstatus = 1, reitti löydetty
	;pathstatus = 2, reittiä ei ole olemassa	
	
	For w.WayPoints = Each WayPoints 
		w\state = 0
		w\Fcost = 0
		w\Gcost = 0
		w\Hcost = 0
	Next
	
	n\PathStatus = 0
	n\PathLocation = 0
	For i = 0 To 19
		n\Path[i] = Null
	Next
	
	Local pvt = CreatePivot()
	PositionEntity(pvt, x,y,z, True)	
	
	temp = CreatePivot()
	PositionEntity(temp, EntityX(n\Collider,True), EntityY(n\Collider,True)+0.15, EntityZ(n\Collider,True))
	
	;käytetään aloituspisteenä waypointia, joka on lähimpänä loppupistettä ja joka on näkyvissä
	dist = 400.0
	For w.WayPoints = Each WayPoints
		xtemp = EntityX(w\obj,True)-EntityX(temp,True)
		;If xtemp < 8.0 Then
		ztemp = EntityZ(w\obj,True)-EntityZ(temp,True)
			;If ztemp < 8.0 Then 
		ytemp = EntityY(w\obj,True)-EntityY(temp,True)
				;If ytemp < 8.0 Then 
		dist2# = (xtemp*xtemp)+(ytemp*ytemp)+(ztemp*ztemp)
		If dist2 < dist Then ; And EntityVisible(w\obj, temp)
			dist = dist2
			StartPoint = w
		EndIf
				;EndIf
			;EndIf
		;EndIf
	Next
	DebugLog "DIST: "+dist
	
	FreeEntity temp
	
	If StartPoint = Null Then Return 2
	StartPoint\state = 1		
	
	;If EndPoint = Null Then 
	EndPoint = Null
	dist# = 400.0
	For w.WayPoints = Each WayPoints
		xtemp = EntityX(pvt,True)-EntityX(w\obj,True)
		;If xtemp =< 8.0 Then
		ztemp = EntityZ(pvt,True)-EntityZ(w\obj,True)
			;If ztemp =< 8 Then
		ytemp = EntityY(pvt,True)-EntityY(w\obj,True)
		dist2# = (xtemp*xtemp)+(ytemp*ytemp)+(ztemp*ztemp)
		
		If dist2 < dist Then ; And EntityVisible(w\obj, pvt)
			dist = dist2
			EndPoint = w
		EndIf				
			;EndIf
		;EndIf
	Next
	;EndIf
	
	FreeEntity pvt
	
	If EndPoint = StartPoint Then 
		If dist < 0.4 Then
			Return 0
		Else
			n\Path[0]=EndPoint
			Return 1					
		EndIf
	EndIf
	If EndPoint = Null Then Return 2
	
	;aloitus- ja lopetuspisteet löydetty, aletaan etsiä reittiä
	
	Repeat 
		
		temp% = False
		smallest.WayPoints = Null
		dist# = 10000.0
		For w.WayPoints = Each WayPoints
			If w\state = 1 Then
				temp = True
				If (w\Fcost) < dist Then 
					dist = w\Fcost
					smallest = w
				EndIf
			EndIf
		Next
		
		If smallest <> Null Then
			
			w = smallest
			w\state = 2
			
			For i = 0 To 4
				If w\connected[i]<>Null Then 
					If w\connected[i]\state < 2 Then 
						
						If w\connected[i]\state=1 Then ;open list
							gtemp# = w\Gcost+w\dist[i]
							If n\NPCtype = NPCtypeMTF Then 
								If w\connected[i]\door = Null Then gtemp = gtemp + 0.5
							EndIf
							If gtemp < w\connected[i]\Gcost Then ;parempi reitti -> overwrite
								w\connected[i]\Gcost = gtemp
								w\connected[i]\Fcost = w\connected[i]\Gcost + w\connected[i]\Hcost
								w\connected[i]\parent = w
							EndIf
						Else
							w\connected[i]\Hcost# = Abs(EntityX(w\connected[i]\obj,True)-EntityX(EndPoint\obj,True))+Abs(EntityZ(w\connected[i]\obj,True)-EntityZ(EndPoint\obj,True))
							gtemp# = w\Gcost+w\dist[i]
							If n\NPCtype = NPCtypeMTF Then 
								If w\connected[i]\door = Null Then gtemp = gtemp + 0.5
							EndIf
							w\connected[i]\Gcost = gtemp
							w\connected[i]\Fcost = w\Gcost+w\Hcost 
							w\connected[i]\parent = w
							w\connected[i]\state=1
						EndIf				
					EndIf
					
				EndIf
			Next
		Else ;open listiltä ei löytynyt mitään
			If EndPoint\state > 0 Then 
				StartPoint\parent = Null
				EndPoint\state = 2
				Exit
			EndIf
		EndIf
		
		If EndPoint\state > 0 Then 
			StartPoint\parent = Null
			EndPoint\state = 2
			Exit
		EndIf
		
	Until temp = False
	
	If EndPoint\state > 0 Then
		
		currpoint.waypoints = EndPoint
		twentiethpoint.waypoints = EndPoint
		
		length = 0
		Repeat
			length = length +1
			currpoint = currpoint\parent
			If length>20 Then
				twentiethpoint = twentiethpoint\parent
			EndIf
		Until currpoint = Null
		
		currpoint.waypoints = EndPoint
		While twentiethpoint<>Null
			length=Min(length-1,19)
			;DebugLog "LENGTH "+length
			twentiethpoint = twentiethpoint\parent
			n\Path[length] = twentiethpoint
		Wend
		
		Return 1
		;RuntimeError length
;		For i = 0 To (length-1)
;			temp =False
;			If length < 20 Then
;				n\Path[length-1-i] = currpoint.WayPoints
;			Else
;				If i < 20 Then
;					n\Path[20-1-i] = w.WayPoints
;				Else
;					;Return 1
;				EndIf
;			EndIf
;			
;			If currpoint = StartPoint Then Return 1
;			
;			If currpoint\parent <> Null Then
;				currpoint = currpoint\parent
;			Else
;				Exit
;			EndIf
;			
;		Next
		
	Else
		
		DebugLog "FUNCTION FindPath() - reittiä ei löytynyt"
		Return 2 ;reittiä määränpäähän ei löytynyt
		
	EndIf
	
End Function
Replace the NPCs type in NPCs.bb with this:

Code: Select all

Type NPCs
	Field obj%, obj2%, obj3%, obj4%, Collider%
	Field NPCtype%, ID%
	Field DropSpeed#, Gravity%
	Field State#, State2#, State3#, PrevState%
	Field MakingNoise%
	
	Field Frame#
	
	Field Angle#
	Field Sound%, SoundChn%, SoundTimer#
	Field Sound2%, SoundChn2%
	
	Field Speed#, CurrSpeed#
	
	Field texture$
	
	Field Idle#
	
	Field Reload#
	
	Field LastSeen%, LastDist#
	
	Field PrevX#, PrevY#, PrevZ#
	
	Field Target.NPCs, TargetID%
	Field EnemyX#, EnemyY#, EnemyZ#
	
	Field Path.WayPoints[20], PathStatus%, PathTimer#, PathLocation%
	
	Field NVX#,NVY#,NVZ#,NVName$
	
	Field GravityMult# = 1.0
	Field MaxGravity# = 0.2
	
	Field MTFVariant%
	Field MTFLeader.NPCs
	Field IsDead%
	Field BlinkTimer#
	Field IgnorePlayer%
End Type
Add this code somewhere in NPCs.bb:

Code: Select all

Function OtherNPCSeesMeNPC%(me.NPCs,other.NPCs)
	If other\BlinkTimer<=0.0 Then Return False
	If EntityDistance(other\Collider,me\Collider)<4.0 Then
		If Abs(DeltaYaw(other\Collider,me\Collider))<40.0 Then
			Return True
		EndIf
	EndIf
	Return False
End Function

Function MeNPCSeesPlayer%(me.NPCs)
	If me\BlinkTimer<=0.0 Then Return False
	If EntityDistance(Collider,me\Collider)<4.0 Then
		If Abs(DeltaYaw(me\Collider,Collider))<40.0 Then
			Return True
		EndIf
	EndIf
	Return False
End Function

Function UpdateMTFUnit(n.NPCs)
	;[Block]
	If n\NPCtype<>NPCtypeMTF Then
		Local realType$ = ""
		Select n\NPCtype
			Case NPCtype173
				realType = "173"
			Case NPCtypeOldMan
				realType = "106"
			Case NPCtypeGuard
				realType = "guard"
			Case NPCtypeD
				realType = "d"
			Case NPCtype372
				realType = "372"
			Case NPCtypeApache
				realType = "apache"
			Case NPCtype096
				realType = "096"
			Case NPCtype049
				realType = "049"
			Case NPCtypeZombie
				realType = "zombie"
			Case NPCtype5131
				realType = "513-1"
			Case NPCtypeTentacle
				realType = "tentacle"
			Case NPCtype860
				realType = "860"
			Case NPCtype939
				realType = "939"
			Case NPCtype066
				realType = "066"
			Case NPCtype178
				realType = "178"
			Case NPCtypePdPlane
				realType = "PDPlane"
			Case NPCtype966
				realType = "966"
			Case NPCtype1048a
				realType = "1048-A"
		End Select
		RuntimeError "Called UpdateMTFUnit on "+realType
	EndIf
	;[End Block]
	
	Local x#,y#,z#
	Local r.Rooms
	Local prevDist#,newDist#
	Local n2.NPCs
	
	n\MaxGravity = 0.03
	n\BlinkTimer=n\BlinkTimer-FPSfactor
	If n\BlinkTimer<=-5.0 Then n\BlinkTimer = 70.0*Rnd(10.0,15.0)
	
	If n\IsDead Then
		n\BlinkTimer = -1.0
		SetNPCFrame(n, 532)
		Return
	EndIf
	
	For d.Doors = Each Doors
		If (EntityDistance(d\frameobj,n\Collider)<1.2) Then
			If (Abs(DeltaYaw(n\Collider,d\frameobj))<45.0) Then
				d\open = True
				d\templocked = True
			EndIf
		EndIf
		;should add something to stop mtfs from opening doors into the void
	Next
	
	DebugLog n\State
	
	If n\Idle>0.0 Then
		n\Idle=n\Idle-FPSfactor
		If n\Idle<=0.0 Then n\Idle = 0.0
	Else
		Select Int(n\State) ;what is this MTF doing
			Case 0 ;wandering around
				;[Block]
				n\Speed = 0.015
				If n\PathTimer<=0.0 Then ;update path
					If n\MTFLeader<>Null Then ;i'll follow the leader
						n\PathStatus = FindPath(n,EntityX(n\MTFLeader\Collider,True),EntityY(n\MTFLeader\Collider,True)+0.1,EntityZ(n\MTFLeader\Collider,True)) ;whatever you say boss
					Else ;i am the leader
						For r = Each Rooms
							If ((Abs(r\x-EntityX(n\Collider,True))>12.0) Or (Abs(r\z-EntityZ(n\Collider,True))>12.0)) And (Rand(1,Max(4-Int(Abs(r\z-EntityZ(n\Collider,True)/8.0)),2))=1) Then
								x = r\x
								y = 0.5
								z = r\z
								DebugLog r\roomtemplate\Name
								Exit
							EndIf
						Next
						n\PathStatus = FindPath(n,x,y,z) ;we're going to this room for no particular reason
					EndIf
					If n\PathStatus = 1 Then
						While n\Path[n\PathLocation]=Null
							If n\PathLocation>19 Then Exit
							n\PathLocation=n\PathLocation+1
						Wend
						If n\PathLocation<19 Then
							If (n\Path[n\PathLocation]<>Null) And (n\Path[n\PathLocation+1]<>Null) Then
								If Abs(DeltaYaw(n\Collider,n\Path[n\PathLocation]\obj))>Abs(DeltaYaw(n\Collider,n\Path[n\PathLocation+1]\obj)) Then
									n\PathLocation=n\PathLocation+1
								EndIf
							EndIf
						EndIf
					EndIf
					n\PathTimer = 70.0 * Rnd(6.0,10.0) ;search again after 6-10 seconds
				ElseIf (n\PathTimer<=70.0 * 2.5) And (n\MTFLeader=Null) Then
					n\PathTimer=n\PathTimer-FPSfactor
					n\CurrSpeed = 0.0
					If Rand(1,35)=1 Then
						RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
					EndIf
					n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
					RotateEntity n\obj,-90.0,n\Angle,0.0,True
				Else
					If n\PathStatus=2 Then
						n\PathTimer=n\PathTimer-(FPSfactor*2.0) ;timer goes down fast
						n\CurrSpeed = 0.0
						If Rand(1,35)=1 Then
							RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
						EndIf
						n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
						RotateEntity n\obj,-90.0,n\Angle,0.0,True
					ElseIf n\PathStatus=1 Then
						If n\Path[n\PathLocation]=Null Then
							If n\PathLocation > 19 Then 
								n\PathLocation = 0 : n\PathStatus = 0
							Else
								n\PathLocation = n\PathLocation + 1
							EndIf
						Else
							prevDist# = EntityDistance(n\Collider,n\Path[n\PathLocation]\obj)
							
							PointEntity n\Collider,n\Path[n\PathLocation]\obj
							RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
							n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
							RotateEntity n\obj,-90.0,n\Angle,0.0,True
							
							n\CurrSpeed = CurveValue(n\Speed,n\CurrSpeed,20.0)
							;MoveEntity n\Collider, 0, 0, n\CurrSpeed * FPSfactor
							TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, True
							AnimateNPC(n,488, 522, n\CurrSpeed*26)
							
							newDist# = EntityDistance(n\Collider,n\Path[n\PathLocation]\obj)
								
							If (newDist<0.2) Or ((prevDist<newDist) And (prevDist<1.0)) Then
								n\PathLocation=n\PathLocation+1
							EndIf
						EndIf
						n\PathTimer=n\PathTimer-FPSfactor ;timer goes down slow
					Else
						n\PathTimer=n\PathTimer-(FPSfactor*2.0) ;timer goes down fast
						If n\MTFLeader = Null Then
							If Rand(1,35)=1 Then
								RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
							EndIf
							n\CurrSpeed = 0.0
						ElseIf EntityDistance(n\Collider,n\MTFLeader\Collider)>1.0 Then
							PointEntity n\Collider,n\MTFLeader\Collider
							RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
							
							n\CurrSpeed = CurveValue(n\Speed,n\CurrSpeed,20.0)
							TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, True
							AnimateNPC(n,488, 522, n\CurrSpeed*26)
						Else
							If Rand(1,35)=1 Then
								RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
							EndIf
							n\CurrSpeed = 0.0
						EndIf
						n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
						RotateEntity n\obj,-90.0,n\Angle,0.0,True
					EndIf
				EndIf
				
				If EntityVisible(n\Collider,Camera) And MeNPCSeesPlayer(n) Then
					n\State = 1
					n\EnemyX = EntityX(Collider,True)
					n\EnemyY = EntityY(Collider,True)
					n\EnemyZ = EntityZ(Collider,True)
					n\State2 = 70.0*15.0 ;give up after 15 seconds
					n\PathTimer=0.0
					n\PathStatus=0
				EndIf
				
				If EntityVisible(n\Collider,Curr173\Collider) And OtherNPCSeesMeNPC(Curr173,n) And (Curr173\Idle<2) Then
					n\State = 2
					n\EnemyX = EntityX(Curr173\Collider,True)
					n\EnemyY = EntityY(Curr173\Collider,True)
					n\EnemyZ = EntityZ(Curr173\Collider,True)
					n\State2 = 70.0*15.0 ;give up after 15 seconds
					n\State3 = 0.0
					n\PathTimer=0.0
					n\PathStatus=0
				EndIf
				;[End Block]
			Case 1 ;searching for player
				;[Block]
				n\Speed = 0.015
				n\State2=n\State2-FPSfactor
				If EntityVisible(n\Collider,Camera) And MeNPCSeesPlayer(n) Then
					n\EnemyX = EntityX(Collider,True)
					n\EnemyY = EntityY(Collider,True)
					n\EnemyZ = EntityZ(Collider,True)
					n\State2 = 70.0*15.0 ;give up after 15 seconds
					n\PathTimer=0.0
					n\PathStatus=0
					
					For n2.NPCs = Each NPCs
						If n2<>n Then
							If (n2\NPCtype = NPCtypeMTF) And (n2\State<2) Then
								n2\State = 1
								n2\EnemyX = EntityX(Collider,True)
								n2\EnemyY = EntityY(Collider,True)
								n2\EnemyZ = EntityZ(Collider,True)
								n2\State2=n2\State2-(FPSfactor*4) ;timer goes down really fast
							EndIf
						EndIf
					Next
					
					If Distance(EntityX(n\Collider,True),EntityZ(n\Collider,True),n\EnemyX,n\EnemyZ)<0.2 Then
;						n\CurrSpeed = 0.0
;						n\State2=n\State2-FPSfactor
;						If Rand(1,30)=1 Then
;							x = Rnd(-6.0,6.0)
;							z = Rnd(-6.0,6.0)
;							
;							n\EnemyX = n\EnemyX+x
;							n\EnemyY = EntityY(n\Collider,True)
;							n\EnemyZ = n\EnemyZ+z
;						EndIf
					Else
						PositionEntity n\obj,n\EnemyX,n\EnemyY,n\EnemyZ,True
						PointEntity n\Collider,n\obj
						RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
						n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
						RotateEntity n\obj,-90.0,n\Angle,0.0,True
						
						n\CurrSpeed = CurveValue(n\Speed,n\CurrSpeed,20.0)
						TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, True
						AnimateNPC(n,488, 522, n\CurrSpeed*26)
					EndIf
				Else
					If n\PathTimer<=0.0 Then ;update path
						n\PathStatus = FindPath(n,n\EnemyX,n\EnemyY+0.1,n\EnemyZ)
						n\PathTimer = 70.0 * Rnd(6.0,10.0) ;search again after 6 seconds
					ElseIf n\PathTimer<=70.0 * 2.5 Then
						n\PathTimer=n\PathTimer-FPSfactor
						n\CurrSpeed = 0.0
						If Rand(1,35)=1 Then
							RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
						EndIf
						n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
						RotateEntity n\obj,-90.0,n\Angle,0.0,True
					Else
						If n\PathStatus=2 Then
							n\PathTimer=n\PathTimer-(FPSfactor*2.0) ;timer goes down fast
							n\CurrSpeed = 0.0
							If Rand(1,35)=1 Then
								RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
							EndIf
							n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
							RotateEntity n\obj,-90.0,n\Angle,0.0,True
						ElseIf n\PathStatus=1 Then
							If n\Path[n\PathLocation]=Null Then
								If n\PathLocation > 19 Then 
									n\PathLocation = 0 : n\PathStatus = 0
								Else
									n\PathLocation = n\PathLocation + 1
								EndIf
							Else
								prevDist# = EntityDistance(n\Collider,n\Path[n\PathLocation]\obj)
								
								PointEntity n\Collider,n\Path[n\PathLocation]\obj
								RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
								n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
								RotateEntity n\obj,-90.0,n\Angle,0.0,True
								
								n\CurrSpeed = CurveValue(n\Speed,n\CurrSpeed,20.0)
								
								TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, True
								AnimateNPC(n,488, 522, n\CurrSpeed*26)
								
								newDist# = EntityDistance(n\Collider,n\Path[n\PathLocation]\obj)
								
								If (newDist<0.2) Or ((prevDist<newDist) And (prevDist<1.0)) Then
									n\PathLocation=n\PathLocation+1
								EndIf
							EndIf
							n\PathTimer=n\PathTimer-FPSfactor ;timer goes down slow
						Else
							PositionEntity n\obj,n\EnemyX,n\EnemyY,n\EnemyZ,True
							If (Distance(EntityX(n\Collider,True),EntityZ(n\Collider,True),n\EnemyX,n\EnemyZ)<0.2) Or (Not EntityVisible(n\obj,n\Collider)) Then
								If Rand(1,35)=1 Then
									RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
								EndIf
								If Rand(1,35)=1 Then
									For wp.Waypoints = Each Waypoints
										If (EntityDistance(wp\obj,n\Collider)<6.0) And (Rand(1,3)=1) Then
											n\EnemyX = EntityX(wp\obj,True)
											n\EnemyY = EntityY(wp\obj,True)
											n\EnemyZ = EntityZ(wp\obj,True)
											n\PathTimer = 0.0
											Exit
										EndIf
									Next
								EndIf
								n\PathTimer=n\PathTimer-FPSfactor ;timer goes down slow
							Else
								PointEntity n\Collider,n\obj
								RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
								n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
								RotateEntity n\obj,-90.0,n\Angle,0.0,True
								
								n\CurrSpeed = CurveValue(n\Speed,n\CurrSpeed,20.0)
								TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, True
								AnimateNPC(n,488, 522, n\CurrSpeed*26)
							EndIf
						EndIf
					EndIf
				EndIf
				
				If n\State2<=0.0 Then
					n\State = 0
				EndIf
				
				If EntityVisible(n\Collider,Curr173\Collider) And OtherNPCSeesMeNPC(Curr173,n) And (Curr173\Idle<2) Then
					n\State = 2
					n\EnemyX = EntityX(Curr173\Collider,True)
					n\EnemyY = EntityY(Curr173\Collider,True)
					n\EnemyZ = EntityZ(Curr173\Collider,True)
					n\State2 = 70.0*15.0 ;give up after 15 seconds
					n\State3 = 0.0
					n\PathTimer=0.0
					n\PathStatus=0
				EndIf
				
				;DebugLog Distance(EntityX(n\Collider,True),EntityZ(n\Collider,True),n\EnemyX,n\EnemyZ)
				
				;[End Block]
			Case 2 ;searching for/looking at 173
				;[Block]
				If Curr173\Idle = 2 Then
					n\State = 0
				Else
					For n2.NPCs = Each NPCs
						If n2<>n Then
							If n2\NPCtype = NPCtypeMTF Then
								n2\State = 2
							EndIf
						EndIf
					Next
					If EntityVisible(n\Collider,Curr173\Collider) Then
						n\State2 = 70.0*15.0
						n\PathTimer = 0.0
						dist# = 1.0
						If n\MTFLeader<>Null Then dist = 2.0
						If Distance(EntityX(n\Collider,True),EntityZ(n\Collider,True),EntityX(Curr173\Collider,True),EntityZ(Curr173\Collider,True))<dist Then
							If n\MTFLeader = Null Then
								n\State3=n\State3+FPSfactor
								DebugLog "CONTAINING 173: "+n\State3
								If n\State3>=70.0*10.0 Then
									Curr173\Idle = 2
								EndIf
							EndIf
						Else
							PositionEntity n\obj,EntityX(Curr173\Collider,True),EntityY(Curr173\Collider,True),EntityZ(Curr173\Collider,True),True
							PointEntity n\Collider,n\obj
							RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
							n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
							RotateEntity n\obj,-90.0,n\Angle,0.0,True
							
							n\CurrSpeed = CurveValue(n\Speed,n\CurrSpeed,20.0)
							TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, True
							AnimateNPC(n,488, 522, n\CurrSpeed*26)
						EndIf
					Else
						If n\PathTimer<=0.0 Then ;update path
							n\PathStatus = FindPath(n,EntityX(Curr173\Collider,True),EntityY(Curr173\Collider,True)+0.1,EntityZ(Curr173\Collider,True))
							n\PathTimer = 70.0 * Rnd(6.0,10.0) ;search again after 6 seconds
						ElseIf n\PathTimer<=70.0 * 2.5 Then
							n\PathTimer=n\PathTimer-FPSfactor
							n\CurrSpeed = 0.0
							If Rand(1,35)=1 Then
								RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
							EndIf
							n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
							RotateEntity n\obj,-90.0,n\Angle,0.0,True
						Else
							If n\PathStatus=2 Then
								n\PathTimer=n\PathTimer-(FPSfactor*2.0) ;timer goes down fast
								n\CurrSpeed = 0.0
								If Rand(1,35)=1 Then
									RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
								EndIf
								n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
								RotateEntity n\obj,-90.0,n\Angle,0.0,True
							ElseIf n\PathStatus=1 Then
								If n\Path[n\PathLocation]=Null Then
									If n\PathLocation > 19 Then 
										n\PathLocation = 0 : n\PathStatus = 0
									Else
										n\PathLocation = n\PathLocation + 1
									EndIf
								Else
									prevDist# = EntityDistance(n\Collider,n\Path[n\PathLocation]\obj)
									
									PointEntity n\Collider,n\Path[n\PathLocation]\obj
									RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
									n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
									RotateEntity n\obj,-90.0,n\Angle,0.0,True
									
									n\CurrSpeed = CurveValue(n\Speed,n\CurrSpeed,20.0)
									
									TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90.0)*n\CurrSpeed * FPSfactor, True
									AnimateNPC(n,488, 522, n\CurrSpeed*26)
									
									newDist# = EntityDistance(n\Collider,n\Path[n\PathLocation]\obj)
									
									If (newDist<0.2) Or ((prevDist<newDist) And (prevDist<1.0)) Then
										n\PathLocation=n\PathLocation+1
									EndIf
								EndIf
								n\PathTimer=n\PathTimer-FPSfactor ;timer goes down slow
							Else
								n\PathTimer=n\PathTimer-(FPSfactor*2.0) ;timer goes down fast
								n\CurrSpeed = 0.0
								If Rand(1,35)=1 Then
									RotateEntity n\Collider,0.0,Rnd(360.0),0.0,True
								EndIf
								n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
								RotateEntity n\obj,-90.0,n\Angle,0.0,True
							EndIf
						EndIf
					EndIf
				EndIf
				;[End Block]
		End Select
		
		If n\MTFLeader<>Null Then
			If EntityDistance(n\Collider,n\MTFLeader\Collider)<0.7 Then
				PointEntity n\Collider,n\MTFLeader\Collider
				RotateEntity n\Collider,0.0,EntityYaw(n\Collider,True),0.0,True
				n\Angle = CurveAngle(EntityYaw(n\Collider,True),n\Angle,20.0)
				RotateEntity n\obj,-90.0,n\Angle,0.0,True
				
				TranslateEntity n\Collider, Cos(EntityYaw(n\Collider,True)-45)* 0.01 * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)-45)* 0.01 * FPSfactor, True
			EndIf
		Else
			For n2.NPCs = Each NPCs
				If (EntityDistance(n\Collider,n2\Collider)<0.7) And (Abs(DeltaYaw(n\Collider,n2\Collider))<80.0) Then
					TranslateEntity n2\Collider, Cos(EntityYaw(n\Collider,True)+90)* 0.01 * FPSfactor, 0, Sin(EntityYaw(n\Collider,True)+90)* 0.01 * FPSfactor, True
				EndIf
			Next
		EndIf
		
		PositionEntity n\obj,EntityX(n\Collider,True),EntityY(n\Collider,True)-0.1,EntityZ(n\Collider,True),True
	EndIf
End Function
And then replace NPCtypeMTF's case in UpdateNPCs with a call to UpdateMTFUnit. Also add some code to 173 so it can target, kill, and get stopped by MTFs, using OtherNPCSeesMeNPC.

These MTFs are much better at containing 173 than the ones currently ingame (they ignore the player properly now). One MTF unit is a leader, while the others will follow the leader around. They have actual blink timers. They have to face the player to detect them, same with 173. They are usually trying to get to the LCZ as soon as possible. The leader can push other MTFs out of their way, to prevent them from getting stuck in any weird places (this isn't fully functional yet).

This code still needs support for voice lines (the MTFs are mute) and shooting the player (they just follow you). They also need to be able to detect, run away from, and get killed by 106.
Juan is our new Jesus, I really hope this gets added in the game.

Mod Edit: Snip or spoiler massive quoted texts such as the one above.
Don't worry about your son, he will be sent to the deep dark depths in no time.

Re: WIP New MTF AI

#3
Man Juan, this is a lot of dedication. I love it. I was starting to miss these robotic bastards. I never saw them in the facility because they usually just got stuck in a wall or something. Most of the time they just completely ignore the concrete statue right next to them and shoot me. In fact, last time I saw them they were camping in 106's chamber. They shot me... a lot. Almost died! I'm glad the MTFs are finally getting the love they deserve. Hopefully they'll truly be a force to be reckoned with and not a wall magnet.
Heheh, you said a bad word.

All of you guys really are special. Did you guys know that? Yes you guys are! Each in your own special way.

Re: WIP New MTF AI

#4
Looking forward for the finished code (if it will happen). You can never have a smart enough MTF.

EDIT: Replaced and inserted the codes. It looks like MTFs no longer can open doors and even try to go through walls.

http://pasteboard.co/ueMX0Ko.png

EDIT 2: They see 173. They dont contain it. They dont transport it to LCZ. The soonest I walk a bit far, they teleport directly there (the same when they did in original 1.2.2...)

And I find this http://pasteboard.co/ufFbP0K.png

Perhaps I did something wrong? Would it be OK to put modified files for download? It would be more user-friendly...
Check my custom map for CB viewtopic.php?t=10630

Re: WIP New MTF AI

#7
Agent G wrote:Did the second... No recompiling.....
The source in the source code folder is just a copy of the source code, the game's not actually pulling it from that folder. You need to recompile the source code into an executable file using Blitz3D. If you don't know how to do that then it's best to just wait to see if someone will be gracious enough to provide a compiled version of the new code for you.

Re: WIP New MTF AI

#9
Basicly this game is made by Juan with addition of Regalis idea and work :\

PS: I will probably be baned and killed for what i have said.
Hello there is a mod where you can participate Better SCP-914. with your help 914 will be properly complicated and unpredictable :P You can help with theory or code, even an sugestion will be greatly apreciated :D

Re: WIP New MTF AI

#10
Hisamera wrote:Basicly this game is made by Juan with addition of Regalis idea and work :\

PS: I will probably be baned and killed for what i have said.
>overdrama queen

This looks promising though, you have probably outdone yourself once again juan.
STEAM
## LOG OFF.