Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1806 yogev_ezra 1
/*
2
------------------------------------------------------------
3
	Fixed Rate Pig - a fixed logic frame rate demo
4
------------------------------------------------------------
5
 * Copyright (C) 2004 David Olofson 
6
 *
7
 * This software is released under the terms of the GPL.
8
 *
9
 * Contact author for permission if you want to use this
10
 * software, or work derived from it, under other terms.
11
 */
12
 
13
#include 
14
#include 
15
#include "engine.h"
16
#include "SDL_image.h"
17
 
18
/* Size of sprite frame table */
19
#define	PIG_MAX_SPRITES		1024
20
 
21
 
22
/*
23
 * Actually remove an objects. Used internally,
24
 * to remove objects that have been marked for
25
 * destruction.
26
 */
27
static void close_object(PIG_object *po);
28
 
29
 
30
/*----------------------------------------------------------
31
	Engine
32
----------------------------------------------------------*/
33
PIG_engine *pig_open(SDL_Surface *screen)
34
{
35
	PIG_engine *pe = (PIG_engine *)calloc(1, sizeof(PIG_engine));
36
	if(!pe)
37
		return NULL;
38
 
39
	pe->screen = screen;
40
	if(!pe->screen)
41
	{
42
		pig_close(pe);
43
		return NULL;
44
	}
45
	if((pe->screen->flags & SDL_HWSURFACE) == SDL_HWSURFACE)
46
	{
47
		pe->buffer = SDL_CreateRGBSurface(SDL_SWSURFACE,
48
				screen->w, screen->h,
49
				screen->format->BitsPerPixel,
50
				screen->format->Rmask,
51
				screen->format->Gmask,
52
				screen->format->Bmask,
53
				screen->format->Amask);
54
		if(!pe->buffer)
55
		{
56
			pig_close(pe);
57
			return NULL;
58
		}
59
		pe->surface = pe->buffer;
60
	}
61
	else
62
		pe->surface = screen;
63
 
64
	pe->pages = 1 + ((screen->flags & SDL_DOUBLEBUF) == SDL_DOUBLEBUF);
65
 
66
	pe->interpolation = 1;
67
	pe->time = 0.0;
68
	pe->view.w = pe->surface->w;
69
	pe->view.h = pe->surface->h;
70
 
71
	pe->sprites = (PIG_sprite **)calloc(PIG_MAX_SPRITES,
72
			sizeof(PIG_sprite *));
73
	if(!pe->sprites)
74
	{
75
		pig_close(pe);
76
		return NULL;
77
	}
78
 
79
	pe->pagedirty[0] = pig_dirty_open(128);
80
	pe->workdirty = pig_dirty_open(256);
81
	if(!pe->pagedirty[0] || !pe->workdirty)
82
	{
83
		pig_close(pe);
84
		return NULL;
85
	}
86
	if(pe->pages > 1)
87
	{
88
		pe->pagedirty[1] = pig_dirty_open(128);
89
		if(!pe->pagedirty[1])
90
		{
91
			pig_close(pe);
92
			return NULL;
93
		}
94
	}
95
 
96
	return pe;
97
}
98
 
99
 
100
void pig_close(PIG_engine *pe)
101
{
102
	if(pe->sprites)
103
	{
104
		int i;
105
		for(i = 0; i < pe->nsprites; ++i)
106
			if(pe->sprites[i])
107
			{
108
				if(pe->sprites[i]->surface)
109
					SDL_FreeSurface(pe->sprites[i]->surface);
110
				free(pe->sprites[i]);
111
			}
112
		free(pe->sprites);
113
	}
114
	while(pe->objects)
115
		close_object(pe->objects);
116
	if(pe->map)
117
		pig_map_close(pe->map);
118
	if(pe->buffer)
119
		SDL_FreeSurface(pe->buffer);
120
	if(pe->pagedirty[0])
121
		pig_dirty_close(pe->pagedirty[0]);
122
	if(pe->pagedirty[1])
123
		pig_dirty_close(pe->pagedirty[1]);
124
	if(pe->workdirty)
125
		pig_dirty_close(pe->workdirty);
126
	free(pe);
127
}
128
 
129
 
130
void pig_viewport(PIG_engine *pe, int x, int y, int w, int h)
131
{
132
	pe->view.x = x;
133
	pe->view.y = y;
134
	pe->view.w = w;
135
	pe->view.h = h;
136
}
137
 
138
 
139
int pig_sprites(PIG_engine *pe, const char *filename, int sw, int sh)
140
{
141
	int x, y, count, handle;
142
	SDL_Surface *tmp = IMG_Load(filename);
143
	if(!tmp)
144
	{
145
		fprintf(stderr, "Could not load '%s'!\n", filename);
146
		return -1;
147
	}
148
 
149
	handle = pe->nsprites;
150
 
151
	if(!sw)
152
		sw = tmp->w;
153
	if(!sh)
154
		sh = tmp->h;
155
 
156
	/* Disable blending, so we get the alpha channel COPIED! */
157
	SDL_SetAlpha(tmp, 0, 0);
158
 
159
	count = 0;
160
	for(y = 0; y <= tmp->h - sh; y += sh)
161
		for(x = 0; x <= tmp->w - sw; x += sw)
162
		{
163
			SDL_Rect r;
164
			SDL_Surface *tmp2;
165
			PIG_sprite *s;
166
			if(pe->nsprites >= PIG_MAX_SPRITES)
167
			{
168
				fprintf(stderr, "Sprite bank full!\n");
169
				return -1;
170
			}
171
			s = (PIG_sprite *)calloc(1, sizeof(PIG_sprite));
172
			if(!s)
173
				return -1;
174
			s->w = sw;
175
			s->h = sh;
176
			s->hotx = sw / 2;
177
			s->hoty = sh / 2;
178
			s->radius = (sw + sh) / 5;
179
			tmp2 = SDL_CreateRGBSurface(SDL_SWSURFACE,
180
					sw, sh, 32,
181
					0xff000000, 0x00ff0000,
182
					0x0000ff00, 0x000000ff);
183
			SDL_SetAlpha(tmp2, 0, 0);
184
			r.x = x;
185
			r.y = y;
186
			r.w = sw;
187
			r.h = sh;
188
			SDL_BlitSurface(tmp, &r, tmp2, NULL);
189
			SDL_SetAlpha(tmp2, SDL_SRCALPHA | SDL_RLEACCEL,
190
					SDL_ALPHA_OPAQUE);
191
			s->surface = SDL_DisplayFormatAlpha(tmp2);
192
			if(!s->surface)
193
			{
194
				fprintf(stderr, "Could not convert sprite %d"
195
						" of '%s'!\n",
196
						count, filename);
197
				return -1;
198
			}
199
			SDL_FreeSurface(tmp2);
200
			pe->sprites[pe->nsprites] = s;
201
			++pe->nsprites;
202
			++count;
203
		}
204
 
205
	SDL_FreeSurface(tmp);
206
	return handle;
207
}
208
 
209
 
210
int pig_hotspot(PIG_engine *pe, int frame, int hotx, int hoty)
211
{
212
	if((frame < 0 ) || (frame >= pe->nsprites))
213
		return -1;
214
 
215
	switch(hotx)
216
	{
217
	  case PIG_UNCHANGED:
218
		break;
219
	  case PIG_MIN:
220
		pe->sprites[frame]->hotx = 0;
221
		break;
222
	  case PIG_CENTER:
223
		pe->sprites[frame]->hotx = pe->sprites[frame]->w / 2;
224
		break;
225
	  case PIG_MAX:
226
		pe->sprites[frame]->hotx = pe->sprites[frame]->w;
227
		break;
228
	  default:
229
		pe->sprites[frame]->hotx = hotx;
230
		break;
231
	}
232
	switch(hoty)
233
	{
234
	  case PIG_UNCHANGED:
235
		break;
236
	  case PIG_MIN:
237
		pe->sprites[frame]->hoty = 0;
238
		break;
239
	  case PIG_CENTER:
240
		pe->sprites[frame]->hoty = pe->sprites[frame]->h / 2;
241
		break;
242
	  case PIG_MAX:
243
		pe->sprites[frame]->hoty = pe->sprites[frame]->h;
244
		break;
245
	  default:
246
		pe->sprites[frame]->hoty = hoty;
247
		break;
248
	}
249
	return 0;
250
}
251
 
252
 
253
int pig_radius(PIG_engine *pe, int frame, int radius)
254
{
255
	if((frame < 0 ) || (frame >= pe->nsprites))
256
		return -1;
257
 
258
	pe->sprites[frame]->radius = radius;
259
	return 0;
260
}
261
 
262
 
263
void pig_start(PIG_engine *pe, int frame)
264
{
265
	PIG_object *po = pe->objects;
266
	pe->time = (double)frame;
267
	pe->frame = frame;
268
	while(po)
269
	{
270
		po->ip.gx = po->ip.ox = po->x;
271
		po->ip.gy = po->ip.oy = po->y;
272
		po->ip.gimage = po->ibase + po->image;
273
		po = po->next;
274
	}
275
}
276
 
277
 
278
static void run_timers(PIG_engine *pe, PIG_object *po)
279
{
280
	int i;
281
	for(i = 0; i < PIG_TIMERS; ++i)
282
		if(po->timer[i])
283
		{
284
			--po->timer[i];
285
			if(!po->timer[i])
286
			{
287
				PIG_event ev;
288
				ev.type = PIG_TIMER0 + i;
289
				po->handler(po, &ev);
290
				if(!po->id)
291
					return;
292
			}
293
		}
294
}
295
 
296
 
297
static void test_offscreen(PIG_engine *pe, PIG_object *po, PIG_sprite *s)
298
{
299
	PIG_event ev;
300
	int hx, hy, w, h;
301
	if(s)
302
	{
303
		hx = s->hotx;
304
		hy = s->hoty;
305
		w = s->w;
306
		h = s->h;
307
	}
308
	else
309
		hx = hy = w = h = 0;
310
	ev.cinfo.sides = (po->y - hy < -h) << PIG_TOP_B;
311
	ev.cinfo.sides |= (po->y - hy >= pe->view.h) << PIG_BOTTOM_B;
312
	ev.cinfo.sides |= (po->x - hx < -w) << PIG_LEFT_B;
313
	ev.cinfo.sides |= (po->x - hx >= pe->view.w) << PIG_RIGHT_B;
314
	if(ev.cinfo.sides)
315
	{
316
		float dx = po->x - po->ip.ox;
317
		float dy = po->y - po->ip.oy;
318
		if(ev.cinfo.sides & PIG_TOP)
319
		{
320
			ev.cinfo.y = 0;
321
			if(dy)
322
				ev.cinfo.x = po->ip.ox - dx * po->ip.oy / dy;
323
		}
324
		else if(ev.cinfo.sides & PIG_BOTTOM)
325
		{
326
			ev.cinfo.y = pe->view.h - 1;
327
			if(dy)
328
				ev.cinfo.x = po->ip.ox + dx *
329
						(ev.cinfo.y - po->ip.oy) / dy;
330
		}
331
		if(ev.cinfo.sides & PIG_LEFT)
332
		{
333
			ev.cinfo.x = 0;
334
			if(dx)
335
				ev.cinfo.y = po->ip.oy - dy * po->ip.ox / dx;
336
		}
337
		else if(ev.cinfo.sides & PIG_RIGHT)
338
		{
339
			ev.cinfo.x = pe->view.w - 1;
340
			if(dx)
341
				ev.cinfo.y = po->ip.oy + dy *
342
						(ev.cinfo.x - po->ip.ox) / dx;
343
		}
344
		ev.type = PIG_OFFSCREEN;
345
		po->handler(po, &ev);
346
	}
347
}
348
 
349
 
350
/* Test for stationary sprite/sprite collision */
351
static void sprite_sprite_one(PIG_object *po, PIG_object *po2, float t, float hitdist)
352
{
353
	float dx, dy, dsquare;
354
	PIG_event ev;
355
	int sides;
356
	float ix = po->ip.ox * (1 - t) + po->x * t;
357
	float iy = po->ip.oy * (1 - t) + po->y * t;
358
	float ix2 = po2->ip.ox * (1 - t) + po2->x * t;
359
	float iy2 = po2->ip.oy * (1 - t) + po2->y * t;
360
	dx = ix - ix2;
361
	dy = iy - iy2;
362
	dsquare = dx*dx + dy*dy;
363
	if(dsquare >= hitdist*hitdist)
364
		return;		/* Nothing... --> */
365
 
366
	if(fabs(dsquare) < 1)
367
		sides = PIG_ALL;
368
	else
369
	{
370
		float d = sqrt(dsquare);
371
		dx /= d;
372
		dy /= d;
373
		if(dx < -0.707)
374
			sides = PIG_LEFT;
375
		else if((dx > 0.707))
376
			sides = PIG_RIGHT;
377
		else
378
			sides = 0;
379
		if(dy < -0.707)
380
			sides |= PIG_TOP;
381
		else if((dy > 0.707))
382
			sides |= PIG_BOTTOM;
383
	}
384
	ev.type = PIG_HIT_OBJECT;
385
	ev.cinfo.ff = 0.0;
386
 
387
	ev.cinfo.x = ix;
388
	ev.cinfo.y = iy;
389
	ev.cinfo.sides = sides;
390
	if(po->hitmask & po2->hitgroup)
391
	{
392
		ev.obj = po2;
393
		po->handler(po, &ev);
394
	}
395
 
396
	if(po2->id && (po2->hitmask & po->hitgroup))
397
	{
398
		int s;
399
		ev.cinfo.x = ix2;
400
		ev.cinfo.y = iy2;
401
		s = ((sides >> PIG_LEFT_B) & 1) << PIG_RIGHT_B;
402
		s |= ((sides >> PIG_RIGHT_B) & 1) << PIG_LEFT_B;
403
		s |= ((sides >> PIG_TOP_B) & 1) << PIG_BOTTOM_B;
404
		s |= ((sides >> PIG_BOTTOM_B) & 1) << PIG_TOP_B;
405
		ev.cinfo.sides = s;
406
		ev.obj = po;
407
		po2->handler(po2, &ev);
408
	}
409
}
410
 
411
 
412
/*
413
 * Check 'po' against all subsequent objects in the list.
414
 * The testing is step size limited so that neither object
415
 * moves more than 25% of the collision distance between tests.
416
 * (25% should be sufficient for correct direction flags.)
417
 */
418
static void test_sprite_sprite(PIG_engine *pe, PIG_object *po, PIG_sprite *s)
419
{
420
	int image;
421
	PIG_object *po2, *next2;
422
	for(po2 = po->next; po2; po2 = next2)
423
	{
424
		float hitdist, d, dmax, t, dt;
425
		next2 = po2->next;
426
		if(!po->id || !po2->id)
427
			break;
428
 
429
		/* Check collision groups and masks */
430
		if(!(po->hitmask & po2->hitgroup) &&
431
				!(po2->hitmask & po->hitgroup))
432
			continue;
433
 
434
		/* Calculate minimum distance */
435
		hitdist = s ? s->radius : 0;
436
		image = po2->ibase + po2->image;
437
		if((image >= 0) && (image < pe->nsprites))
438
			hitdist += pe->sprites[image]->radius;
439
		if(hitdist < 1)
440
			hitdist = 1;
441
 
442
		/* Calculate number of testing steps */
443
		dmax = fabs(po->ip.ox - po->x);
444
		d = fabs(po->ip.oy - po->y);
445
		dmax = d > dmax ? d : dmax;
446
		d = fabs(po2->ip.ox - po2->x);
447
		dmax = d > dmax ? d : dmax;
448
		d = fabs(po2->ip.oy - po2->y);
449
		dmax = d > dmax ? d : dmax;
450
		if(dmax > 1)
451
			dt = hitdist / (dmax * 4);
452
		else
453
			dt = 1;
454
 
455
		/* Sweep test! */
456
		for(t = 0; t < 1; t += dt)
457
			sprite_sprite_one(po, po2, t, hitdist);
458
	}
459
}
460
 
461
 
462
/*
463
 * Returns a non-zero value if the tile at (x, y) is marked for
464
 * collisions on the side indicated by 'mask'.
465
 */
466
static __inline__ int check_tile(PIG_map *m, int x, int y, int mask)
467
{
468
	int mx, my;
469
	/*
470
	 * Must check < 0 first! (Division rounds
471
	 * towards zero - not downwards.)
472
	 */
473
	if(x < 0 || y < 0)
474
		return PIG_NONE;
475
 
476
	mx = x / m->tw;
477
	my = y / m->th;
478
	if(mx >= m->w || my >= m->h)
479
		return PIG_NONE;
480
 
481
	return m->hit[my * m->w + mx] & mask;
482
}
483
 
484
 
485
int pig_test_map(PIG_engine *pe, int x, int y)
486
{
487
	int mx, my;
488
	if(x < 0 || y < 0)
489
		return PIG_NONE;
490
 
491
	mx = x / pe->map->tw;
492
	my = y / pe->map->th;
493
	if(mx >= pe->map->w || my >= pe->map->h)
494
		return PIG_NONE;
495
 
496
	return pe->map->hit[my * pe->map->w + mx];
497
}
498
 
499
 
500
/*
501
 * Simple implementation that checks only for top edge collisions.
502
 * (Full top/bottom/left/right checks with proper handling of
503
 * corners and rows of tiles is a lot more complicated, so I'll
504
 * leave that out for now, rather than hacking something simple
505
 * but incorrect.)
506
 */
507
int pig_test_map_vector(PIG_engine *pe, int x1, int y1, int x2, int y2,
508
		int mask, PIG_cinfo *ci)
509
{
510
	PIG_cinfo lci;
511
	PIG_map *m = pe->map;
512
	int x, y;
513
	int dist = 2000000000L;
514
	if(!ci)
515
		ci = &lci;
516
	ci->sides = 0;
517
	if((mask & PIG_TOP) && (y1 < y2))
518
	{
519
		/* Test for tiles that can be hit from the top */
520
		for(y = y1 + m->th - y1 % m->th; y <= y2; y += m->th)
521
		{
522
			x = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
523
			if(check_tile(m, x, y + 1, PIG_TOP))
524
			{
525
				dist = (x-x1) * (x-x1) + (y-y1) * (y-y1);
526
				ci->x = x;
527
				ci->y = y - 1;
528
				ci->sides |= PIG_TOP;
529
				break;
530
			}
531
		}
532
	}
533
	if(ci->sides)
534
		ci->ff = sqrt((x2 - x1) * (x2 - x1) +
535
				(y2 - y1) * (y2 - y1) / dist);
536
	return ci->sides;
537
}
538
 
539
 
540
static void test_sprite_map(PIG_engine *pe, PIG_object *po, PIG_sprite *s)
541
{
542
	PIG_event ev;
543
	if(pig_test_map_vector(pe, po->ip.ox, po->ip.oy, po->x, po->y,
544
			po->tilemask, &ev.cinfo))
545
	{
546
		ev.type = PIG_HIT_TILE;
547
		po->handler(po, &ev);
548
	}
549
}
550
 
551
 
552
static void run_logic(PIG_engine *pe)
553
{
554
	PIG_object *po, *next;
555
	int image;
556
 
557
	/* Shift logic coordinates */
558
	for(po = pe->objects; po; po = po->next)
559
	{
560
		po->ip.ox = po->x;
561
		po->ip.oy = po->y;
562
	}
563
 
564
	if(pe->before_objects)
565
		pe->before_objects(pe);
566
 
567
	for(po = pe->objects; po; po = next)
568
	{
569
		PIG_event ev;
570
		/*
571
		 * We must grab the next pointer before
572
		 * we call any event handlers, as they
573
		 * may cause objects to remove themselves!
574
		 */
575
		next = po->next;
576
		ev.type = PIG_PREFRAME;
577
		po->handler(po, &ev);
578
	}
579
 
580
	for(po = pe->objects; po; po = next)
581
	{
582
		PIG_sprite *s;
583
		next = po->next;
584
		image = po->ibase + po->image;
585
		if((image >= 0) && (image < pe->nsprites))
586
			s = pe->sprites[image];
587
		else
588
			s = NULL;
589
 
590
		/* Move! */
591
		po->vx += po->ax;
592
		po->vy += po->ay;
593
		po->x += po->vx;
594
		po->y += po->vy;
595
 
596
		/* Check and handle events */
597
		if(po->handler)
598
		{
599
			run_timers(pe, po);
600
			if(po->id)
601
				test_offscreen(pe, po, s);
602
			if(po->id && (po->hitmask || po->hitgroup))
603
				test_sprite_sprite(pe, po, s);
604
			if(po->id && po->tilemask)
605
				test_sprite_map(pe, po, s);
606
 
607
		}
608
	}
609
 
610
	for(po = pe->objects; po; po = next)
611
	{
612
		next = po->next;
613
		if(po->id)
614
		{
615
			PIG_event ev;
616
			ev.type = PIG_POSTFRAME;
617
			po->handler(po, &ev);
618
			++po->age;
619
		}
620
	}
621
 
622
	if(pe->after_objects)
623
		pe->after_objects(pe);
624
}
625
 
626
 
627
void pig_animate(PIG_engine *pe, float frames)
628
{
629
	/* Advance logic time */
630
	int i = floor(pe->time + frames) - floor(pe->time);
631
	while(i--)
632
	{
633
		run_logic(pe);
634
		++pe->frame;
635
	}
636
	pe->time += frames;
637
}
638
 
639
 
640
void pig_dirty(PIG_engine *pe, SDL_Rect *dr)
641
{
642
	SDL_Rect r;
643
	r.x = 0;
644
	r.y = 0;
645
	r.w = pe->surface->w;
646
	r.h = pe->surface->h;
647
	if(dr)
648
		pig_intersectrect(dr, &r);
649
	if(r.w && r.h)
650
		pig_dirty_add(pe->pagedirty[pe->page], &r);
651
}
652
 
653
 
654
static void tile_area(PIG_engine *pe, SDL_Rect *r)
655
{
656
	SDL_Rect cr;
657
	int x, y, startx, starty, maxx, maxy, tilesperrow;
658
	cr = *r;
659
	cr.x += pe->view.x;
660
	cr.y += pe->view.y;
661
	SDL_SetClipRect(pe->surface, &cr);
662
 
663
	startx = r->x / pe->map->tw;
664
	starty = r->y / pe->map->th;
665
	maxx = (r->x + r->w + pe->map->tw - 1) / pe->map->tw;
666
	maxy = (r->y + r->h + pe->map->th - 1) / pe->map->th;
667
	if(maxx > pe->map->w - 1)
668
		maxx = pe->map->w - 1;
669
	if(maxy > pe->map->h - 1)
670
		maxy = pe->map->h - 1;
671
	tilesperrow = pe->map->tiles->w / pe->map->tw;
672
 
673
	for(y = starty; y <= maxy; ++y)
674
		for(x = startx; x <= maxx; ++x)
675
		{
676
			SDL_Rect from, to;
677
			int c = pe->map->map[y * pe->map->w + x];
678
			from.x = c % tilesperrow * pe->map->tw;
679
			from.y = c / tilesperrow * pe->map->th;
680
			from.w = pe->map->tw;
681
			from.h = pe->map->th;
682
			to.x = pe->view.x + x * pe->map->tw;
683
			to.y = pe->view.y + y * pe->map->th;
684
			SDL_BlitSurface(pe->map->tiles, &from,
685
					pe->surface, &to);
686
		}
687
}
688
 
689
 
690
void remove_sprites(PIG_engine *pe)
691
{
692
	SDL_Rect r;
693
	PIG_sprite *s;
694
	PIG_object *po, *next;
695
 
696
	/*
697
	 * Remove all objects, using the information that
698
	 * remains from the last frame. The actual removal
699
	 * is done by drawing over the sprites with tiles
700
	 * from the map.
701
	 *
702
	 * We assume that most objects don't overlap. If
703
	 * they do that a lot, we could use a "dirty map"
704
	 * to avoid rendering the same tiles multiple times
705
	 * in the overlapping areas.
706
	 */
707
	for(po = pe->objects; po; po = next)
708
	{
709
		next = po->next;
710
		if((po->ip.gimage < 0) || (po->ip.gimage >= pe->nsprites))
711
			continue;
712
		s = pe->sprites[po->ip.gimage];
713
		r.x = po->ip.gx - s->hotx;
714
		r.y = po->ip.gy - s->hoty;
715
		r.w = s->w;
716
		r.h = s->h;
717
		pig_intersectrect(&pe->view, &r);
718
		if(r.w && r.h)
719
			tile_area(pe, &r);
720
 
721
		/*
722
		 * Delete dead objects *after* they've
723
		 * been removed from the rendering buffer!
724
		 */
725
		if(!po->id)
726
			close_object(po);
727
	}
728
}
729
 
730
 
731
void draw_sprites(PIG_engine *pe)
732
{
733
	PIG_dirtytable *pdt;
734
	PIG_sprite *s;
735
	PIG_object *po;
736
	float fframe = pe->time - floor(pe->time);
737
	SDL_SetClipRect(pe->surface, &pe->view);
738
 
739
	/* Swap the work and display/back page dirtytables */
740
	pdt = pe->workdirty;
741
	pe->workdirty = pe->pagedirty[pe->page];
742
	pe->pagedirty[pe->page] = pdt;
743
 
744
	/* Clear the display/back page dirtytable */
745
	pdt->count = 0;
746
 
747
	/* Update positions and render all objects */
748
	po = pe->objects;
749
	while(po)
750
	{
751
		/* Calculate graphic coordinates */
752
		if(pe->interpolation)
753
		{
754
			po->ip.gx = po->ip.ox * (1 - fframe) + po->x * fframe;
755
			po->ip.gy = po->ip.oy * (1 - fframe) + po->y * fframe;
756
		}
757
		else
758
		{
759
			po->ip.gx = po->x;
760
			po->ip.gy = po->y;
761
		}
762
		po->ip.gimage = po->ibase + po->image;
763
 
764
		/* Render the sprite! */
765
		if((po->ip.gimage >= 0) && (po->ip.gimage < pe->nsprites))
766
		{
767
			SDL_Rect dr;
768
			s = pe->sprites[po->ip.gimage];
769
			dr.x = po->ip.gx - s->hotx + pe->view.x;
770
			dr.y = po->ip.gy - s->hoty + pe->view.y;
771
			SDL_BlitSurface(pe->sprites[po->ip.gimage]->surface,
772
				NULL, pe->surface, &dr);
773
			/*
774
			 * We use the clipped rect for the dirtyrect!
775
			 */
776
			if(dr.w && dr.h)
777
				pig_dirty_add(pdt, &dr);
778
		}
779
		po = po->next;
780
	}
781
 
782
	/* Merge the display/back page table into the work table */
783
	pig_dirty_merge(pe->workdirty, pdt);
784
}
785
 
786
 
787
void pig_refresh(PIG_engine *pe)
788
{
789
	remove_sprites(pe);
790
	draw_sprites(pe);
791
}
792
 
793
 
794
void pig_refresh_all(PIG_engine *pe)
795
{
796
	tile_area(pe, &pe->view);
797
	pig_dirty(pe, NULL);
798
	draw_sprites(pe);
799
}
800
 
801
 
802
static void show_rects(PIG_engine *pe, PIG_dirtytable *pdt)
803
{
804
	int i;
805
	Uint32 color;
806
	if(!pe->buffer)
807
	{
808
		pe->buffer = SDL_CreateRGBSurface(SDL_SWSURFACE,
809
				pe->screen->w, pe->screen->h,
810
				pe->screen->format->BitsPerPixel,
811
				pe->screen->format->Rmask,
812
				pe->screen->format->Gmask,
813
				pe->screen->format->Bmask,
814
				pe->screen->format->Amask);
815
		if(!pe->buffer)
816
			return;
817
		pe->surface = pe->buffer;
818
		tile_area(pe, &pe->view);
819
	}
820
	if(!pe->buffer)
821
		return;
822
 
823
	pe->direct = 0;
824
 
825
	for(i = 0; i < pdt->count; ++i)
826
	{
827
		SDL_Rect r;
828
		r = pdt->rects[i];
829
		r.x -= 32;
830
		r.y -= 32;
831
		r.w += 64;
832
		r.h += 64;
833
		SDL_BlitSurface(pe->buffer, &r, pe->screen, &r);
834
	}
835
 
836
	color = SDL_MapRGB(pe->screen->format, 255, 0, 255);
837
	for(i = 0; i < pdt->count; ++i)
838
	{
839
		SDL_Rect r;
840
		r = pdt->rects[i];
841
		r.h = 1;
842
		SDL_FillRect(pe->screen, &r, color);
843
		r.y += pdt->rects[i].h - 1;
844
		SDL_FillRect(pe->screen, &r, color);
845
		r = pdt->rects[i];
846
		r.w = 1;
847
		SDL_FillRect(pe->screen, &r, color);
848
		r.x += pdt->rects[i].w - 1;
849
		SDL_FillRect(pe->screen, &r, color);
850
	}
851
}
852
 
853
 
854
void pig_flip(PIG_engine *pe)
855
{
856
	PIG_dirtytable *pdt = pe->workdirty;
857
	int i;
858
	SDL_SetClipRect(pe->surface, NULL);
859
 
860
	if(pe->show_dirtyrects)
861
	{
862
		show_rects(pe, pdt);
863
		for(i = 0; i < pdt->count; ++i)
864
		{
865
			pdt->rects[i].x -= 32;
866
			pdt->rects[i].y -= 32;
867
			pdt->rects[i].w += 64;
868
			pdt->rects[i].h += 64;
869
			pig_intersectrect(&pe->buffer->clip_rect, &pdt->rects[i]);
870
		}
871
	}
872
	else if(pe->surface == pe->buffer)
873
		for(i = 0; i < pdt->count; ++i)
874
			SDL_BlitSurface(pe->buffer, pdt->rects + i,
875
					pe->screen, pdt->rects + i);
876
 
877
	if((pe->screen->flags & SDL_HWSURFACE) == SDL_HWSURFACE)
878
	{
879
		SDL_Flip(pe->screen);
880
		if(pe->pages > 1)
881
			pe->page = 1 - pe->page;
882
	}
883
	else
884
		SDL_UpdateRects(pe->screen, pdt->count, pdt->rects);
885
 
886
	if(pe->direct)
887
		pe->surface = pe->screen;
888
	else
889
		pe->surface = pe->buffer ? pe->buffer : pe->screen;
890
}
891
 
892
 
893
void pig_draw_sprite(PIG_engine *pe, int frame, int x, int y)
894
{
895
	SDL_Rect dr;
896
	if(frame >= pe->nsprites)
897
		return;
898
	dr.x = x - pe->sprites[frame]->hotx + pe->view.x;
899
	dr.y = y - pe->sprites[frame]->hoty + pe->view.y;
900
	SDL_BlitSurface(pe->sprites[frame]->surface, NULL,
901
			pe->surface, &dr);
902
}
903
 
904
 
905
/*----------------------------------------------------------
906
	Map
907
----------------------------------------------------------*/
908
PIG_map *pig_map_open(PIG_engine *pe, int w, int h)
909
{
910
	if(pe->map)
911
		pig_map_close(pe->map);
912
 
913
	pe->map = (PIG_map *)calloc(1, sizeof(PIG_map));
914
	if(!pe->map)
915
		return NULL;
916
 
917
	pe->map->owner = pe;
918
	pe->map->w = w;
919
	pe->map->h = h;
920
	pe->map->hit = (unsigned char *)calloc(w, h);
921
	if(!pe->map->hit)
922
	{
923
		pig_map_close(pe->map);
924
		return NULL;
925
	}
926
	pe->map->map = (unsigned char *)calloc(w, h);
927
	if(!pe->map->map)
928
	{
929
		pig_map_close(pe->map);
930
		return NULL;
931
	}
932
	return pe->map;
933
}
934
 
935
 
936
void pig_map_close(PIG_map *pm)
937
{
938
	PIG_engine *pe = pm->owner;
939
	if(pm->tiles)
940
		SDL_FreeSurface(pm->tiles);
941
	free(pm->hit);
942
	free(pm->map);
943
	free(pe->map);
944
	pe->map = NULL;
945
}
946
 
947
 
948
int pig_map_tiles(PIG_map *pm, const char *filename, int tw, int th)
949
{
950
	SDL_Surface *tmp;
951
	pm->tw = tw;
952
	pm->th = th;
953
	tmp = IMG_Load(filename);
954
	if(!tmp)
955
	{
956
		fprintf(stderr, "Could not load '%s'!\n", filename);
957
		return -1;
958
	}
959
	pm->tiles = SDL_DisplayFormat(tmp);
960
	if(!pm->tiles)
961
	{
962
		fprintf(stderr, "Could not convert '%s'!\n", filename);
963
		return -1;
964
	}
965
	SDL_FreeSurface(tmp);
966
	return 0;
967
}
968
 
969
 
970
void pig_map_collisions(PIG_map *pm, unsigned first, unsigned count, PIG_sides sides)
971
{
972
	int i;
973
	if(first > 255)
974
		return;
975
	if(first + count > 255)
976
		count = 255 - first;
977
	for(i = first; i < first + count; ++i)
978
		pm->hitinfo[i] = sides;
979
}
980
 
981
 
982
/*
983
 * Load a map from a string (one byte/tile). 'trans'
984
 * is a string used for translating 'data' into integer
985
 * tile indices. Each position in 'trans' corresponds
986
 * to one tile in the tile palette.
987
 */
988
int pig_map_from_string(PIG_map *pm, const char *trans, const char *data)
989
{
990
	int x, y, z;
991
 
992
	/* Load the map */
993
	z = 0;
994
	for(y = 0; y < pm->h; ++y)
995
		for(x = 0; x < pm->w; ++x)
996
		{
997
			const char *f;
998
			int c = data[z];
999
			if(!c)
1000
			{
1001
				fprintf(stderr, "Map string too short!\n");
1002
				return -1;
1003
			}
1004
			f = strchr(trans, c);
1005
			if(!f)
1006
			{
1007
				fprintf(stderr, "Character '%c' not in"
1008
						" the translation string!\n",
1009
						c);
1010
				return -1;
1011
			}
1012
			pm->map[z] = f - trans;
1013
			++z;
1014
		}
1015
	/* Generate collision map */
1016
	for(y = 0; y < pm->h; ++y)
1017
		for(x = 0; x < pm->w; ++x)
1018
			pm->hit[y * pm->w + x] =
1019
					pm->hitinfo[pm->map[y * pm->w + x]];
1020
	return 0;
1021
}
1022
 
1023
 
1024
/*----------------------------------------------------------
1025
	Object
1026
----------------------------------------------------------*/
1027
 
1028
 
1029
static PIG_object *get_object(PIG_engine *pe)
1030
{
1031
	PIG_object *po;
1032
	if(pe->object_pool)
1033
	{
1034
		po = pe->object_pool;
1035
		pe->object_pool = po->next;
1036
		memset(po, 0, sizeof(PIG_object));
1037
	}
1038
	else
1039
	{
1040
		po = (PIG_object *)calloc(1, sizeof(PIG_object));
1041
		if(!po)
1042
			return NULL;
1043
	}
1044
	po->id = ++pe->object_id_counter;
1045
	return po;
1046
}
1047
 
1048
 
1049
static void free_object(PIG_object *po)
1050
{
1051
	po->prev = NULL;
1052
	po->next = po->owner->object_pool;
1053
	po->owner->object_pool = po;
1054
	po->id = 0;
1055
}
1056
 
1057
 
1058
PIG_object *pig_object_open(PIG_engine *pe, int x, int y, int last)
1059
{
1060
	PIG_object *po = get_object(pe);
1061
	if(!po)
1062
		return NULL;
1063
 
1064
	po->owner = pe;
1065
	po->tilemask = PIG_ALL;
1066
	po->hitmask = 0;
1067
	po->hitgroup = 0;
1068
 
1069
	if(last && pe->objects)
1070
	{
1071
		PIG_object *lo = pe->objects;
1072
		while(lo->next)
1073
			lo = lo->next;
1074
		po->prev = lo;
1075
		po->next = NULL;
1076
		lo->next = po;
1077
	}
1078
	else
1079
	{
1080
		po->prev = NULL;
1081
		po->next = pe->objects;
1082
		if(po->next)
1083
			po->next->prev = po;
1084
		pe->objects = po;
1085
	}
1086
 
1087
	po->x = x;
1088
	po->y = y;
1089
	po->ip.ox = x;
1090
	po->ip.oy = y;
1091
	return po;
1092
}
1093
 
1094
 
1095
static void close_object(PIG_object *po)
1096
{
1097
	if(po == po->owner->objects)
1098
		po->owner->objects = po->next;
1099
	else if(po->prev)
1100
		po->prev->next = po->next;
1101
	if(po->next)
1102
		po->next->prev = po->prev;
1103
	free_object(po);
1104
}
1105
 
1106
 
1107
void pig_object_close(PIG_object *po)
1108
{
1109
	if(!po->id)
1110
		fprintf(stderr, "Object %p closed more than once!\n", po);
1111
	po->id = 0;	/* Mark for eventual removal and destruction */
1112
}
1113
 
1114
 
1115
void pig_object_close_all(PIG_engine *pe)
1116
{
1117
	while(pe->objects)
1118
		close_object(pe->objects);
1119
}
1120
 
1121
 
1122
PIG_object *pig_object_find(PIG_object *start, int id)
1123
{
1124
	PIG_object *pob, *pof;
1125
	if(start)
1126
		pob = pof = start;
1127
	else
1128
	{
1129
		pof = start->owner->objects;
1130
		while(pof)
1131
		{
1132
			if(pof->id == id)
1133
				return pof;
1134
			pof = pof->next;
1135
		}
1136
		return NULL;
1137
	}
1138
	while(1)
1139
	{
1140
		if(pob)
1141
		{
1142
			if(pob->id == id)
1143
				return pob;
1144
			pob = pob->prev;
1145
		}
1146
		if(pof)
1147
		{
1148
			if(pof->id == id)
1149
				return pof;
1150
			pof = pof->next;
1151
		}
1152
		else
1153
			if(!pob)
1154
				return NULL;
1155
	}
1156
}