~pygame/pygame/trunk

« back to all changes in this revision

Viewing changes to lib/sprite.py

  • Committer: pygame
  • Date: 2017-01-10 00:31:42 UTC
  • Revision ID: git-v1:2eea4f299a2e791f884608d7ed601558634af73c
commit 1639c41a8cb3433046882ede92c80ce69d59016b
Author: Thomas Kluyver <takowl@gmail.com>
Date:   Sun Jan 8 18:46:46 2017 +0000

    Build newer versions of libogg and libvorbis into Linux base images

    Closes #317
    Closes #323

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
##    pygame - Python Game Library
 
2
##    Copyright (C) 2000-2003, 2007  Pete Shinners
 
3
##              (C) 2004 Joe Wreschnig
 
4
##    This library is free software; you can redistribute it and/or
 
5
##    modify it under the terms of the GNU Library General Public
 
6
##    License as published by the Free Software Foundation; either
 
7
##    version 2 of the License, or (at your option) any later version.
 
8
##
 
9
##    This library is distributed in the hope that it will be useful,
 
10
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
##    Library General Public License for more details.
 
13
##
 
14
##    You should have received a copy of the GNU Library General Public
 
15
##    License along with this library; if not, write to the Free
 
16
##    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
##
 
18
##    Pete Shinners
 
19
##    pete@shinners.org
 
20
 
 
21
"""pygame module with basic game object classes
 
22
 
 
23
This module contains several simple classes to be used within games. There
 
24
are the main Sprite class and several Group classes that contain Sprites.
 
25
The use of these classes is entirely optional when using Pygame. The classes
 
26
are fairly lightweight and only provide a starting place for the code
 
27
that is common to most games.
 
28
 
 
29
The Sprite class is intended to be used as a base class for the different
 
30
types of objects in the game. There is also a base Group class that simply
 
31
stores sprites. A game could create new types of Group classes that operate
 
32
on specially customized Sprite instances they contain.
 
33
 
 
34
The basic Sprite class can draw the Sprites it contains to a Surface. The
 
35
Group.draw() method requires that each Sprite have a Surface.image attribute
 
36
and a Surface.rect. The Group.clear() method requires these same attributes
 
37
and can be used to erase all the Sprites with background. There are also
 
38
more advanced Groups: pygame.sprite.RenderUpdates() and
 
39
pygame.sprite.OrderedUpdates().
 
40
 
 
41
Lastly, this module contains several collision functions. These help find
 
42
sprites inside multiple groups that have intersecting bounding rectangles.
 
43
To find the collisions, the Sprites are required to have a Surface.rect
 
44
attribute assigned.
 
45
 
 
46
The groups are designed for high efficiency in removing and adding Sprites
 
47
to them. They also allow cheap testing to see if a Sprite already exists in
 
48
a Group. A given Sprite can exist in any number of groups. A game could use
 
49
some groups to control object rendering, and a completely separate set of
 
50
groups to control interaction or player movement. Instead of adding type
 
51
attributes or bools to a derived Sprite class, consider keeping the
 
52
Sprites inside organized Groups. This will allow for easier lookup later
 
53
in the game.
 
54
 
 
55
Sprites and Groups manage their relationships with the add() and remove()
 
56
methods. These methods can accept a single or multiple group arguments for
 
57
membership.  The default initializers for these classes also take a
 
58
single group or list of groups as argments for initial membership. It is safe
 
59
to repeatedly add and remove the same Sprite from a Group.
 
60
 
 
61
While it is possible to design sprite and group classes that don't derive
 
62
from the Sprite and AbstractGroup classes below, it is strongly recommended
 
63
that you extend those when you create a new Sprite or Group class.
 
64
 
 
65
Sprites are not thread safe, so lock them yourself if using threads.
 
66
 
 
67
"""
 
68
 
 
69
##todo
 
70
## a group that holds only the 'n' most recent elements.
 
71
## sort of like the GroupSingle class, but holding more
 
72
## than one sprite
 
73
##
 
74
## drawing groups that can 'automatically' store the area
 
75
## underneath so they can "clear" without needing a background
 
76
## function. obviously a little slower than normal, but nice
 
77
## to use in many situations. (also remember it must "clear"
 
78
## in the reverse order that it draws :])
 
79
##
 
80
## the drawing groups should also be able to take a background
 
81
## function, instead of just a background surface. the function
 
82
## would take a surface and a rectangle on that surface to erase.
 
83
##
 
84
## perhaps more types of collision functions? the current two
 
85
## should handle just about every need, but perhaps more optimized
 
86
## specific ones that aren't quite so general but fit into common
 
87
## specialized cases.
 
88
 
 
89
import pygame
 
90
from pygame import Rect
 
91
from pygame.time import get_ticks
 
92
from operator import truth
 
93
 
 
94
# Python 3 does not have the callable function, but an equivalent can be made
 
95
# with the hasattr function.
 
96
if 'callable' not in dir(__builtins__):
 
97
    callable = lambda obj: hasattr(obj, '__call__')
 
98
 
 
99
# Don't depend on pygame.mask if it's not there...
 
100
try:
 
101
    from pygame.mask import from_surface
 
102
except:
 
103
    pass
 
104
 
 
105
 
 
106
class Sprite(object):
 
107
    """simple base class for visible game objects
 
108
 
 
109
    pygame.sprite.Sprite(*groups): return Sprite
 
110
 
 
111
    The base class for visible game objects. Derived classes will want to
 
112
    override the Sprite.update() method and assign Sprite.image and Sprite.rect
 
113
    attributes.  The initializer can accept any number of Group instances that
 
114
    the Sprite will become a member of.
 
115
 
 
116
    When subclassing the Sprite class, be sure to call the base initializer
 
117
    before adding the Sprite to Groups.
 
118
 
 
119
    """
 
120
 
 
121
    def __init__(self, *groups):
 
122
        self.__g = {} # The groups the sprite is in
 
123
        if groups:
 
124
            self.add(*groups)
 
125
 
 
126
    def add(self, *groups):
 
127
        """add the sprite to groups
 
128
 
 
129
        Sprite.add(*groups): return None
 
130
 
 
131
        Any number of Group instances can be passed as arguments. The
 
132
        Sprite will be added to the Groups it is not already a member of.
 
133
 
 
134
        """
 
135
        has = self.__g.__contains__
 
136
        for group in groups:
 
137
            if hasattr(group, '_spritegroup'):
 
138
                if not has(group):
 
139
                    group.add_internal(self)
 
140
                    self.add_internal(group)
 
141
            else:
 
142
                self.add(*group)
 
143
 
 
144
    def remove(self, *groups):
 
145
        """remove the sprite from groups
 
146
 
 
147
        Sprite.remove(*groups): return None
 
148
 
 
149
        Any number of Group instances can be passed as arguments. The Sprite
 
150
        will be removed from the Groups it is currently a member of.
 
151
 
 
152
        """
 
153
        has = self.__g.__contains__
 
154
        for group in groups:
 
155
            if hasattr(group, '_spritegroup'):
 
156
                if has(group):
 
157
                    group.remove_internal(self)
 
158
                    self.remove_internal(group)
 
159
            else:
 
160
                self.remove(*group)
 
161
 
 
162
    def add_internal(self, group):
 
163
        self.__g[group] = 0
 
164
 
 
165
    def remove_internal(self, group):
 
166
        del self.__g[group]
 
167
 
 
168
    def update(self, *args):
 
169
        """method to control sprite behavior
 
170
 
 
171
        Sprite.update(*args):
 
172
 
 
173
        The default implementation of this method does nothing; it's just a
 
174
        convenient "hook" that you can override. This method is called by
 
175
        Group.update() with whatever arguments you give it.
 
176
 
 
177
        There is no need to use this method if not using the convenience
 
178
        method by the same name in the Group class.
 
179
 
 
180
        """
 
181
        pass
 
182
 
 
183
    def kill(self):
 
184
        """remove the Sprite from all Groups
 
185
 
 
186
        Sprite.kill(): return None
 
187
 
 
188
        The Sprite is removed from all the Groups that contain it. This won't
 
189
        change anything about the state of the Sprite. It is possible to
 
190
        continue to use the Sprite after this method has been called, including
 
191
        adding it to Groups.
 
192
 
 
193
        """
 
194
        for c in self.__g:
 
195
            c.remove_internal(self)
 
196
        self.__g.clear()
 
197
 
 
198
    def groups(self):
 
199
        """list of Groups that contain this Sprite
 
200
 
 
201
        Sprite.groups(): return group_list
 
202
 
 
203
        Returns a list of all the Groups that contain this Sprite.
 
204
 
 
205
        """
 
206
        return list(self.__g)
 
207
 
 
208
    def alive(self):
 
209
        """does the sprite belong to any groups
 
210
 
 
211
        Sprite.alive(): return bool
 
212
 
 
213
        Returns True when the Sprite belongs to one or more Groups.
 
214
        """
 
215
        return truth(self.__g)
 
216
 
 
217
    def __repr__(self):
 
218
        return "<%s sprite(in %d groups)>" % (self.__class__.__name__, len(self.__g))
 
219
 
 
220
 
 
221
class DirtySprite(Sprite):
 
222
    """a more featureful subclass of Sprite with more attributes
 
223
 
 
224
    pygame.sprite.DirtySprite(*groups): return DirtySprite
 
225
 
 
226
    Extra DirtySprite attributes with their default values:
 
227
 
 
228
    dirty = 1
 
229
        If set to 1, it is repainted and then set to 0 again.
 
230
        If set to 2, it is always dirty (repainted each frame;
 
231
        flag is not reset).
 
232
        If set to 0, it is not dirty and therefore not repainted again.
 
233
 
 
234
    blendmode = 0
 
235
        It's the special_flags argument of Surface.blit; see the blendmodes in
 
236
        the Surface.blit documentation
 
237
 
 
238
    source_rect = None
 
239
        This is the source rect to use. Remember that it is relative to the top
 
240
        left corner (0, 0) of self.image.
 
241
 
 
242
    visible = 1
 
243
        Normally this is 1. If set to 0, it will not be repainted. (If you
 
244
        change visible to 1, you must set dirty to 1 for it to be erased from
 
245
        the screen.)
 
246
 
 
247
    _layer = 0
 
248
        A READ ONLY value, it is read when adding it to the LayeredUpdates
 
249
        group. For details see documentation of sprite.LayeredUpdates.
 
250
 
 
251
    """
 
252
 
 
253
    def __init__(self, *groups):
 
254
 
 
255
        self.dirty = 1
 
256
        self.blendmode = 0  # pygame 1.8, referred to as special_flags in
 
257
                            # the documentation of Surface.blit
 
258
        self._visible = 1
 
259
        self._layer = 0     # READ ONLY by LayeredUpdates or LayeredDirty
 
260
        self.source_rect = None
 
261
        Sprite.__init__(self, *groups)
 
262
 
 
263
    def _set_visible(self, val):
 
264
        """set the visible value (0 or 1) and makes the sprite dirty"""
 
265
        self._visible = val
 
266
        if self.dirty < 2:
 
267
            self.dirty = 1
 
268
 
 
269
    def _get_visible(self):
 
270
        """return the visible value of that sprite"""
 
271
        return self._visible
 
272
 
 
273
    visible = property(lambda self: self._get_visible(),
 
274
                       lambda self, value: self._set_visible(value),
 
275
                       doc="you can make this sprite disappear without "
 
276
                           "removing it from the group,\n"
 
277
                           "assign 0 for invisible and 1 for visible")
 
278
 
 
279
    def __repr__(self):
 
280
        return "<%s DirtySprite(in %d groups)>" % \
 
281
            (self.__class__.__name__, len(self.groups()))
 
282
 
 
283
 
 
284
class AbstractGroup(object):
 
285
    """base class for containers of sprites
 
286
 
 
287
    AbstractGroup does everything needed to behave as a normal group. You can
 
288
    easily subclass a new group class from this or the other groups below if
 
289
    you want to add more features.
 
290
 
 
291
    Any AbstractGroup-derived sprite groups act like sequences and support
 
292
    iteration, len, and so on.
 
293
 
 
294
    """
 
295
 
 
296
    # dummy val to identify sprite groups, and avoid infinite recursion
 
297
    _spritegroup = True
 
298
 
 
299
    def __init__(self):
 
300
        self.spritedict = {}
 
301
        self.lostsprites = []
 
302
 
 
303
    def sprites(self):
 
304
        """get a list of sprites in the group
 
305
 
 
306
        Group.sprite(): return list
 
307
 
 
308
        Returns an object that can be looped over with a 'for' loop. (For now,
 
309
        it is always a list, but this could change in a future version of
 
310
        pygame.) Alternatively, you can get the same information by iterating
 
311
        directly over the sprite group, e.g. 'for sprite in group'.
 
312
 
 
313
        """
 
314
        return list(self.spritedict)
 
315
 
 
316
    def add_internal(self, sprite):
 
317
        self.spritedict[sprite] = 0
 
318
 
 
319
    def remove_internal(self, sprite):
 
320
        r = self.spritedict[sprite]
 
321
        if r:
 
322
            self.lostsprites.append(r)
 
323
        del self.spritedict[sprite]
 
324
 
 
325
    def has_internal(self, sprite):
 
326
        return sprite in self.spritedict
 
327
 
 
328
    def copy(self):
 
329
        """copy a group with all the same sprites
 
330
 
 
331
        Group.copy(): return Group
 
332
 
 
333
        Returns a copy of the group that is an instance of the same class
 
334
        and has the same sprites in it.
 
335
 
 
336
        """
 
337
        return self.__class__(self.sprites())
 
338
 
 
339
    def __iter__(self):
 
340
        return iter(self.sprites())
 
341
 
 
342
    def __contains__(self, sprite):
 
343
        return self.has(sprite)
 
344
 
 
345
    def add(self, *sprites):
 
346
        """add sprite(s) to group
 
347
 
 
348
        Group.add(sprite, list, group, ...): return None
 
349
 
 
350
        Adds a sprite or sequence of sprites to a group.
 
351
 
 
352
        """
 
353
        for sprite in sprites:
 
354
            # It's possible that some sprite is also an iterator.
 
355
            # If this is the case, we should add the sprite itself,
 
356
            # and not the iterator object.
 
357
            if isinstance(sprite, Sprite):
 
358
                if not self.has_internal(sprite):
 
359
                    self.add_internal(sprite)
 
360
                    sprite.add_internal(self)
 
361
            else:
 
362
                try:
 
363
                    # See if sprite is an iterator, like a list or sprite
 
364
                    # group.
 
365
                    self.add(*sprite)
 
366
                except (TypeError, AttributeError):
 
367
                    # Not iterable. This is probably a sprite that is not an
 
368
                    # instance of the Sprite class or is not an instance of a
 
369
                    # subclass of the Sprite class. Alternately, it could be an
 
370
                    # old-style sprite group.
 
371
                    if hasattr(sprite, '_spritegroup'):
 
372
                        for spr in sprite.sprites():
 
373
                            if not self.has_internal(spr):
 
374
                                self.add_internal(spr)
 
375
                                spr.add_internal(self)
 
376
                    elif not self.has_internal(sprite):
 
377
                        self.add_internal(sprite)
 
378
                        sprite.add_internal(self)
 
379
 
 
380
    def remove(self, *sprites):
 
381
        """remove sprite(s) from group
 
382
 
 
383
        Group.remove(sprite, list, or group, ...): return None
 
384
 
 
385
        Removes a sprite or sequence of sprites from a group.
 
386
 
 
387
        """
 
388
        # This function behaves essentially the same as Group.add. It first
 
389
        # tries to handle each argument as an instance of the Sprite class. If
 
390
        # that failes, then it tries to handle the argument as an iterable
 
391
        # object. If that failes, then it tries to handle the argument as an
 
392
        # old-style sprite group. Lastly, if that fails, it assumes that the
 
393
        # normal Sprite methods should be used.
 
394
        for sprite in sprites:
 
395
            if isinstance(sprite, Sprite):
 
396
                if self.has_internal(sprite):
 
397
                    self.remove_internal(sprite)
 
398
                    sprite.remove_internal(self)
 
399
            else:
 
400
                try:
 
401
                    self.remove(*sprite)
 
402
                except (TypeError, AttributeError):
 
403
                    if hasattr(sprite, '_spritegroup'):
 
404
                        for spr in sprite.sprites():
 
405
                            if self.has_internal(spr):
 
406
                                self.remove_internal(spr)
 
407
                                spr.remove_internal(self)
 
408
                    elif self.has_internal(sprite):
 
409
                        self.remove_internal(sprite)
 
410
                        sprite.remove_internal(self)
 
411
 
 
412
    def has(self, *sprites):
 
413
        """ask if group has a sprite or sprites
 
414
 
 
415
        Group.has(sprite or group, ...): return bool
 
416
 
 
417
        Returns True if the given sprite or sprites are contained in the
 
418
        group. Alternatively, you can get the same information using the
 
419
        'in' operator, e.g. 'sprite in group', 'subgroup in group'.
 
420
 
 
421
        """
 
422
        return_value = False
 
423
 
 
424
        for sprite in sprites:
 
425
            if isinstance(sprite, Sprite):
 
426
                # Check for Sprite instance's membership in this group
 
427
                if self.has_internal(sprite):
 
428
                    return_value = True
 
429
                else:
 
430
                    return False
 
431
            else:
 
432
                try:
 
433
                    if self.has(*sprite):
 
434
                        return_value = True
 
435
                    else:
 
436
                        return False
 
437
                except (TypeError, AttributeError):
 
438
                    if hasattr(sprite, '_spritegroup'):
 
439
                        for spr in sprite.sprites():
 
440
                            if self.has_internal(spr):
 
441
                                return_value = True
 
442
                            else:
 
443
                                return False
 
444
                    else:
 
445
                        if self.has_internal(sprite):
 
446
                            return_value = True
 
447
                        else:
 
448
                            return False
 
449
 
 
450
        return return_value
 
451
 
 
452
    def update(self, *args):
 
453
        """call the update method of every member sprite
 
454
 
 
455
        Group.update(*args): return None
 
456
 
 
457
        Calls the update method of every member sprite. All arguments that
 
458
        were passed to this method are passed to the Sprite update function.
 
459
 
 
460
        """
 
461
        for s in self.sprites():
 
462
            s.update(*args)
 
463
 
 
464
    def draw(self, surface):
 
465
        """draw all sprites onto the surface
 
466
 
 
467
        Group.draw(surface): return None
 
468
 
 
469
        Draws all of the member sprites onto the given surface.
 
470
 
 
471
        """
 
472
        sprites = self.sprites()
 
473
        surface_blit = surface.blit
 
474
        for spr in sprites:
 
475
            self.spritedict[spr] = surface_blit(spr.image, spr.rect)
 
476
        self.lostsprites = []
 
477
 
 
478
    def clear(self, surface, bgd):
 
479
        """erase the previous position of all sprites
 
480
 
 
481
        Group.clear(surface, bgd): return None
 
482
 
 
483
        Clears the area under every drawn sprite in the group. The bgd
 
484
        argument should be Surface which is the same dimensions as the
 
485
        screen surface. The bgd could also be a function which accepts
 
486
        the given surface and the area to be cleared as arguments.
 
487
 
 
488
        """
 
489
        if callable(bgd):
 
490
            for r in self.lostsprites:
 
491
                bgd(surface, r)
 
492
            for r in self.spritedict.values():
 
493
                if r:
 
494
                    bgd(surface, r)
 
495
        else:
 
496
            surface_blit = surface.blit
 
497
            for r in self.lostsprites:
 
498
                surface_blit(bgd, r, r)
 
499
            for r in self.spritedict.values():
 
500
                if r:
 
501
                    surface_blit(bgd, r, r)
 
502
 
 
503
    def empty(self):
 
504
        """remove all sprites
 
505
 
 
506
        Group.empty(): return None
 
507
 
 
508
        Removes all the sprites from the group.
 
509
 
 
510
        """
 
511
        for s in self.sprites():
 
512
            self.remove_internal(s)
 
513
            s.remove_internal(self)
 
514
 
 
515
    def __nonzero__(self):
 
516
        return truth(self.sprites())
 
517
 
 
518
    def __len__(self):
 
519
        """return number of sprites in group
 
520
 
 
521
        Group.len(group): return int
 
522
 
 
523
        Returns the number of sprites contained in the group.
 
524
 
 
525
        """
 
526
        return len(self.sprites())
 
527
 
 
528
    def __repr__(self):
 
529
        return "<%s(%d sprites)>" % (self.__class__.__name__, len(self))
 
530
 
 
531
class Group(AbstractGroup):
 
532
    """container class for many Sprites
 
533
 
 
534
    pygame.sprite.Group(*sprites): return Group
 
535
 
 
536
    A simple container for Sprite objects. This class can be subclassed to
 
537
    create containers with more specific behaviors. The constructor takes any
 
538
    number of Sprite arguments to add to the Group. The group supports the
 
539
    following standard Python operations:
 
540
 
 
541
        in      test if a Sprite is contained
 
542
        len     the number of Sprites contained
 
543
        bool    test if any Sprites are contained
 
544
        iter    iterate through all the Sprites
 
545
 
 
546
    The Sprites in the Group are not ordered, so the Sprites are drawn and
 
547
    iterated over in no particular order.
 
548
 
 
549
    """
 
550
    def __init__(self, *sprites):
 
551
        AbstractGroup.__init__(self)
 
552
        self.add(*sprites)
 
553
 
 
554
RenderPlain = Group
 
555
RenderClear = Group
 
556
 
 
557
class RenderUpdates(Group):
 
558
    """Group class that tracks dirty updates
 
559
 
 
560
    pygame.sprite.RenderUpdates(*sprites): return RenderUpdates
 
561
 
 
562
    This class is derived from pygame.sprite.Group(). It has an enhanced draw
 
563
    method that tracks the changed areas of the screen.
 
564
 
 
565
    """
 
566
    def draw(self, surface):
 
567
       spritedict = self.spritedict
 
568
       surface_blit = surface.blit
 
569
       dirty = self.lostsprites
 
570
       self.lostsprites = []
 
571
       dirty_append = dirty.append
 
572
       for s in self.sprites():
 
573
           r = spritedict[s]
 
574
           newrect = surface_blit(s.image, s.rect)
 
575
           if r:
 
576
               if newrect.colliderect(r):
 
577
                   dirty_append(newrect.union(r))
 
578
               else:
 
579
                   dirty_append(newrect)
 
580
                   dirty_append(r)
 
581
           else:
 
582
               dirty_append(newrect)
 
583
           spritedict[s] = newrect
 
584
       return dirty
 
585
 
 
586
class OrderedUpdates(RenderUpdates):
 
587
    """RenderUpdates class that draws Sprites in order of addition
 
588
 
 
589
    pygame.sprite.OrderedUpdates(*spites): return OrderedUpdates
 
590
 
 
591
    This class derives from pygame.sprite.RenderUpdates().  It maintains
 
592
    the order in which the Sprites were added to the Group for rendering.
 
593
    This makes adding and removing Sprites from the Group a little
 
594
    slower than regular Groups.
 
595
 
 
596
    """
 
597
    def __init__(self, *sprites):
 
598
        self._spritelist = []
 
599
        RenderUpdates.__init__(self, *sprites)
 
600
 
 
601
    def sprites(self):
 
602
        return list(self._spritelist)
 
603
 
 
604
    def add_internal(self, sprite):
 
605
        RenderUpdates.add_internal(self, sprite)
 
606
        self._spritelist.append(sprite)
 
607
 
 
608
    def remove_internal(self, sprite):
 
609
        RenderUpdates.remove_internal(self, sprite)
 
610
        self._spritelist.remove(sprite)
 
611
 
 
612
 
 
613
class LayeredUpdates(AbstractGroup):
 
614
    """LayeredUpdates Group handles layers, which are drawn like OrderedUpdates
 
615
 
 
616
    pygame.sprite.LayeredUpdates(*spites, **kwargs): return LayeredUpdates
 
617
 
 
618
    This group is fully compatible with pygame.sprite.Sprite.
 
619
    New in pygame 1.8.0
 
620
 
 
621
    """
 
622
 
 
623
    _init_rect = Rect(0, 0, 0, 0)
 
624
 
 
625
    def __init__(self, *sprites, **kwargs):
 
626
        """initialize an instance of LayeredUpdates with the given attributes
 
627
 
 
628
        You can set the default layer through kwargs using 'default_layer'
 
629
        and an integer for the layer. The default layer is 0.
 
630
 
 
631
        If the sprite you add has an attribute _layer, then that layer will be
 
632
        used. If **kwarg contains 'layer', then the passed sprites will be
 
633
        added to that layer (overriding the sprite._layer attribute). If
 
634
        neither the sprite nor **kwarg has a 'layer', then the default layer is
 
635
        used to add the sprites.
 
636
 
 
637
        """
 
638
        self._spritelayers = {}
 
639
        self._spritelist = []
 
640
        AbstractGroup.__init__(self)
 
641
        self._default_layer = kwargs.get('default_layer', 0)
 
642
 
 
643
        self.add(*sprites, **kwargs)
 
644
 
 
645
    def add_internal(self, sprite, layer=None):
 
646
        """Do not use this method directly.
 
647
 
 
648
        It is used by the group to add a sprite internally.
 
649
 
 
650
        """
 
651
        self.spritedict[sprite] = self._init_rect
 
652
 
 
653
        if layer is None:
 
654
            try:
 
655
                layer = sprite._layer
 
656
            except AttributeError:
 
657
                layer = sprite._layer = self._default_layer
 
658
        elif hasattr(sprite, '_layer'):
 
659
            sprite._layer = layer
 
660
 
 
661
        sprites = self._spritelist # speedup
 
662
        sprites_layers = self._spritelayers
 
663
        sprites_layers[sprite] = layer
 
664
 
 
665
        # add the sprite at the right position
 
666
        # bisect algorithmus
 
667
        leng = len(sprites)
 
668
        low = mid = 0
 
669
        high = leng - 1
 
670
        while low <= high:
 
671
            mid = low + (high - low) // 2
 
672
            if sprites_layers[sprites[mid]] <= layer:
 
673
                low = mid + 1
 
674
            else:
 
675
                high = mid - 1
 
676
        # linear search to find final position
 
677
        while mid < leng and sprites_layers[sprites[mid]] <= layer:
 
678
            mid += 1
 
679
        sprites.insert(mid, sprite)
 
680
 
 
681
    def add(self, *sprites, **kwargs):
 
682
        """add a sprite or sequence of sprites to a group
 
683
 
 
684
        LayeredUpdates.add(*sprites, **kwargs): return None
 
685
 
 
686
        If the sprite you add has an attribute _layer, then that layer will be
 
687
        used. If **kwarg contains 'layer', then the passed sprites will be
 
688
        added to that layer (overriding the sprite._layer attribute). If
 
689
        neither the sprite nor **kwarg has a 'layer', then the default layer is
 
690
        used to add the sprites.
 
691
 
 
692
        """
 
693
 
 
694
        if not sprites:
 
695
            return
 
696
        if 'layer' in kwargs:
 
697
            layer = kwargs['layer']
 
698
        else:
 
699
            layer = None
 
700
        for sprite in sprites:
 
701
            # It's possible that some sprite is also an iterator.
 
702
            # If this is the case, we should add the sprite itself,
 
703
            # and not the iterator object.
 
704
            if isinstance(sprite, Sprite):
 
705
                if not self.has_internal(sprite):
 
706
                    self.add_internal(sprite, layer)
 
707
                    sprite.add_internal(self)
 
708
            else:
 
709
                try:
 
710
                    # See if sprite is an iterator, like a list or sprite
 
711
                    # group.
 
712
                    self.add(*sprite, **kwargs)
 
713
                except (TypeError, AttributeError):
 
714
                    # Not iterable. This is probably a sprite that is not an
 
715
                    # instance of the Sprite class or is not an instance of a
 
716
                    # subclass of the Sprite class. Alternately, it could be an
 
717
                    # old-style sprite group.
 
718
                    if hasattr(sprite, '_spritegroup'):
 
719
                        for spr in sprite.sprites():
 
720
                            if not self.has_internal(spr):
 
721
                                self.add_internal(spr, layer)
 
722
                                spr.add_internal(self)
 
723
                    elif not self.has_internal(sprite):
 
724
                        self.add_internal(sprite, layer)
 
725
                        sprite.add_internal(self)
 
726
 
 
727
    def remove_internal(self, sprite):
 
728
        """Do not use this method directly.
 
729
 
 
730
        The group uses it to add a sprite.
 
731
 
 
732
        """
 
733
        self._spritelist.remove(sprite)
 
734
        # these dirty rects are suboptimal for one frame
 
735
        r = self.spritedict[sprite]
 
736
        if r is not self._init_rect:
 
737
            self.lostsprites.append(r) # dirty rect
 
738
        if hasattr(sprite, 'rect'):
 
739
            self.lostsprites.append(sprite.rect) # dirty rect
 
740
 
 
741
        del self.spritedict[sprite]
 
742
        del self._spritelayers[sprite]
 
743
 
 
744
    def sprites(self):
 
745
        """return a ordered list of sprites (first back, last top).
 
746
 
 
747
        LayeredUpdates.sprites(): return sprites
 
748
 
 
749
        """
 
750
        return list(self._spritelist)
 
751
 
 
752
    def draw(self, surface):
 
753
        """draw all sprites in the right order onto the passed surface
 
754
 
 
755
        LayeredUpdates.draw(surface): return Rect_list
 
756
 
 
757
        """
 
758
        spritedict = self.spritedict
 
759
        surface_blit = surface.blit
 
760
        dirty = self.lostsprites
 
761
        self.lostsprites = []
 
762
        dirty_append = dirty.append
 
763
        init_rect = self._init_rect
 
764
        for spr in self.sprites():
 
765
            rec = spritedict[spr]
 
766
            newrect = surface_blit(spr.image, spr.rect)
 
767
            if rec is init_rect:
 
768
                dirty_append(newrect)
 
769
            else:
 
770
                if newrect.colliderect(rec):
 
771
                    dirty_append(newrect.union(rec))
 
772
                else:
 
773
                    dirty_append(newrect)
 
774
                    dirty_append(rec)
 
775
            spritedict[spr] = newrect
 
776
        return dirty
 
777
 
 
778
    def get_sprites_at(self, pos):
 
779
        """return a list with all sprites at that position
 
780
 
 
781
        LayeredUpdates.get_sprites_at(pos): return colliding_sprites
 
782
 
 
783
        Bottom sprites are listed first; the top ones are listed last.
 
784
 
 
785
        """
 
786
        _sprites = self._spritelist
 
787
        rect = Rect(pos, (0, 0))
 
788
        colliding_idx = rect.collidelistall(_sprites)
 
789
        colliding = [_sprites[i] for i in colliding_idx]
 
790
        return colliding
 
791
 
 
792
    def get_sprite(self, idx):
 
793
        """return the sprite at the index idx from the groups sprites
 
794
 
 
795
        LayeredUpdates.get_sprite(idx): return sprite
 
796
 
 
797
        Raises IndexOutOfBounds if the idx is not within range.
 
798
 
 
799
        """
 
800
        return self._spritelist[idx]
 
801
 
 
802
    def remove_sprites_of_layer(self, layer_nr):
 
803
        """remove all sprites from a layer and return them as a list
 
804
 
 
805
        LayeredUpdates.remove_sprites_of_layer(layer_nr): return sprites
 
806
 
 
807
        """
 
808
        sprites = self.get_sprites_from_layer(layer_nr)
 
809
        self.remove(*sprites)
 
810
        return sprites
 
811
 
 
812
    #---# layer methods
 
813
    def layers(self):
 
814
        """return a list of unique defined layers defined.
 
815
 
 
816
        LayeredUpdates.layers(): return layers
 
817
 
 
818
        """
 
819
        return sorted(set(self._spritelayers.values()))
 
820
 
 
821
    def change_layer(self, sprite, new_layer):
 
822
        """change the layer of the sprite
 
823
 
 
824
        LayeredUpdates.change_layer(sprite, new_layer): return None
 
825
 
 
826
        The sprite must have been added to the renderer already. This is not
 
827
        checked.
 
828
 
 
829
        """
 
830
        sprites = self._spritelist # speedup
 
831
        sprites_layers = self._spritelayers # speedup
 
832
 
 
833
        sprites.remove(sprite)
 
834
        sprites_layers.pop(sprite)
 
835
 
 
836
        # add the sprite at the right position
 
837
        # bisect algorithmus
 
838
        leng = len(sprites)
 
839
        low = mid = 0
 
840
        high = leng - 1
 
841
        while low <= high:
 
842
            mid = low + (high - low) // 2
 
843
            if sprites_layers[sprites[mid]] <= new_layer:
 
844
                low = mid + 1
 
845
            else:
 
846
                high = mid - 1
 
847
        # linear search to find final position
 
848
        while mid < leng and sprites_layers[sprites[mid]] <= new_layer:
 
849
            mid += 1
 
850
        sprites.insert(mid, sprite)
 
851
        if hasattr(sprite, 'layer'):
 
852
            sprite.layer = new_layer
 
853
 
 
854
        # add layer info
 
855
        sprites_layers[sprite] = new_layer
 
856
 
 
857
    def get_layer_of_sprite(self, sprite):
 
858
        """return the layer that sprite is currently in
 
859
 
 
860
        If the sprite is not found, then it will return the default layer.
 
861
 
 
862
        """
 
863
        return self._spritelayers.get(sprite, self._default_layer)
 
864
 
 
865
    def get_top_layer(self):
 
866
        """return the top layer
 
867
 
 
868
        LayeredUpdates.get_top_layer(): return layer
 
869
 
 
870
        """
 
871
        return self._spritelayers[self._spritelist[-1]]
 
872
 
 
873
    def get_bottom_layer(self):
 
874
        """return the bottom layer
 
875
 
 
876
        LayeredUpdates.get_bottom_layer(): return layer
 
877
 
 
878
        """
 
879
        return self._spritelayers[self._spritelist[0]]
 
880
 
 
881
    def move_to_front(self, sprite):
 
882
        """bring the sprite to front layer
 
883
 
 
884
        LayeredUpdates.move_to_front(sprite): return None
 
885
 
 
886
        Brings the sprite to front by changing the sprite layer to the top-most
 
887
        layer. The sprite is added at the end of the list of sprites in that
 
888
        top-most layer.
 
889
 
 
890
        """
 
891
        self.change_layer(sprite, self.get_top_layer())
 
892
 
 
893
    def move_to_back(self, sprite):
 
894
        """move the sprite to the bottom layer
 
895
 
 
896
        LayeredUpdates.move_to_back(sprite): return None
 
897
 
 
898
        Moves the sprite to the bottom layer by moving it to a new layer below
 
899
        the current bottom layer.
 
900
 
 
901
        """
 
902
        self.change_layer(sprite, self.get_bottom_layer() - 1)
 
903
 
 
904
    def get_top_sprite(self):
 
905
        """return the topmost sprite
 
906
 
 
907
        LayeredUpdates.get_top_sprite(): return Sprite
 
908
 
 
909
        """
 
910
        return self._spritelist[-1]
 
911
 
 
912
    def get_sprites_from_layer(self, layer):
 
913
        """return all sprites from a layer ordered as they where added
 
914
 
 
915
        LayeredUpdates.get_sprites_from_layer(layer): return sprites
 
916
 
 
917
        Returns all sprites from a layer. The sprites are ordered in the
 
918
        sequence that they where added. (The sprites are not removed from the
 
919
        layer.
 
920
 
 
921
        """
 
922
        sprites = []
 
923
        sprites_append = sprites.append
 
924
        sprite_layers = self._spritelayers
 
925
        for spr in self._spritelist:
 
926
            if sprite_layers[spr] == layer:
 
927
                sprites_append(spr)
 
928
            elif sprite_layers[spr] > layer:# break after because no other will
 
929
                                            # follow with same layer
 
930
                break
 
931
        return sprites
 
932
 
 
933
    def switch_layer(self, layer1_nr, layer2_nr):
 
934
        """switch the sprites from layer1_nr to layer2_nr
 
935
 
 
936
        LayeredUpdates.switch_layer(layer1_nr, layer2_nr): return None
 
937
 
 
938
        The layers number must exist. This method does not check for the
 
939
        existence of the given layers.
 
940
 
 
941
        """
 
942
        sprites1 = self.remove_sprites_of_layer(layer1_nr)
 
943
        for spr in self.get_sprites_from_layer(layer2_nr):
 
944
            self.change_layer(spr, layer1_nr)
 
945
        self.add(layer=layer2_nr, *sprites1)
 
946
 
 
947
 
 
948
class LayeredDirty(LayeredUpdates):
 
949
    """LayeredDirty Group is for DirtySprites; subclasses LayeredUpdates
 
950
 
 
951
    pygame.sprite.LayeredDirty(*spites, **kwargs): return LayeredDirty
 
952
 
 
953
    This group requires pygame.sprite.DirtySprite or any sprite that
 
954
    has the following attributes:
 
955
        image, rect, dirty, visible, blendmode (see doc of DirtySprite).
 
956
 
 
957
    It uses the dirty flag technique and is therefore faster than
 
958
    pygame.sprite.RenderUpdates if you have many static sprites.  It
 
959
    also switches automatically between dirty rect updating and full
 
960
    screen drawing, so you do no have to worry which would be faster.
 
961
 
 
962
    As with the pygame.sprite.Group, you can specify some additional attributes
 
963
    through kwargs:
 
964
        _use_update: True/False   (default is False)
 
965
        _default_layer: default layer where the sprites without a layer are
 
966
            added
 
967
        _time_threshold: treshold time for switching between dirty rect mode
 
968
            and fullscreen mode; defaults to updating at 80 frames per second,
 
969
            which is equal to 1000.0 / 80.0
 
970
 
 
971
    New in pygame 1.8.0
 
972
 
 
973
    """
 
974
 
 
975
    def __init__(self, *sprites, **kwargs):
 
976
        """initialize group.
 
977
 
 
978
        pygame.sprite.LayeredDirty(*spites, **kwargs): return LayeredDirty
 
979
 
 
980
        You can specify some additional attributes through kwargs:
 
981
            _use_update: True/False   (default is False)
 
982
            _default_layer: default layer where the sprites without a layer are
 
983
                added
 
984
            _time_threshold: treshold time for switching between dirty rect
 
985
                mode and fullscreen mode; defaults to updating at 80 frames per
 
986
                second, which is equal to 1000.0 / 80.0
 
987
 
 
988
        """
 
989
        LayeredUpdates.__init__(self, *sprites, **kwargs)
 
990
        self._clip = None
 
991
 
 
992
        self._use_update = False
 
993
 
 
994
        self._time_threshold = 1000.0 / 80.0 # 1000.0 / fps
 
995
 
 
996
        self._bgd = None
 
997
        for key, val in kwargs.items():
 
998
            if key in ['_use_update', '_time_threshold', '_default_layer']:
 
999
                if hasattr(self, key):
 
1000
                    setattr(self, key, val)
 
1001
 
 
1002
    def add_internal(self, sprite, layer=None):
 
1003
        """Do not use this method directly.
 
1004
 
 
1005
        It is used by the group to add a sprite internally.
 
1006
 
 
1007
        """
 
1008
        # check if all needed attributes are set
 
1009
        if not hasattr(sprite, 'dirty'):
 
1010
            raise AttributeError()
 
1011
        if not hasattr(sprite, 'visible'):
 
1012
            raise AttributeError()
 
1013
        if not hasattr(sprite, 'blendmode'):
 
1014
            raise AttributeError()
 
1015
 
 
1016
        if not isinstance(sprite, DirtySprite):
 
1017
            raise TypeError()
 
1018
 
 
1019
        if sprite.dirty == 0: # set it dirty if it is not
 
1020
            sprite.dirty = 1
 
1021
 
 
1022
        LayeredUpdates.add_internal(self, sprite, layer)
 
1023
 
 
1024
    def draw(self, surface, bgd=None):
 
1025
        """draw all sprites in the right order onto the given surface
 
1026
 
 
1027
        LayeredDirty.draw(surface, bgd=None): return Rect_list
 
1028
 
 
1029
        You can pass the background too. If a self.bgd is already set to some
 
1030
        value that is not None, then the bgd argument has no effect.
 
1031
 
 
1032
        """
 
1033
        # speedups
 
1034
        _orig_clip = surface.get_clip()
 
1035
        _clip = self._clip
 
1036
        if _clip is None:
 
1037
            _clip = _orig_clip
 
1038
 
 
1039
        _surf = surface
 
1040
        _sprites = self._spritelist
 
1041
        _old_rect = self.spritedict
 
1042
        _update = self.lostsprites
 
1043
        _update_append = _update.append
 
1044
        _ret = None
 
1045
        _surf_blit = _surf.blit
 
1046
        _rect = Rect
 
1047
        if bgd is not None:
 
1048
            self._bgd = bgd
 
1049
        _bgd = self._bgd
 
1050
        init_rect = self._init_rect
 
1051
 
 
1052
        _surf.set_clip(_clip)
 
1053
        # -------
 
1054
        # 0. decide whether to render with update or flip
 
1055
        start_time = get_ticks()
 
1056
        if self._use_update: # dirty rects mode
 
1057
            # 1. find dirty area on screen and put the rects into _update
 
1058
            # still not happy with that part
 
1059
            for spr in _sprites:
 
1060
                if 0 < spr.dirty:
 
1061
                    # chose the right rect
 
1062
                    if spr.source_rect:
 
1063
                        _union_rect = _rect(spr.rect.topleft,
 
1064
                                            spr.source_rect.size)
 
1065
                    else:
 
1066
                        _union_rect = _rect(spr.rect)
 
1067
 
 
1068
                    _union_rect_collidelist = _union_rect.collidelist
 
1069
                    _union_rect_union_ip = _union_rect.union_ip
 
1070
                    i = _union_rect_collidelist(_update)
 
1071
                    while -1 < i:
 
1072
                        _union_rect_union_ip(_update[i])
 
1073
                        del _update[i]
 
1074
                        i = _union_rect_collidelist(_update)
 
1075
                    _update_append(_union_rect.clip(_clip))
 
1076
 
 
1077
                    if _old_rect[spr] is not init_rect:
 
1078
                        _union_rect = _rect(_old_rect[spr])
 
1079
                        _union_rect_collidelist = _union_rect.collidelist
 
1080
                        _union_rect_union_ip = _union_rect.union_ip
 
1081
                        i = _union_rect_collidelist(_update)
 
1082
                        while -1 < i:
 
1083
                            _union_rect_union_ip(_update[i])
 
1084
                            del _update[i]
 
1085
                            i = _union_rect_collidelist(_update)
 
1086
                        _update_append(_union_rect.clip(_clip))
 
1087
            # can it be done better? because that is an O(n**2) algorithm in
 
1088
            # worst case
 
1089
 
 
1090
            # clear using background
 
1091
            if _bgd is not None:
 
1092
                for rec in _update:
 
1093
                    _surf_blit(_bgd, rec, rec)
 
1094
 
 
1095
            # 2. draw
 
1096
            for spr in _sprites:
 
1097
                if 1 > spr.dirty:
 
1098
                    if spr._visible:
 
1099
                        # sprite not dirty; blit only the intersecting part
 
1100
                        _spr_rect = spr.rect
 
1101
                        if spr.source_rect is not None:
 
1102
                            _spr_rect = Rect(spr.rect.topleft,
 
1103
                                             spr.source_rect.size)
 
1104
                        _spr_rect_clip = _spr_rect.clip
 
1105
                        for idx in _spr_rect.collidelistall(_update):
 
1106
                            # clip
 
1107
                            clip = _spr_rect_clip(_update[idx])
 
1108
                            _surf_blit(spr.image,
 
1109
                                       clip,
 
1110
                                       (clip[0] - _spr_rect[0],
 
1111
                                        clip[1] - _spr_rect[1],
 
1112
                                        clip[2],
 
1113
                                        clip[3]),
 
1114
                                       spr.blendmode)
 
1115
                else: # dirty sprite
 
1116
                    if spr._visible:
 
1117
                        _old_rect[spr] = _surf_blit(spr.image,
 
1118
                                                    spr.rect,
 
1119
                                                    spr.source_rect,
 
1120
                                                    spr.blendmode)
 
1121
                    if spr.dirty == 1:
 
1122
                        spr.dirty = 0
 
1123
            _ret = list(_update)
 
1124
        else: # flip, full screen mode
 
1125
            if _bgd is not None:
 
1126
                _surf_blit(_bgd, (0, 0))
 
1127
            for spr in _sprites:
 
1128
                if spr._visible:
 
1129
                    _old_rect[spr] = _surf_blit(spr.image,
 
1130
                                                spr.rect,
 
1131
                                                spr.source_rect,
 
1132
                                                spr.blendmode)
 
1133
            _ret = [_rect(_clip)] # return only the part of the screen changed
 
1134
 
 
1135
 
 
1136
        # timing for switching modes
 
1137
        # How may a good threshold be found? It depends on the hardware.
 
1138
        end_time = get_ticks()
 
1139
        if end_time-start_time > self._time_threshold:
 
1140
            self._use_update = False
 
1141
        else:
 
1142
            self._use_update = True
 
1143
 
 
1144
##        # debug
 
1145
##        print "               check: using dirty rects:", self._use_update
 
1146
 
 
1147
        # emtpy dirty rects list
 
1148
        _update[:] = []
 
1149
 
 
1150
        # -------
 
1151
        # restore original clip
 
1152
        _surf.set_clip(_orig_clip)
 
1153
        return _ret
 
1154
 
 
1155
    def clear(self, surface, bgd):
 
1156
        """use to set background
 
1157
 
 
1158
        Group.clear(surface, bgd): return None
 
1159
 
 
1160
        """
 
1161
        self._bgd = bgd
 
1162
 
 
1163
    def repaint_rect(self, screen_rect):
 
1164
        """repaint the given area
 
1165
 
 
1166
        LayeredDirty.repaint_rect(screen_rect): return None
 
1167
 
 
1168
        screen_rect is in screen coordinates.
 
1169
 
 
1170
        """
 
1171
        if self._clip:
 
1172
            self.lostsprites.append(screen_rect.clip(self._clip))
 
1173
        else:
 
1174
            self.lostsprites.append(Rect(screen_rect))
 
1175
 
 
1176
    def set_clip(self, screen_rect=None):
 
1177
        """clip the area where to draw; pass None (default) to reset the clip
 
1178
 
 
1179
        LayeredDirty.set_clip(screen_rect=None): return None
 
1180
 
 
1181
        """
 
1182
        if screen_rect is None:
 
1183
            self._clip = pygame.display.get_surface().get_rect()
 
1184
        else:
 
1185
            self._clip = screen_rect
 
1186
        self._use_update = False
 
1187
 
 
1188
    def get_clip(self):
 
1189
        """get the area where drawing will occur
 
1190
 
 
1191
        LayeredDirty.get_clip(): return Rect
 
1192
 
 
1193
        """
 
1194
        return self._clip
 
1195
 
 
1196
    def change_layer(self, sprite, new_layer):
 
1197
        """change the layer of the sprite
 
1198
 
 
1199
        LayeredUpdates.change_layer(sprite, new_layer): return None
 
1200
 
 
1201
        The sprite must have been added to the renderer already. This is not
 
1202
        checked.
 
1203
 
 
1204
        """
 
1205
        LayeredUpdates.change_layer(self, sprite, new_layer)
 
1206
        if sprite.dirty == 0:
 
1207
            sprite.dirty = 1
 
1208
 
 
1209
    def set_timing_treshold(self, time_ms):
 
1210
        """set the treshold in milliseconds
 
1211
 
 
1212
        set_timing_treshold(time_ms): return None
 
1213
 
 
1214
        Defaults to 1000.0 / 80.0. This means that the screen will be painted
 
1215
        using the flip method rather than the update method if the update
 
1216
        method is taking so long to update the screen that the frame rate falls
 
1217
        below 80 frames per second.
 
1218
 
 
1219
        """
 
1220
        self._time_threshold = time_ms
 
1221
 
 
1222
 
 
1223
class GroupSingle(AbstractGroup):
 
1224
    """A group container that holds a single most recent item.
 
1225
 
 
1226
    This class works just like a regular group, but it only keeps a single
 
1227
    sprite in the group. Whatever sprite has been added to the group last will
 
1228
    be the only sprite in the group.
 
1229
 
 
1230
    You can access its one sprite as the .sprite attribute.  Assigning to this
 
1231
    attribute will properly remove the old sprite and then add the new one.
 
1232
 
 
1233
    """
 
1234
 
 
1235
    def __init__(self, sprite=None):
 
1236
        AbstractGroup.__init__(self)
 
1237
        self.__sprite = None
 
1238
        if sprite is not None:
 
1239
            self.add(sprite)
 
1240
 
 
1241
    def copy(self):
 
1242
        return GroupSingle(self.__sprite)
 
1243
 
 
1244
    def sprites(self):
 
1245
        if self.__sprite is not None:
 
1246
            return [self.__sprite]
 
1247
        else:
 
1248
            return []
 
1249
 
 
1250
    def add_internal(self, sprite):
 
1251
        if self.__sprite is not None:
 
1252
            self.__sprite.remove_internal(self)
 
1253
            self.remove_internal(self.__sprite)
 
1254
        self.__sprite = sprite
 
1255
 
 
1256
    def __nonzero__(self):
 
1257
        return self.__sprite is not None
 
1258
 
 
1259
    def _get_sprite(self):
 
1260
        return self.__sprite
 
1261
 
 
1262
    def _set_sprite(self, sprite):
 
1263
        self.add_internal(sprite)
 
1264
        sprite.add_internal(self)
 
1265
        return sprite
 
1266
 
 
1267
    sprite = property(_get_sprite,
 
1268
                      _set_sprite,
 
1269
                      None,
 
1270
                      "The sprite contained in this group")
 
1271
 
 
1272
    def remove_internal(self, sprite):
 
1273
        if sprite is self.__sprite:
 
1274
            self.__sprite = None
 
1275
        if sprite in self.spritedict:
 
1276
            AbstractGroup.remove_internal(self, sprite)
 
1277
 
 
1278
    def has_internal(self, sprite):
 
1279
        return self.__sprite is sprite
 
1280
 
 
1281
    # Optimizations...
 
1282
    def __contains__(self, sprite):
 
1283
        return self.__sprite is sprite
 
1284
 
 
1285
 
 
1286
# Some different collision detection functions that could be used.
 
1287
def collide_rect(left, right):
 
1288
    """collision detection between two sprites, using rects.
 
1289
 
 
1290
    pygame.sprite.collide_rect(left, right): return bool
 
1291
 
 
1292
    Tests for collision between two sprites. Uses the pygame.Rect colliderect
 
1293
    function to calculate the collision. It is intended to be passed as a
 
1294
    collided callback function to the *collide functions. Sprites must have
 
1295
    "rect" attributes.
 
1296
 
 
1297
    New in pygame 1.8.0
 
1298
 
 
1299
    """
 
1300
    return left.rect.colliderect(right.rect)
 
1301
 
 
1302
class collide_rect_ratio:
 
1303
    """A callable class that checks for collisions using scaled rects
 
1304
 
 
1305
    The class checks for collisions between two sprites using a scaled version
 
1306
    of the sprites' rects. Is created with a ratio; the instance is then
 
1307
    intended to be passed as a collided callback function to the *collide
 
1308
    functions.
 
1309
 
 
1310
    New in pygame 1.8.1
 
1311
 
 
1312
    """
 
1313
 
 
1314
    def __init__(self, ratio):
 
1315
        """create a new collide_rect_ratio callable
 
1316
 
 
1317
        Ratio is expected to be a floating point value used to scale
 
1318
        the underlying sprite rect before checking for collisions.
 
1319
 
 
1320
        """
 
1321
        self.ratio = ratio
 
1322
 
 
1323
    def __call__(self, left, right):
 
1324
        """detect collision between two sprites using scaled rects
 
1325
 
 
1326
        pygame.sprite.collide_rect_ratio(ratio)(left, right): return bool
 
1327
 
 
1328
        Tests for collision between two sprites. Uses the pygame.Rect
 
1329
        colliderect function to calculate the collision after scaling the rects
 
1330
        by the stored ratio. Sprites must have "rect" attributes.
 
1331
 
 
1332
        """
 
1333
 
 
1334
        ratio = self.ratio
 
1335
 
 
1336
        leftrect = left.rect
 
1337
        width = leftrect.width
 
1338
        height = leftrect.height
 
1339
        leftrect = leftrect.inflate(width * ratio - width,
 
1340
                                    height * ratio - height)
 
1341
 
 
1342
        rightrect = right.rect
 
1343
        width = rightrect.width
 
1344
        height = rightrect.height
 
1345
        rightrect = rightrect.inflate(width * ratio - width,
 
1346
                                      height * ratio - height)
 
1347
 
 
1348
        return leftrect.colliderect(rightrect)
 
1349
 
 
1350
def collide_circle(left, right):
 
1351
    """detect collision between two sprites using circles
 
1352
 
 
1353
    pygame.sprite.collide_circle(left, right): return bool
 
1354
 
 
1355
    Tests for collision between two sprites by testing whether two circles
 
1356
    centered on the sprites overlap. If the sprites have a "radius" attribute,
 
1357
    then that radius is used to create the circle; otherwise, a circle is
 
1358
    created that is big enough to completely enclose the sprite's rect as
 
1359
    given by the "rect" attribute. This function is intended to be passed as
 
1360
    a collided callback function to the *collide functions. Sprites must have a
 
1361
    "rect" and an optional "radius" attribute.
 
1362
 
 
1363
    New in pygame 1.8.0
 
1364
 
 
1365
    """
 
1366
 
 
1367
    xdistance = left.rect.centerx - right.rect.centerx
 
1368
    ydistance = left.rect.centery - right.rect.centery
 
1369
    distancesquared = xdistance ** 2 + ydistance ** 2
 
1370
    
 
1371
    if hasattr(left, 'radius'):
 
1372
        leftradius = left.radius
 
1373
    else:
 
1374
        leftrect = left.rect
 
1375
        # approximating the radius of a square by using half of the diagonal, 
 
1376
        # might give false positives (especially if its a long small rect)
 
1377
        leftradius = 0.5 * ((leftrect.width ** 2 + leftrect.height ** 2) ** 0.5)
 
1378
        # store the radius on the sprite for next time
 
1379
        setattr(left, 'radius', leftradius)
 
1380
        
 
1381
    if hasattr(right, 'radius'):
 
1382
        rightradius = right.radius
 
1383
    else:
 
1384
        rightrect = right.rect
 
1385
        # approximating the radius of a square by using half of the diagonal
 
1386
        # might give false positives (especially if its a long small rect)
 
1387
        rightradius = 0.5 * ((rightrect.width ** 2 + rightrect.height ** 2) ** 0.5)
 
1388
        # store the radius on the sprite for next time
 
1389
        setattr(right, 'radius', rightradius)
 
1390
    return distancesquared <= (leftradius + rightradius) ** 2
 
1391
 
 
1392
class collide_circle_ratio(object):
 
1393
    """detect collision between two sprites using scaled circles
 
1394
 
 
1395
    This callable class checks for collisions between two sprites using a
 
1396
    scaled version of a sprite's radius. It is created with a ratio as the
 
1397
    argument to the constructor. The instance is then intended to be passed as
 
1398
    a collided callback function to the *collide functions.
 
1399
 
 
1400
    New in pygame 1.8.1
 
1401
 
 
1402
    """
 
1403
 
 
1404
    def __init__(self, ratio):
 
1405
        """creates a new collide_circle_ratio callable instance
 
1406
 
 
1407
        The given ratio is expected to be a floating point value used to scale
 
1408
        the underlying sprite radius before checking for collisions.
 
1409
        
 
1410
        When the ratio is ratio=1.0, then it behaves exactly like the 
 
1411
        collide_circle method.
 
1412
 
 
1413
        """
 
1414
        self.ratio = ratio
 
1415
 
 
1416
 
 
1417
    def __call__(self, left, right):
 
1418
        """detect collision between two sprites using scaled circles
 
1419
 
 
1420
        pygame.sprite.collide_circle_radio(ratio)(left, right): return bool
 
1421
 
 
1422
        Tests for collision between two sprites by testing whether two circles
 
1423
        centered on the sprites overlap after scaling the circle's radius by
 
1424
        the stored ratio. If the sprites have a "radius" attribute, that is
 
1425
        used to create the circle; otherwise, a circle is created that is big
 
1426
        enough to completely enclose the sprite's rect as given by the "rect"
 
1427
        attribute. Intended to be passed as a collided callback function to the
 
1428
        *collide functions. Sprites must have a "rect" and an optional "radius"
 
1429
        attribute.
 
1430
 
 
1431
        """
 
1432
 
 
1433
        ratio = self.ratio
 
1434
        xdistance = left.rect.centerx - right.rect.centerx
 
1435
        ydistance = left.rect.centery - right.rect.centery
 
1436
        distancesquared = xdistance ** 2 + ydistance ** 2
 
1437
 
 
1438
        if hasattr(left, "radius"):
 
1439
            leftradius = left.radius * ratio
 
1440
        else:
 
1441
            leftrect = left.rect
 
1442
            leftradius = ratio * 0.5 * ((leftrect.width ** 2 + leftrect.height ** 2) ** 0.5)
 
1443
            # store the radius on the sprite for next time
 
1444
            setattr(left, 'radius', leftradius)
 
1445
 
 
1446
        if hasattr(right, "radius"):
 
1447
            rightradius = right.radius * ratio
 
1448
        else:
 
1449
            rightrect = right.rect
 
1450
            rightradius = ratio * 0.5 * ((rightrect.width ** 2 + rightrect.height ** 2) ** 0.5)
 
1451
            # store the radius on the sprite for next time
 
1452
            setattr(right, 'radius', rightradius)
 
1453
 
 
1454
        return distancesquared <= (leftradius + rightradius) ** 2
 
1455
 
 
1456
def collide_mask(left, right):
 
1457
    """collision detection between two sprites, using masks.
 
1458
 
 
1459
    pygame.sprite.collide_mask(SpriteLeft, SpriteRight): bool
 
1460
 
 
1461
    Tests for collision between two sprites by testing if their bitmasks
 
1462
    overlap. If the sprites have a "mask" attribute, that is used as the mask;
 
1463
    otherwise, a mask is created from the sprite image. Intended to be passed
 
1464
    as a collided callback function to the *collide functions. Sprites must
 
1465
    have a "rect" and an optional "mask" attribute.
 
1466
 
 
1467
    New in pygame 1.8.0
 
1468
 
 
1469
    """
 
1470
    xoffset = right.rect[0] - left.rect[0]
 
1471
    yoffset = right.rect[1] - left.rect[1]
 
1472
    try:
 
1473
        leftmask = left.mask
 
1474
    except AttributeError:
 
1475
        leftmask = from_surface(left.image)
 
1476
    try:
 
1477
        rightmask = right.mask
 
1478
    except AttributeError:
 
1479
        rightmask = from_surface(right.image)
 
1480
    return leftmask.overlap(rightmask, (xoffset, yoffset))
 
1481
 
 
1482
def spritecollide(sprite, group, dokill, collided=None):
 
1483
    """find Sprites in a Group that intersect another Sprite
 
1484
 
 
1485
    pygame.sprite.spritecollide(sprite, group, dokill, collided=None):
 
1486
        return Sprite_list
 
1487
 
 
1488
    Return a list containing all Sprites in a Group that intersect with another
 
1489
    Sprite. Intersection is determined by comparing the Sprite.rect attribute
 
1490
    of each Sprite.
 
1491
 
 
1492
    The dokill argument is a bool. If set to True, all Sprites that collide
 
1493
    will be removed from the Group.
 
1494
 
 
1495
    The collided argument is a callback function used to calculate if two
 
1496
    sprites are colliding. it should take two sprites as values, and return a
 
1497
    bool value indicating if they are colliding. If collided is not passed, all
 
1498
    sprites must have a "rect" value, which is a rectangle of the sprite area,
 
1499
    which will be used to calculate the collision.
 
1500
 
 
1501
    """
 
1502
    if dokill:
 
1503
 
 
1504
        crashed = []
 
1505
        append = crashed.append
 
1506
 
 
1507
        if collided:
 
1508
            for s in group.sprites():
 
1509
                if collided(sprite, s):
 
1510
                    s.kill()
 
1511
                    append(s)
 
1512
        else:
 
1513
            spritecollide = sprite.rect.colliderect
 
1514
            for s in group.sprites():
 
1515
                if spritecollide(s.rect):
 
1516
                    s.kill()
 
1517
                    append(s)
 
1518
 
 
1519
        return crashed
 
1520
 
 
1521
    elif collided:
 
1522
        return [s for s in group if collided(sprite, s)]
 
1523
    else:
 
1524
        spritecollide = sprite.rect.colliderect
 
1525
        return [s for s in group if spritecollide(s.rect)]
 
1526
 
 
1527
 
 
1528
def groupcollide(groupa, groupb, dokilla, dokillb, collided=None):
 
1529
    """detect collision between a group and another group
 
1530
 
 
1531
    pygame.sprite.groupcollide(groupa, groupb, dokilla, dokillb):
 
1532
        return dict
 
1533
 
 
1534
    Given two groups, this will find the intersections between all sprites in
 
1535
    each group. It returns a dictionary of all sprites in the first group that
 
1536
    collide. The value for each item in the dictionary is a list of the sprites
 
1537
    in the second group it collides with. The two dokill arguments control if
 
1538
    the sprites from either group will be automatically removed from all
 
1539
    groups. Collided is a callback function used to calculate if two sprites
 
1540
    are colliding. it should take two sprites as values, and return a bool
 
1541
    value indicating if they are colliding. If collided is not passed, all
 
1542
    sprites must have a "rect" value, which is a rectangle of the sprite area
 
1543
    that will be used to calculate the collision.
 
1544
 
 
1545
    """
 
1546
    crashed = {}
 
1547
    SC = spritecollide
 
1548
    if dokilla:
 
1549
        for s in groupa.sprites():
 
1550
            c = SC(s, groupb, dokillb, collided)
 
1551
            if c:
 
1552
                crashed[s] = c
 
1553
                s.kill()
 
1554
    else:
 
1555
        for s in groupa:
 
1556
            c = SC(s, groupb, dokillb, collided)
 
1557
            if c:
 
1558
                crashed[s] = c
 
1559
    return crashed
 
1560
 
 
1561
def spritecollideany(sprite, group, collided=None):
 
1562
    """finds any sprites in a group that collide with the given sprite
 
1563
 
 
1564
    pygame.sprite.spritecollideany(sprite, group): return sprite
 
1565
 
 
1566
    Given a sprite and a group of sprites, this will return return any single
 
1567
    sprite that collides with with the given sprite. If there are no
 
1568
    collisions, then this returns None.
 
1569
 
 
1570
    If you don't need all the features of the spritecollide function, this
 
1571
    function will be a bit quicker.
 
1572
 
 
1573
    Collided is a callback function used to calculate if two sprites are
 
1574
    colliding. It should take two sprites as values and return a bool value
 
1575
    indicating if they are colliding. If collided is not passed, then all
 
1576
    sprites must have a "rect" value, which is a rectangle of the sprite area,
 
1577
    which will be used to calculate the collision.
 
1578
 
 
1579
    """
 
1580
    if collided:
 
1581
        for s in group:
 
1582
            if collided(sprite, s):
 
1583
                return s
 
1584
    else:
 
1585
        # Special case old behaviour for speed.
 
1586
        spritecollide = sprite.rect.colliderect
 
1587
        for s in group:
 
1588
            if spritecollide(s.rect):
 
1589
                return s
 
1590
    return None