bw logo

Appendix A. Source files

A.1. <res>/scripts/cell/RandomNavigator.py

import BigWorld
import math
import random
import Math


class RandomNavigator( BigWorld.Entity ):

    TIMER_WAITING_FOR_NAVMESH = 1
    
    #---------------------------------------------------------------------
    # Constructor.
    #---------------------------------------------------------------------
    def __init__( self ):
        BigWorld.Entity .__init__( self )
        self.destination = self.position
        self.addTimer( 5.0, 0, RandomNavigator.TIMER_WAITING_FOR_NAVMESH )

    #---------------------------------------------------------------------
    # This method is called when a timer expires.
    #---------------------------------------------------------------------
    def onTimer(self, timerId, userId):
        if userId == RandomNavigator.TIMER_WAITING_FOR_NAVMESH:
            if self.canNavigateTo( self.position ) == None:
                self.addTimer( 5.0, 0, RandomNavigator.TIMER_WAITING_FOR_NAVMESH )
            else:
                self.navigateStep( self.destination, 5.0, 10.0 )


    #---------------------------------------------------------------------
    # This method is called when we've finished moving to a point.
    #---------------------------------------------------------------------
    def onMove(self, controllerId, userId):
        if ( self.position - self.destination ).length > 0.1:
            self.navigateStep( self.destination, 5.0, 10.0 )
        else:
            self.destination = None
            while self.destination == None:
                randomDestination = ( 
                    self.position.x + random.randrange(-400, 400, 1.0),
                    self.position.y,
                    self.position.z + random.randrange(-400, 400, 1.0) )
                self.destination = self.canNavigateTo( randomDestination )

            self.navigateStep( self.destination, 5.0, 10.0 )

# RandomNavigator.py

A.2. <res>/scripts/client/RandomNavigator.py

import math
import BigWorld
from keys import *


# --------------------------------------------------------------------------
# Section: class RandomNavigator
# --------------------------------------------------------------------------


class RandomNavigator( BigWorld.Entity ):
    stdModel = 'characters/avatars/base/base.model'

    def __init__( self ):
        BigWorld.Entity.__init__( self )


    def prerequisites( self ):
        return [ RandomNavigator.stdModel ]


    def enterWorld( self ):
        self.model = BigWorld.Model( RandomNavigator.stdModel )
        BigWorld.addShadowEntity( self )
        self.targetCaps = [ CAP_CAN_HIT , CAP_CAN_USE ]
        self.filter = BigWorld.AvatarDropFilter()
        

    def leaveWorld( self ):
        BigWorld.delShadowEntity( self )
        self.model = None


    def use( self ):
        pass


#RandomNavigator.py

A.3. <res>/scripts/base/RandomNavigator.py

import FantasyDemo

# --------------------------------------------------------------------------
# Section: class RandomNavigator
# --------------------------------------------------------------------------

class RandomNavigator( FantasyDemo.Base ):

    def __init__( self ):
        FantasyDemo.Base.__init__( self )


# RandomNavigator.py  

A.4. <res>/scripts/editor/RandomNavigator.py

class RandomNavigator:
    def modelName( self, props ):
        return 'characters/avatars/base/base.model'

# RandomNavigator.py

A.5. <res>/scripts/entity_defs/RandomNavigator.def

<root>
    <Volatile>
        <position/>
        <yaw/>
    </Volatile>

    <Properties>

        <destination>
            <Type>            PYTHON                </Type>
            <Flags>            CELL_PRIVATE        </Flags>
        </destination>

    </Properties>

    <ClientMethods>
    </ClientMethods>

    <CellMethods>
    </CellMethods>
</root>

A.6. <res>/scripts/base/ElPolloDiablo.py

 import FantasyDemo

# -------------------------------------------------------------------
# Section: class ElPolloDiablo
# -------------------------------------------------------------------

class ElPolloDiablo( FantasyDemo.Base ):
	pass

# ElPolloDiablo.py

A.7. <res>/scripts/client/ElPolloDiablo.py

import BigWorld

MODEL = "characters/npc/chicken/chicken.model"

# -------------------------------------------------------------------
# Class ElPolloDiablo:
#
# ElPolloDiablo follows an entity, or wanders between two patrol nodes
# -------------------------------------------------------------------
class ElPolloDiablo(BigWorld.Entity):

	# -----------------------------------------------------------------
	# Method: __init__
	# Description:
	#	- Defines all variables used by the entity. This includes
	#	  setting variables to None.
	#	- Does not call any of the accessor methods. Any variables set are
	#	  for the purposes of stability.
	# ------------------------------------------------------------------
	def __init__( self ):
		BigWorld.Entity.__init__( self )
		self.filter = BigWorld.AvatarDropFilter()
		self.model = None


	# ------------------------------------------------------------------
	# Method: prerequisites
	# Description:
	#	- Return a list of the resources that we want loaded in the background
	#	for us before onEnterWorld() is called.
	# ------------------------------------------------------------------
	def prerequisites( self ):
		return [ MODEL ]


	# ------------------------------------------------------------------
	# Method: onEnterWorld
	# Description:
	#	- Creates a model for the ElPolloDiablo.
	# ------------------------------------------------------------------
	def onEnterWorld( self, prereqs ):
		self.model = prereqs[ MODEL ]
		self.model.scale = (4.0, 4.0, 4.0)


	# ------------------------------------------------------------------
	# This method is called when the entity leaves the world
	# ------------------------------------------------------------------
	def onLeaveWorld( self ):
		self.model = None


	# ------------------------------------------------------------------
	# Method: name
	# Description:
	#	- Part of the entity interface: This allows the client to get a string
	#	  name for the entity.
	# ------------------------------------------------------------------
	def name( self ):
		return "El Pollo Diablo"

#ElPolloDiablo.py

A.8. <res>/scripts/entity_defs/ElPolloDiablo.def

<root>
	<Volatile>
		<position/>
		<yaw/>
	</Volatile>

	<Implements>
		<Interface> BaseAndCell </Interface>
	</Implements>

	<Properties>
		<!-- 0 is wander, 1 is follow targetID -->
		<mode>
			<Type>		INT32		</Type>
			<Flags>		CELL_PRIVATE		</Flags>
			<Persistent>	false		</Persistent>
			<Default>	0		</Default>
		</mode>

		<targetID>
			<Type>		OBJECT_ID		</Type>
			<Flags>		CELL_PRIVATE		</Flags>
			<Persistent>	false		</Persistent>
		</targetID>

		<nextNode>
			<Type>		PATROL_NODE		</Type>
			<Flags>		CELL_PRIVATE		</Flags>
			<Persistent>	false		</Persistent>
		</nextNode>

		<prevNode>
			<Type>		PATROL_NODE		</Type>
			<Flags>		CELL_PRIVATE		</Flags>
			<Persistent>	false		</Persistent>
		</prevNode>
	</Properties>

	<ClientMethods>
	</ClientMethods>

	<CellMethods>
		<startFollow>
			<Args>
				<id>		OBJECT_ID		</id>	<!-- EntityID -->
			</Args>
		</startFollow>

		<stopFollow>
		</stopFollow>
	</CellMethods>

	<BaseMethods>
	</BaseMethods>

</root>

A.9. <res>/scripts/cell/ElPolloDiablo.py - Before

"This module implements the ElPolloDiablo entity."

# BigWorld Modules
import BigWorld

# Python modules
import random
import math

#todo: replace this with math module
def distance(v1, v2):
	"Returns the distance between two 3d vectors"
	x = v2[0] - v1[0]
	z = v2[2] - v1[2]
	#ignore y value due to the current 13k hack
	return math.sqrt(x * x + z * z)


# ----------------------------------------------------------------------------
# Section: class ElPolloDiablo
# ----------------------------------------------------------------------------
class ElPolloDiablo( BigWorld.Entity ):
	"An ElPolloDiablo entity."

	PATROL_MODE		= 0
	FOLLOW_MODE		= 1

	VELOCITY		= 20

	PATROL_DISTANCE		= 2

	FOLLOW_DISTANCE		= 10
	FOLLOW_ANGLE		= math.pi

	#------------------------------------------------------------------------
	# Constructor
	#------------------------------------------------------------------------

	def __init__( self ):
		BigWorld.Entity.__init__( self )

		# random yaw
		yaw = random.uniform(-math.pi, math.pi)

		self.direction = (0.0, 0.0, yaw)

		if self.mode == ElPolloDiablo.PATROL_MODE:
			self.stopFollow()
		else:
			self.startFollow( self.targetID )

	def onTimer( self, controllerID, userData ):
		self.think()

	def startFollow( self, targetID ):
		self.mode = ElPolloDiablo.FOLLOW_MODE
		self.targetID = targetID
		self.cancel( "Movement" )
		self.think()

	def stopFollow( self ):
		self.mode = ElPolloDiablo.PATROL_MODE
		self.nextNode = None
		self.cancel( "Movement" )
		self.think()

	def think( self ):
		if self.mode == ElPolloDiablo.PATROL_MODE:
			self.patrol()
		else:
			self.follow()


	# Patrol brain
	def patrol( self ):
		# If we haven't got any nodes, find a pair
		if self.nextNode is None:
			self.setupNodes()
		if self.nextNode is None:
			# If we can't find a pair of nodes, wait 5 seconds and try again
			self.cancel( "Movement" )
			self.addTimer( 5 )
			return

		# If we've arrived, turn around
		if self.closeEnoughToNode():
			self.swapNodes()

		# Navigate to slightly closer than self.closeEnoughToNode()
		self.navigate( self.nextNode.position, ElPolloDiablo.VELOCITY, True, 500, 0.5, ElPolloDiablo.PATROL_DISTANCE * 0.8 )

	def closeEnoughToNode( self ):
		target = self.nextNode
		return distance( self.position, target.position ) <= ElPolloDiablo.PATROL_DISTANCE

	def setupNodes( self ):
		self.prevNode = None
		self.nextNode = None
		closest = None
		dist = 500
		for i in BigWorld.userDataObjects.values():
			if i.__class__.__name__ != "PatrolNode" or len(i.patrolLinks) == 0:
				continue
			if distance( self.position, i.position ) < dist:
				closest = i
				dist = distance( self.position, i.position )
		if closest is not None:
			after = closest.patrolLinks[ 0 ]
			while distance( closest.position, after.position ) < ElPolloDiablo.PATROL_DISTANCE * 3:
				after = after.patrolLinks[ 0 ]
				if after is None or after.uuid == closest.uuid:
					after = None
					break
			if after is not None:
				self.prevNode = closest
				self.nextNode = after

	def swapNodes( self ):
		temp = self.nextNode
		self.nextNode = self.prevNode
		self.prevNode = temp

	def onNavigate( self, controllerID, userData ):
		# Arrived. Turn around.
		self.swapNodes()
		self.think()
		
	def onNavigateFailed( self, controllerID, userData ):
		# Can't get there. Turn around
		self.swapNodes()
		self.think()


	# Follow brain
	def follow( self ):
		# If self.targetID doesn't exist, switch to patrol mode
		if not BigWorld.entities.has_key( self.targetID ):
			self.stopFollow()
			return

		# If target isn't in this space, switch to patrol mode
		target = BigWorld.entities[ self.targetID ]
		if target.spaceID != self.spaceID:
			self.stopFollow()
			return

		# If we've arrived, wait here for target to move away
		if self.closeEnoughToTarget():
			self.cancel( "Movement" )
			self.addTimer( 5 )
			return

		# Follow our target
		target = BigWorld.entities[ self.targetID ]
		try:
			self.navigateFollow( target, ElPolloDiablo.FOLLOW_ANGLE, ElPolloDiablo.FOLLOW_DISTANCE, ElPolloDiablo.VELOCITY, 500, 500, True, 0.5 )
		except ValueError, e:
			# No path found
			self.cancel( "Movement" )
			self.addTimer( 5 )

	def closeEnoughToTarget( self ):
		target = BigWorld.entities[ self.targetID ]
		return distance( self.position, target.position ) <= ElPolloDiablo.FOLLOW_DISTANCE

	def onMove( self, controllerID, userData ):
		self.think()

# ElPolloDiablo.py

A.10. <res>/scripts/cell/ElPolloDiablo.py - After

"This module implements the ElPolloDiablo entity."

# BigWorld Modules
import BigWorld

# Python modules
import random
import math

#todo: replace this with math module
def distance(v1, v2):
	"Returns the distance between two 3d vectors"
	x = v2[0] - v1[0]
	z = v2[2] - v1[2]
	#ignore y value due to the current 13k hack
	return math.sqrt(x * x + z * z)


# ----------------------------------------------------------------------------
# Section: class ElPolloDiablo
# ----------------------------------------------------------------------------
class ElPolloDiablo( BigWorld.Entity ):
	"An ElPolloDiablo entity."

	PATROL_MODE		= 0
	FOLLOW_MODE		= 1

	VELOCITY		= 20

	PATROL_DISTANCE		= 2

	FOLLOW_DISTANCE		= 10
	FOLLOW_ANGLE		= math.pi

	#------------------------------------------------------------------------
	# Constructor
	#------------------------------------------------------------------------

	def __init__( self ):
		BigWorld.Entity.__init__( self )

		# random yaw
		yaw = random.uniform(-math.pi, math.pi)

		self.direction = (0.0, 0.0, yaw)

		if self.mode == ElPolloDiablo.PATROL_MODE:
			self.stopFollow()
		else:
			self.startFollow( self.targetID )

	def onTimer( self, controllerID, userData ):
		self.think()

	def startFollow( self, targetID ):
		self.mode = ElPolloDiablo.FOLLOW_MODE
		self.targetID = targetID
		self.cancel( "Movement" )
		self.think()

	def stopFollow( self ):
		self.mode = ElPolloDiablo.PATROL_MODE
		self.nextNode = None
		self.cancel( "Movement" )
		self.think()

	def think( self ):
		if self.mode == ElPolloDiablo.PATROL_MODE:
			self.patrol()
		else:
			self.follow()


	# Patrol brain
	def patrol( self ):
		# If we haven't got any nodes, find a pair
		if self.nextNode is None:
			self.setupNodes()
		if self.nextNode is None:
			# If we can't find a pair of nodes, wait 5 seconds and try again
			self.cancel( "Movement" )
			self.addTimer( 5 )
			return

		# If we've arrived, turn around
		if self.closeEnoughToNode():
			self.swapNodes()

		# Navigate towards self.nextNode.position
		dest = self.canNavigateTo( self.nextNode.position, 500, 0.5 )
		if dest is None:
			# No path found
			self.cancel( "Movement" )
			self.addTimer( 5 )
			return
		self.navigateStep( dest, ElPolloDiablo.VELOCITY, 500, 500, True, 0.5 )

	def closeEnoughToNode( self ):
		target = self.nextNode
		return distance( self.position, target.position ) <= ElPolloDiablo.PATROL_DISTANCE

	def setupNodes( self ):
		self.prevNode = None
		self.nextNode = None
		closest = None
		dist = 500
		for i in BigWorld.userDataObjects.values():
			if i.__class__.__name__ != "PatrolNode" or len(i.patrolLinks) == 0:
				continue
			if distance( self.position, i.position ) < dist:
				closest = i
				dist = distance( self.position, i.position )
		if closest is not None:
			after = closest.patrolLinks[ 0 ]
			while distance( closest.position, after.position ) < ElPolloDiablo.PATROL_DISTANCE * 3:
				after = after.patrolLinks[ 0 ]
				if after is None or after.uuid == closest.uuid:
					after = None
					break
			if after is not None:
				self.prevNode = closest
				self.nextNode = after

	def swapNodes( self ):
		temp = self.nextNode
		self.nextNode = self.prevNode
		self.prevNode = temp

	def onMoveFailure( self, controllerID, userData ):
		# Can't get there. Turn around
		self.swapNodes()
		self.think()

	# Follow brain
	def follow( self ):
		# If self.targetID doesn't exist, switch to patrol mode
		if not BigWorld.entities.has_key( self.targetID ):
			self.stopFollow()
			return

		# If target isn't in this space, switch to patrol mode
		target = BigWorld.entities[ self.targetID ]
		if target.spaceID != self.spaceID:
			self.stopFollow()
			return

		# If we've arrived, wait here for target to move away
		if self.closeEnoughToTarget():
			self.cancel( "Movement" )
			self.addTimer( 5 )
			return

		# Follow our target
		yaw = target.yaw + ElPolloDiablo.FOLLOW_ANGLE
		offset = ( ElPolloDiablo.FOLLOW_DISTANCE * math.sin( yaw ), 0, ElPolloDiablo.FOLLOW_DISTANCE * math.cos( yaw ) )
		dest = self.canNavigateTo( target.position + offset, 500, 0.5 )
		if dest is None:
			# No path found
			self.cancel( "Movement" )
			self.addTimer( 5 )
			return
		self.navigateStep( dest, ElPolloDiablo.VELOCITY, 500, 500, True, 0.5 )

	def closeEnoughToTarget( self ):
		target = BigWorld.entities[ self.targetID ]
		return distance( self.position, target.position ) <= ElPolloDiablo.FOLLOW_DISTANCE

	def onMove( self, controllerID, userData ):
		self.think()

# ElPolloDiablo.py