/
character.py
208 lines (151 loc) · 5.14 KB
/
character.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# Source code distributed under the Copyright (c) 2008, Chris Lewis
# Authors: Chris Lewis (http://work.chris.to), John David Funge (www.jfunge.com)
#
# Licensed under the Academic Free License version 3.0
# (for details see LICENSE.txt in this directory).
import util
import vec
import mm
import pygame
import math
from numpy import *
class Shape:
def __init__(self):
self.orientation = zeros((util.numDim,), float)
self.orientation[0] = 1.0
self.position = zeros((util.numDim,), float)
def setPosition(self, position):
vec.copy(position, self.position)
def setOrientation(self, orientation):
assert util.isAlmostEq(1.0, vec.length(orientation)), 'invalid orientation ' + str(orientation)
vec.copy(orientation, self.orientation)
class Circle(Shape):
def __init__(self, radius):
Shape.__init__(self)
self.radius = radius
def getRadius(self):
return self.radius
def getMaxExtent(self):
return self.radius
@mm.multimethod(Circle, Circle, ndarray)
def normalTo(c0, c1, n):
return vec.normalize(c1.position - c0.position, n)
@mm.multimethod(Circle, Circle)
def distanceTo(c0, c1):
return vec.length(c0.position - c1.position) - c0.radius - c1.radius
@mm.multimethod(Circle, Circle)
def isTouching(c0, c1):
return vec.lengthSq(c0.position - c1.position) <= (c0.radius + c1.radius)*(c1.radius + c1.radius)
@mm.multimethod(Circle, Circle, ndarray)
def nearestIntersection(c0, c1, q):
p = c1.position
v = c1.orientation
rp = p - c0.position
k0 = vec.lengthSq(rp) - c0.getRadius() * c0.getRadius()
k1 = vec.dot(v, rp)
roots = []
k = k1 * k1 - k0
if util.isAlmostZero(k):
roots.append(-k1)
elif 0.0 < k:
kSqrt = math.sqrt(k)
roots.append(-k1 - kSqrt)
roots.append(-k1 + kSqrt)
assert roots[0] < roots[1]
vec.set(Inf, q)
for root in roots:
if util.isAlmostZero(root) or 0 < root:
q = vec.scale(v, root, q)
q = q + p
break
return q
class Obstacle:
def __init__(self, shape):
self.shape = shape
self.speed = 0.0
self.rndr = None
self.mass = Inf
self.velocity = zeros((util.numDim,), float)
self.tmp = zeros((util.numDim,), float)
self.actualVelocity = None
self.actualSpeed = 0.0
def getShape(self):
return self.shape
def setRenderer(self, rndr):
self.rndr = rndr
def render(self):
if self.rndr:
self.rndr.render(self)
def normalTo(self, o, n):
return normalTo(self.shape, o.shape, n)
def getPosition(self):
return self.shape.position
def getOrientation(self):
assert util.isAlmostEq(1.0, vec.length(self.shape.orientation)), 'invalid orientation ' + str(self.shape.orientation)
return self.shape.orientation
def getMaxExtent(self):
return self.shape.getMaxExtent()
def setPosition(self, position):
self.shape.setPosition(position)
def setVelocity(self, velocity):
s = vec.length(velocity)
if util.isAlmostZero(s):
self.setSpeed(0)
# Don't change the orientation if the speed is zero.
else:
self.setSpeed(s)
self.shape.setOrientation(vec.normalize(velocity, self.velocity))
def getVelocity(self):
# TODO: cache to avoid recomputing
return vec.scale(self.shape.orientation, self.speed, self.velocity)
def setSpeed(self, speed):
self.speed = speed
def getSpeed(self):
return self.speed
def isTouching(self, o):
return isTouching(self.shape, o.shape)
def isColliding(self, o):
v0 = self.getVelocity()
v1 = o.getVelocity()
self.tmp = self.normalTo(o, self.tmp)
return self.isTouching(o) and dot(v0, self.tmp) > dot(v1, self.tmp)
def distanceTo(self, o):
return distanceTo(self.shape, o.shape)
def nearestIntersection(self, o, p):
return nearestIntersection(self.shape, o.shape, p)
def setActualVelocity(self, velocity):
self.actualVelocity = velocity
self.actualSpeed = vec.length(velocity)
def getActualVelocity(self):
return self.actualVelocity
def getActualSpeed(self):
return self.actualSpeed
class Character(Obstacle):
def __init__(self, shape, brain):
Obstacle.__init__(self, shape)
self.brain = brain
self.tagged = False
self.maxSpeed = 100.0
self.mass = 1.0
self.maxForce = 150.0
self.name = "The NPC With No Name"
def tag(self):
self.tagged = True
def untag(self):
self.tagged = False
def render(self):
if self.tagged and self.rndr:
self.rndr.flashOn()
else:
self.rndr.flashOff()
Obstacle.render(self)
def getController(self):
return self.brain
def calcAction(self):
self.brain.calcAction()
def getAction(self):
return self.brain.getAction()
def setName(self, name):
self.name = name
def getName(self):
return self.name