1 /*
2   Copyright (C) 2009 Facundo Domínguez
3 
4   This file is part of Spacejunk.
5 
6   Spacejunk is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10 
11   Foobar is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15 
16   You should have received a copy of the GNU General Public License
17   along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #ifndef COLLISIONENGINE_H
21 #define COLLISIONENGINE_H
22 
23 #include "collist.h"
24 #include "gamebody.h"
25 #include <list>
26 
27 template<class Item,class Data>
28 class CollisionEngine;
29 class cWorld;
30 template <class Item,class Data>
31 struct _s {
32     CollisionEngine<Item,Data> * ce;
33     bool fordelete;
34     Item * i;
35 };
36 
37 /**
38  * This class handles collisions of all the bodies on a given area.
39  */
40 template<class Item,class Data>
41 class CollisionEngine {
42 public:
43 
44     /**
45      * Creates a collision engine.
46      * @param min_x left bound of the collision area.
47      * @param min_y top bound of the collision area.
48      * @param max_x right bound of the collision area.
49      * @param max_y bottom bound of the collision area.
50      */
51     CollisionEngine(int min_x,int min_y,int max_x,int max_y,Data * w);
52 
53     /**
54      * Destroys a collision engine.
55      */
56     ~CollisionEngine();
57 
58     /**
59      * Adds a body to be considered by the collision engine.
60      * @param c The body to add.
61      */
62     void add(Item * c);
63 
64     /**
65      * Checks for collisions and calls the proper action for each
66      * colliding object.
67      */
68     void step(int (*Handler)(int,void**));
69 
70     void deleteBody(Item * c);
71 
72 private:
73     COLLIST_LIST * clist;
74     Data * data;
75     typedef std::map<Item*,_s<Item,Data>* > ItemMap;
76     ItemMap _sl;
77 
erase(Item * c)78     void erase(Item * c) {
79         typename ItemMap::iterator i=_sl.find(c);
80         if (i!=_sl.end()) {
81             _sl.erase(i);
82         }
83     };
84 
85     template <class ItemG,class DataG>
86     friend void get_item(COLLIST_ITEM * ci);
87 };
88 
89 template <class ItemG,class DataG>
get_item(COLLIST_ITEM * ci)90 void get_item(COLLIST_ITEM * ci) {
91     _s<ItemG,DataG> * d=static_cast<_s<ItemG,DataG>*>(ci->userdata);
92     if (d->fordelete) {
93         remove_from_collist_during_callback(d->ce->clist,ci);
94         d->ce->erase(d->i);
95         delete d;
96         return;
97     }
98     GameBody * c=static_cast<GameBody*>(d->i);
99     ci->x=fmin(c->getPositionX()-c->hotspot.x,c->lastTestPosition().x-c->hotspot.x);
100     ci->y=fmin(c->getPositionY()-c->hotspot.y,c->lastTestPosition().y-c->hotspot.y);
101     ci->w=c->getWidth()+fabs(c->getPositionX()-c->lastTestPosition().x);
102     ci->h=c->getHeight()+fabs(c->getPositionY()-c->lastTestPosition().y);
103 }
104 
105 template<class Item,class Data>
CollisionEngine(int min_x,int min_y,int max_x,int max_y,Data * w)106 CollisionEngine<Item,Data>::CollisionEngine(int min_x,int min_y,int max_x,int max_y,Data * w) {
107     clist=create_collist(min_x,min_y,max_x,max_y,get_item<Item,Data>);
108     data=w;
109 }
110 
111 template<class Item,class Data>
add(Item * c)112 void CollisionEngine<Item,Data>::add(Item * c) {
113     typename ItemMap::iterator i=_sl.find(c);
114     _s<Item,Data> * d;
115     if (i!=_sl.end()) {
116         d=i->second;
117     } else {
118         d=new _s<Item,Data>;
119         _sl[c]=d;
120     }
121     d->fordelete=false;
122     if (i==_sl.end()) {
123         d->ce=this;
124         d->i=c;
125         add_to_collist(clist,(void*)d);
126     }
127 }
128 
129 template<class Item,class Data>
~CollisionEngine()130 CollisionEngine<Item,Data>::~CollisionEngine() {
131     for (typename ItemMap::iterator i=_sl.begin();i!=_sl.end();i++) delete i->second;
132     _sl.clear();
133     destroy_collist(clist);
134 };
135 
136 
137 
138 bool circle_swept_collision(GameBody * gb1,GameBody * gb2,real*t,Vector2d * normal);
139 bool box_swept_collision(GameBody * gb1,GameBody * gb2);
140 bool swept_collision(GameBody * gb1,GameBody * gb2,real*t,Vector2d * normal);
141 
142 template <class Item,class Data,int (*Handler)(Data*,Item*,Item*,real,const Vector2d &)>
wrap_collision_handler(int n,void ** array)143 int wrap_collision_handler(int n, void **array) {
144     _s<Item,Data> ** arr=(_s<Item,Data>**)array;
145     for (int i=0;i<n;i++) {
146         _s<Item,Data> * f=arr[i*2+1],*s=arr[i*2+2];
147         Vector2d normal;
148         real t;
149         if (!f->fordelete && !s->fordelete &&
150                 swept_collision(static_cast<GameBody*>(f->i),static_cast<GameBody*>(s->i),&t,&normal)) {
151             Handler((Data*)array[0],f->i,s->i,t,normal);
152         }
153     }
154     return 0;
155 }
156 
157 template<class Item,class Data>
step(int (* Handler)(int,void **))158 void CollisionEngine<Item,Data>::step(int (*Handler)(int,void**)) {
159     do_collist_collisions(clist,Handler,(void*)data);
160     for (typename ItemMap::iterator i=_sl.begin();i!=_sl.end();i++)
161         if (!i->second->fordelete) static_cast<GameBody*>(i->second->i)->testForCollision();
162 }
163 
164 template<class Item,class Data>
deleteBody(Item * c)165 void CollisionEngine<Item,Data>::deleteBody(Item * c) {
166     typename ItemMap::iterator i=_sl.find(c);
167     if (i!=_sl.end()) {
168         i->second->fordelete=true;
169     }
170 };
171 
172 #endif // COLLISIONENGINE_H
173