1cfd74d65d832137e20e193c960802afba73b5d38sm/*
23c1e67e433728684b5f228c5d4f3e5b1457bb271sm * Copyright (C) 2010 The Android Open Source Project
3cfd74d65d832137e20e193c960802afba73b5d38sm *
4cfd74d65d832137e20e193c960802afba73b5d38sm * Licensed under the Apache License, Version 2.0 (the "License");
5cfd74d65d832137e20e193c960802afba73b5d38sm * you may not use this file except in compliance with the License.
6cfd74d65d832137e20e193c960802afba73b5d38sm * You may obtain a copy of the License at
7cfd74d65d832137e20e193c960802afba73b5d38sm *
8cfd74d65d832137e20e193c960802afba73b5d38sm *      http://www.apache.org/licenses/LICENSE-2.0
9cfd74d65d832137e20e193c960802afba73b5d38sm *
10cfd74d65d832137e20e193c960802afba73b5d38sm * Unless required by applicable law or agreed to in writing, software
11cfd74d65d832137e20e193c960802afba73b5d38sm * distributed under the License is distributed on an "AS IS" BASIS,
12cfd74d65d832137e20e193c960802afba73b5d38sm * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cfd74d65d832137e20e193c960802afba73b5d38sm * See the License for the specific language governing permissions and
14cfd74d65d832137e20e193c960802afba73b5d38sm * limitations under the License.
15cfd74d65d832137e20e193c960802afba73b5d38sm */
16cfd74d65d832137e20e193c960802afba73b5d38sm
17cfd74d65d832137e20e193c960802afba73b5d38smpackage com.replica.replicaisland;
18cfd74d65d832137e20e193c960802afba73b5d38sm
19cfd74d65d832137e20e193c960802afba73b5d38smimport java.util.Comparator;
20cfd74d65d832137e20e193c960802afba73b5d38sm
21cfd74d65d832137e20e193c960802afba73b5d38smimport com.replica.replicaisland.CollisionParameters.HitType;
22cfd74d65d832137e20e193c960802afba73b5d38sm
23cfd74d65d832137e20e193c960802afba73b5d38sm/**
24cfd74d65d832137e20e193c960802afba73b5d38sm * A system for calculating collisions between moving game objects.  This system accepts collision
25cfd74d65d832137e20e193c960802afba73b5d38sm * volumes from game objects each frame and performs a series of tests to see which of them
26cfd74d65d832137e20e193c960802afba73b5d38sm * overlap.  Collisions are only considered between offending "attack" volumes and receiving
27cfd74d65d832137e20e193c960802afba73b5d38sm * "vulnerability" volumes.  This implementation works by using a sweep-and-prune algorithm:
28cfd74d65d832137e20e193c960802afba73b5d38sm * objects to be considered are sorted in the x axis and then compared in one dimension for
29cfd74d65d832137e20e193c960802afba73b5d38sm * overlaps.  A bounding volume that encompasses all attack and vulnerability volumes is used for
30cfd74d65d832137e20e193c960802afba73b5d38sm * this test, and when an intersection is found the actual offending and receiving volumes are
31cfd74d65d832137e20e193c960802afba73b5d38sm * compared.  If an intersection is detected both objects receive notification via a
32cfd74d65d832137e20e193c960802afba73b5d38sm * HitReactionComponent, if one has been specified.
33cfd74d65d832137e20e193c960802afba73b5d38sm */
34cfd74d65d832137e20e193c960802afba73b5d38smpublic class GameObjectCollisionSystem extends BaseObject {
359d4cc2572d37983607df38b0f4216ed76ac51814sm    private static final int MAX_COLLIDING_OBJECTS = 256;
369d4cc2572d37983607df38b0f4216ed76ac51814sm    private static final int COLLISION_RECORD_POOL_SIZE = 256;
37cfd74d65d832137e20e193c960802afba73b5d38sm    private static final CollisionVolumeComparator sCollisionVolumeComparator
38cfd74d65d832137e20e193c960802afba73b5d38sm        = new CollisionVolumeComparator();
39cfd74d65d832137e20e193c960802afba73b5d38sm    private static CollisionVolume.FlipInfo sFlip = new CollisionVolume.FlipInfo();
40cfd74d65d832137e20e193c960802afba73b5d38sm    private static CollisionVolume.FlipInfo sOtherFlip = new CollisionVolume.FlipInfo();
41cfd74d65d832137e20e193c960802afba73b5d38sm
42cfd74d65d832137e20e193c960802afba73b5d38sm    FixedSizeArray<CollisionVolumeRecord> mObjects;
43cfd74d65d832137e20e193c960802afba73b5d38sm    CollisionVolumeRecordPool mRecordPool;
44cfd74d65d832137e20e193c960802afba73b5d38sm	private boolean mDrawDebugBoundingVolume = false;
45cfd74d65d832137e20e193c960802afba73b5d38sm	private boolean mDrawDebugCollisionVolumes = false;
46cfd74d65d832137e20e193c960802afba73b5d38sm
47cfd74d65d832137e20e193c960802afba73b5d38sm
48cfd74d65d832137e20e193c960802afba73b5d38sm    public GameObjectCollisionSystem() {
49cfd74d65d832137e20e193c960802afba73b5d38sm        super();
50cfd74d65d832137e20e193c960802afba73b5d38sm        mObjects = new FixedSizeArray<CollisionVolumeRecord>(MAX_COLLIDING_OBJECTS);
51cfd74d65d832137e20e193c960802afba73b5d38sm        mObjects.setComparator(sCollisionVolumeComparator);
52cfd74d65d832137e20e193c960802afba73b5d38sm        //mObjects.setSorter(new ShellSorter<CollisionVolumeRecord>());
53cfd74d65d832137e20e193c960802afba73b5d38sm        mRecordPool = new CollisionVolumeRecordPool(COLLISION_RECORD_POOL_SIZE);
54cfd74d65d832137e20e193c960802afba73b5d38sm    }
55cfd74d65d832137e20e193c960802afba73b5d38sm
56cfd74d65d832137e20e193c960802afba73b5d38sm    @Override
57cfd74d65d832137e20e193c960802afba73b5d38sm    public void reset() {
58cfd74d65d832137e20e193c960802afba73b5d38sm        final int count = mObjects.getCount();
59cfd74d65d832137e20e193c960802afba73b5d38sm
60cfd74d65d832137e20e193c960802afba73b5d38sm        for (int x = 0; x < count; x++) {
61cfd74d65d832137e20e193c960802afba73b5d38sm            mRecordPool.release(mObjects.get(x));
62cfd74d65d832137e20e193c960802afba73b5d38sm        }
63cfd74d65d832137e20e193c960802afba73b5d38sm        mObjects.clear();
64cfd74d65d832137e20e193c960802afba73b5d38sm
65cfd74d65d832137e20e193c960802afba73b5d38sm        mDrawDebugBoundingVolume = false;
66cfd74d65d832137e20e193c960802afba73b5d38sm        mDrawDebugCollisionVolumes = false;
67cfd74d65d832137e20e193c960802afba73b5d38sm    }
68cfd74d65d832137e20e193c960802afba73b5d38sm
69cfd74d65d832137e20e193c960802afba73b5d38sm    /**
70cfd74d65d832137e20e193c960802afba73b5d38sm     * Adds a game object, and its related volumes, to the dynamic collision world for one frame.
71cfd74d65d832137e20e193c960802afba73b5d38sm     * Once registered for collisions the object may damage other objects via attack volumes or
72cfd74d65d832137e20e193c960802afba73b5d38sm     * receive damage from other volumes via vulnerability volumes.
73cfd74d65d832137e20e193c960802afba73b5d38sm     * @param object  The object to consider for collision.
74cfd74d65d832137e20e193c960802afba73b5d38sm     * @param reactionComponent  A HitReactionComponent to notify when an intersection is calculated.
75cfd74d65d832137e20e193c960802afba73b5d38sm     * If null, the intersection will still occur and no notification will be sent.
76cfd74d65d832137e20e193c960802afba73b5d38sm     * @param boundingVolume  A volume that describes the game object in space.  It should encompass
77cfd74d65d832137e20e193c960802afba73b5d38sm     * all of the attack and vulnerability volumes.
78cfd74d65d832137e20e193c960802afba73b5d38sm     * @param attackVolumes  A list of volumes that can hit other game objects.  May be null.
79cfd74d65d832137e20e193c960802afba73b5d38sm     * @param vulnerabilityVolumes  A list of volumes that can receive hits from other game objects.
80cfd74d65d832137e20e193c960802afba73b5d38sm     * May be null.
81cfd74d65d832137e20e193c960802afba73b5d38sm     */
82cfd74d65d832137e20e193c960802afba73b5d38sm    public void registerForCollisions(GameObject object,
83cfd74d65d832137e20e193c960802afba73b5d38sm            HitReactionComponent reactionComponent,
84cfd74d65d832137e20e193c960802afba73b5d38sm            CollisionVolume boundingVolume,
85cfd74d65d832137e20e193c960802afba73b5d38sm            FixedSizeArray<CollisionVolume> attackVolumes,
86cfd74d65d832137e20e193c960802afba73b5d38sm            FixedSizeArray<CollisionVolume> vulnerabilityVolumes) {
87cfd74d65d832137e20e193c960802afba73b5d38sm        CollisionVolumeRecord record = mRecordPool.allocate();
88cfd74d65d832137e20e193c960802afba73b5d38sm        if (record != null && object != null && boundingVolume != null
89cfd74d65d832137e20e193c960802afba73b5d38sm                && (attackVolumes != null || vulnerabilityVolumes != null)) {
90cfd74d65d832137e20e193c960802afba73b5d38sm            record.object = object;
91cfd74d65d832137e20e193c960802afba73b5d38sm            record.boundingVolume = boundingVolume;
92cfd74d65d832137e20e193c960802afba73b5d38sm            record.attackVolumes = attackVolumes;
93cfd74d65d832137e20e193c960802afba73b5d38sm            record.vulnerabilityVolumes = vulnerabilityVolumes;
94cfd74d65d832137e20e193c960802afba73b5d38sm            record.reactionComponent = reactionComponent;
95cfd74d65d832137e20e193c960802afba73b5d38sm            mObjects.add(record);
96cfd74d65d832137e20e193c960802afba73b5d38sm        }
97cfd74d65d832137e20e193c960802afba73b5d38sm    }
98cfd74d65d832137e20e193c960802afba73b5d38sm
99cfd74d65d832137e20e193c960802afba73b5d38sm    @Override
100cfd74d65d832137e20e193c960802afba73b5d38sm    public void update(float timeDelta, BaseObject parent) {
101cfd74d65d832137e20e193c960802afba73b5d38sm        // Sort the objects by their x position.
102cfd74d65d832137e20e193c960802afba73b5d38sm        mObjects.sort(true);
103cfd74d65d832137e20e193c960802afba73b5d38sm
104cfd74d65d832137e20e193c960802afba73b5d38sm        final int count = mObjects.getCount();
105cfd74d65d832137e20e193c960802afba73b5d38sm        for (int x = 0; x < count; x++) {
106cfd74d65d832137e20e193c960802afba73b5d38sm            final CollisionVolumeRecord record = mObjects.get(x);
107cfd74d65d832137e20e193c960802afba73b5d38sm            final Vector2 position = record.object.getPosition();
108cfd74d65d832137e20e193c960802afba73b5d38sm            sFlip.flipX = (record.object.facingDirection.x < 0.0f);
109cfd74d65d832137e20e193c960802afba73b5d38sm            sFlip.flipY = (record.object.facingDirection.y < 0.0f);
110cfd74d65d832137e20e193c960802afba73b5d38sm            sFlip.parentWidth = record.object.width;
111cfd74d65d832137e20e193c960802afba73b5d38sm            sFlip.parentHeight = record.object.height;
112cfd74d65d832137e20e193c960802afba73b5d38sm
113cfd74d65d832137e20e193c960802afba73b5d38sm            if (sSystemRegistry.debugSystem != null) {
114cfd74d65d832137e20e193c960802afba73b5d38sm            	drawDebugVolumes(record);
115cfd74d65d832137e20e193c960802afba73b5d38sm            }
116cfd74d65d832137e20e193c960802afba73b5d38sm
117cfd74d65d832137e20e193c960802afba73b5d38sm            final float maxX = record.boundingVolume.getMaxXPosition(sFlip) + position.x;
118cfd74d65d832137e20e193c960802afba73b5d38sm            for (int y = x + 1; y < count; y++) {
119cfd74d65d832137e20e193c960802afba73b5d38sm                final CollisionVolumeRecord other = mObjects.get(y);
120cfd74d65d832137e20e193c960802afba73b5d38sm                final Vector2 otherPosition = other.object.getPosition();
121cfd74d65d832137e20e193c960802afba73b5d38sm                sOtherFlip.flipX = (other.object.facingDirection.x < 0.0f);
122cfd74d65d832137e20e193c960802afba73b5d38sm                sOtherFlip.flipY = (other.object.facingDirection.y < 0.0f);
123cfd74d65d832137e20e193c960802afba73b5d38sm                sOtherFlip.parentWidth = other.object.width;
124cfd74d65d832137e20e193c960802afba73b5d38sm                sOtherFlip.parentHeight = other.object.height;
125cfd74d65d832137e20e193c960802afba73b5d38sm
126cfd74d65d832137e20e193c960802afba73b5d38sm                if (otherPosition.x + other.boundingVolume.getMinXPosition(sOtherFlip) > maxX) {
127cfd74d65d832137e20e193c960802afba73b5d38sm                    // These objects can't possibly be colliding.  And since the list is sorted,
128cfd74d65d832137e20e193c960802afba73b5d38sm                    // there are no potentially colliding objects after this object
129cfd74d65d832137e20e193c960802afba73b5d38sm                    // either, so we're done!
130cfd74d65d832137e20e193c960802afba73b5d38sm                    break;
131cfd74d65d832137e20e193c960802afba73b5d38sm                } else {
132cfd74d65d832137e20e193c960802afba73b5d38sm                	final boolean testRequired = (record.attackVolumes != null && other.vulnerabilityVolumes != null) ||
133cfd74d65d832137e20e193c960802afba73b5d38sm                		(record.vulnerabilityVolumes != null && other.attackVolumes != null);
134cfd74d65d832137e20e193c960802afba73b5d38sm                    if (testRequired && record.boundingVolume.intersects(position, sFlip,
135cfd74d65d832137e20e193c960802afba73b5d38sm                        other.boundingVolume, otherPosition, sOtherFlip)) {
136cfd74d65d832137e20e193c960802afba73b5d38sm                        // These two objects are potentially colliding.
137cfd74d65d832137e20e193c960802afba73b5d38sm                        // Now we must test all attack vs vulnerability boxes.
138cfd74d65d832137e20e193c960802afba73b5d38sm                        final int hit = testAttackAgainstVulnerability(
139cfd74d65d832137e20e193c960802afba73b5d38sm                                record.attackVolumes,
140cfd74d65d832137e20e193c960802afba73b5d38sm                                other.vulnerabilityVolumes,
141cfd74d65d832137e20e193c960802afba73b5d38sm                                position,
142cfd74d65d832137e20e193c960802afba73b5d38sm                                otherPosition,
143cfd74d65d832137e20e193c960802afba73b5d38sm                                sFlip,
144cfd74d65d832137e20e193c960802afba73b5d38sm                                sOtherFlip);
145cfd74d65d832137e20e193c960802afba73b5d38sm                        if (hit != HitType.INVALID) {
146cfd74d65d832137e20e193c960802afba73b5d38sm                            boolean hitAccepted = false;
147cfd74d65d832137e20e193c960802afba73b5d38sm                            if (other.reactionComponent != null) {
148cfd74d65d832137e20e193c960802afba73b5d38sm                                hitAccepted = other.reactionComponent.receivedHit(
149cfd74d65d832137e20e193c960802afba73b5d38sm                                        other.object, record.object, hit);
150cfd74d65d832137e20e193c960802afba73b5d38sm                            }
151cfd74d65d832137e20e193c960802afba73b5d38sm                            if (record.reactionComponent != null) {
152cfd74d65d832137e20e193c960802afba73b5d38sm                                record.reactionComponent.hitVictim(
153cfd74d65d832137e20e193c960802afba73b5d38sm                                        record.object, other.object, hit, hitAccepted);
154cfd74d65d832137e20e193c960802afba73b5d38sm                            }
155cfd74d65d832137e20e193c960802afba73b5d38sm
156cfd74d65d832137e20e193c960802afba73b5d38sm                        }
157cfd74d65d832137e20e193c960802afba73b5d38sm
158cfd74d65d832137e20e193c960802afba73b5d38sm                        final int hit2 = testAttackAgainstVulnerability(
159cfd74d65d832137e20e193c960802afba73b5d38sm                                other.attackVolumes,
160cfd74d65d832137e20e193c960802afba73b5d38sm                                record.vulnerabilityVolumes,
161cfd74d65d832137e20e193c960802afba73b5d38sm                                otherPosition,
162cfd74d65d832137e20e193c960802afba73b5d38sm                                position,
163cfd74d65d832137e20e193c960802afba73b5d38sm                                sOtherFlip,
164cfd74d65d832137e20e193c960802afba73b5d38sm                                sFlip);
165cfd74d65d832137e20e193c960802afba73b5d38sm                        if (hit2 != HitType.INVALID) {
166cfd74d65d832137e20e193c960802afba73b5d38sm                            boolean hitAccepted = false;
167cfd74d65d832137e20e193c960802afba73b5d38sm                            if (record.reactionComponent != null) {
168cfd74d65d832137e20e193c960802afba73b5d38sm                                hitAccepted = record.reactionComponent.receivedHit(
169cfd74d65d832137e20e193c960802afba73b5d38sm                                        record.object, other.object, hit2);
170cfd74d65d832137e20e193c960802afba73b5d38sm                            }
171cfd74d65d832137e20e193c960802afba73b5d38sm                            if (other.reactionComponent != null) {
172cfd74d65d832137e20e193c960802afba73b5d38sm                                other.reactionComponent.hitVictim(
173cfd74d65d832137e20e193c960802afba73b5d38sm                                        other.object, record.object, hit2, hitAccepted);
174cfd74d65d832137e20e193c960802afba73b5d38sm                            }
175cfd74d65d832137e20e193c960802afba73b5d38sm
176cfd74d65d832137e20e193c960802afba73b5d38sm                        }
177cfd74d65d832137e20e193c960802afba73b5d38sm                    }
178cfd74d65d832137e20e193c960802afba73b5d38sm                }
179cfd74d65d832137e20e193c960802afba73b5d38sm            }
180cfd74d65d832137e20e193c960802afba73b5d38sm            // This is a little tricky.  Since we always sweep forward in the list it's safe
181cfd74d65d832137e20e193c960802afba73b5d38sm            // to invalidate the current record after we've tested it.  This way we don't have to
182cfd74d65d832137e20e193c960802afba73b5d38sm            // iterate over the object list twice.
183cfd74d65d832137e20e193c960802afba73b5d38sm            mRecordPool.release(record);
184cfd74d65d832137e20e193c960802afba73b5d38sm        }
185cfd74d65d832137e20e193c960802afba73b5d38sm
186cfd74d65d832137e20e193c960802afba73b5d38sm        mObjects.clear();
187cfd74d65d832137e20e193c960802afba73b5d38sm    }
188cfd74d65d832137e20e193c960802afba73b5d38sm
189cfd74d65d832137e20e193c960802afba73b5d38sm    /** Compares the passed list of attack volumes against the passed list of vulnerability volumes
190cfd74d65d832137e20e193c960802afba73b5d38sm     * and returns a hit type if an intersection is found.
191cfd74d65d832137e20e193c960802afba73b5d38sm     * @param attackVolumes  Offensive collision volumes.
192cfd74d65d832137e20e193c960802afba73b5d38sm     * @param vulnerabilityVolumes  Receiving collision volumes.
193cfd74d65d832137e20e193c960802afba73b5d38sm     * @param attackPosition  The world position of the attacking object.
194cfd74d65d832137e20e193c960802afba73b5d38sm     * @param vulnerabilityPosition  The world position of the receiving object.
195cfd74d65d832137e20e193c960802afba73b5d38sm     * @return  The hit type of the first attacking volume that intersects a vulnerability volume,
196cfd74d65d832137e20e193c960802afba73b5d38sm     * or HitType.INVALID if no intersections are found.
197cfd74d65d832137e20e193c960802afba73b5d38sm     */
198cfd74d65d832137e20e193c960802afba73b5d38sm    private int testAttackAgainstVulnerability(
199cfd74d65d832137e20e193c960802afba73b5d38sm            FixedSizeArray<CollisionVolume> attackVolumes,
200cfd74d65d832137e20e193c960802afba73b5d38sm            FixedSizeArray<CollisionVolume> vulnerabilityVolumes,
201cfd74d65d832137e20e193c960802afba73b5d38sm            Vector2 attackPosition,
202cfd74d65d832137e20e193c960802afba73b5d38sm            Vector2 vulnerabilityPosition,
203cfd74d65d832137e20e193c960802afba73b5d38sm            CollisionVolume.FlipInfo attackFlip,
204cfd74d65d832137e20e193c960802afba73b5d38sm            CollisionVolume.FlipInfo vulnerabilityFlip) {
205cfd74d65d832137e20e193c960802afba73b5d38sm        int intersectionType = HitType.INVALID;
206cfd74d65d832137e20e193c960802afba73b5d38sm        if (attackVolumes != null && vulnerabilityVolumes != null) {
207cfd74d65d832137e20e193c960802afba73b5d38sm            final int attackCount = attackVolumes.getCount();
208cfd74d65d832137e20e193c960802afba73b5d38sm            for (int x = 0; x < attackCount && intersectionType == HitType.INVALID; x++) {
209cfd74d65d832137e20e193c960802afba73b5d38sm                final CollisionVolume attackVolume = attackVolumes.get(x);
210cfd74d65d832137e20e193c960802afba73b5d38sm                final int hitType = attackVolume.getHitType();
211cfd74d65d832137e20e193c960802afba73b5d38sm                if (hitType != HitType.INVALID) {
212cfd74d65d832137e20e193c960802afba73b5d38sm                    final int vulnerabilityCount = vulnerabilityVolumes.getCount();
213cfd74d65d832137e20e193c960802afba73b5d38sm                    for (int y = 0; y < vulnerabilityCount; y++) {
214cfd74d65d832137e20e193c960802afba73b5d38sm                        final CollisionVolume vulnerabilityVolume = vulnerabilityVolumes.get(y);
215cfd74d65d832137e20e193c960802afba73b5d38sm                        final int vulnerableType = vulnerabilityVolume.getHitType();
216cfd74d65d832137e20e193c960802afba73b5d38sm                        if (vulnerableType == HitType.INVALID || vulnerableType == hitType) {
217cfd74d65d832137e20e193c960802afba73b5d38sm                            if (attackVolume.intersects(attackPosition, attackFlip,
218cfd74d65d832137e20e193c960802afba73b5d38sm                                    vulnerabilityVolume, vulnerabilityPosition,
219cfd74d65d832137e20e193c960802afba73b5d38sm                                    vulnerabilityFlip)) {
220cfd74d65d832137e20e193c960802afba73b5d38sm                                intersectionType = hitType;
221cfd74d65d832137e20e193c960802afba73b5d38sm                                break;
222cfd74d65d832137e20e193c960802afba73b5d38sm                            }
223cfd74d65d832137e20e193c960802afba73b5d38sm                        }
224cfd74d65d832137e20e193c960802afba73b5d38sm                    }
225cfd74d65d832137e20e193c960802afba73b5d38sm                }
226cfd74d65d832137e20e193c960802afba73b5d38sm            }
227cfd74d65d832137e20e193c960802afba73b5d38sm        }
228cfd74d65d832137e20e193c960802afba73b5d38sm
229cfd74d65d832137e20e193c960802afba73b5d38sm        return intersectionType;
230cfd74d65d832137e20e193c960802afba73b5d38sm    }
231cfd74d65d832137e20e193c960802afba73b5d38sm
232cfd74d65d832137e20e193c960802afba73b5d38sm    private final void drawDebugVolumes(CollisionVolumeRecord record) {
233cfd74d65d832137e20e193c960802afba73b5d38sm    	final Vector2 position = record.object.getPosition();
234cfd74d65d832137e20e193c960802afba73b5d38sm    	if (mDrawDebugBoundingVolume) {
235cfd74d65d832137e20e193c960802afba73b5d38sm	    	final CollisionVolume boundingVolume = record.boundingVolume;
236cfd74d65d832137e20e193c960802afba73b5d38sm	    	sSystemRegistry.debugSystem.drawShape(
237cfd74d65d832137e20e193c960802afba73b5d38sm	    			position.x + boundingVolume.getMinXPosition(sFlip), position.y + boundingVolume.getMinYPosition(sFlip),
238cfd74d65d832137e20e193c960802afba73b5d38sm	    			boundingVolume.getMaxX() - boundingVolume.getMinX(),
239cfd74d65d832137e20e193c960802afba73b5d38sm	    			boundingVolume.getMaxY() - boundingVolume.getMinY(),
240cfd74d65d832137e20e193c960802afba73b5d38sm	    			DebugSystem.SHAPE_CIRCLE,
241cfd74d65d832137e20e193c960802afba73b5d38sm	    			DebugSystem.COLOR_OUTLINE);
242cfd74d65d832137e20e193c960802afba73b5d38sm    	}
243cfd74d65d832137e20e193c960802afba73b5d38sm    	if (mDrawDebugCollisionVolumes) {
244cfd74d65d832137e20e193c960802afba73b5d38sm	    	if (record.attackVolumes != null) {
245cfd74d65d832137e20e193c960802afba73b5d38sm	    		final int attackVolumeCount = record.attackVolumes.getCount();
246cfd74d65d832137e20e193c960802afba73b5d38sm	    		for (int y = 0; y < attackVolumeCount; y++) {
247cfd74d65d832137e20e193c960802afba73b5d38sm	    			CollisionVolume volume = record.attackVolumes.get(y);
248cfd74d65d832137e20e193c960802afba73b5d38sm	    			sSystemRegistry.debugSystem.drawShape(
249cfd74d65d832137e20e193c960802afba73b5d38sm	    					position.x + volume.getMinXPosition(sFlip), position.y + volume.getMinYPosition(sFlip),
250cfd74d65d832137e20e193c960802afba73b5d38sm	    					volume.getMaxX() - volume.getMinX(),
251cfd74d65d832137e20e193c960802afba73b5d38sm	    					volume.getMaxY() - volume.getMinY(),
252cfd74d65d832137e20e193c960802afba73b5d38sm	    	    			volume.getClass() == AABoxCollisionVolume.class ? DebugSystem.SHAPE_BOX : DebugSystem.SHAPE_CIRCLE,
253cfd74d65d832137e20e193c960802afba73b5d38sm	    	    			DebugSystem.COLOR_RED);
254cfd74d65d832137e20e193c960802afba73b5d38sm	    		}
255cfd74d65d832137e20e193c960802afba73b5d38sm	    	}
256cfd74d65d832137e20e193c960802afba73b5d38sm
257cfd74d65d832137e20e193c960802afba73b5d38sm	    	if (record.vulnerabilityVolumes != null) {
258cfd74d65d832137e20e193c960802afba73b5d38sm	    		final int vulnVolumeCount = record.vulnerabilityVolumes.getCount();
259cfd74d65d832137e20e193c960802afba73b5d38sm	    		for (int y = 0; y < vulnVolumeCount; y++) {
260cfd74d65d832137e20e193c960802afba73b5d38sm	    			CollisionVolume volume = record.vulnerabilityVolumes.get(y);
261cfd74d65d832137e20e193c960802afba73b5d38sm	    			sSystemRegistry.debugSystem.drawShape(
262cfd74d65d832137e20e193c960802afba73b5d38sm	    					position.x + volume.getMinXPosition(sFlip), position.y + volume.getMinYPosition(sFlip),
263cfd74d65d832137e20e193c960802afba73b5d38sm	    					volume.getMaxX() - volume.getMinX(),
264cfd74d65d832137e20e193c960802afba73b5d38sm	    					volume.getMaxY() - volume.getMinY(),
265cfd74d65d832137e20e193c960802afba73b5d38sm	    	    			volume.getClass() == AABoxCollisionVolume.class ? DebugSystem.SHAPE_BOX : DebugSystem.SHAPE_CIRCLE,
266cfd74d65d832137e20e193c960802afba73b5d38sm	    	    			DebugSystem.COLOR_BLUE);
267cfd74d65d832137e20e193c960802afba73b5d38sm	    		}
268cfd74d65d832137e20e193c960802afba73b5d38sm	    	}
269cfd74d65d832137e20e193c960802afba73b5d38sm    	}
270cfd74d65d832137e20e193c960802afba73b5d38sm    }
271cfd74d65d832137e20e193c960802afba73b5d38sm
272cfd74d65d832137e20e193c960802afba73b5d38sm    public void setDebugPrefs(boolean drawBoundingVolumes, boolean drawCollisionVolumes) {
273cfd74d65d832137e20e193c960802afba73b5d38sm		mDrawDebugBoundingVolume = drawBoundingVolumes;
274cfd74d65d832137e20e193c960802afba73b5d38sm		mDrawDebugCollisionVolumes = drawCollisionVolumes;
275cfd74d65d832137e20e193c960802afba73b5d38sm	}
276cfd74d65d832137e20e193c960802afba73b5d38sm
277cfd74d65d832137e20e193c960802afba73b5d38sm    /** A record of a single game object and its associated collision info.  */
278cfd74d65d832137e20e193c960802afba73b5d38sm    private class CollisionVolumeRecord extends AllocationGuard {
279cfd74d65d832137e20e193c960802afba73b5d38sm        public GameObject object;
280cfd74d65d832137e20e193c960802afba73b5d38sm        public HitReactionComponent reactionComponent;
281cfd74d65d832137e20e193c960802afba73b5d38sm        public CollisionVolume boundingVolume;
282cfd74d65d832137e20e193c960802afba73b5d38sm        public FixedSizeArray<CollisionVolume> attackVolumes;
283cfd74d65d832137e20e193c960802afba73b5d38sm        public FixedSizeArray<CollisionVolume> vulnerabilityVolumes;
284cfd74d65d832137e20e193c960802afba73b5d38sm
285cfd74d65d832137e20e193c960802afba73b5d38sm        public void reset() {
286cfd74d65d832137e20e193c960802afba73b5d38sm            object = null;
287cfd74d65d832137e20e193c960802afba73b5d38sm            attackVolumes = null;
288cfd74d65d832137e20e193c960802afba73b5d38sm            vulnerabilityVolumes = null;
289cfd74d65d832137e20e193c960802afba73b5d38sm            boundingVolume = null;
290cfd74d65d832137e20e193c960802afba73b5d38sm            reactionComponent = null;
291cfd74d65d832137e20e193c960802afba73b5d38sm        }
292cfd74d65d832137e20e193c960802afba73b5d38sm    }
293cfd74d65d832137e20e193c960802afba73b5d38sm
294cfd74d65d832137e20e193c960802afba73b5d38sm    /** A pool of collision volume records.  */
295cfd74d65d832137e20e193c960802afba73b5d38sm    private class CollisionVolumeRecordPool extends TObjectPool<CollisionVolumeRecord> {
296cfd74d65d832137e20e193c960802afba73b5d38sm
297cfd74d65d832137e20e193c960802afba73b5d38sm        public CollisionVolumeRecordPool(int count) {
298cfd74d65d832137e20e193c960802afba73b5d38sm            super(count);
299cfd74d65d832137e20e193c960802afba73b5d38sm        }
300cfd74d65d832137e20e193c960802afba73b5d38sm
301cfd74d65d832137e20e193c960802afba73b5d38sm        @Override
302cfd74d65d832137e20e193c960802afba73b5d38sm        protected void fill() {
303cfd74d65d832137e20e193c960802afba73b5d38sm            for (int x = 0; x < getSize(); x++) {
304cfd74d65d832137e20e193c960802afba73b5d38sm                getAvailable().add(new CollisionVolumeRecord());
305cfd74d65d832137e20e193c960802afba73b5d38sm            }
306cfd74d65d832137e20e193c960802afba73b5d38sm        }
307cfd74d65d832137e20e193c960802afba73b5d38sm
308cfd74d65d832137e20e193c960802afba73b5d38sm        @Override
309cfd74d65d832137e20e193c960802afba73b5d38sm        public void release(Object entry) {
310cfd74d65d832137e20e193c960802afba73b5d38sm            ((CollisionVolumeRecord)entry).reset();
311cfd74d65d832137e20e193c960802afba73b5d38sm            super.release(entry);
312cfd74d65d832137e20e193c960802afba73b5d38sm        }
313cfd74d65d832137e20e193c960802afba73b5d38sm
314cfd74d65d832137e20e193c960802afba73b5d38sm    }
315cfd74d65d832137e20e193c960802afba73b5d38sm
316cfd74d65d832137e20e193c960802afba73b5d38sm    /**
317cfd74d65d832137e20e193c960802afba73b5d38sm     * Comparator for game objects that considers the world position of the object's bounding
318cfd74d65d832137e20e193c960802afba73b5d38sm     * volume and sorts objects from left to right on the x axis. */
319cfd74d65d832137e20e193c960802afba73b5d38sm    public final static class CollisionVolumeComparator implements Comparator<CollisionVolumeRecord> {
320cfd74d65d832137e20e193c960802afba73b5d38sm        private static CollisionVolume.FlipInfo sCompareFlip = new CollisionVolume.FlipInfo();
321cfd74d65d832137e20e193c960802afba73b5d38sm        public int compare(CollisionVolumeRecord object1, CollisionVolumeRecord object2) {
322cfd74d65d832137e20e193c960802afba73b5d38sm            int result = 0;
323cfd74d65d832137e20e193c960802afba73b5d38sm            if (object1 == null && object2 != null) {
324cfd74d65d832137e20e193c960802afba73b5d38sm                result = 1;
325cfd74d65d832137e20e193c960802afba73b5d38sm            } else if (object1 != null && object2 == null) {
326cfd74d65d832137e20e193c960802afba73b5d38sm                result = -1;
327cfd74d65d832137e20e193c960802afba73b5d38sm            } else if (object1 != null && object2 != null) {
328cfd74d65d832137e20e193c960802afba73b5d38sm                sCompareFlip.flipX = (object1.object.facingDirection.x < 0.0f);
329cfd74d65d832137e20e193c960802afba73b5d38sm                sCompareFlip.flipY = (object1.object.facingDirection.y < 0.0f);
330cfd74d65d832137e20e193c960802afba73b5d38sm                sCompareFlip.parentWidth = object1.object.width;
331cfd74d65d832137e20e193c960802afba73b5d38sm                sCompareFlip.parentHeight = object1.object.height;
332cfd74d65d832137e20e193c960802afba73b5d38sm
333cfd74d65d832137e20e193c960802afba73b5d38sm                final float minX1 = object1.object.getPosition().x
334cfd74d65d832137e20e193c960802afba73b5d38sm                    + object1.boundingVolume.getMinXPosition(sCompareFlip);
335cfd74d65d832137e20e193c960802afba73b5d38sm
336cfd74d65d832137e20e193c960802afba73b5d38sm                sCompareFlip.flipX = (object2.object.facingDirection.x < 0.0f);
337cfd74d65d832137e20e193c960802afba73b5d38sm                sCompareFlip.flipY = (object2.object.facingDirection.y < 0.0f);
338cfd74d65d832137e20e193c960802afba73b5d38sm                sCompareFlip.parentWidth = object2.object.width;
339cfd74d65d832137e20e193c960802afba73b5d38sm                sCompareFlip.parentHeight = object2.object.height;
340cfd74d65d832137e20e193c960802afba73b5d38sm
341cfd74d65d832137e20e193c960802afba73b5d38sm                final float minX2 = object2.object.getPosition().x
342cfd74d65d832137e20e193c960802afba73b5d38sm                    + object2.boundingVolume.getMinXPosition(sCompareFlip);
343cfd74d65d832137e20e193c960802afba73b5d38sm
344cfd74d65d832137e20e193c960802afba73b5d38sm                final float delta = minX1 - minX2;
345cfd74d65d832137e20e193c960802afba73b5d38sm                if (delta < 0.0f) {
346cfd74d65d832137e20e193c960802afba73b5d38sm                    result = -1;
347cfd74d65d832137e20e193c960802afba73b5d38sm                } else if (delta > 0.0f) {
348cfd74d65d832137e20e193c960802afba73b5d38sm                    result = 1;
349cfd74d65d832137e20e193c960802afba73b5d38sm                }
350cfd74d65d832137e20e193c960802afba73b5d38sm            }
351cfd74d65d832137e20e193c960802afba73b5d38sm            return result;
352cfd74d65d832137e20e193c960802afba73b5d38sm        }
353cfd74d65d832137e20e193c960802afba73b5d38sm    }
354cfd74d65d832137e20e193c960802afba73b5d38sm
355cfd74d65d832137e20e193c960802afba73b5d38sm
356cfd74d65d832137e20e193c960802afba73b5d38sm
357cfd74d65d832137e20e193c960802afba73b5d38sm}
358