TickHook: Fix crash when TickHook isn't set.
[gemrb.git] / gemrb / core / Particles.cpp
blob0fcbbeb31f561a4fe2fcd104a355465791dbbd65
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2006 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program 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
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "Particles.h"
23 #include "Interface.h"
24 #include "Video.h"
25 #include "Game.h"
27 Color sparkcolors[MAX_SPARK_COLOR][MAX_SPARK_PHASE];
28 bool inited = false;
30 #define SPARK_COUNT 13
32 static int spark_color_indices[SPARK_COUNT]={12,5,0,6,1,8,2,7,9,3,4,10,11};
34 void TranslateColor(const char *value, Color &color)
36 int r = 0;
37 int g = 0;
38 int b = 0;
39 //if not RGB then try to interpret it as a dword
40 if (strnicmp(value,"RGB(",4)) {
41 r = strtol(value,NULL,0);
42 color.r = r&0xff;
43 color.g = (r>>8)&0xff;
44 color.b = (r>>16)&0xff;
45 color.a = (r>>24)&0xff;
47 sscanf(value+4,"%d,%d,%d)", &r, &g, &b);
48 color.r=r;
49 color.g=g;
50 color.b=b;
53 void InitSparks()
55 int i,j;
56 AutoTable tab("sprklclr");
57 if (!tab)
58 return;
60 memset(sparkcolors,0,sizeof(sparkcolors));
61 for (i=0;i<MAX_SPARK_COLOR;i++) {
62 for (j=0;j<MAX_SPARK_PHASE;j++) {
63 sparkcolors[i][j].a=0xff;
66 i = tab->GetRowCount();
67 if (i>MAX_SPARK_COLOR) {
68 i = MAX_SPARK_COLOR;
70 while (i--) {
71 for (int j=0;j<MAX_SPARK_PHASE;j++) {
72 int idx;
74 if (i<SPARK_COUNT) {
75 idx = spark_color_indices[i];
76 } else {
77 idx = i;
79 const char *value = tab->QueryField(idx,j);
80 TranslateColor(value, sparkcolors[i][j]);
83 inited = true;
86 Particles::Particles(int s)
88 points = (Element *) malloc(s*sizeof(Element) );
89 memset(points, -1, s*sizeof(Element) );
91 for (int i=0;i<MAX_SPARK_PHASE;i++) {
92 bitmap[i]=NULL;
95 fragments = NULL;
96 if (!inited) {
97 InitSparks();
99 size = last_insert = s;
100 color = 0;
101 phase = P_FADE;
102 owner = NULL;
103 type = SP_TYPE_POINT;
104 path = SP_PATH_FALL;
105 spawn_type = SP_SPAWN_NONE;
106 timetolive = 0;
109 Particles::~Particles()
111 if (points) {
112 free(points);
115 for (int i=0;i<MAX_SPARK_PHASE;i++) {
116 delete( bitmap[i]);
119 delete fragments;
122 void Particles::SetBitmap(unsigned int FragAnimID)
124 //int i;
126 delete fragments;
128 fragments = new CharAnimations(FragAnimID, 0);
130 for (i=0;i<MAX_SPARK_PHASE;i++) {
131 delete( bitmap[i] );
134 AnimationFactory* af = ( AnimationFactory* )
135 gamedata->GetFactoryResource( BAM, IE_BAM_CLASS_ID );
137 if (af == NULL) {
138 return;
141 for (i=0;i<MAX_SPARK_PHASE; i++) {
142 bitmap[i] = af->GetCycle( i );
148 bool Particles::AddNew(const Point &point)
150 int st;
152 switch(path)
154 case SP_PATH_EXPL:
155 st = pos.h+last_insert%15;
156 break;
157 case SP_PATH_RAIN:
158 case SP_PATH_FLIT:
159 st = core->Roll(3,5,MAX_SPARK_PHASE)<<4;
160 break;
161 case SP_PATH_FOUNT:
162 st =(MAX_SPARK_PHASE + 2*pos.h);
163 break;
164 case SP_PATH_FALL:
165 default:
166 st =(MAX_SPARK_PHASE + pos.h)<<4;
167 break;
169 int i = last_insert;
170 while (i--) {
171 if (points[i].state == -1) {
172 points[i].state = st;
173 points[i].pos = point;
174 last_insert = i;
175 return false;
178 i = size;
179 while (i--!=last_insert) {
180 if (points[i].state == -1) {
181 points[i].state = st;
182 points[i].pos = point;
183 last_insert = i;
184 return false;
187 return true;
190 void Particles::Draw(const Region &screen)
192 int length; //used only for raindrops
194 Video *video=core->GetVideoDriver();
195 Region region = video->GetViewport();
196 if (owner) {
197 region.x-=pos.x;
198 region.y-=pos.y;
200 int i = size;
201 while (i--) {
202 if (points[i].state == -1) {
203 continue;
205 int state;
207 switch(path) {
208 case SP_PATH_FLIT:
209 case SP_PATH_RAIN:
210 state = points[i].state>>4;
211 break;
212 default:
213 state = points[i].state;
214 break;
217 if (state>=MAX_SPARK_PHASE) {
218 length = 6-abs(state-MAX_SPARK_PHASE-6);
219 state = 0;
220 } else {
221 state=MAX_SPARK_PHASE-state-1;
222 length=0;
224 Color clr = sparkcolors[color][state];
225 switch (type) {
226 case SP_TYPE_BITMAP:
228 if (bitmap[state]) {
229 Sprite2D *frame = bitmap[state]->GetFrame(points[i].state&255);
230 video->BlitGameSprite(frame,
231 points[i].pos.x+screen.x,
232 points[i].pos.y+screen.y, 0, clr,
233 NULL, NULL, &screen);
236 if (fragments) {
237 //IE_ANI_CAST stance has a simple looping animation
238 Animation** anims = fragments->GetAnimation( IE_ANI_CAST, i );
239 if (anims) {
240 Animation* anim = anims[0];
241 Sprite2D* nextFrame = anim->GetFrame(anim->GetCurrentFrame());
242 video->BlitGameSprite( nextFrame, points[i].pos.x - region.x, points[i].pos.y - region.y,
243 0, clr, NULL, fragments->GetPartPalette(0), &screen);
246 break;
247 case SP_TYPE_CIRCLE:
248 video->DrawCircle (points[i].pos.x-region.x,
249 points[i].pos.y-region.y, 2, clr, true);
250 break;
251 case SP_TYPE_POINT:
252 default:
253 video->SetPixel (points[i].pos.x-region.x,
254 points[i].pos.y-region.y, clr, true);
255 break;
256 // this is more like a raindrop
257 case SP_TYPE_LINE:
258 if (length) {
259 video->DrawLine (points[i].pos.x+region.x,
260 points[i].pos.y+region.y,
261 points[i].pos.x+region.x+(i&1),
262 points[i].pos.y+region.y+length, clr, true);
264 break;
269 void Particles::AddParticles(int count)
271 while (count--) {
272 Point p;
274 switch (path) {
275 case SP_PATH_EXPL:
276 p.x = pos.w/2+core->Roll(1,pos.w/2,pos.w/4);
277 p.y = pos.h/2+(last_insert&7);
278 break;
279 case SP_PATH_FALL:
280 default:
281 p.x = core->Roll(1,pos.w,0);
282 p.y = core->Roll(1,pos.h/2,0);
283 break;
284 case SP_PATH_RAIN:
285 case SP_PATH_FLIT:
286 p.x = core->Roll(1,pos.w,0);
287 p.y = core->Roll(1,pos.h,0);
288 break;
289 case SP_PATH_FOUNT:
290 p.x = core->Roll(1,pos.w/2,pos.w/4);
291 p.y = core->Roll(1,pos.h/2,0);
292 break;
294 if (AddNew(p) ) {
295 break;
300 int Particles::Update()
302 int drawn=false;
303 int i;
304 int grow;
306 if (phase==P_EMPTY) {
307 return drawn;
310 if (timetolive) {
311 if (timetolive<core->GetGame()->GameTime) {
312 spawn_type = SP_SPAWN_NONE;
313 phase = P_FADE;
317 switch(spawn_type) {
318 case SP_SPAWN_NONE:
319 grow = 0;
320 break;
321 case SP_SPAWN_FULL:
322 grow = size;
323 spawn_type=SP_SPAWN_NONE;
324 break;
325 case SP_SPAWN_SOME:
326 default:
327 grow = size/10;
329 for(i=0;i<size;i++) {
330 if (points[i].state==-1) {
331 continue;
333 drawn=true;
334 if (!points[i].state) {
335 grow++;
337 points[i].state--;
339 switch (path) {
340 case SP_PATH_FALL:
341 points[i].pos.y+=3+((i>>2)&3);
342 points[i].pos.y%=pos.h;
343 break;
344 case SP_PATH_RAIN:
345 points[i].pos.x+=pos.w+(i&1);
346 points[i].pos.x%=pos.w;
347 points[i].pos.y+=3+((i>>2)&3);
348 points[i].pos.y%=pos.h;
349 break;
350 case SP_PATH_FLIT:
351 if (points[i].state<=MAX_SPARK_PHASE<<4) {
352 break;
354 points[i].pos.x+=core->Roll(1,3,pos.w-2);
355 points[i].pos.x%=pos.w;
356 points[i].pos.y+=(i&3)+1;
357 break;
358 case SP_PATH_EXPL:
359 points[i].pos.y+=1;
360 break;
361 case SP_PATH_FOUNT:
362 if (points[i].state<=MAX_SPARK_PHASE) {
363 break;
365 if (points[i].state<(MAX_SPARK_PHASE+pos.h)) {
366 if ( (points[i].state&7) == 7) {
367 points[i].pos.x+=(i&3)-1;
369 points[i].pos.y+=2;
370 } else {
371 if ( (points[i].state&7) == 7) {
372 points[i].pos.x+=(i&3)-1;
374 points[i].pos.y-=2;
376 break;
379 if (phase==P_GROW) {
380 AddParticles(grow);
381 drawn=true;
383 if (!drawn) {
384 phase = P_EMPTY;
386 return drawn;