187 lines
4.5 KiB
Python
187 lines
4.5 KiB
Python
from panda3d.core import *
|
|
from direct.interval.IntervalGlobal import *
|
|
from direct.fsm.FSM import *
|
|
import random, math
|
|
|
|
estateRadius = 130
|
|
estateCenter = (0, -40)
|
|
|
|
houseRadius = 15
|
|
houses = ((60, 10), (42, 75), (-37, 35), (80, -80), (-70, -120), (-55, -40))
|
|
|
|
dist = 2
|
|
|
|
def inCircle(x, y, c=estateCenter, r=estateRadius):
|
|
center_x, center_y = c
|
|
square_dist = (center_x - x) ** 2 + (center_y - y) ** 2
|
|
return square_dist <= r ** 2
|
|
|
|
def housePointCollision(x, y):
|
|
for i, h in enumerate(houses):
|
|
if inCircle(x, y, h, houseRadius):
|
|
return 1
|
|
|
|
return 0
|
|
|
|
def generatePos():
|
|
def get():
|
|
r = random.randint(0, estateRadius) - estateRadius / 2
|
|
r2 = random.randint(0, estateRadius) - estateRadius / 2
|
|
x = r + estateCenter[0]
|
|
y = r2 + estateCenter[1]
|
|
assert inCircle(x, y)
|
|
return x, y
|
|
|
|
p = get()
|
|
while housePointCollision(*p):
|
|
p = get()
|
|
|
|
return p
|
|
|
|
def lineInCircle(pt1, pt2, circlePoint, circleRadius=houseRadius):
|
|
x1, y1 = pt1
|
|
x2, y2 = pt2
|
|
|
|
dist = math.hypot(x2 - x1, y2 - y1)
|
|
if dist == 0:
|
|
return 0
|
|
|
|
dx = (x2 - x1) / dist
|
|
dy = (y2 - y1) / dist
|
|
|
|
t = dx * (circlePoint[0] - x1) + dy * (circlePoint[1] - y1)
|
|
|
|
ex = t * dx + x1
|
|
ey = t * dy + y1
|
|
|
|
d2 = math.hypot(ex - circlePoint[0], ey - circlePoint[1])
|
|
return d2 <= circleRadius
|
|
|
|
def houseCollision(pt1, pt2):
|
|
for i, h in enumerate(houses):
|
|
if lineInCircle(pt1, pt2, h):
|
|
return 1
|
|
|
|
return 0
|
|
|
|
def generatePath(start, end):
|
|
points = [start]
|
|
if not houseCollision(start, end):
|
|
points.append(end)
|
|
return points
|
|
|
|
while True:
|
|
next = generatePos()
|
|
while houseCollision(points[-1], next):
|
|
next = generatePos()
|
|
|
|
points.append(next)
|
|
if not houseCollision(next, end):
|
|
points.append(end)
|
|
return points
|
|
|
|
def angle(A, B):
|
|
ax = A.getX()
|
|
ay = A.getY()
|
|
|
|
bx = B.getX()
|
|
by = B.getY()
|
|
|
|
return math.atan2(by-ay, bx-ax)
|
|
|
|
class PetMoverAI(FSM):
|
|
def __init__(self, pet):
|
|
self.pet = pet
|
|
FSM.__init__(self, 'PetMoverAI-%d' % self.pet.doId)
|
|
self.chaseTarget = None
|
|
self.__seq = None
|
|
self.fwdSpeed = 10.0
|
|
self.rotSpeed = 360.0
|
|
self.__moveFromStill()
|
|
self.__chaseCallback = None
|
|
|
|
def enterStill(self):
|
|
taskMgr.doMethodLater(random.randint(15, 60), self.__moveFromStill, self.pet.uniqueName('next-state'))
|
|
|
|
def exitStill(self):
|
|
taskMgr.remove(self.pet.uniqueName('next-state'))
|
|
|
|
def __moveFromStill(self, task=None):
|
|
choices = ["Wander"]
|
|
nextState = random.choice(choices)
|
|
self.request(nextState)
|
|
|
|
def enterWander(self):
|
|
target = self.getPoint()
|
|
self.walkToPoint(target)
|
|
|
|
def getPoint(self):
|
|
x, y = generatePos()
|
|
return Point3(x, y, 0)
|
|
|
|
def walkToPoint(self, target):
|
|
here = self.pet.getPos()
|
|
dist = Vec3(here - target).length()
|
|
|
|
self.__seq = Sequence(Func(self.pet.lookAt, target), Func(self.pet.setP, 0), self.pet.posInterval(dist / self.fwdSpeed, target, here),
|
|
Func(self.__stateComplete))
|
|
self.__seq.start()
|
|
|
|
def exitWander(self):
|
|
if self.__seq:
|
|
self.__seq.pause()
|
|
|
|
self.__seq = None
|
|
|
|
def __stateComplete(self):
|
|
try:
|
|
self.request("Still")
|
|
except:
|
|
pass
|
|
|
|
def destroy(self):
|
|
self.demand("Off")
|
|
|
|
def setFwdSpeed(self, speed):
|
|
self.fwdSpeed = speed
|
|
|
|
def getFwdSpeed(self):
|
|
return self.fwdSpeed
|
|
|
|
def setRotSpeed(self, speed):
|
|
self.rotSpeed = speed
|
|
|
|
def getRotSpeed(self):
|
|
return self.rotSpeed
|
|
|
|
def lock(self):
|
|
if self.state != "Still":
|
|
self.demand("Still")
|
|
|
|
def enterChase(self, target=None):
|
|
if not target:
|
|
target = hidden.attachNewNode('target')
|
|
target.setPos(self.getPoint())
|
|
|
|
pos = target.getPos()
|
|
theta = angle(self.pet.getPos(), pos) * (math.pi / 180)
|
|
dx = dist * math.cos(theta)
|
|
dy = dist * math.sin(theta)
|
|
|
|
self.walkToPoint(Point3(pos.getX() - dx, pos.getY() - dy, pos.getZ()))
|
|
|
|
def exitChase(self):
|
|
if self.__chaseCallback:
|
|
self.__chaseCallback()
|
|
self.__chaseCallback = None
|
|
|
|
if self.__seq:
|
|
self.__seq.pause()
|
|
|
|
self.__seq = None
|
|
|
|
def walkToAvatar(self, av, callback=None):
|
|
if callback:
|
|
self.__chaseCallback = callback
|
|
|
|
self.demand("Chase", av)
|