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