1 // BlinkenSisters - Hunt for the Lost Pixels
2 //     Bringing back the fun of the 80s
3 //
4 // (C) 2005-07 Rene Schickbauer, Wolfgang Dautermann
5 //
6 // See License.txt for licensing information
7 //
8 
9 
10 
11 #include "globals.h"
12 #include "monstersprites.h"
13 #include "memops.h"
14 #include "levelhandler.h"
15 #include "colission.h"
16 #include "errorhandler.h"
17 #include "convert.h"
18 #include "engine.h"
19 #include <stdlib.h> // Try to fix problem with solaris (see also changes in version 1.6)
20 
21 SDL_Surface *MONSTERSPRITE_Left[MAX_MONSTER_TYPES];
22 SDL_Surface *MONSTERSPRITE_Right[MAX_MONSTER_TYPES];
23 bool monstersLoaded = false;
24 bool hasEatenPixel;
25 
loadMonsterSprites()26 void loadMonsterSprites() {
27 	char fullfname[MAX_FNAME_LENGTH];
28 	SDL_Surface* temp;
29 
30 	for(Uint32 i = 0; i < MAX_MONSTER_TYPES; i++) {
31 		sprintf(fullfname, "%smonster%d_left.bmp", configGetPath(""), (i+1));
32 		temp = IMG_Load(fullfname);
33 		if(!temp) {
34 			DIE(ERROR_IMAGE_READ, fullfname);
35 		}
36 		MONSTERSPRITE_Left[i] = convertToBSSurface(temp);
37 		SDL_FreeSurface(temp);
38 
39 		sprintf(fullfname, "%smonster%d_right.bmp", configGetPath(""), (i+1));
40 		temp = IMG_Load(fullfname);
41 		if(!temp) {
42 			DIE(ERROR_IMAGE_READ, fullfname);
43 		}
44 		MONSTERSPRITE_Right[i] = convertToBSSurface(temp);
45 		SDL_FreeSurface(temp);
46 
47 	}
48 	monstersLoaded = true;
49 }
50 
51 
unloadMonsterSprites()52 void unloadMonsterSprites() {
53 	if(!monstersLoaded) {
54 		return;
55 	}
56 	printf("Freeing monster sprites...\n");
57 	for(Uint32 i = 0; i < MAX_MONSTER_TYPES; i++) {
58 		SDL_FreeSurface(MONSTERSPRITE_Left[i]);
59 		SDL_FreeSurface(MONSTERSPRITE_Right[i]);
60 	}
61 	monstersLoaded = false;
62 }
63 
addMonster(const Uint32 type,const Uint32 x,const Uint32 y)64 void addMonster(const Uint32 type, const Uint32 x, const Uint32 y) {
65 	MONSTERS* tthis = (MONSTERS*)malloc(sizeof(MONSTERS));
66 	tthis->next = 0;
67 	tthis->type = type;
68 	tthis->startx = x;
69 	tthis->starty = y;
70 	tthis->spritenum = 0;
71 	if(!lhandle.monsters) {
72 		lhandle.monsters = tthis;
73 	} else {
74 		MONSTERS *last = lhandle.monsters;
75 		while(last->next) {
76 			last = last->next;
77 		}
78 		last->next = tthis;
79 	}
80 	lhandle.numMonsters++;
81 }
82 
initMonsters()83 void initMonsters() {
84 	MONSTERS* tthis = lhandle.monsters;
85 	while(tthis) {
86 
87 		tthis->ai.turnDefault = 150;
88 		if(tthis->type < 3) {
89 			tthis->maxspeed = 0.1 * (tthis->type + 1);
90 			tthis->monstervx = -tthis->maxspeed;
91 		} else if(tthis->type == 3) {
92 			tthis->maxspeed = 0.3;
93 			tthis->monstervx = tthis->maxspeed;
94 		} else if(tthis->type == 4) {
95 			tthis->maxspeed = 1.6;
96 			tthis->monstervx = tthis->maxspeed;
97 			tthis->ai.turnDefault = 100;
98 		} else {
99 			tthis->maxspeed = 0.3;
100 			tthis->monstervx = tthis->maxspeed;
101 		}
102 		tthis->isAlive = 1;
103 		tthis->score = (tthis->type + 1) * 100;
104 		tthis->monsterx = tthis->startx;
105 		tthis->monstery = tthis->starty;
106 		tthis->mtype = MOVE_LEFT;
107 		tthis->newmtype = MOVE_LEFT;
108 		tthis->spritetick = BS_GetTicks();
109 		tthis->spritenum = 0;
110 		tthis->isDying = false;
111 		tthis->dyeCount = TILESIZE;
112 		tthis->ai.turnCounter = 0;
113 		tthis = tthis->next;
114 	}
115 }
116 
freeMonsters()117 void freeMonsters() {
118 	lhandle.numMonsters = 0;
119 	MONSTERS* tthis;
120 	MONSTERS* tnext = lhandle.monsters;
121 	tthis = lhandle.monsters;
122 	while(tnext) {
123 		tnext = tthis->next;
124 		free(tthis);
125 		tthis = tnext;
126 	}
127 	lhandle.monsters = 0;
128 }
129 
blitMonsterSprite(const Sint32 x,const Sint32 y,SDL_Surface * srcSurface,const Uint32 num,const Uint32 maxheight)130 void blitMonsterSprite(const Sint32 x, const Sint32 y, SDL_Surface* srcSurface, const Uint32 num, const Uint32 maxheight) {
131 	// Ignore sprites we can't display anyway
132 	if(maxheight <= 0 || maxheight > TILESIZE) {
133 		return;
134 	}
135 
136     static SDL_Rect src;
137 	static SDL_Rect dest;
138 
139     src.x = 0;
140     src.y = TILESIZE * num;
141     src.w = dest.w = TILESIZE;
142     src.h = dest.h = maxheight;
143     dest.x = x;
144     dest.y = y + (TILESIZE - maxheight);
145 
146 	SDL_BlitSurface(srcSurface, &src, gScreen, &dest);
147 }
148 
showMonsterSprites(const Uint32 screenXoffs,const Uint32 screenYoffs)149 void showMonsterSprites(const Uint32 screenXoffs, const Uint32 screenYoffs) {
150 	MONSTERS* monster = lhandle.monsters;
151 	SDL_Surface* monstersprite = 0;
152 	Uint32 spritetick = BS_GetTicks();
153 	Sint32 scr_x;
154 	Sint32 scr_y;
155 	while(monster) {
156 		if(!monster->isAlive && !monster->isDying) {
157 			monster = monster->next;
158 			continue;
159 		}
160 		switch (monster->newmtype) {
161 			case MOVE_LEFT:
162 				monstersprite = MONSTERSPRITE_Left[monster->type];
163 				break;
164 			case MOVE_RIGHT:
165 				monstersprite = MONSTERSPRITE_Right[monster->type];
166 				break;
167 			case JUMP_LEFT:
168 				break;
169 			case JUMP_RIGHT:
170 				break;
171 			default:
172 				break;
173 		}
174 		if(monster->mtype != monster->newmtype) {
175 			monster->spritenum = 0;
176 		} else {
177 			if(monster->spritetick < spritetick) {
178 				if(!turboMode) {
179 					monster->spritetick = spritetick + SPRITE_SWITCHWAIT;
180 				} else {
181 					monster->spritetick = spritetick + SPRITE_SWITCHWAIT / 4;
182 				}
183 				if(monster->isDying) {
184 					monster->dyeCount -= MONSTER_DIESPEED;
185 					if(monster->dyeCount <= 0) {
186 						monster->isDying = false;
187 						continue;
188 					}
189 				} else {
190 					if(!monster->mustStop) {
191 						monster->spritenum++;
192 					}
193 					if(monster->spritenum >= (Uint32)(monstersprite->h / TILESIZE)) {
194 						monster->spritenum = 0;
195 					}
196 				}
197 			}
198 		}
199 
200 		scr_x = (Uint32)monster->monsterx - screenXoffs;
201 		scr_y = (Uint32)monster->monstery - screenYoffs;
202 
203 
204 		if(scr_x > -TILESIZE && scr_x < SCR_WIDTH && scr_y > -TILESIZE && scr_y < SCR_HEIGHT) {
205 			//printf("sn: %d\n", monster->spritenum);
206 			blitMonsterSprite(scr_x, scr_y, monstersprite, monster->spritenum, monster->dyeCount);
207 		}
208 
209 		monster->mtype = monster->newmtype;
210 		monster = monster->next;
211 	}
212 }
213 
monsterPhysics(const Uint32 playerx,const Uint32 playery)214 void monsterPhysics(const Uint32 playerx, const Uint32 playery) {;
215 	MONSTERS* monster = lhandle.monsters;
216 
217 	bool leftWalkTile, leftStopTile, rightWalkTile, rightStopTile, exactOnTile;
218 
219 	while(monster) {
220 		monster->seePlayer = false;
221 		monster->mustStop = false;
222 
223 
224 		if(monster->isAlive) {
225 			// Look ahead to the next floor tile in our direction and change direction if it's missing
226 			Sint32 alignedx = ((Sint32)(monster->monsterx / TILESIZE)) * TILESIZE;
227 			Sint32 alignedy = ((Sint32)(monster->monstery / TILESIZE)) * TILESIZE;
228 			if(monster->ai.turnCounter > 0) {
229 				monster->ai.turnCounter--;
230 			}
231 
232 			// Get walking and blocking tiles
233 			if(abs((Sint32)(alignedx - monster->monsterx)) <= abs((Sint32)monster->monstervx)) {
234 				// Monster more or less *exactly* on top of tile
235 				exactOnTile = true;
236 				leftWalkTile = checkForTile(alignedx - TILESIZE, alignedy + TILESIZE);
237 				rightWalkTile = checkForTile(alignedx + TILESIZE, alignedy + TILESIZE);
238 				leftStopTile = checkForTile(alignedx - TILESIZE, alignedy);
239 				rightStopTile = checkForTile(alignedx + TILESIZE, alignedy);
240 			} else {
241 				exactOnTile = false;
242 				leftWalkTile = checkForTile(alignedx, alignedy + TILESIZE);
243 				rightWalkTile = checkForTile(alignedx + TILESIZE, alignedy + TILESIZE);
244 				leftStopTile = checkForTile(alignedx, alignedy);
245 				rightStopTile = checkForTile(alignedx + TILESIZE, alignedy);
246 			}
247 
248 
249 			if(monster->type == 0) {
250 				// No special for brown monster
251 			} else if(monster->type == 1) {
252 				// Blue monster runs away when it sees the player eating a pixel
253 				if(  hasEatenPixel && abs((int)(monster->monstery - playery)) < (TILESIZE * 5)
254 					 && abs((int)(monster->monsterx - playerx)) < (TILESIZE * 15)) {
255 					monster->seePlayer = true;
256 					if(playerx < monster->monsterx && monster->monstervx < 0) {
257 						monster->monstervx = monster->maxspeed * 4.0;
258 						monster->ai.turnCounter = monster->ai.turnDefault;
259 					} else if(playerx > monster->monsterx && monster->monstervx > 0) {
260 						monster->monstervx = -monster->maxspeed * 4.0;
261 						monster->ai.turnCounter = monster->ai.turnDefault;
262 					}
263 				}
264 			} else if(monster->type == 2) {
265 				// No special handling of yellow monster
266 
267 			} else if(monster->type == 3) {
268 				// Eye-monster can try to switch to player direction when it "sees" it e.g. Player is at about same Y coords as monster
269 				if(abs((int)(monster->monstery - playery)) < (TILESIZE * 2) && abs((int)(monster->monsterx - playerx)) < (TILESIZE * 10)) {
270 					monster->seePlayer = true;
271 					if(playerx < monster->monsterx) {
272 						monster->monstervx = -monster->maxspeed;
273 						monster->ai.turnCounter = monster->ai.turnDefault;
274 					} else {
275 						monster->monstervx = monster->maxspeed;
276 						monster->ai.turnCounter = monster->ai.turnDefault;
277 					}
278 				}
279 
280 			} else if(monster->type == 4) {
281 				// Buggy monster patrols a small place, except when it sees the player
282 				// Player in sight?
283 				if(abs((int)(monster->monstery - playery)) < (TILESIZE * 3) && abs((int)(monster->monsterx - playerx)) < (TILESIZE * 3)) {
284 					monster->seePlayer = true;
285 					if((playerx+1) < monster->monsterx) {
286 						monster->monstervx = -monster->maxspeed;
287 						monster->ai.turnCounter = 100;
288 					} else if((playerx-1) > monster->monsterx) {
289 						monster->monstervx = monster->maxspeed;
290 						monster->ai.turnCounter = 100;
291 					} else {
292 						monster->mustStop = true;
293 					}
294 				} else if(monster->ai.turnCounter == 0) {
295 					// Need to do a turn
296 					monster->monstervx = -monster->monstervx;
297 					monster->monsterx += monster->monstervx;
298 					monster->ai.turnCounter = 100;
299 				}
300 			}
301 
302 
303 
304 			// Check crossing of tile-end to the left
305 			if(monster->monstervx < 0 && (monster->monsterx + monster->monstervx) < alignedx &&
306 			   !checkForTile((Uint32)alignedx - TILESIZE, (Uint32)alignedy + TILESIZE)) {
307 				if(monster->type > 2 && monster->seePlayer) {
308 					monster->mustStop = true;
309 				} else {
310 					monster->monstervx = monster->maxspeed;
311 					monster->ai.turnCounter = monster->ai.turnDefault;
312 				}
313 			}
314 
315 			// Check crossing of tile-end to the right
316 			if(monster->monstervx > 0 && (monster->monsterx + monster->monstervx) > (alignedx + (double)TILESIZE) &&
317 			   !checkForTile((Uint32)alignedx + TILESIZE*2, (Uint32)alignedy + TILESIZE)) {
318 				if(monster->type > 2 && monster->seePlayer) {
319 					monster->mustStop = true;
320 				} else {
321 					monster->monstervx = -monster->maxspeed;
322 					monster->ai.turnCounter = monster->ai.turnDefault;
323 				}
324 			}
325 
326 			// This is an ugly workaround for monsters that move off the allocated plattforms
327 			if(exactOnTile && (!leftWalkTile || leftStopTile) && (!rightWalkTile || rightStopTile)) {
328 				monster->mustStop = true;
329 			} else if(!monster->mustStop && !exactOnTile && monster->monstervx > 0.0 && !rightWalkTile) {
330 				monster->monstervx = -monster->maxspeed;
331 			} else if(!monster->mustStop && !exactOnTile && monster->monstervx < 0.0 && !leftWalkTile) {
332 				monster->monstervx = monster->maxspeed;
333 			}
334 
335 			if(!monster->mustStop) {
336 
337 				monster->monsterx += monster->monstervx;
338 				if((monster->monstervx < 0 && checkForTile((Sint32)alignedx, (Sint32)alignedy)) ||
339 				   (monster->monstervx > 0 && checkForTile((Sint32)alignedx + TILESIZE, (Sint32)alignedy))) {
340 					if(monster->type != 1) {
341 						monster->monstervx = -monster->monstervx;
342 					} else if(monster->monstervx > 0) {
343 						monster->monstervx = -monster->maxspeed;
344 					} else {
345 						monster->monstervx = monster->maxspeed;
346 					}
347 					monster->monsterx += monster->monstervx;
348 					monster->ai.turnCounter = monster->ai.turnDefault;;
349 				}
350 			}
351 
352 			if(monster->monstervx > 0) {
353 				monster->newmtype = MOVE_RIGHT;
354 			} else {
355 				monster->newmtype = MOVE_LEFT;
356 			}
357 		}
358 		monster = monster->next;
359 	}
360 }
361