Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/api/Sprite.py
blob: 5ba779633d5f85b4ab8e48e5619a93dde29201b2 (plain)
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#cp1252

import pygame
import math
import Image
import api.Math as CMath
from api.Vector import CVector

from Mouse import CMouse

# TODO: Cambiar las conversiones de grados a radianes y viceversa usando las funciones.

# TODO: This class is a rotating sprite. update() in each frame rotates the sprite
# image, so this  is inneficient for sprites that does not rotate continuoulsy.

class CSpriteOLD(pygame.sprite.Sprite):
    
    """ An enhanced Sprite class
        expects a gameEngine.Scene class as its one parameter
        Use methods to change image, direction, speed
        Will automatically travel in direction and speed indicated
        Automatically rotates to point in indicated direction
        Five kinds of boundary collision
    """
    
    mPos = None
    
    mOffsetX = 0
    mOffsetY = 0

    mTimeState = 0

    def __init__(self):
        pygame.sprite.Sprite.__init__(self)

#        self.mGroup = aGroup

        self.mPos = CVector(0.0, 0.0)
        self.mVel = CVector(0.0, 0.0)
        self.mAccel = CVector(0.0, 0.0)
    
        # Registration point offset.
        self.mOffsetX = 0
        self.mOffsetY = 0
    
        # Speed of the sprite.
        self.mSpeed = 0

        self.maxSpeed = 50
        self.minSpeed = -3

        # TODO: Take this values from a constants class.
        self.setBounds(0, 0, 1200, 900)
        
        # Constants for the actions then the sprite reaches a border.
        #CSprite.WRAP = 0        # Wrap around the edges.
        #CSprite.BOUNCE = 1      # Bounce off the screen changing direction.
        #CSprite.STOP = 2        # Stop at the edge of the screen.
        #CSprite.HIDE = 3        # Move off-stage and stop.
        #CSprite.CONTINUE = 4    # Move on forever.

        # Action for the sprite when it reaches a border.
        #self.mBoundAction = CSprite.WRAP
                
                
                
        #create a default text image as a placeholder
        #This will usually be changed by a setImage call
        #self.font = pygame.font.Font("freesansbold.ttf", 30)
        self.font = pygame.font.Font('assets/fonts/DejaVuSans.ttf', 30)
        self.mImageMaster = self.font.render(">sprite>", True, (0, 0,0), (0xFF, 0xFF, 0xFF))
        
        # Set the image of the sprite.
        self.image = self.mImageMaster
        self.rect = self.image.get_rect()

        # Set the position of the image.
        # oldCenter is used to draw the trace in drawTrace().
        # oldCenter is the position of the sprite in the previous frame.
        #self.oldCenter = (self.mPos.getX(), self.mPos.getY())
        #self.rect.center = (self.mPos.getX(), self.mPos.getY())
        
        self.calculatePositionWithOffset()
        
        # Angle of rotation in degrees used for movement calculations.
        self.mAngle = 0
        # Angle of rotation in degrees of the sprite image (usually the same as mAngle).
        self.mRotation = 0
        
        #self.pressed = False
        self.mactive = False
        self.mclicked = False
        
        self.states = {}
        self.currentState = "default"
        
        self.mTimeState = 0
        
    def setRegistrationPointOffset(self, aOffsetX, aOffsetY):
        self.mOffsetX = aOffsetX
        self.mOffsetY = aOffsetY

    def setXY(self, aX, aY):
        #print 'alan', dir(self.mPos)
        self.mPos.setXY(aX, aY)
        #self.rect.center = (self.mPos.getX(), self.mPos.getY())
        self.calculatePositionWithOffset()

    def setVelXY(self, aVelX, aVelY):
        self.mVel.setXY(aVelX, aVelY)
        self.mVel.truncate(self.maxSpeed)
        
    def setVelVec(self, aVec):
        self.mVel.setXY(aVec.getX(), aVec.getY())
        self.mVel.truncate(self.maxSpeed)
        
    def setAccelXY(self, aAccelX, aAccelY):
        self.mAccel.setXY(aAccelX, aAccelY)
    
    def setMaxSpeed(self, aMaxSpeed):
        self.maxSpeed = aMaxSpeed

    def update(self):
        
        self.mTimeState += 1
        
        #self.oldCenter = self.rect.center
        # Run the logic for the sprite.
        self.updateLogic()
        # Calculate next position based on angle and velocity. 
        self.__updatePosition()
        # Rotate the image according to the angle of the sprite.
        #self.__rotateSpriteImage()
        #self.checkBounds()
        
        #self.rect.center = (self.mPos.getX(), self.mPos.getY())
        
        # TODO: Test this. Need to pass a reference to background to draw. 
        # self.drawTrace((255,0,0))

        self.mVel.add(self.mAccel)

        self.mVel.truncate(self.maxSpeed)
        #if (self.mVel.getX() > self.maxSpeed):
        #    self.mVel.setX(self.maxSpeed)
        #if (self.mVel.getY() > self.maxSpeed):
        #    self.mVel.setY(self.maxSpeed)"""
            
        self.mPos.add(self.mVel)
        
        self.calculatePositionWithOffset()
        
        self.mclicked = False
        
        if CMouse().firstPress():
            if self.rect.collidepoint(CMouse().getPos()):
                #print("first press in button")
                self.mactive = True

        #check for mouse release
        if self.mactive == True:
            if CMouse().release():
                self.mactive = False
                if self.rect.collidepoint(CMouse().getPos()):
                    #print("release in button")
                    self.mclicked = True
                    

    #---------------------------------------------------------------------------
    # updateLogic().
    # Override this method to implement the sprite's response to events.
    # This is the function that contains the logic of the sprite (it's behaviour). 
    #
    # Parameters:
    # Nothing.
    #
    # Returns: Nothing.
    #---------------------------------------------------------------------------
    def updateLogic(self):
        pass

    #---------------------------------------------------------------------------
    # __updatePosition().
    # Calculate the next position of the sprite based on angle and velocity. 
    #
    # Parameters:
    # Nothing.
    #
    # Returns: Nothing.
    #---------------------------------------------------------------------------
    def __updatePosition(self):
        theta = CMath.degToRad(self.mAngle)
        self.dx = math.cos(theta) * self.mSpeed
        self.dy = math.sin(theta) * self.mSpeed
        self.dy *= -1

        self.mPos.setX(self.mPos.getX() + self.dx)
        self.mPos.setY(self.mPos.getY() + self.dy)

    #---------------------------------------------------------------------------
    # __rotateSpriteImage().
    # Rotates the sprite image according to the rotation attribute. 
    # The registration point is assumed to be in the center of the image. 
    # This function is called in update() every frame.
    #
    # Parameters:
    # Nothing.
    #
    # Returns: Nothing.
    #---------------------------------------------------------------------------
    def __rotateSpriteImage(self):
        oldCenter = self.rect.center
        self.oldCenter = oldCenter
        self.image = pygame.transform.rotate(self.mImageMaster, self.mRotation)
        self.rect = self.image.get_rect()
        self.rect.center = oldCenter
        # Debug: Draw the collision circle.
        #pygame.draw.circle(self.image, (255,0,0), (self.rect.w/2 , self.rect.h/2), self.radius, 2)
    
    def setSpeed(self, speed):
        """ immediately sets the objects speed to the 
            given value.
        """
        self.mSpeed = speed

    def speedUp(self, amount):
        """ changes speed by the given amount
            Use a negative value to slow down
        """
        self.mSpeed += amount
        if self.mSpeed < self.minSpeed:
            self.mSpeed = self.minSpeed
        if self.mSpeed > self.maxSpeed:
            self.mSpeed = self.maxSpeed
    
    #TODO
    def setAngle(self, direction):
        """ sets both the direction of motion 
            and visual rotation to the given angle
            If you want to set one or the other, 
            set them directly. Angle measured in degrees
        """            
        self.mAngle = direction
        self.mRotation = direction
    
    #---------------------------------------------------------------------------
    # turnBy().
    # Turn by the sprite by given number of degrees. Changes both angles (the 
    # angle of the sprite and the rotation attribute of the image). A positive
    # angle is counter-clockwise and a negative angle is clockwise.
    #
    # Parameters:
    # aAngleInc: Number of degrees to add to the current rotation angle.
    #
    # Returns: Nothing.
    #---------------------------------------------------------------------------
    def turnBy(self, aAngleInc):
        self.mAngle = CMath.toStandardAngle(self.mAngle + aAngleInc) 
        self.mRotation = self.mAngle
    
    # TODO: Es igual que la anterior pero solo para mRotation
    # Estas cuentas estan mal, hay que usar toStandardAngle().
    def rotateBy(self, amt):
        """ change visual orientation by given
            number of degrees. Does not change direction
            of travel. 
        """
        self.mRotation += amt
        if self.mRotation > 360:
            self.mRotation = amt
        if self.mRotation < 0:
            self.mRotation = 360 - amt

    #----------------------------------------------------------------------------------------------------------
    # setImage().
    # Loads the given file name as the master image that then is rotated.
    # The sprite must be facing east (angle 0). In the main loop the sprite is rotated automatically.
    #
    # Parameters:
    # aImageFilename: File path of the sprite image to be loaded.
    # aIsTransparent: If the image is transparent (True) or not (True, by default). 
    #
    # Returns: Nothing.
    #----------------------------------------------------------------------------------------------------------
    def loadImage(self, aImageFilename, aIsTransparent=True):
        self.mImageMaster = Image.loadImage(aImageFilename, aIsTransparent)
        self.setImage(self.mImageMaster)
        #self.image = self.mImageMaster
        #self.rect = self.image.get_rect()
        #self.rect.center = (self.mPos.getX(), self.mPos.getY())
    
    def setImage(self, aImage):
        self.mImageMaster = aImage
        self.image = aImage
        self.rect = self.image.get_rect()
        #self.rect.center = (self.mPos.getX(), self.mPos.getY())
        self.calculatePositionWithOffset()
    
    def setDX(self, dx):
        """ changes dx value and updates vector """
        self.dx = dx
        self.updateVector()
    
    def addDX(self, amt):
        """ adds amt to dx, updates vector """
        self.dx += amt
        self.updateVector()
        
    def setDY(self, dy):
        """ changes dy value and updates vector """
        self.dy = dy
        self.updateVector()

    def addDY(self, amt):
        """ adds amt to dy and updates vector """
        self.dy += amt
        self.updateVector()
    
    def setComponents(self, components):
        """ expects (dx, dy) for components
            change speed and angle according to dx, dy values """
            
        (self.dx, self.dy) = components
        self.updateVector()
        
    def setBoundAction (self, aBoundAction):
        self.mBoundAction = aBoundAction
        
    def getBoundAction(self):
        return self.mBoundAction

    def setPosition (self, position):
        """ place the sprite directly at the given position
            expects an (x, y) tuple
        """
        self.mPos.setX(position.getX())
        self.mPos.setY(position.getY())
        
    def moveBy (self, vector):
        """ move the sprite by the (dx, dy) values in vector
            automatically calls checkBounds. Doesn't change 
            speed or angle settings.
        """
        (dx, dy) = vector
        self.mPos.x += dx
        self.mPos.y += dy
        self.checkBounds()

    def forward(self, amt):
        """ move amt pixels in the current direction
            of travel
        """
        
        #calculate dx dy based on current direction
        radians = self.mAngle * math.pi / 180
        dx = amt * math.cos(radians)
        dy = amt * math.sin(radians) * -1
        
        self.mPos.x += dx
        self.mPos.y += dy
        
    def addForce(self, amt, angle):
        """ apply amt of thrust in angle.
            change speed and dir accordingly
            add a force straight down to simulate gravity
            in rotation direction to simulate spacecraft thrust
            in dir direction to accelerate forward
            at an angle for retro-rockets, etc.
        """

        #calculate dx dy based on angle
        radians = angle * math.pi / 180
        dx = amt * math.cos(radians)
        dy = amt * math.sin(radians) * -1
        
        self.dx += dx
        self.dy += dy
        self.updateVector()
        
    def updateVector(self):
        #calculate new speed and angle based on dx, dy
        #call this any time you change dx or dy
        
        self.mSpeed = math.sqrt((self.dx * self.dx) + (self.dy * self.dy))
        
        dy = self.dy * -1
        dx = self.dx
        
        radians = math.atan2(dy, dx)
        self.mAngle = radians / math.pi * 180

    def setSpeedLimits(self, amax, amin):
        """ determines maximum and minimum
            speeds you will allow through
            speedUp() method.  You can still
            directly set any speed you want
            with setSpeed() Default values:
                max: 10
                min: -3
        """
        self.maxSpeed = amax
        self.minSpeed = amin

    def dataTrace(self):
        """ utility method for debugging
            print major properties
            extend to add your own properties
        """
        print "x: %d, y: %d, speed: %.2f, dir: %.f, dx: %.2f, dy: %.2f" % \
              (self.mPos.x, self.mPos.y, self.mSpeed, self.mAngle, self.dx, self.dy)
            
    def mouseDown(self):
        """ boolean function. Returns True if the mouse is 
            clicked over the sprite, False otherwise
        """
        self.pressed = False
        if CMouse().pressed():
            if self.rect.collidepoint(CMouse().getPos()):
                self.pressed = True
        return self.pressed

    def mouseOver(self):
        if not CMouse().pressed():
            if self.rect.collidepoint(CMouse().getPos()):
                return True
            else:
                return False
        else:
            return False
            
    def clicked(self):
        """ Boolean function. Returns True only if mouse
            is pressed and released over sprite           
        """
        """released = False
        if self.pressed:
            if (not CMouse().pressed()):
                if self.rect.collidepoint(CMouse().getPos()):
                    released = True
            return released"""
        return self.mclicked
        
    def collidesWith(self, target):
        """ boolean function. Returns True if the sprite
            is currently colliding with the target sprite,
            False otherwise
        """
        collision = False
        if self.rect.colliderect(target.rect):
            collision = True
        return collision
    
    def collidesGroup(self, target):
        """ wrapper for pygame.sprite.collideany
            simplifies checking sprite - group collisions
            returns result of collision check (sprite from group 
            that was hit or None)
        """
        return pygame.sprite.spritecollideany(self, target)
        
    def distanceTo(self, point):
        """ returns distance to any point in pixels
            can be used in circular collision detection
        """
        (pointx, pointy) = point
        dx = self.mPos.x - pointx
        dy = self.mPos.y - pointy
        
        dist = math.sqrt((dx * dx) + (dy * dy))
        return dist
    
    def dirTo(self, point):
        """ returns direction (in degrees) to 
            a point """
        
        (pointx, pointy) = point
        dx = self.mPos.x - pointx
        dy = self.mPos.y - pointy
        dy *= -1
        
        radians = math.atan2(dy, dx)
        direction = radians * 180 / math.pi
        direction += 180
        return direction
    
    #TODO: no hay scene, ver como agarra el background...
    def drawTrace(self, color=(0x00, 0x00, 0x00)):
        """ traces a line between previous position
            and current position of object 
        """
        pygame.draw.line(self.scene.background, color, self.oldCenter,
                         self.rect.center, 3)
        self.screen.blit(self.scene.background, (0, 0))
        
    def addState(self, stateName, stateImageFile):
        """ Creates a new sprite state with the associated name
            and image. Useful to build multi-state sprites.
        """
        #load the image
        # TODO: Use the loading function.
        tempImage = pygame.image.load(stateImageFile)
        tempImage.convert()
        self.states[stateName] = tempImage    
        
    def setState(self, stateName):
        """ attempts to set the sprite to the indicated state
            (image)
        """
        self.mImageMaster = self.states[stateName]
        self.rect = self.mImageMaster.get_rect()
        self.currentState = stateName
        
    def getState(self):
        """ returns the current state name 
            (default if no states have been explicitly set)
        """
        return self.currentState 

    #---------------------------------------------------------------------------
    # setBounds().
    # Sets the boundaries used in checkBounds() to implement sprite behaviour
    # when it reaches the edge of the screen or this bound rectangle.
    #
    # Parameters:
    # aMinX: Horizontal coordinate of the left edge.
    # aMaxX: Horizontal coordinate of the right edge.
    # aMinY: Vertical coordinate of the top edge.
    # aMaxY: Vertical coordinate of the bottom edge.
    #
    # Returns: Nothing.
    #---------------------------------------------------------------------------
    def setBounds(self, aMinX, aMinY, aMaxX, aMaxY):
        self.mMinX = aMinX
        self.mMaxX = aMaxX
        self.mMinY = aMinY
        self.mMaxY = aMaxY
    
    def checkBounds(self):
        """ checks boundary and acts based on 
            self.BoundAction.
            WRAP: wrap around screen (default)
            BOUNCE: bounce off screen
            STOP: stop at edge of screen
            HIDE: move off stage and wait
            CONTINUE: keep going at present course and speed
            
            automatically called by update
        """
        
        # TODO
        scrWidth = self.mMaxX
        scrHeight = self.mMaxY
        #print scrWidth, scrHeight
        
        #create variables to simplify checking
        offRight = offLeft = offTop = offBottom = offScreen = False
        
        if self.mPos.x > scrWidth:
            offRight = True
        if self.mPos.x < 0:
            offLeft = True
        if self.mPos.y > scrHeight:
            offBottom = True
        if self.mPos.y < 0:
            offTop = True
            
        if offRight or offLeft or offTop or offBottom:
            offScreen = True
        
        if self.mBoundAction == self.WRAP:
            if offRight:
                self.mPos.x = 0
            if offLeft:
                self.mPos.x = scrWidth
            if offBottom:
                self.mPos.y = 0
            if offTop:
                self.mPos.y = scrHeight
        
        elif self.mBoundAction == self.BOUNCE:
            if offLeft or offRight:
                self.dx *= -1
            if offTop or offBottom:
                self.dy *= -1
                
            self.updateVector()
            self.mRotation = self.mAngle
        
        elif self.mBoundAction == self.STOP:
            if offScreen:
                self.mSpeed = 0
        
        elif self.mBoundAction == self.HIDE:
            if offScreen:
                self.mSpeed = 0
                self.setPosition((-1000, -1000))
        
        elif self.mBoundAction == self.CONTINUE:
            pass
            
        else:
            # assume it's continue - keep going forever
            pass    
    
    def destroy(self):
        self.font = None
        self.mImageMaster = None
        self.image = None
    
    def calculatePositionWithOffset(self):
        self.rect = self.image.get_rect()
        #print self.mOffsetX, self.mOffsetY
        self.rect.x = self.mPos.getX() - self.mOffsetX
        self.rect.y = self.mPos.getY() - self.mOffsetY
        
    def getX(self):
        return self.mPos.getX()
    
    def getY(self):
        return self.mPos.getY()
    
    def setTimeState(self, aTimeState):
        self.mTimeState = aTimeState
        
    def getTimeState(self):
        return self.mTimeState
    
    def getSize(self):
        if self.image:
            rect = self.image.get_rect()
            return (rect[2], rect[3])
        else:
            return (0,0)