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