1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/random.h"
24 
25 #include "hdb/hdb.h"
26 #include "hdb/ai.h"
27 #include "hdb/ai-player.h"
28 #include "hdb/gfx.h"
29 #include "hdb/lua-script.h"
30 #include "hdb/map.h"
31 #include "hdb/mpc.h"
32 #include "hdb/sound.h"
33 #include "hdb/window.h"
34 
35 namespace HDB {
36 
37 //-------------------------------------------------------------------
38 //
39 //	OMNIBOT : This guy moves on a path and if he sees the player
40 //		directly ahead, he will shoot at him
41 //
42 //-------------------------------------------------------------------
43 
aiOmniBotInit(AIEntity * e)44 void aiOmniBotInit(AIEntity *e) {
45 	if (e->value1 == 1)
46 		e->aiAction = aiOmniBotMove;
47 	else if (g_hdb->_ai->findPath(e))
48 		e->aiAction = aiOmniBotAction;
49 }
50 
aiOmniBotInit2(AIEntity * e)51 void aiOmniBotInit2(AIEntity *e) {
52 	e->standdownGfx[0] = e->movedownGfx[0];
53 	e->standupGfx[0] = e->movedownGfx[0];
54 	e->standleftGfx[0] = e->moveleftGfx[0];
55 	e->standrightGfx[0] = e->moverightGfx[0];
56 	e->standdownFrames = e->standupFrames = e->standleftFrames = e->standrightFrames = 1;
57 	e->draw = g_hdb->_ai->getStandFrameDir(e);
58 }
59 
aiOmniBotMove(AIEntity * e)60 void aiOmniBotMove(AIEntity *e) {
61 	if (e->goalX)
62 		g_hdb->_ai->animateEntity(e);
63 	else
64 		g_hdb->_ai->animEntFrames(e);
65 }
66 
aiOmniBotAction(AIEntity * e)67 void aiOmniBotAction(AIEntity *e) {
68 	AIEntity *p = g_hdb->_ai->getPlayer();
69 	if (e->goalX) {
70 		if (!e->sequence) {
71 			g_hdb->_ai->animateEntity(e);
72 			// Is the Player collding?
73 			if (hitPlayer(e->x, e->y) && (p->level == e->level)) {
74 				g_hdb->_ai->killPlayer(DEATH_FRIED);
75 				return;
76 			}
77 
78 			// Shoot player ?
79 			if (onEvenTile(e->x, e->y) && g_hdb->getActionMode()) {
80 				int xv = 0, yv = 0, result;
81 				bool shoot = false;
82 
83 				// FIXME: Is reloading Player required here?
84 				p = g_hdb->_ai->getPlayer();
85 
86 				// On same level/screen?
87 				if ((e->level != p->level) || g_hdb->_ai->playerDead() || !e->onScreen)
88 					return;
89 
90 				// Is Player in Line of Sight?
91 				switch (e->dir) {
92 				case DIR_UP:
93 					if (p->x == e->x && p->y < e->y) {
94 						shoot = true;
95 						yv = -1;
96 					}
97 					break;
98 				case DIR_DOWN:
99 					if (p->x == e->x && p->y > e->y) {
100 						shoot = true;
101 						yv = 1;
102 					}
103 					break;
104 				case DIR_LEFT:
105 					if (p->x < e->x && p->y == e->y) {
106 						shoot = true;
107 						xv = -1;
108 					}
109 					break;
110 				case DIR_RIGHT:
111 					if (p->x > e->x && p->y == e->y) {
112 						shoot = true;
113 						xv = 1;
114 					}
115 					break;
116 				case DIR_NONE:
117 				default:
118 					break;
119 				}
120 
121 				// If shoot = true, take the shot
122 				// (1) Check we're not shooting into a solid tile
123 				// (2) Check we're not shooting into an Entity unless it's the player
124 				AIEntity *hit = g_hdb->_ai->legalMoveOverWater(e->tileX + xv, e->tileY + yv, e->level, &result);
125 				if (shoot && !hit && result) {
126 					AIEntity *omni = g_hdb->_ai->spawn(AI_OMNIBOT_MISSILE, e->dir, e->tileX + xv, e->tileY + yv, nullptr, nullptr, nullptr, DIR_NONE, e->level, 0, 0, 1);
127 					omni->xVel = xv * kPlayerMoveSpeed * 2;
128 					omni->yVel = yv * kPlayerMoveSpeed * 2;
129 					if (g_hdb->_map->onScreen(e->tileX, e->tileY))
130 						g_hdb->_sound->playSound(SND_OMNIBOT_FIRE);
131 					if (!g_hdb->getActionMode()) {
132 						omni->xVel >>= 1;
133 						omni->yVel >>= 1;
134 					}
135 					e->sequence = 16;
136 				}
137 			}
138 		}
139 	} else {
140 		g_hdb->_ai->findPath(e);
141 		if (e->onScreen)
142 			g_hdb->_sound->playSound(SND_OMNIBOT_AMBIENT);
143 	}
144 
145 	if (e->sequence)
146 		e->sequence--;
147 }
148 
149 //-------------------------------------------------------------------
150 //
151 //	OMNIBOT MISSILE : Used by the FOURFIRER and OMNIBOT, this deadly
152 //		missile flies through the air, killing anything it hits
153 //
154 //-------------------------------------------------------------------
155 
aiOmniBotMissileInit(AIEntity * e)156 void aiOmniBotMissileInit(AIEntity *e) {
157 	e->state = STATE_MOVEDOWN;
158 	e->aiAction = aiOmniBotMissileAction;
159 }
160 
aiOmniBotMissileInit2(AIEntity * e)161 void aiOmniBotMissileInit2(AIEntity *e) {
162 	for (int i = 0; i < e->movedownFrames; i++)
163 		e->moveleftGfx[i] = e->moverightGfx[i] = e->moveupGfx[i] = e->movedownGfx[i];
164 
165 	e->moveleftFrames = e->moverightFrames = e->moveupFrames = e->movedownFrames;
166 	e->draw = e->movedownGfx[0];
167 }
168 
aiOmniBotMissileAction(AIEntity * e)169 void aiOmniBotMissileAction(AIEntity *e) {
170 	AIEntity *p = g_hdb->_ai->getPlayer();
171 
172 	g_hdb->_ai->animEntFrames(e);
173 	e->x += e->xVel;
174 	e->y += e->yVel;
175 	e->tileX = e->x / kTileWidth;
176 	e->tileY = e->y / kTileHeight;
177 
178 	// Did we hit a solid wall?
179 	int result;
180 	AIEntity *hit = g_hdb->_ai->legalMoveOverWaterIgnore(e->tileX, e->tileY, e->level, &result, e);
181 
182 	if (hit || !result) {
183 		g_hdb->_ai->addAnimateTarget(e->x, e->y, 0, 3, ANIM_FAST, false, false, "steam_puff_sit");
184 		g_hdb->_ai->removeEntity(e);
185 	}
186 
187 	// On Even tiles, check for hitting player
188 	if (onEvenTile(e->x, e->y))
189 		if (hitPlayer(e->x, e->y) && (p->level == e->level)) {
190 			g_hdb->_ai->killPlayer(DEATH_NORMAL);
191 			g_hdb->_ai->addAnimateTarget(e->x, e->y, 0, 3, ANIM_FAST, false, false, "steam_puff_sit");
192 			g_hdb->_ai->removeEntity(e);
193 		}
194 }
195 
196 //-------------------------------------------------------------------
197 //
198 //	TURNBOT : Moves straight ahead until it hits a wall, then turns
199 //		right and continues.
200 //
201 //-------------------------------------------------------------------
202 
aiTurnBotInit(AIEntity * e)203 void aiTurnBotInit(AIEntity *e) {
204 	e->aiAction = aiTurnBotAction;
205 }
206 
aiTurnBotInit2(AIEntity * e)207 void aiTurnBotInit2(AIEntity *e) {
208 	e->draw = g_hdb->_ai->getStandFrameDir(e);
209 }
210 
aiTurnBotChoose(AIEntity * e)211 void aiTurnBotChoose(AIEntity *e) {
212 	static const int xvAhead[5] = { 9, 0, 0, -1, 1 };
213 	static const int yvAhead[5] = { 9, -1, 1, 0, 0 };
214 	static const AIDir turnRight[5] = { DIR_NONE, DIR_RIGHT, DIR_LEFT, DIR_UP, DIR_DOWN };
215 	static const AIState dirState[5] = { STATE_NONE, STATE_MOVEUP, STATE_MOVEDOWN, STATE_MOVELEFT, STATE_MOVERIGHT };
216 
217 	int xv = xvAhead[e->dir];
218 	int yv = yvAhead[e->dir];
219 	if (g_hdb->_map->getMapBGTileFlags(e->tileX + xv, e->tileY + yv) & (kFlagSolid | kFlagWater)) {
220 		e->xVel = e->yVel = 0;
221 		e->animFrame = 0;
222 		e->animDelay = e->animCycle;
223 		e->dir = turnRight[e->dir];
224 		e->state = dirState[e->dir];
225 	} else {
226 		e->xVel = xv * kPlayerMoveSpeed;
227 		e->yVel = yv * kPlayerMoveSpeed;
228 		if (!g_hdb->getActionMode()) {
229 			e->xVel >>= 1;
230 			e->yVel >>= 1;
231 		}
232 		e->goalX = e->tileX + xv;
233 		e->goalY = e->tileY + yv;
234 		e->state = dirState[e->dir];
235 		if (e->dir == DIR_DOWN)
236 			e->animFrame = 3;
237 	}
238 }
239 
aiTurnBotAction(AIEntity * e)240 void aiTurnBotAction(AIEntity *e) {
241 	if (e->goalX)
242 		g_hdb->_ai->animateEntity(e);
243 	else {
244 		aiTurnBotChoose(e);
245 		g_hdb->_ai->animateEntity(e);
246 		if (e->onScreen)
247 			g_hdb->_sound->playSound(SND_TURNBOT_TURN);
248 	}
249 
250 	if (e->onScreen && onEvenTile(e->x, e->y) && g_hdb->_ai->checkPlayerCollision(e->x, e->y, 0) && !g_hdb->_ai->playerDead())
251 		g_hdb->_ai->killPlayer(DEATH_NORMAL);
252 }
253 
254 //-------------------------------------------------------------------
255 //
256 //	SHOCKBOT : Moves on a path, electrifying all tiles surrounding it
257 //		that are METAL.  Will pause when changing directions.
258 //
259 //-------------------------------------------------------------------
260 
aiShockBotInit(AIEntity * e)261 void aiShockBotInit(AIEntity *e) {
262 	g_hdb->_ai->findPath(e);
263 	e->aiAction = aiShockBotAction;
264 	e->animCycle = 0;
265 	e->sequence = 0;
266 	e->aiDraw = aiShockBotShock;
267 }
268 
aiShockBotInit2(AIEntity * e)269 void aiShockBotInit2(AIEntity *e) {
270 	e->standupFrames = e->standdownFrames = e->standleftFrames = e->standrightFrames =
271 		e->moveupFrames = e->moverightFrames = e->moveleftFrames = e->movedownFrames;
272 
273 	for (int i = 0; i < e->movedownFrames; i++)
274 		e->standupGfx[i] = e->standleftGfx[i] = e->standrightGfx[i] = e->standdownGfx[i] = e->moveupGfx[i] = e->moveleftGfx[i] = e->moverightGfx[i] = e->movedownGfx[i];
275 
276 	e->draw = g_hdb->_ai->getStandFrameDir(e);
277 }
278 
aiShockBotAction(AIEntity * e)279 void aiShockBotAction(AIEntity *e) {
280 	if (e->goalX) {
281 		if (!e->sequence) {
282 			if (hitPlayer(e->x, e->y))
283 				g_hdb->_ai->killPlayer(DEATH_SHOCKED);
284 			g_hdb->_ai->animateEntity(e);
285 		} else
286 			g_hdb->_ai->animEntFrames(e);
287 	} else {
288 		g_hdb->_ai->findPath(e);
289 		e->sequence = 20;
290 		g_hdb->_ai->animEntFrames(e);
291 		if (e->onScreen)
292 			g_hdb->_sound->playSound(SND_SHOCKBOT_AMBIENT);
293 
294 	}
295 
296 	if (e->sequence)
297 		e->sequence--;
298 }
299 
aiShockBotShock(AIEntity * e,int mx,int my)300 void aiShockBotShock(AIEntity *e, int mx, int my) {
301 	static const int offX[8] = { -1, 0, 1, 1, 1, 0, -1, -1 };
302 	static const int offY[8] = { -1, -1, -1, 0, 1, 1, 1, 0 };
303 
304 	// Only on a exact tile boundary do we change the shocked tiles
305 	// Start at top left and go around
306 	if (g_hdb->_map->getMapBGTileFlags(e->tileX, e->tileY) & kFlagMetal)
307 		e->special1Gfx[e->animFrame]->drawMasked(e->tileX * kTileWidth - mx, e->tileY * kTileHeight - my);
308 
309 	for (int i = 0; i < 8; i++) {
310 		uint32 flags = g_hdb->_map->getMapBGTileFlags(e->tileX + offX[i], e->tileY + offY[i]);
311 		if (flags & kFlagMetal) {
312 			// Is the shocking tile onScreen?
313 			if (g_hdb->_map->checkXYOnScreen((e->tileX + offX[i]) * kTileWidth, (e->tileY + offY[i]) * kTileHeight)) {
314 				// Draw shocking tile animation
315 				e->special1Gfx[e->animFrame]->drawMasked((e->tileX + offX[i])*kTileWidth - mx, (e->tileY + offY[i])*kTileHeight - my);
316 				// Did the player get fried?
317 				// Check every 4 frames
318 				if (e->onScreen && !e->animFrame && g_hdb->_ai->checkPlayerTileCollision(e->tileX + offX[i], e->tileY + offY[i]) && !g_hdb->_ai->playerDead()) {
319 					g_hdb->_ai->killPlayer(DEATH_SHOCKED);
320 					return;
321 				}
322 				if (!e->animFrame && g_hdb->_map->boomBarrelExist(e->tileX + offX[i], e->tileY + offY[i])) {
323 					AIEntity *e2 = g_hdb->_ai->findEntityType(AI_BOOMBARREL, e->tileX + offX[i], e->tileY + offY[i]);
324 					aiBarrelExplode(e2);
325 				}
326 			}
327 		}
328 	}
329 }
330 
331 //-------------------------------------------------------------------
332 //
333 //	RIGHTBOT
334 //
335 //	Rules: Follows the right-hand wall.  That's it!
336 //
337 //-------------------------------------------------------------------
338 
aiRightBotInit(AIEntity * e)339 void aiRightBotInit(AIEntity *e) {
340 	e->moveSpeed = kPlayerMoveSpeed;
341 	if (!g_hdb->getActionMode())
342 		e->moveSpeed >>= 1;
343 	e->aiAction = aiRightBotAction;
344 }
345 
aiRightBotInit2(AIEntity * e)346 void aiRightBotInit2(AIEntity *e) {
347 	switch (e->dir) {
348 	case DIR_UP:
349 		e->draw = e->moveupGfx[0];
350 		e->state = STATE_MOVEUP;
351 		break;
352 	case DIR_DOWN:
353 		e->draw = e->movedownGfx[0];
354 		e->state = STATE_MOVEDOWN;
355 		break;
356 	case DIR_LEFT:
357 		e->draw = e->moveleftGfx[0];
358 		e->state = STATE_MOVELEFT;
359 		break;
360 	case DIR_RIGHT:
361 		e->draw = e->moverightGfx[0];
362 		e->state = STATE_MOVERIGHT;
363 		break;
364 	case DIR_NONE:
365 	default:
366 		break;
367 	}
368 }
369 
aiRightBotFindGoal(AIEntity * e)370 void aiRightBotFindGoal(AIEntity *e) {
371 	static const int xvAhead[5] = { 9, 0, 0,-1, 1 };
372 	static const int yvAhead[5] = { 9,-1, 1, 0, 0 };
373 	static const int xvAToR[5]  = { 9, 1,-1,-1, 1 };
374 	static const int yvAToR[5]  = { 9,-1, 1,-1, 1 };
375 	static const int xvToR[5]   = { 9, 1,-1, 0, 0 };
376 	static const int yvToR[5]   = { 9, 0, 0,-1, 1 };
377 	static const int xvToL[5]   = { 9,-1, 1, 0, 0 };
378 	static const int yvToL[5]   = { 9, 0, 0, 1,-1 };
379 
380 	AIEntity *p = g_hdb->_ai->getPlayer();
381 	int rotate = 0;
382 
383 	int	xv, yv;
384 	int	bg, bg2, bg3;
385 	AIEntity *e1, *e2, *e3;
386 	int	sx, sy;
387 
388 	do {
389 		xv = xvAhead[e->dir];	// Search Ahead
390 		yv = yvAhead[e->dir];
391 		int xv2 = xvAToR[e->dir];	// Search Ahead and to the Right
392 		int yv2 = yvAToR[e->dir];
393 		int xv3 = xvToR[e->dir];	// Search to the Right
394 		int yv3 = yvToR[e->dir];
395 
396 		// Search until we hit a wall...or empty space to our right (and forward)
397 		bool hit = false;
398 		sx = e->tileX;
399 		sy = e->tileY;
400 
401 		while (!hit) {
402 			bg = g_hdb->_map->getMapBGTileFlags(sx + xv, sy + yv) & (kFlagSolid | kFlagWater | kFlagSlime | kFlagSpecial);
403 			e1 = g_hdb->_ai->findEntity(sx + xv, sy + yv);
404 			if (e1 && e1 == p)
405 				e1 = nullptr;
406 			bg2 = g_hdb->_map->getMapBGTileFlags(sx + xv2, sy + yv2) & (kFlagSolid | kFlagWater | kFlagSlime | kFlagSpecial);
407 			e2 = g_hdb->_ai->findEntity(sx + xv2, sy + yv2);
408 			if (e2 && e2 == p)
409 				e2 = nullptr;
410 			bg3 = g_hdb->_map->getMapBGTileFlags(sx + xv3, sy + yv3) & (kFlagSolid | kFlagWater | kFlagSlime | kFlagSpecial);
411 			e3 = g_hdb->_ai->findEntity(sx + xv3, sy + yv3);
412 			if (e3 && e3 == p)
413 				e3 = nullptr;
414 
415 			// Okay to move forward?
416 			if ((!bg && !e1) && (bg2 || e2 || bg3 || e3)) {
417 				sx += xv;
418 				sy += yv;
419 				rotate = 0;
420 			} else
421 				hit = true;
422 		}
423 
424 		// Are we stuck in a corner?
425 		if (sx == e->tileX && sy == e->tileY) {
426 			sx = e->tileX;
427 			sy = e->tileY;
428 			rotate += 1;
429 
430 			// Need to check for turning RIGHT when we're in a corner
431 			xv = xvToL[e->dir];
432 			yv = yvToL[e->dir];
433 
434 			// Check Tile flags to our left and right
435 			bg = g_hdb->_map->getMapBGTileFlags(sx + xv, sy + yv) & (kFlagSolid | kFlagWater | kFlagSlime | kFlagSpecial);
436 			e1 = g_hdb->_ai->findEntity(sx + xv, sy + yv);
437 			bg2 = g_hdb->_map->getMapBGTileFlags(sx + xv3, sy + yv3) & (kFlagSolid | kFlagWater | kFlagSlime | kFlagSpecial);
438 			e2 = g_hdb->_ai->findEntity(sx + xv3, sy + yv3);
439 			if (e1 && e1->type == AI_GUY)
440 				e1 = nullptr;
441 			if (e2 && e2->type == AI_GUY)
442 				e2 = nullptr;
443 
444 			// Is tile to the right clear?
445 			// Is tile to the left clear?
446 			// If neither, go backwards
447 			if (!bg2 && !e2) {
448 				switch (e->dir) {
449 				case DIR_UP:
450 					e->dir = DIR_RIGHT;
451 					break;
452 				case DIR_DOWN:
453 					e->dir = DIR_LEFT;
454 					break;
455 				case DIR_LEFT:
456 					e->dir = DIR_UP;
457 					break;
458 				case DIR_RIGHT:
459 					e->dir = DIR_DOWN;
460 					break;
461 				case DIR_NONE:
462 				default:
463 					break;
464 				}
465 			} else if (!bg && !e1) {
466 				switch (e->dir) {
467 				case DIR_UP:
468 					e->dir = DIR_LEFT;
469 					break;
470 				case DIR_DOWN:
471 					e->dir = DIR_RIGHT;
472 					break;
473 				case DIR_LEFT:
474 					e->dir = DIR_DOWN;
475 					break;
476 				case DIR_RIGHT:
477 					e->dir = DIR_UP;
478 					break;
479 				case DIR_NONE:
480 				default:
481 					break;
482 				}
483 			} else {
484 				switch (e->dir) {
485 				case DIR_UP:
486 					e->dir = DIR_DOWN;
487 					yv = 1;
488 					xv = 0;
489 					break;
490 				case DIR_DOWN:
491 					e->dir = DIR_UP;
492 					yv = -1;
493 					xv = 0;
494 					break;
495 				case DIR_LEFT:
496 					e->dir = DIR_RIGHT;
497 					yv = 0;
498 					xv = 1;
499 					break;
500 				case DIR_RIGHT:
501 					e->dir = DIR_LEFT;
502 					yv = 0;
503 					xv = -1;
504 					break;
505 				case DIR_NONE:
506 				default:
507 					break;
508 				}
509 				sx += xv;
510 				sy += yv;
511 				rotate = 4;
512 			}
513 		}
514 	} while (rotate >= 1 && rotate < 4);
515 
516 	switch (e->dir) {
517 	case DIR_UP:
518 		e->state = STATE_MOVEUP;
519 		break;
520 	case DIR_DOWN:
521 		e->state = STATE_MOVEDOWN;
522 		break;
523 	case DIR_LEFT:
524 		e->state = STATE_MOVELEFT;
525 		break;
526 	case DIR_RIGHT:
527 		e->state = STATE_MOVERIGHT;
528 		break;
529 	case DIR_NONE:
530 	default:
531 		break;
532 	}
533 
534 	e->goalX = sx;
535 	e->goalY = sy;
536 	e->xVel = xv * e->moveSpeed;
537 	e->yVel = yv * e->moveSpeed;
538 	if (e->onScreen)
539 		g_hdb->_sound->playSound(SND_RIGHTBOT_TURN);
540 }
541 
aiRightBotAction(AIEntity * e)542 void aiRightBotAction(AIEntity *e) {
543 	AIEntity *p = g_hdb->_ai->getPlayer();
544 
545 	if (e->goalX) {
546 		if (e->onScreen && g_hdb->_ai->checkPlayerCollision(e->x, e->y, 0) && p->state != STATE_DEAD && p->level == e->level && !g_hdb->_ai->playerDead())
547 			g_hdb->_ai->killPlayer(DEATH_NORMAL);
548 		g_hdb->_ai->animateEntity(e);
549 	} else {
550 		aiRightBotFindGoal(e);
551 		g_hdb->_ai->animEntFrames(e);
552 	}
553 }
554 
555 //-------------------------------------------------------------------
556 //
557 //	PUSHBOT : Very simple, this guy goes forward and pushes anything in his
558 //		path all the way until it can't go any further.  Then, he turns 180
559 //		degress and comes back until he can't go any further.  Then... he
560 //		turns 180 degrees and does it all over again!
561 //
562 //-------------------------------------------------------------------
563 
aiPushBotInit(AIEntity * e)564 void aiPushBotInit(AIEntity *e) {
565 	if (e->value1 != 1)
566 		e->aiAction = aiPushBotAction;
567 }
568 
aiPushBotInit2(AIEntity * e)569 void aiPushBotInit2(AIEntity *e) {
570 	e->draw = g_hdb->_ai->getStandFrameDir(e);
571 }
572 
aiPushBotAction(AIEntity * e)573 void aiPushBotAction(AIEntity *e) {
574 	static const AIState moveState[5] = { STATE_NONE, STATE_MOVEUP, STATE_MOVEDOWN, STATE_MOVELEFT, STATE_MOVERIGHT };
575 	static const int xvAhead[5] = { 9, 0, 0,-1, 1 };
576 	static const int yvAhead[5] = { 9,-1, 1, 0, 0 };
577 	static const AIDir oneEighty[5] = { DIR_NONE, DIR_DOWN, DIR_UP, DIR_RIGHT, DIR_LEFT };
578 
579 	AIEntity *e1 = nullptr;
580 
581 	if (e->goalX) {
582 		g_hdb->_ai->animateEntity(e);
583 		if (hitPlayer(e->x, e->y))
584 			g_hdb->_ai->killPlayer(DEATH_NORMAL);
585 	} else {
586 		if (hitPlayer(e->x, e->y))
587 			g_hdb->_ai->killPlayer(DEATH_NORMAL);
588 
589 		// Where to go next
590 		int nx = e->tileX + xvAhead[e->dir];
591 		int ny = e->tileY + yvAhead[e->dir];
592 
593 		int result;
594 		e1 = g_hdb->_ai->legalMove(nx, ny, e->level, &result);
595 
596 		// Push something
597 		// Turn Around
598 		// Move Forward
599 		if (e1 && onEvenTile(e1->x, e1->y) && (e1->type == AI_LIGHTBARREL || e1->type == AI_HEAVYBARREL || e1->type == AI_BOOMBARREL || e1->type == AI_CRATE)) {
600 			// Actually going over a floating crate?
601 			if (e1 && (e1->state == STATE_FLOATING || e1->state == STATE_MELTED)) {
602 				e->state = moveState[e->dir];
603 				g_hdb->_ai->setEntityGoal(e, nx, ny);
604 				g_hdb->_ai->animateEntity(e);
605 				return;
606 			}
607 
608 			int nx2 = nx + xvAhead[e->dir];
609 			int ny2 = ny + yvAhead[e->dir];
610 
611 			uint32 bgFlags = g_hdb->_map->getMapBGTileFlags(nx2, ny2);
612 			uint32 fgFlags = g_hdb->_map->getMapFGTileFlags(nx2, ny2);
613 			AIEntity *e2 = g_hdb->_ai->findEntity(nx2, ny2);
614 			result = (e->level == 1) ? (bgFlags & kFlagSolid) : !(fgFlags & kFlagGrating) && (bgFlags & kFlagSolid);
615 
616 			// If we're going to push something onto a floating thing, that's ok
617 			if (e2 && (e2->state == STATE_FLOATING || e2->state == STATE_MELTED))
618 				e2 = nullptr;
619 
620 			// If no walls in front & no entities
621 			if (!result && !e2 && e1->state != STATE_EXPLODING) {
622 				e->state = moveState[e->dir];
623 				g_hdb->_ai->setEntityGoal(e, nx, ny);
624 
625 				e1->dir = e->dir;
626 				e1->state = e->state;
627 				e1->moveSpeed = e->moveSpeed;
628 				g_hdb->_ai->setEntityGoal(e1, nx2, ny2);
629 				switch (e1->type) {
630 				case AI_CRATE:
631 					g_hdb->_sound->playSound(SND_CRATE_SLIDE);
632 					break;
633 				case AI_HEAVYBARREL:
634 				case AI_BOOMBARREL:
635 					g_hdb->_sound->playSound(SND_HEAVY_SLIDE);
636 					break;
637 				case AI_LIGHTBARREL:
638 					g_hdb->_sound->playSound(SND_LIGHT_SLIDE);
639 					break;
640 				default:
641 					break;
642 				}
643 			} else {
644 				if (e->onScreen)
645 					g_hdb->_sound->playSound(SND_PUSHBOT_STRAIN);
646 				e->dir = oneEighty[e->dir];
647 				e->state = moveState[e->dir];
648 				nx = e->tileX + xvAhead[e->dir];
649 				ny = e->tileY + yvAhead[e->dir];
650 				e1 = g_hdb->_ai->legalMove(nx, ny, e->level, &result);
651 				if (!e1 && result)
652 					g_hdb->_ai->setEntityGoal(e, nx, ny);
653 			}
654 		} else if (!result || (e1 && !onEvenTile(e1->x, e1->y))) {
655 			e->dir = oneEighty[e->dir];
656 			e->state = moveState[e->dir];
657 			nx = e->tileX + xvAhead[e->dir];
658 			ny = e->tileY + yvAhead[e->dir];
659 			e1 = g_hdb->_ai->legalMove(nx, ny, e->level, &result);
660 			if (!e1 && result)
661 				g_hdb->_ai->setEntityGoal(e, nx, ny);
662 		} else {
663 			e->state = moveState[e->dir];
664 			g_hdb->_ai->setEntityGoal(e, nx, ny);
665 		}
666 		g_hdb->_ai->animateEntity(e);
667 	}
668 }
669 
670 //-------------------------------------------------------------------
671 //
672 //	RAILRIDER : crazy green goopy dude -- he gives you rides on his
673 //		special track!
674 //
675 //-------------------------------------------------------------------
676 
aiRailRiderInit(AIEntity * e)677 void aiRailRiderInit(AIEntity *e) {
678 	if (e->type == AI_RAILRIDER_ON) {
679 		// On the tracks already - spawn RED arrow
680 		g_hdb->_ai->addToPathList(e->tileX, e->tileY, 0, e->dir);
681 		e->state = STATE_STANDUP;
682 		e->aiAction = aiRailRiderOnAction;
683 		e->aiUse = aiRailRiderOnUse;
684 	} else {
685 		e->state = STATE_STANDDOWN;
686 		e->sequence = 0;
687 		e->aiAction = aiRailRiderAction;
688 		e->aiUse = aiRailRiderUse;
689 	}
690 	e->moveSpeed = kPlayerMoveSpeed;
691 }
692 
aiRailRiderInit2(AIEntity * e)693 void aiRailRiderInit2(AIEntity *e) {
694 	e->draw = e->standdownGfx[0];
695 }
696 
697 // Talking to RailRider off track
aiRailRiderUse(AIEntity * e)698 void aiRailRiderUse(AIEntity *e) {
699 	e->sequence = 1;
700 }
701 
aiRailRiderAction(AIEntity * e)702 void aiRailRiderAction(AIEntity *e) {
703 	switch (e->sequence) {
704 		// Waiting for Dialog to goaway
705 	case 1:
706 		// Dialog gone?
707 		if (!g_hdb->_window->dialogActive()) {
708 			e->sequence = 2;
709 			switch (e->dir) {
710 			case DIR_UP:
711 				e->xVel = 0;
712 				e->yVel = -1;
713 				break;
714 			case DIR_DOWN:
715 				e->xVel = 0;
716 				e->yVel = 1;
717 				break;
718 			case DIR_LEFT:
719 				e->xVel = -1;
720 				e->yVel = 0;
721 				break;
722 			case DIR_RIGHT:
723 				e->xVel = 1;
724 				e->yVel = 0;
725 				break;
726 			case DIR_NONE:
727 			default:
728 				break;
729 			}
730 		}
731 		break;
732 		// Walking over to track
733 	case 2:
734 		e->x += e->xVel;
735 		e->y += e->yVel;
736 		if (onEvenTile(e->x, e->y)) {
737 			ArrowPath *arrowPath;
738 			e->tileX = e->x / kTileWidth;
739 			e->tileY = e->y / kTileHeight;
740 			e->sequence = 3;	// Wait for use
741 			e->type = AI_RAILRIDER_ON;
742 			e->state = STATE_STANDUP;
743 			e->aiAction = aiRailRiderOnAction;
744 			e->aiUse = aiRailRiderOnUse;
745 			arrowPath = g_hdb->_ai->findArrowPath(e->tileX, e->tileY);
746 
747 			if (arrowPath == nullptr)
748 				return;
749 
750 			e->dir = arrowPath->dir;
751 			e->value1 = 0;	// Not in a tunnel
752 		}
753 		break;
754 	default:
755 		break;
756 	}
757 
758 	// Cycle through animation frames
759 	if (e->animDelay-- > 0)
760 		return;
761 	e->animDelay = e->animCycle;
762 	e->animFrame++;
763 	if (e->animFrame == e->standdownFrames)
764 		e->animFrame = 0;
765 
766 	e->draw = e->standdownGfx[e->animFrame];
767 }
768 
769 // Talking to RailRider on track
aiRailRiderOnUse(AIEntity * e)770 void aiRailRiderOnUse(AIEntity *e) {
771 	AIEntity *p = g_hdb->_ai->getPlayer();
772 
773 	if (p->tileX == e->tileX) {
774 		if (p->tileY > e->tileY)
775 			g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY - 1);
776 		else
777 			g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY + 1);
778 	} else if (p->tileX > e->tileX)
779 		g_hdb->_ai->setEntityGoal(p, p->tileX - 1, p->tileY);
780 	else
781 		g_hdb->_ai->setEntityGoal(p, p->tileX + 1, p->tileY);
782 
783 	e->sequence = -1;	// Waiting for player to board
784 }
785 
aiRailRiderOnAction(AIEntity * e)786 void aiRailRiderOnAction(AIEntity *e) {
787 	static const int xv[5] = { 9, 0, 0, -1, 1 };
788 	static const int yv[5] = { 9, -1, 1, 0, 0 };
789 
790 	AIEntity*p = g_hdb->_ai->getPlayer();
791 
792 	switch (e->sequence) {
793 	// Player is boarding
794 	case -1:
795 		if (!p->goalX)
796 			e->sequence = 1; // Boarded yet?
797 		// fallthrough
798 	// Cycle Animation Frames
799 	case 3:
800 		if (e->animDelay-- > 0)
801 			return;
802 		e->animDelay = e->animCycle;
803 		e->animFrame++;
804 		if (e->animFrame == e->standupFrames)
805 			e->animFrame = 0;
806 
807 		e->draw = e->standupGfx[e->animFrame];
808 		break;
809 	// Player is in - lock him
810 	case 1:
811 		g_hdb->_ai->setPlayerInvisible(true);
812 		g_hdb->_ai->setPlayerLock(true);
813 		g_hdb->_ai->setEntityGoal(e, e->tileX + xv[e->dir], e->tileY + yv[e->dir]);
814 		g_hdb->_sound->playSound(SND_RAILRIDER_TASTE);
815 		e->sequence = 2;
816 		e->value1 = 0;
817 		// fallthrough
818 
819 	// New RailRider gfx
820 	// Move the RailRider
821 	case 2: {
822 		// Done moving to next spot?
823 		if (!e->goalX) {
824 			ArrowPath *arrowPath = g_hdb->_ai->findArrowPath(e->tileX, e->tileY);
825 			if (arrowPath) {
826 				// Stop Arrow?
827 				if (!arrowPath->type) {
828 					HereT *h;
829 					e->sequence = 4;	// Get Player off RailRider - RIGHT SIDE ONLY
830 					p->tileX = e->tileX;
831 					p->tileY = e->tileY;
832 					p->x = e->x;
833 					p->y = e->y;
834 					// Try to find a HERE icon to either side of the track and go there
835 					switch (e->dir) {
836 					case DIR_UP:
837 						h = g_hdb->_ai->findHere(e->tileX - 1, e->tileY);
838 						if (h)
839 							g_hdb->_ai->setEntityGoal(p, e->tileX - 1, e->tileY);
840 						else
841 							g_hdb->_ai->setEntityGoal(p, e->tileX + 1, e->tileY);
842 						break;
843 					case DIR_DOWN:
844 						h = g_hdb->_ai->findHere(e->tileX + 1, e->tileY);
845 						if (h)
846 							g_hdb->_ai->setEntityGoal(p, e->tileX + 1, e->tileY);
847 						else
848 							g_hdb->_ai->setEntityGoal(p, e->tileX - 1, e->tileY);
849 						break;
850 					case DIR_LEFT:
851 						h = g_hdb->_ai->findHere(e->tileX, e->tileY + 1);
852 						if (h)
853 							g_hdb->_ai->setEntityGoal(p, e->tileX, e->tileY + 1);
854 						else
855 							g_hdb->_ai->setEntityGoal(p, e->tileX, e->tileY - 1);
856 						break;
857 					case DIR_RIGHT:
858 						h = g_hdb->_ai->findHere(e->tileX, e->tileY - 1);
859 						if (h)
860 							g_hdb->_ai->setEntityGoal(p, e->tileX, e->tileY - 1);
861 						else
862 							g_hdb->_ai->setEntityGoal(p, e->tileX, e->tileY + 1);
863 						break;
864 					case DIR_NONE:
865 					default:
866 						break;
867 					}
868 					g_hdb->_ai->setPlayerInvisible(false);
869 					g_hdb->_sound->playSound(SND_RAILRIDER_EXIT);
870 				} else if (arrowPath->type == 1) {
871 					e->dir = arrowPath->dir;
872 					g_hdb->_ai->setEntityGoal(e, e->tileX + xv[e->dir], e->tileY + yv[e->dir]);
873 				}
874 			} else
875 				g_hdb->_ai->setEntityGoal(e, e->tileX + xv[e->dir], e->tileY + yv[e->dir]);
876 
877 			g_hdb->_sound->playSound(SND_RAILRIDER_ONTRACK);
878 		}
879 
880 		p->tileX = e->tileX;
881 		p->tileY = e->tileY;
882 		p->x = e->x;
883 		p->y = e->y;
884 		g_hdb->_ai->animateEntity(e);
885 		switch (e->dir) {
886 		case DIR_UP:
887 			e->draw = e->moveupGfx[0];
888 			break;
889 		case DIR_DOWN:
890 			e->draw = e->movedownGfx[0];
891 			break;
892 		case DIR_LEFT:
893 			e->draw = e->moveleftGfx[0];
894 			break;
895 		case DIR_RIGHT:
896 			e->draw = e->moverightGfx[0];
897 			break;
898 		case DIR_NONE:
899 		default:
900 			break;
901 		}
902 		g_hdb->_map->centerMapXY(e->x + 16, e->y + 16);
903 
904 		SingleTele t;
905 		// Did we hit a tunnel entrance?
906 		if (onEvenTile(e->x, e->y) && g_hdb->_ai->findTeleporterDest(e->tileX, e->tileY, &t) && !e->value1 && !e->dir2) {
907 			// Set tunnel destination
908 			e->value1 = t.x;
909 			e->value2 = t.y;
910 			e->dir2 = (AIDir)(t.x + t.y);	// Flag for coming out of tunnel
911 		}
912 
913 		// Are we going through a tunnel?
914 		if (e->value1) {
915 			// Reach the End?
916 			// If not, don't draw RailRider
917 			if (onEvenTile(e->x, e->y) && e->tileX == e->value1 && e->tileY == e->value2)
918 				e->value1 = 0;
919 			else
920 				e->draw = nullptr;
921 		} else if (e->dir2 && e->dir2 != (AIDir)(e->tileX + e->tileY))
922 			e->dir2 = DIR_NONE;
923 		break;
924 		}
925 	// Waiting for Player to move to Dest
926 	case 4:
927 		if (!p->goalX) {
928 			g_hdb->_ai->setPlayerLock(false);
929 			e->sequence = 3;	// Wait for Use
930 		}
931 
932 		// Cycle Animation frames
933 		if (e->animDelay-- > 0)
934 			return;
935 
936 		e->animDelay = e->animCycle;
937 		e->animFrame++;
938 		if (e->animFrame == e->standupFrames)
939 			e->animFrame = 0;
940 
941 		e->draw = e->standupGfx[e->animFrame];
942 		break;
943 	default:
944 		break;
945 	}
946 }
947 
948 //-------------------------------------------------------------------
949 //
950 //	MAINTBOT : This little fella likes to cause trouble!  He just jubs
951 //		around the map and looks for stuff to press.  Touch him and you die.
952 //
953 //-------------------------------------------------------------------
954 
aiMaintBotInit(AIEntity * e)955 void aiMaintBotInit(AIEntity *e) {
956 	// value1 field determines whether the "MMM!" sound plays
957 	// 1 means NO
958 	e->int1 = e->value1;
959 	e->aiAction = aiMaintBotAction;
960 	e->value1 = 0;
961 	g_hdb->_ai->findPath(e);
962 }
963 
aiMaintBotInit2(AIEntity * e)964 void aiMaintBotInit2(AIEntity *e) {
965 	e->draw = g_hdb->_ai->getStandFrameDir(e);
966 }
967 
aiMaintBotAction(AIEntity * e)968 void aiMaintBotAction(AIEntity *e) {
969 	static const AIState useState[5]   = {STATE_NONE, STATE_USEUP, STATE_USEDOWN, STATE_USELEFT, STATE_USERIGHT};
970 	static const AIState standState[5] = {STATE_NONE, STATE_STANDUP, STATE_STANDDOWN, STATE_STANDLEFT, STATE_STANDRIGHT};
971 	static const int xvAhead[5]  = {9, 0, 0,-1, 1};
972 	static const int yvAhead[5]  = {9,-1, 1, 0, 0};
973 	static const int whistles[3] = {SND_MBOT_WHISTLE1, SND_MBOT_WHISTLE2, SND_MBOT_WHISTLE3};
974 	static const AIDir lookRight[5] = {DIR_NONE, DIR_RIGHT, DIR_LEFT, DIR_UP, DIR_DOWN};
975 	static const AIDir lookLeft[5] = {DIR_NONE, DIR_LEFT, DIR_RIGHT, DIR_DOWN, DIR_UP};
976 	static const AIDir dirList[5] = {DIR_NONE, DIR_UP, DIR_DOWN, DIR_LEFT, DIR_RIGHT};
977 
978 	// Waiting at an arrow (or hit by player)?
979 	if (e->sequence) {
980 		e->sequence--;
981 		g_hdb->_ai->animEntFrames(e);
982 
983 		// Use Something here
984 		if (!e->value2)
985 			switch (e->sequence) {
986 			case 50:
987 				if (e->onScreen && !e->int1 && !g_hdb->isDemo()) {
988 					if (g_hdb->_rnd->getRandomNumber(1))
989 						g_hdb->_sound->playSound(SND_MBOT_HMMM2);
990 					else
991 						g_hdb->_sound->playSound(SND_MBOT_HMMM);
992 				}
993 				break;
994 			// Need to USE the object
995 			case 30: {
996 				e->state = useState[e->dir];
997 				int nx = e->tileX + xvAhead[e->dir];
998 				int ny = e->tileY + yvAhead[e->dir];
999 				AIEntity *it = g_hdb->_ai->findEntity(nx, ny);
1000 				if (it) {
1001 					if (e->onScreen)
1002 						e->value1 = 1;
1003 					g_hdb->useEntity(it);
1004 					break;
1005 				}
1006 				// Did the MaintBot use an Action Tile?
1007 				if (g_hdb->_ai->checkActionList(e, nx, ny, true)) {
1008 					if (e->onScreen)
1009 						e->value1 = 1;
1010 					break;
1011 				}
1012 				// Did the MaintBot use an AutoAction Tile?
1013 				if (g_hdb->_ai->checkAutoList(e, nx, ny)) {
1014 					if (e->onScreen)
1015 						e->value1 = 1;
1016 					break;
1017 				}
1018 				// Did the MaintBot use a LUA Tile?
1019 				if (g_hdb->_ai->checkLuaList(e, nx, ny)) {
1020 					if (e->onScreen)
1021 						e->value1 = 1;
1022 					break;
1023 				}
1024 				break;
1025 				}
1026 			// Play a sound if we used something
1027 			case 25:
1028 				e->value1 = 0;
1029 				break;
1030 			// Change to Standing frames
1031 			case 20:
1032 				e->state = standState[e->dir];
1033 				break;
1034 			// All done - find a new path
1035 			case 0:
1036 				e->dir = e->dir2;
1037 				g_hdb->_ai->findPath(e);
1038 				g_hdb->_ai->animateEntity(e);
1039 				break;
1040 			default:
1041 				break;
1042 			}
1043 		// Deciding where to go at 4-way
1044 		else {
1045 			switch (e->sequence) {
1046 			// HMM
1047 			case 50:
1048 				if (e->onScreen && !e->int1 && !g_hdb->isDemo())
1049 					g_hdb->_sound->playSound(SND_MBOT_HMMM);
1050 				break;
1051 			// Look Right
1052 			case 40:
1053 				e->dir = lookRight[e->dir2];
1054 				e->state = standState[e->dir];
1055 				break;
1056 			// Look Left
1057 			case 30:
1058 				e->dir = lookLeft[e->dir];
1059 				e->state = standState[e->dir];
1060 				break;
1061 			// HMM2
1062 			case 25:
1063 				if (e->onScreen && !e->int1 && !g_hdb->isDemo())
1064 					g_hdb->_sound->playSound(SND_MBOT_HMMM2);
1065 				break;
1066 			// Decide direction and GO
1067 			case 0:
1068 				{
1069 					int dir = (g_hdb->_rnd->getRandomNumber(3)) + 1;
1070 					e->dir = dirList[dir];
1071 					g_hdb->_ai->findPath(e);
1072 					if (e->onScreen && !g_hdb->isDemo())
1073 						g_hdb->_sound->playSound(whistles[g_hdb->_rnd->getRandomNumber(2)]);
1074 				}
1075 				break;
1076 			default:
1077 				break;
1078 			}
1079 		}
1080 		return;
1081 	}
1082 
1083 	// Moving already, keep going
1084 	if (e->goalX) {
1085 		g_hdb->_ai->animateEntity(e);
1086 		if (hitPlayer(e->x, e->y)) {
1087 			g_hdb->_ai->killPlayer(DEATH_GRABBED);
1088 			if (!g_hdb->isDemo())
1089 				g_hdb->_sound->playSound(SND_MBOT_DEATH);
1090 		}
1091 	} else {
1092 		// Check if there's an arrow UNDER the bot, and if its RED
1093 		// If so, turn in that direction and use something
1094 		ArrowPath *ar = g_hdb->_ai->findArrowPath(e->tileX, e->tileY);
1095 		if (ar) {
1096 			// STOP		Arrow
1097 			// GO		Arrow
1098 			// 4-way	Arrow
1099 			if (!ar->type) {
1100 				e->dir2 = e->dir; // dir2 holds the last direction we were travelling in
1101 				e->dir = ar->dir;
1102 				e->sequence = 64; // sequence is the timer of events
1103 				e->state = standState[e->dir];
1104 				e->value2 = 0;
1105 				return;
1106 			} else if (ar->type == 1) {
1107 				g_hdb->_ai->findPath(e);
1108 				if (!g_hdb->isDemo())
1109 					g_hdb->_sound->playSound(whistles[g_hdb->_rnd->getRandomNumber(2)]);
1110 			} else {
1111 				e->sequence = 64;
1112 				e->dir2 = e->dir;
1113 				e->value2 = 1;
1114 				return;
1115 			}
1116 		}
1117 		g_hdb->_ai->animateEntity(e);
1118 	}
1119 }
1120 
1121 //-------------------------------------------------------------------
1122 //
1123 //	FOURFIRER : This bot turns and fires in the direction it's facing,
1124 //		but only if the player is visible
1125 //
1126 //-------------------------------------------------------------------
1127 
aiFourFirerInit(AIEntity * e)1128 void aiFourFirerInit(AIEntity *e) {
1129 	e->value1 = 0;
1130 	e->aiAction = aiFourFirerAction;
1131 }
1132 
aiFourFirerInit2(AIEntity * e)1133 void aiFourFirerInit2(AIEntity *e) {
1134 	e->draw = g_hdb->_ai->getStandFrameDir(e);
1135 }
1136 
aiFourFirerAction(AIEntity * e)1137 void aiFourFirerAction(AIEntity *e) {
1138 	static const AIState state[5] = {STATE_NONE, STATE_STANDUP, STATE_STANDDOWN, STATE_STANDLEFT, STATE_STANDRIGHT};
1139 	static const AIDir turn[5] = {DIR_NONE, DIR_RIGHT, DIR_LEFT, DIR_UP, DIR_DOWN};
1140 
1141 	AIEntity *p = g_hdb->_ai->getPlayer();
1142 	// Time to turn right?
1143 	if (!e->value1) {
1144 		e->dir = turn[e->dir];
1145 		e->state = state[e->dir];
1146 		e->value1 = 16;
1147 		if (e->onScreen)
1148 			g_hdb->_sound->playSound(SND_FOURFIRE_TURN);
1149 	}
1150 	e->value1--;
1151 
1152 	// Waiting before firing again?
1153 	if (e->sequence) {
1154 		e->sequence--;
1155 		return;
1156 	}
1157 
1158 	g_hdb->_ai->animEntFrames(e);
1159 
1160 	// Can we see the player on the same level?
1161 	if ((e->level != p->level) || g_hdb->_ai->playerDead() || !e->onScreen)
1162 		return;
1163 
1164 	// Check player direction
1165 	bool shoot = false;
1166 	int xv = 0;
1167 	int yv = 0;
1168 
1169 	switch (e->dir) {
1170 	case DIR_UP:
1171 		if (p->x == e->x && p->y < e->y) {
1172 			shoot = true;
1173 			yv = -1; }
1174 		break;
1175 	case DIR_DOWN:
1176 		if (p->x == e->x && p->y > e->y) {
1177 			shoot = true;
1178 			yv = 1;
1179 		}
1180 		break;
1181 	case DIR_LEFT:
1182 		if (p->y == e->y && p->x < e->x) {
1183 			shoot = true;
1184 			xv = -1;
1185 		}
1186 		break;
1187 	case DIR_RIGHT:
1188 		if (p->y == e->y && p->x > e->x) {
1189 			shoot = true;
1190 			xv = 1;
1191 		}
1192 		break;
1193 	case DIR_NONE:
1194 	default:
1195 		break;
1196 	}
1197 
1198 	// Shoot if needed
1199 	// Make sure not shooting into solid tile
1200 	// Make sure if shooting at entity it is the player
1201 	int result;
1202 	AIEntity *hit = g_hdb->_ai->legalMoveOverWater(e->tileX + xv, e->tileY + yv, e->level, &result);
1203 	if (hit && hit->type == AI_GUY)
1204 		hit = nullptr;
1205 
1206 	if (shoot && !hit && result) {
1207 		AIEntity *fire = g_hdb->_ai->spawn(AI_OMNIBOT_MISSILE, e->dir, e->tileX + xv, e->tileY + yv, nullptr, nullptr, nullptr, DIR_NONE, e->level, 0, 0, 1);
1208 		if (g_hdb->_map->onScreen(e->tileX, e->tileY))
1209 			g_hdb->_sound->playSound(SND_FOUR_FIRE);
1210 		fire->xVel = xv * kPlayerMoveSpeed * 2;
1211 		fire->yVel = yv * kPlayerMoveSpeed * 2;
1212 		if (!g_hdb->getActionMode()) {
1213 			fire->xVel >>= 1;
1214 			fire->yVel >>= 1;
1215 		}
1216 		e->sequence = 16;
1217 		if (hitPlayer(fire->tileX*kTileWidth, fire->tileY*kTileHeight))
1218 			g_hdb->_ai->killPlayer(DEATH_FRIED);
1219 	}
1220 }
1221 
1222 //-------------------------------------------------------------------
1223 //
1224 //	DEADEYE : Crazy attack dog with Chompie(tm) sounds!  Will sit in one spot
1225 //		looking around, then run in a random direction and distance.  If, while
1226 //		scanning, Deadeye sees the player, he goes nuts and attacks!
1227 //
1228 //-------------------------------------------------------------------
1229 
aiDeadEyeInit(AIEntity * e)1230 void aiDeadEyeInit(AIEntity *e) {
1231 	e->sequence = 64;
1232 	e->blinkFrames = e->goalX = 0;
1233 	if (e->value1 == 1)
1234 		e->aiAction = aiDeadEyeWalkInPlace;
1235 	else
1236 		e->aiAction = aiDeadEyeAction;
1237 }
1238 
aiDeadEyeInit2(AIEntity * e)1239 void aiDeadEyeInit2(AIEntity *e) {
1240 	e->draw = g_hdb->_ai->getStandFrameDir(e);
1241 }
1242 
aiDeadEyeWalkInPlace(AIEntity * e)1243 void aiDeadEyeWalkInPlace(AIEntity *e) {
1244 	static const AIState state[5] = {STATE_NONE, STATE_MOVEUP, STATE_MOVEDOWN, STATE_MOVELEFT, STATE_MOVERIGHT};
1245 
1246 	e->sequence--;
1247 
1248 	switch (e->sequence) {
1249 	case 50:
1250 	case 40:
1251 	case 30:
1252 	case 20:
1253 	case 10:
1254 		{
1255 			int rnd = g_hdb->_rnd->getRandomNumber(3) + 1;
1256 			e->dir = (AIDir)rnd;
1257 			e->state = state[rnd];
1258 			if (e->onScreen) {
1259 				if (e->sequence == 50)
1260 					g_hdb->_sound->playSound(SND_DEADEYE_AMB01);
1261 				else if (e->sequence == 10)
1262 					g_hdb->_sound->playSound(SND_DEADEYE_AMB02);
1263 			}
1264 		}
1265 		break;
1266 	case 0:
1267 		e->sequence = 64;
1268 		break;
1269 	default:
1270 		break;
1271 	}
1272 	g_hdb->_ai->animEntFrames(e);
1273 }
1274 
aiDeadEyeAction(AIEntity * e)1275 void aiDeadEyeAction(AIEntity *e) {
1276 	static const AIState state[5] = {STATE_NONE, STATE_MOVEUP, STATE_MOVEDOWN, STATE_MOVELEFT, STATE_MOVERIGHT};
1277 	static const int xvAhead[5] = {9, 0, 0, -1, 1};
1278 	static const int yvAhead[5] = {9, -1, 1, 0, 0};
1279 
1280 	if (e->sequence) {
1281 		e->sequence--;
1282 
1283 		if (e->blinkFrames)	// Between attacks timer
1284 			e->blinkFrames--;
1285 
1286 		// Is player visible to us?
1287 		AIEntity *p = g_hdb->_ai->getPlayer();
1288 		if (e->onScreen && p->level == e->level && !e->blinkFrames) {
1289 			bool nuts = false;
1290 			switch (e->dir) {
1291 			case DIR_UP:
1292 				if (p->tileX == e->tileX && p->tileY < e->tileY)
1293 					nuts = true;
1294 				break;
1295 			case DIR_DOWN:
1296 				if (p->tileX == e->tileX && p->tileY > e->tileY)
1297 					nuts = true;
1298 				break;
1299 			case DIR_LEFT:
1300 				if (p->tileY == e->tileY && p->tileX < e->tileX)
1301 					nuts = true;
1302 				break;
1303 			case DIR_RIGHT:
1304 				if (p->tileY == e->tileY && p->tileX > e->tileX)
1305 					nuts = true;
1306 				break;
1307 			case DIR_NONE:
1308 			default:
1309 				break;
1310 			}
1311 
1312 			// Did we see the player (and we're done moving)?
1313 			if (nuts && e->aiAction != aiDeadEyeWalkInPlace) {
1314 				e->sequence = 0;
1315 				e->blinkFrames = 20;
1316 
1317 				int xv = xvAhead[e->dir];
1318 				int yv = yvAhead[e->dir];
1319 				int newX = e->tileX + xv;
1320 				int newY = e->tileY + yv;
1321 
1322 				bool okToMove = false;
1323 				bool done = false;
1324 				do {
1325 					int result;
1326 					AIEntity *hit = g_hdb->_ai->legalMove(newX, newY, e->level, &result);
1327 					if (hit && hit->type == AI_GUY)
1328 						hit = nullptr;
1329 					if (result && !hit) {
1330 						okToMove = true;
1331 						newX += xv;
1332 						newY += yv;
1333 						if (newX == p->tileX && newY == p->tileY)
1334 							done = true;
1335 					} else {
1336 						newX -= xv;
1337 						newY -= yv;
1338 						done = true;
1339 					}
1340 				} while (!done);
1341 
1342 				// If we can move in the direction of the player, set our goal at him
1343 				if (okToMove) {
1344 					e->moveSpeed = kPlayerMoveSpeed << 1;
1345 					g_hdb->_ai->setEntityGoal(e, newX, newY);
1346 					(p->tileX & 1) ? g_hdb->_sound->playSound(SND_DEADEYE_ATTACK01) : g_hdb->_sound->playSound(SND_DEADEYE_ATTACK02);
1347 				}
1348 				g_hdb->_ai->animateEntity(e);
1349 				return;
1350 			}
1351 		}
1352 
1353 		switch (e->sequence) {
1354 		// Look around
1355 		case 50:
1356 		case 40:
1357 		case 30:
1358 		case 20:
1359 		case 10:
1360 			{
1361 				int dir = g_hdb->_rnd->getRandomNumber(3) + 1;
1362 				e->dir = (AIDir)dir;
1363 				e->state = state[dir];
1364 
1365 				if (e->onScreen) {
1366 					if (e->sequence == 50)
1367 						g_hdb->_sound->playSound(SND_DEADEYE_AMB01);
1368 					else if (e->sequence == 10)
1369 						g_hdb->_sound->playSound(SND_DEADEYE_AMB02);
1370 				}
1371 			}
1372 			break;
1373 		case 0:
1374 			{
1375 				// Pick a random direction and random number of tiles in that direction
1376 				int dir = g_hdb->_rnd->getRandomNumber(3) + 1;
1377 				int walk = g_hdb->_rnd->getRandomNumber(4) + 1;
1378 
1379 				e->dir = (AIDir)dir;
1380 				e->state = state[dir];
1381 
1382 				int xv = xvAhead[dir] * walk;
1383 				if (e->tileX + xv < 1)
1384 					xv = 1 - e->tileX;
1385 				if (e->tileX + xv > g_hdb->_map->_width)
1386 					xv = g_hdb->_map->_width - e->tileX - 1;
1387 
1388 				int yv = yvAhead[dir] * walk;
1389 				if (e->tileY + yv < 1)
1390 					yv = 1 - e->tileY;
1391 				if (e->tileY + yv > g_hdb->_map->_height)
1392 					yv = g_hdb->_map->_height - e->tileY - 1;
1393 
1394 				e->value1 = xvAhead[dir];
1395 				e->value2 = yvAhead[dir];
1396 				e->moveSpeed = kPlayerMoveSpeed;
1397 				int result;
1398 				AIEntity *hit = g_hdb->_ai->legalMove(e->tileX + xvAhead[e->dir], e->tileY + yvAhead[e->dir], e->level, &result);
1399 				if (hit && hit->type == AI_GUY)
1400 					hit = nullptr;
1401 
1402 				if (!hit && result)
1403 					g_hdb->_ai->setEntityGoal(e, e->tileX + xv, e->tileY + yv);
1404 			}
1405 			break;
1406 		default:
1407 			break;
1408 		}
1409 		g_hdb->_ai->animEntFrames(e);
1410 		return;
1411 	}
1412 
1413 	// In the process of moving around
1414 	if (e->goalX) {
1415 		// Hit the player
1416 		if (hitPlayer(e->x, e->y)) {
1417 			g_hdb->_ai->killPlayer(DEATH_GRABBED);
1418 			return;
1419 		}
1420 		// Did we run into a wall, entity, water, slime etc?
1421 		// If so, Pick new direction
1422 		if (onEvenTile(e->x, e->y)) {
1423 			int result;
1424 			AIEntity *hit = g_hdb->_ai->legalMove(e->tileX + e->value1, e->tileY + e->value2, e->level, &result);
1425 			if (hit && hit->type == AI_GUY)
1426 				hit = nullptr;
1427 			if (!result || hit) {
1428 				g_hdb->_ai->stopEntity(e);
1429 				e->state = STATE_MOVEDOWN;
1430 				e->sequence = 64;
1431 				return;
1432 			}
1433 		}
1434 		g_hdb->_ai->animateEntity(e);
1435 	} else
1436 		// If not, start looking around
1437 		e->sequence = 64;
1438 }
1439 
1440 //-------------------------------------------------------------------
1441 //
1442 //	LASER
1443 //
1444 //-------------------------------------------------------------------
1445 
aiLaserInit(AIEntity * e)1446 void aiLaserInit(AIEntity *e) {
1447 	e->aiDraw = aiLaserDraw;
1448 	// start & end of laser beam
1449 	e->value1 = e->value2 = 0;
1450 }
1451 
aiLaserInit2(AIEntity * e)1452 void aiLaserInit2(AIEntity *e) {
1453 	e->draw = g_hdb->_ai->getStandFrameDir(e);
1454 	if (!g_hdb->_ai->_gfxLaserbeamUD[0]) {
1455 		char name[64];
1456 		for (int i = 0; i < 4; i++) {
1457 			sprintf(name, FORCEFIELD_UD"0%d", i + 1);
1458 			g_hdb->_ai->_gfxLaserbeamUD[i] = g_hdb->_gfx->loadTile(name);
1459 			sprintf(name, FORCESPLASH_TOP"0%d", i + 1);
1460 			g_hdb->_ai->_gfxLaserbeamUDTop[i] = g_hdb->_gfx->loadTile(name);
1461 			sprintf(name, FORCESPLASH_BTM"0%d", i + 1);
1462 			g_hdb->_ai->_gfxLaserbeamUDBottom[i] = g_hdb->_gfx->loadTile(name);
1463 			sprintf(name, FORCEFIELD_LR"0%d", i + 1);
1464 			g_hdb->_ai->_gfxLaserbeamLR[i] = g_hdb->_gfx->loadTile(name);
1465 			sprintf(name, FORCESPLASH_LEFT"0%d", i + 1);
1466 			g_hdb->_ai->_gfxLaserbeamLRLeft[i] = g_hdb->_gfx->loadTile(name);
1467 			sprintf(name, FORCESPLASH_RIGHT"0%d", i + 1);
1468 			g_hdb->_ai->_gfxLaserbeamLRRight[i] = g_hdb->_gfx->loadTile(name);
1469 		}
1470 	}
1471 }
1472 
aiLaserAction(AIEntity * e)1473 void aiLaserAction(AIEntity *e) {
1474 	static const int xva[] = {9, 0, 0,-1, 1};
1475 	static const int yva[] = {9,-1, 1, 0, 0};
1476 
1477 	AIEntity *hit = e;
1478 	int moveOK = 0;
1479 	int moveCount = 0;
1480 
1481 	do {
1482 		int nx = hit->tileX;
1483 		int ny = hit->tileY;
1484 
1485 		if (hit->type != AI_DIVERTER) {
1486 			hit->int1 = xva[hit->dir];
1487 			hit->int2 = yva[hit->dir];
1488 
1489 			if (hit->dir == DIR_UP || hit->dir == DIR_DOWN)
1490 				hit->value1 = ny;
1491 			else
1492 				hit->value1 = nx;
1493 		} else {
1494 			// diverter is on y-plane?
1495 			if (hit->tileX == e->tileX) {
1496 				hit->value1 = nx;
1497 				hit->int2 = 0;
1498 				switch (hit->dir2) {
1499 				case DIR_UP:
1500 					hit->int1 = 1;
1501 					break;
1502 				case DIR_DOWN:
1503 					hit->int1 = -1;
1504 					break;
1505 				case DIR_LEFT:
1506 					hit->int1 = -1;
1507 					break;
1508 				case DIR_RIGHT:
1509 					hit->int1 = 1;
1510 					break;
1511 				case DIR_NONE:
1512 				default:
1513 					break;
1514 				}
1515 			} else {
1516 				// diverter is on x-plane
1517 				hit->value1 = ny;
1518 				hit->int1 = 0;
1519 				switch (hit->dir2) {
1520 				case DIR_UP:
1521 					hit->int2 = 1;
1522 					break;
1523 				case DIR_DOWN:
1524 					hit->int2 = 1;
1525 					break;
1526 				case DIR_LEFT:
1527 					hit->int2 = -1;
1528 					break;
1529 				case DIR_RIGHT:
1530 					hit->int2 = -1;
1531 					break;
1532 				case DIR_NONE:
1533 				default:
1534 					break;
1535 				}
1536 			}
1537 		}
1538 		e = hit;
1539 
1540 		//
1541 		// scan for all legal moves
1542 		//
1543 		do {
1544 			nx += e->int1;
1545 			ny += e->int2;
1546 			hit = g_hdb->_ai->legalMoveOverWater(nx, ny, e->level, &moveOK);
1547 			g_hdb->_map->setLaserBeam(nx, ny, 1);
1548 			if (hit && hit->type != AI_LASERBEAM) {
1549 				//
1550 				// hit player = death
1551 				//
1552 				if (hit == g_hdb->_ai->getPlayer() && onEvenTile(hit->x, hit->y) && !g_hdb->_ai->playerDead())
1553 					g_hdb->_ai->killPlayer(DEATH_FRIED);
1554 				else if (hit->type == AI_BOOMBARREL && hit->state != STATE_EXPLODING && onEvenTile(hit->x, hit->y)) {
1555 					// hit BOOM BARREL = explodes
1556 					aiBarrelExplode(hit);
1557 					aiBarrelBlowup(hit, nx, ny);
1558 				} else if (hit->type == AI_LIGHTBARREL || hit->type == AI_HEAVYBARREL || hit->type == AI_CRATE) {
1559 					// hit LIGHT/HEAVY BARREL = blocking
1560 					moveOK = 0;
1561 				} else if (hit->type == AI_DIVERTER) {
1562 					// hit a diverter?
1563 					moveOK = 0;
1564 				} else if (onEvenTile(hit->x, hit->y) && hit != g_hdb->_ai->getPlayer()) {
1565 					switch (hit->type) {
1566 					// cannot kill Vortexians!
1567 					case AI_VORTEXIAN:
1568 						continue;
1569 
1570 					case AI_BOOMBARREL:
1571 						if (hit->state == STATE_EXPLODING)
1572 							continue;
1573 						break;
1574 					case AI_LASER:
1575 						g_hdb->_ai->_laserRescan = true;
1576 						break;
1577 					case ITEM_KEYCARD_WHITE:
1578 					case ITEM_KEYCARD_BLUE:
1579 					case ITEM_KEYCARD_RED:
1580 					case ITEM_KEYCARD_GREEN:
1581 					case ITEM_KEYCARD_PURPLE:
1582 					case ITEM_KEYCARD_BLACK:
1583 					case ITEM_CABKEY:
1584 						g_hdb->_window->centerTextOut("CARD DESTROYED!", 306, 5 * 60);
1585 						g_hdb->_sound->playSound(SND_QUEST_FAILED);
1586 						break;
1587 					default:
1588 						break;
1589 					}
1590 					g_hdb->_ai->removeEntity(hit);
1591 					g_hdb->_ai->addAnimateTarget(nx * kTileWidth,
1592 						ny * kTileHeight, 0, 3, ANIM_NORMAL, false, false, GROUP_EXPLOSION_BOOM_SIT);
1593 					g_hdb->_sound->playSound(SND_BARREL_EXPLODE);
1594 				}
1595 			}
1596 		} while (moveOK);
1597 
1598 		if (e->int2) {
1599 			e->value2 = ny;
1600 			// check for hitting the BACK of a Diverter.  It stops the laser.
1601 			if (hit && hit->type == AI_DIVERTER) {
1602 				if (e->int2 < 0 && hit->state != STATE_DIVERTER_BL && hit->state != STATE_DIVERTER_BR)
1603 					hit = nullptr;
1604 				else if (e->int2 > 0 && hit->state != STATE_DIVERTER_TL && hit->state != STATE_DIVERTER_TR)
1605 					hit = nullptr;
1606 			}
1607 		} else {
1608 			e->value2 = nx;
1609 			// check for hitting the BACK of a Diverter.  It stops the laser.
1610 			if (hit && hit->type == AI_DIVERTER) {
1611 				if (e->int1 < 0 && hit->state != STATE_DIVERTER_BR && hit->state != STATE_DIVERTER_TR)
1612 					hit = nullptr;
1613 				else if (e->int1 > 0 && hit->state != STATE_DIVERTER_TL && hit->state != STATE_DIVERTER_BL)
1614 					hit = nullptr;
1615 			}
1616 		}
1617 
1618 		moveCount++;
1619 
1620 		// It is possible to set a configuration which leads to a closed loop.
1621 		// Thus, we're breaking it here
1622 		if (moveCount > 1000)
1623 			hit = nullptr;
1624 	} while (hit && hit->type == AI_DIVERTER);
1625 }
1626 
aiLaserDraw(AIEntity * e,int mx,int my)1627 void aiLaserDraw(AIEntity *e, int mx, int my) {
1628 	int	i;
1629 	int	frame = e->movedownFrames & 3;
1630 	int onScreen = 0;
1631 
1632 	switch (e->dir) {
1633 	case DIR_UP:
1634 	{
1635 		for (i = e->value1 - 1; i > e->value2; i--)
1636 			onScreen += g_hdb->_ai->_gfxLaserbeamUD[frame]->drawMasked(e->x - mx, i * kTileWidth - my);
1637 		onScreen += g_hdb->_ai->_gfxLaserbeamUDBottom[frame & 3]->drawMasked(e->x - mx, i * kTileWidth - my);
1638 		if (onScreen) {
1639 			g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
1640 			g_hdb->_ai->_laserOnScreen = true;
1641 		}
1642 	}
1643 		break;
1644 	case DIR_DOWN:
1645 	{
1646 		for (i = e->value1 + 1; i < e->value2; i++)
1647 			onScreen += g_hdb->_ai->_gfxLaserbeamUD[frame]->drawMasked(e->x - mx, i * kTileWidth - my);
1648 		onScreen += g_hdb->_ai->_gfxLaserbeamUDBottom[frame]->drawMasked(e->x - mx, i * kTileWidth - my);
1649 		if (onScreen) {
1650 			g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
1651 			g_hdb->_ai->_laserOnScreen = true;
1652 		}
1653 	}
1654 		break;
1655 	case DIR_LEFT:
1656 	{
1657 		for (i = e->value1 - 1; i > e->value2; i--)
1658 			onScreen += g_hdb->_ai->_gfxLaserbeamLR[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
1659 		onScreen += g_hdb->_ai->_gfxLaserbeamLRRight[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
1660 		if (onScreen) {
1661 			g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
1662 			g_hdb->_ai->_laserOnScreen = true;
1663 		}
1664 	}
1665 		break;
1666 	case DIR_RIGHT:
1667 	{
1668 		for (i = e->value1 + 1; i < e->value2; i++)
1669 			onScreen += g_hdb->_ai->_gfxLaserbeamLR[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
1670 		onScreen += g_hdb->_ai->_gfxLaserbeamLRLeft[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
1671 		if (onScreen) {
1672 			g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
1673 			g_hdb->_ai->_laserOnScreen = true;
1674 		}
1675 	}
1676 		break;
1677 	case DIR_NONE:
1678 	default:
1679 		break;
1680 	}
1681 	e->movedownFrames++;
1682 }
1683 
1684 //-------------------------------------------------------------------
1685 //
1686 //	DIVERTER
1687 //
1688 //-------------------------------------------------------------------
1689 
aiDiverterInit(AIEntity * e)1690 void aiDiverterInit(AIEntity *e) {
1691 	e->aiDraw = aiDiverterDraw;
1692 	e->aiAction = aiDiverterAction;
1693 	e->moveSpeed = kPlayerMoveSpeed << 1;
1694 	e->dir2 = e->dir;
1695 }
1696 
aiDiverterInit2(AIEntity * e)1697 void aiDiverterInit2(AIEntity *e) {
1698 	e->movedownGfx[0] = e->standdownGfx[0];
1699 	e->moveupGfx[0] = e->standupGfx[0];
1700 	e->moveleftGfx[0] = e->standleftGfx[0];
1701 	e->moverightGfx[0] = e->standrightGfx[0];
1702 	e->movedownFrames =
1703 		e->moveupFrames =
1704 		e->moveleftFrames =
1705 		e->moverightFrames = 1;
1706 
1707 	// this is to handle loadgames...
1708 	AIDir d = e->dir2;
1709 	if (e->dir2 == DIR_NONE)
1710 		d = e->dir;
1711 	switch (d) {
1712 	case DIR_DOWN:
1713 		e->state = STATE_DIVERTER_BL;
1714 		e->draw = e->standdownGfx[0];
1715 		break;
1716 	case DIR_UP:
1717 		e->state = STATE_DIVERTER_BR;
1718 		e->draw = e->standupGfx[0];
1719 		break;
1720 	case DIR_LEFT:
1721 		e->state = STATE_DIVERTER_TL;
1722 		e->draw = e->standleftGfx[0];
1723 		break;
1724 	case DIR_RIGHT:
1725 		e->state = STATE_DIVERTER_TR;
1726 		e->draw = e->standrightGfx[0];
1727 		break;
1728 	case DIR_NONE:
1729 	default:
1730 		break;
1731 	}
1732 
1733 	g_hdb->_ai->_laserRescan = true;
1734 }
1735 
aiDiverterAction(AIEntity * e)1736 void aiDiverterAction(AIEntity *e) {
1737 	if (e->goalX) {
1738 		g_hdb->_ai->animateEntity(e);
1739 		g_hdb->_ai->_laserRescan = true;
1740 
1741 		// have to reset the state because we might have been moved...
1742 		switch (e->dir2) {
1743 		case DIR_DOWN:
1744 			e->state = STATE_DIVERTER_BL;
1745 			e->draw = e->standdownGfx[0];
1746 			break;
1747 		case DIR_UP:
1748 			e->state = STATE_DIVERTER_BR;
1749 			e->draw = e->standupGfx[0];
1750 			break;
1751 		case DIR_LEFT:
1752 			e->state = STATE_DIVERTER_TL;
1753 			e->draw = e->standleftGfx[0];
1754 			break;
1755 		case DIR_RIGHT:
1756 			e->state = STATE_DIVERTER_TR;
1757 			e->draw = e->standrightGfx[0];
1758 			break;
1759 		case DIR_NONE:
1760 		default:
1761 			break;
1762 		}
1763 	}
1764 }
1765 
aiDiverterDraw(AIEntity * e,int mx,int my)1766 void aiDiverterDraw(AIEntity *e, int mx, int my) {
1767 	if (!e->value1 && !e->value2)
1768 		return;
1769 
1770 	int	frame = e->movedownFrames & 3;
1771 	int onScreen = 0;
1772 	int i;
1773 	switch (e->dir2) {
1774 	case DIR_UP:
1775 		if (e->tileY == e->value1 && e->int2) {	// going down or right?
1776 			for (i = e->value1 + 1; i < e->value2; i++)
1777 				onScreen += g_hdb->_ai->_gfxLaserbeamUD[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
1778 			onScreen += g_hdb->_ai->_gfxLaserbeamUDTop[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
1779 			if (onScreen) {
1780 				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
1781 				g_hdb->_ai->_laserOnScreen = true;
1782 			}
1783 		} else {
1784 			for (i = e->value1 + 1; i < e->value2; i++)
1785 				onScreen += g_hdb->_ai->_gfxLaserbeamLR[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
1786 			onScreen += g_hdb->_ai->_gfxLaserbeamLRLeft[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
1787 			if (onScreen) {
1788 				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
1789 				g_hdb->_ai->_laserOnScreen = true;
1790 			}
1791 		}
1792 		break;
1793 	case DIR_DOWN:
1794 		if (e->tileY == e->value1 && e->int2) {	// going down or left?
1795 			for (i = e->value1 + 1; i < e->value2; i++)
1796 				onScreen += g_hdb->_ai->_gfxLaserbeamUD[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
1797 			onScreen += g_hdb->_ai->_gfxLaserbeamUDTop[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
1798 			if (onScreen) {
1799 				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
1800 				g_hdb->_ai->_laserOnScreen = true;
1801 			}
1802 		} else {
1803 			for (i = e->value1 - 1; i > e->value2; i--)
1804 				onScreen += g_hdb->_ai->_gfxLaserbeamLR[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
1805 			onScreen += g_hdb->_ai->_gfxLaserbeamLRRight[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
1806 			if (onScreen) {
1807 				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
1808 				g_hdb->_ai->_laserOnScreen = true;
1809 			}
1810 		}
1811 		break;
1812 	case DIR_LEFT:
1813 		if (e->tileY == e->value1 && e->int2) {	// going up or left?
1814 			for (i = e->value1 - 1; i > e->value2; i--)
1815 				onScreen += g_hdb->_ai->_gfxLaserbeamUD[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
1816 			onScreen += g_hdb->_ai->_gfxLaserbeamUDBottom[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
1817 			if (onScreen) {
1818 				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
1819 				g_hdb->_ai->_laserOnScreen = true;
1820 			}
1821 		} else {
1822 			for (i = e->value1 - 1; i > e->value2; i--)
1823 				onScreen += g_hdb->_ai->_gfxLaserbeamLR[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
1824 			onScreen += g_hdb->_ai->_gfxLaserbeamLRRight[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
1825 			if (onScreen) {
1826 				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
1827 				g_hdb->_ai->_laserOnScreen = true;
1828 			}
1829 		}
1830 		break;
1831 	case DIR_RIGHT:
1832 		if (e->tileY == e->value1 && e->int2) {	// going up or right?
1833 			for (i = e->value1 - 1; i > e->value2; i--)
1834 				onScreen += g_hdb->_ai->_gfxLaserbeamUD[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
1835 			onScreen += g_hdb->_ai->_gfxLaserbeamUDBottom[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
1836 			if (onScreen) {
1837 				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
1838 				g_hdb->_ai->_laserOnScreen = true;
1839 			}
1840 		} else {
1841 			for (i = e->value1 + 1; i < e->value2; i++)
1842 				onScreen += g_hdb->_ai->_gfxLaserbeamLR[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
1843 			onScreen += g_hdb->_ai->_gfxLaserbeamLRLeft[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
1844 			if (onScreen) {
1845 				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
1846 				g_hdb->_ai->_laserOnScreen = true;
1847 			}
1848 		}
1849 		break;
1850 	case DIR_NONE:
1851 	default:
1852 		break;
1853 	}
1854 	e->movedownFrames++;
1855 }
1856 
1857 //-------------------------------------------------------------------
1858 //
1859 //	MEERKAT : nutty little groundhog dude that will bite Guy if he's on
1860 //		his mound.  That blows 1-5 gems outta Guy!
1861 //
1862 //-------------------------------------------------------------------
1863 
aiMeerkatInit(AIEntity * e)1864 void aiMeerkatInit(AIEntity *e) {
1865 	e->state = STATE_NONE;
1866 	e->sequence = 0;
1867 	if (e->value1 == 1) {
1868 		e->aiAction = aiMeerkatLookAround;
1869 		e->state = STATE_MEER_LOOK;
1870 	} else
1871 		e->aiAction = aiMeerkatAction;
1872 }
1873 
aiMeerkatInit2(AIEntity * e)1874 void aiMeerkatInit2(AIEntity *e) {
1875 	//  hidden at the start!
1876 	e->draw = nullptr;
1877 
1878 	// make the looking around cycle better...
1879 	e->movedownGfx[3] = e->movedownGfx[1];
1880 	e->movedownFrames++;
1881 }
1882 
aiMeerkatDraw(AIEntity * e,int mx,int my)1883 void aiMeerkatDraw(AIEntity *e, int mx, int my) {
1884 	char word[3];
1885 	g_hdb->_window->getGemGfx()->drawMasked(e->value1 - mx, e->value2 - my, 255 - e->blinkFrames * 16);
1886 	g_hdb->_gfx->setCursor(e->value1 + 12 - mx, e->value2 - 8 - my);
1887 	word[2] = 0;
1888 	if (!e->special1Frames) {
1889 		word[0] = '0';
1890 		word[1] = 0;
1891 	} else {
1892 		word[0] = '-';
1893 		word[1] = '0' + e->special1Frames;
1894 	}
1895 	g_hdb->_gfx->drawText(word);
1896 }
1897 
aiMeerkatAction(AIEntity * e)1898 void aiMeerkatAction(AIEntity *e) {
1899 	static const int gem_xv[] = { 0, 0,-2,-3,-4,-4,-3,-2,-2,-2,-2,-1,-1, 100};
1900 	static const int gem_yv[] = {-6,-5,-4,-3,-2,-1, 0, 0, 1, 2, 3, 4, 5, 100};
1901 
1902 	AIEntity *p = g_hdb->_ai->getPlayer();
1903 
1904 	switch (e->sequence) {
1905 		// waiting to see the player
1906 	case 0:
1907 		if ((abs(p->tileX - e->tileX) <= 1 && p->tileY == e->tileY) ||
1908 			(abs(p->tileY - e->tileY) <= 1 && p->tileX == e->tileX)) {
1909 			e->sequence = 1;
1910 			e->state = STATE_MEER_MOVE;
1911 			e->animFrame = 0;
1912 			e->animCycle = 1;
1913 			e->animDelay = e->animCycle;
1914 			if (e->onScreen)
1915 				g_hdb->_sound->playSound(SND_MEERKAT_WARNING);
1916 		}
1917 		break;
1918 		// time to show the mound for a sec...
1919 	case 1:
1920 		g_hdb->_ai->animateEntity(e);
1921 		if (!e->animFrame && e->animDelay == e->animCycle)
1922 			e->sequence++;
1923 		if (e->sequence == 2) {
1924 			e->state = STATE_MEER_APPEAR;
1925 			e->animFrame = 0;
1926 			e->animDelay = e->animCycle;
1927 			if (e->onScreen)
1928 				g_hdb->_sound->playSound(SND_MEERKAT_APPEAR);
1929 		}
1930 		break;
1931 
1932 		// pop outta the dirt!
1933 	case 2:
1934 		g_hdb->_ai->animateEntity(e);
1935 		// done w/sequence?
1936 		if (!e->animFrame && e->animDelay == e->animCycle) {
1937 			e->sequence++;
1938 			e->state = STATE_MEER_LOOK;
1939 			e->animFrame = 0;
1940 			e->animCycle = 2;
1941 			e->animDelay = e->animCycle;
1942 		}
1943 		break;
1944 
1945 		// looking around...... time to bite the player!?
1946 	case 3:
1947 	case 4:
1948 		g_hdb->_ai->animateEntity(e);
1949 		if (!e->animFrame && e->animDelay == e->animCycle) {
1950 			e->sequence++;
1951 			if (e->sequence == 5)
1952 				e->state = STATE_MEER_DISAPPEAR;
1953 		}
1954 		if (g_hdb->_ai->checkPlayerTileCollision(e->tileX, e->tileY)) {
1955 			e->state = STATE_MEER_BITE;
1956 			e->sequence = 6;
1957 			e->animFrame = 0;
1958 			e->animDelay = e->animCycle;
1959 			if (e->onScreen)
1960 				g_hdb->_sound->playSound(SND_MEERKAT_BITE);
1961 		}
1962 		break;
1963 
1964 		// going back underground!
1965 	case 5:
1966 		g_hdb->_ai->animateEntity(e);
1967 		if (!e->animFrame && e->animDelay == e->animCycle) {
1968 			e->sequence = 0;
1969 			e->state = STATE_NONE;
1970 			e->draw = nullptr;
1971 		}
1972 		break;
1973 
1974 		// biting the player right now!
1975 	case 6:
1976 		g_hdb->_ai->animateEntity(e);
1977 		// hit the player?
1978 		if (g_hdb->_ai->checkPlayerTileCollision(e->tileX, e->tileY)) {
1979 			g_hdb->_ai->stopEntity(p);
1980 			g_hdb->_ai->setPlayerLock(true);
1981 			e->sequence = 7;
1982 			p->moveSpeed <<= 1;
1983 			if (g_hdb->_ai->findEntity(p->tileX, p->tileY + 1))
1984 				g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY - 1);
1985 			else
1986 				g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY + 1);
1987 			e->aiDraw = aiMeerkatDraw;
1988 			e->value1 = e->x;
1989 			e->value2 = e->y;
1990 			e->blinkFrames = 0;		// index into movement table...
1991 
1992 			// figure # of gems to take
1993 			e->special1Frames = g_hdb->_rnd->getRandomNumber(4) + 1;
1994 			int	amt = g_hdb->_ai->getGemAmount();
1995 			if (amt - e->special1Frames < 0)
1996 				e->special1Frames = amt;
1997 
1998 			// if we're in Puzzle Mode and there's no gems left, give one back
1999 			if (!g_hdb->getActionMode() && !(e->special1Frames - amt) && e->special1Frames)
2000 				e->special1Frames--;
2001 
2002 			amt -= e->special1Frames;
2003 			g_hdb->_ai->setGemAmount(amt);
2004 		}
2005 		// go back to looking?
2006 		if (!e->animFrame && e->animDelay == e->animCycle) {
2007 			e->sequence = 3;
2008 			e->state = STATE_MEER_LOOK;
2009 			e->animFrame = 0;
2010 			e->animDelay = e->animCycle;
2011 		}
2012 		break;
2013 
2014 		// waiting for player to blast backward
2015 	case 7:
2016 		g_hdb->_ai->animateEntity(e);
2017 		if (!p->goalX) {
2018 			p->moveSpeed = kPlayerMoveSpeed;
2019 			g_hdb->_ai->setPlayerLock(false);
2020 			e->sequence = 5;
2021 			e->state = STATE_MEER_DISAPPEAR;
2022 			e->animFrame = 0;
2023 			e->animDelay = e->animCycle;
2024 		}
2025 		break;
2026 
2027 	default:
2028 		break;
2029 	}
2030 
2031 	// blasting a gem outta Guy?
2032 	if (e->value1) {
2033 		if (gem_xv[e->blinkFrames] == 100) {
2034 			e->value1 = 0;
2035 			e->aiDraw = nullptr;
2036 			return;
2037 		}
2038 		e->value1 += gem_xv[e->blinkFrames];
2039 		e->value2 += gem_yv[e->blinkFrames];
2040 		e->blinkFrames++;
2041 	}
2042 }
2043 
aiMeerkatLookAround(AIEntity * e)2044 void aiMeerkatLookAround(AIEntity *e) {
2045 	g_hdb->_ai->animEntFrames(e);
2046 }
2047 
2048 //-------------------------------------------------------------------
2049 //
2050 //	FATFROG : Just sits in place and blasts out his tongue if you're
2051 //		within range.
2052 //
2053 //-------------------------------------------------------------------
2054 
aiFatFrogInit(AIEntity * e)2055 void aiFatFrogInit(AIEntity *e) {
2056 	e->aiAction = aiFatFrogAction;
2057 }
2058 
aiFatFrogInit2(AIEntity * e)2059 void aiFatFrogInit2(AIEntity *e) {
2060 	e->draw = g_hdb->_ai->getStandFrameDir(e);
2061 	// load tongue tiles
2062 	switch (e->dir) {
2063 	case DIR_DOWN:
2064 		if (!g_hdb->_ai->_tileFroglickMiddleUD) {
2065 			g_hdb->_ai->_tileFroglickMiddleUD = g_hdb->_gfx->loadTile(TILE_FFTONGUE_UD_MIDDLE);
2066 			g_hdb->_ai->_tileFroglickWiggleUD[0] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_UD_WIGGLE_L);
2067 			g_hdb->_ai->_tileFroglickWiggleUD[1] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_UD_WIGGLE_M);
2068 			g_hdb->_ai->_tileFroglickWiggleUD[2] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_UD_WIGGLE_R);
2069 		}
2070 		e->state = STATE_STANDDOWN;
2071 		break;
2072 	case DIR_LEFT:
2073 		if (!g_hdb->_ai->_tileFroglickMiddleLR)
2074 			g_hdb->_ai->_tileFroglickMiddleLR = g_hdb->_gfx->loadTile(TILE_FFTONGUE_LR_MIDDLE);
2075 
2076 		if (!g_hdb->_ai->_tileFroglickWiggleLeft[0]) {
2077 			g_hdb->_ai->_tileFroglickWiggleLeft[0] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_L_WIGGLE_U);
2078 			g_hdb->_ai->_tileFroglickWiggleLeft[1] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_L_WIGGLE_M);
2079 			g_hdb->_ai->_tileFroglickWiggleLeft[2] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_L_WIGGLE_D);
2080 		}
2081 		e->state = STATE_STANDLEFT;
2082 		break;
2083 	case DIR_RIGHT:
2084 		if (!g_hdb->_ai->_tileFroglickMiddleLR)
2085 			g_hdb->_ai->_tileFroglickMiddleLR = g_hdb->_gfx->loadTile(TILE_FFTONGUE_LR_MIDDLE);
2086 
2087 		if (!g_hdb->_ai->_tileFroglickWiggleRight[0]) {
2088 			g_hdb->_ai->_tileFroglickWiggleRight[0] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_R_WIGGLE_U);
2089 			g_hdb->_ai->_tileFroglickWiggleRight[1] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_R_WIGGLE_M);
2090 			g_hdb->_ai->_tileFroglickWiggleRight[2] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_R_WIGGLE_D);
2091 		}
2092 		e->state = STATE_STANDRIGHT;
2093 		break;
2094 	default:
2095 		break;
2096 	}
2097 }
2098 
aiFatFrogAction(AIEntity * e)2099 void aiFatFrogAction(AIEntity *e) {
2100 	AIEntity *p = g_hdb->_ai->getPlayer();
2101 
2102 	switch (e->state) {
2103 		//-------------------------------------------------------------------
2104 		// WAITING TO ATTACK
2105 		//-------------------------------------------------------------------
2106 	case STATE_STANDDOWN:
2107 		e->draw = e->standdownGfx[e->animFrame];
2108 		// is player within 2 tiles below fatfrog?
2109 		if (p->tileX == e->tileX && p->tileY - e->tileY < 3 && p->tileY > e->tileY) {
2110 			e->state = STATE_LICKDOWN;
2111 			e->animDelay = e->animCycle << 2;
2112 			e->animFrame = 0;
2113 		}
2114 		// cycle animation frames
2115 		if (e->animDelay-- > 0)
2116 			return;
2117 		e->animDelay = e->animCycle << 2;
2118 		e->animFrame++;
2119 		if (e->animFrame == e->standdownFrames)
2120 			e->animFrame = 0;
2121 		if (!g_hdb->_rnd->getRandomNumber(29) && e->onScreen)
2122 			g_hdb->_sound->playSound(SND_FROG_RIBBIT1);
2123 		break;
2124 
2125 	case STATE_STANDLEFT:
2126 		e->draw = e->standleftGfx[e->animFrame];
2127 		// is player within 2 tiles below fatfrog?
2128 		if (p->tileY == e->tileY && e->tileX - p->tileX < 3 && p->tileX < e->tileX) {
2129 			e->state = STATE_LICKLEFT;
2130 			e->animDelay = e->animCycle << 2;
2131 			e->animFrame = 0;
2132 		}
2133 		// cycle animation frames
2134 		if (e->animDelay-- > 0)
2135 			return;
2136 		e->animDelay = e->animCycle << 2;
2137 		e->animFrame++;
2138 		if (e->animFrame == e->standleftFrames)
2139 			e->animFrame = 0;
2140 		if (!g_hdb->_rnd->getRandomNumber(29) && e->onScreen)
2141 			g_hdb->_sound->playSound(SND_FROG_RIBBIT2);
2142 		break;
2143 
2144 	case STATE_STANDRIGHT:
2145 		e->draw = e->standrightGfx[e->animFrame];
2146 		// is player within 2 tiles below fatfrog?
2147 		if (p->tileY == e->tileY && p->tileX - e->tileX < 3 && p->tileX > e->tileX) {
2148 			e->state = STATE_LICKRIGHT;
2149 			e->animDelay = e->animCycle << 2;
2150 			e->animFrame = 0;
2151 		}
2152 		// cycle animation frames
2153 		if (e->animDelay-- > 0)
2154 			return;
2155 		e->animDelay = e->animCycle << 2;
2156 		e->animFrame++;
2157 		if (e->animFrame == e->standrightFrames)
2158 			e->animFrame = 0;
2159 		if (!g_hdb->_rnd->getRandomNumber(29) && e->onScreen)
2160 			g_hdb->_sound->playSound(SND_FROG_RIBBIT2);
2161 		break;
2162 
2163 		//-------------------------------------------------------------------
2164 		// LICK ATTACK
2165 		//-------------------------------------------------------------------
2166 	case STATE_LICKDOWN:
2167 		e->draw = e->movedownGfx[e->animFrame];
2168 		// ready to start licking?
2169 		if (e->animFrame == e->movedownFrames - 1 && !e->value1) {
2170 			e->value1 = 1;
2171 			e->aiDraw = aiFatFrogTongueDraw;
2172 			g_hdb->_sound->playSound(SND_FROG_LICK);
2173 		} else if (e->animFrame == e->movedownFrames - 1 && e->value1) {
2174 			// animate licking
2175 
2176 			// check player death
2177 			if (((p->tileX == e->tileX && p->tileY == e->tileY + 1) ||					// in front of frog + 1 tile!?
2178 				(e->value1 > 3 && p->tileX == e->tileX && p->tileY < e->tileY + 3)) &&	// in front of frog + 2 tiles!?
2179 				g_hdb->_ai->playerDead() == false)
2180 				g_hdb->_ai->killPlayer(DEATH_NORMAL);
2181 
2182 			e->value1++;
2183 			if (e->value1 == 14) {
2184 				e->animFrame = e->value1 = 0;
2185 				e->aiDraw = nullptr;
2186 				e->state = STATE_STANDDOWN;
2187 			}
2188 		} else {
2189 			// animate pre-licking
2190 			// cycle animation frames
2191 			if (e->animDelay-- > 0)
2192 				return;
2193 			e->animDelay = e->animCycle;
2194 			e->animFrame++;
2195 		}
2196 		break;
2197 
2198 	case STATE_LICKLEFT:
2199 		e->draw = e->moveleftGfx[e->animFrame];
2200 		// ready to start licking?
2201 		if (e->animFrame == e->moveleftFrames - 1 && !e->value1) {
2202 			e->value1 = 1;
2203 			e->aiDraw = aiFatFrogTongueDraw;
2204 			g_hdb->_sound->playSound(SND_FROG_LICK);
2205 		} else if (e->animFrame == e->moveleftFrames - 1 && e->value1) {
2206 			// animate licking
2207 
2208 			// check player death
2209 			if (((p->tileY == e->tileY && p->tileX == e->tileX - 1) ||					// in front of frog + 1 tile!?
2210 				(e->value1 > 3 && p->tileY == e->tileY && p->tileX > e->tileX - 3)) &&	// in front of frog + 2 tiles!?
2211 				g_hdb->_ai->playerDead() == false)
2212 				g_hdb->_ai->killPlayer(DEATH_NORMAL);
2213 
2214 			e->value1++;
2215 			if (e->value1 == 14) {
2216 				e->animFrame = e->value1 = 0;
2217 				e->aiDraw = nullptr;
2218 				e->state = STATE_STANDLEFT;
2219 			}
2220 		} else {
2221 			// animate pre-licking
2222 			// cycle animation frames
2223 			if (e->animDelay-- > 0)
2224 				return;
2225 			e->animDelay = e->animCycle;
2226 			e->animFrame++;
2227 		}
2228 		break;
2229 
2230 	case STATE_LICKRIGHT:
2231 		e->draw = e->moverightGfx[e->animFrame];
2232 		// ready to start licking?
2233 		if (e->animFrame == e->moverightFrames - 1 && !e->value1) {
2234 			e->value1 = 1;
2235 			e->aiDraw = aiFatFrogTongueDraw;
2236 			g_hdb->_sound->playSound(SND_FROG_LICK);
2237 		} else if (e->animFrame == e->moverightFrames - 1 && e->value1) {
2238 			// animate licking
2239 			// check player death
2240 			//
2241 			if (((p->tileY == e->tileY && p->tileX == e->tileX + 1) ||					// in front of frog + 1 tile!?
2242 				(e->value1 > 3 && p->tileY == e->tileY && p->tileX < e->tileX + 3)) &&	// in front of frog + 2 tiles!?
2243 				g_hdb->_ai->playerDead() == false)
2244 				g_hdb->_ai->killPlayer(DEATH_NORMAL);
2245 
2246 			e->value1++;
2247 			if (e->value1 == 14) {
2248 				e->animFrame = e->value1 = 0;
2249 				e->aiDraw = nullptr;
2250 				e->state = STATE_STANDRIGHT;
2251 			}
2252 		} else {
2253 			// animate pre-licking
2254 			// cycle animation frames
2255 			if (e->animDelay-- > 0)
2256 				return;
2257 			e->animDelay = e->animCycle;
2258 			e->animFrame++;
2259 		}
2260 		break;
2261 	default:
2262 		// no op
2263 		break;
2264 	}
2265 }
2266 
aiFatFrogTongueDraw(AIEntity * e,int mx,int my)2267 void aiFatFrogTongueDraw(AIEntity *e, int mx, int my) {
2268 	int	nx, ny;
2269 
2270 	switch (e->state) {
2271 	case STATE_LICKDOWN:
2272 		switch (e->value1) {
2273 		case 1:
2274 		case 2:
2275 		case 3:
2276 		case 13:
2277 		case 14:
2278 			nx = e->x;
2279 			ny = e->y + 32;
2280 			g_hdb->_ai->_tileFroglickWiggleUD[1]->drawMasked(nx - mx, ny - my);
2281 			break;
2282 		case 4:
2283 		case 7:
2284 		case 10:
2285 			nx = e->x;
2286 			ny = e->y + 32;
2287 			g_hdb->_ai->_tileFroglickMiddleUD->drawMasked(nx - mx, ny - my);
2288 			g_hdb->_ai->_tileFroglickWiggleUD[0]->drawMasked(nx - mx, ny + 32 - my);
2289 			break;
2290 		case 5:
2291 		case 8:
2292 		case 11:
2293 			nx = e->x;
2294 			ny = e->y + 32;
2295 			g_hdb->_ai->_tileFroglickMiddleUD->drawMasked(nx - mx, ny - my);
2296 			g_hdb->_ai->_tileFroglickWiggleUD[1]->drawMasked(nx - mx, ny + 32 - my);
2297 			break;
2298 		case 6:
2299 		case 9:
2300 		case 12:
2301 			nx = e->x;
2302 			ny = e->y + 32;
2303 			g_hdb->_ai->_tileFroglickMiddleUD->drawMasked(nx - mx, ny - my);
2304 			g_hdb->_ai->_tileFroglickWiggleUD[2]->drawMasked(nx - mx, ny + 32 - my);
2305 			break;
2306 		default:
2307 			break;
2308 		}
2309 		break;
2310 
2311 	case STATE_LICKLEFT:
2312 		switch (e->value1) {
2313 		case 1:
2314 		case 2:
2315 		case 3:
2316 		case 13:
2317 		case 14:
2318 			nx = e->x - 32;
2319 			ny = e->y;
2320 			g_hdb->_ai->_tileFroglickWiggleLeft[1]->drawMasked(nx - mx, ny - my);
2321 			break;
2322 		case 4:
2323 		case 7:
2324 		case 10:
2325 			nx = e->x - 32;
2326 			ny = e->y;
2327 			g_hdb->_ai->_tileFroglickMiddleLR->drawMasked(nx - mx, ny - my);
2328 			g_hdb->_ai->_tileFroglickWiggleLeft[0]->drawMasked(nx - 32 - mx, ny - my);
2329 			break;
2330 		case 5:
2331 		case 8:
2332 		case 11:
2333 			nx = e->x - 32;
2334 			ny = e->y;
2335 			g_hdb->_ai->_tileFroglickMiddleLR->drawMasked(nx - mx, ny - my);
2336 			g_hdb->_ai->_tileFroglickWiggleLeft[1]->drawMasked(nx - 32 - mx, ny - my);
2337 			break;
2338 		case 6:
2339 		case 9:
2340 		case 12:
2341 			nx = e->x - 32;
2342 			ny = e->y;
2343 			g_hdb->_ai->_tileFroglickMiddleLR->drawMasked(nx - mx, ny - my);
2344 			g_hdb->_ai->_tileFroglickWiggleLeft[2]->drawMasked(nx - 32 - mx, ny - my);
2345 			break;
2346 		default:
2347 			break;
2348 		}
2349 		break;
2350 
2351 	case STATE_LICKRIGHT:
2352 		switch (e->value1) {
2353 		case 1:
2354 		case 2:
2355 		case 3:
2356 		case 13:
2357 		case 14:
2358 			nx = e->x + 32;
2359 			ny = e->y;
2360 			g_hdb->_ai->_tileFroglickWiggleRight[1]->drawMasked(nx - 32 - mx, ny - my);
2361 			break;
2362 		case 4:
2363 		case 7:
2364 		case 10:
2365 			nx = e->x + 32;
2366 			ny = e->y;
2367 			g_hdb->_ai->_tileFroglickMiddleLR->drawMasked(nx - mx, ny - my);
2368 			g_hdb->_ai->_tileFroglickWiggleRight[0]->drawMasked(nx + 32 - mx, ny - my);
2369 			break;
2370 		case 5:
2371 		case 8:
2372 		case 11:
2373 			nx = e->x + 32;
2374 			ny = e->y;
2375 			g_hdb->_ai->_tileFroglickMiddleLR->drawMasked(nx - mx, ny - my);
2376 			g_hdb->_ai->_tileFroglickWiggleRight[1]->drawMasked(nx + 32 - mx, ny - my);
2377 			break;
2378 		case 6:
2379 		case 9:
2380 		case 12:
2381 			nx = e->x + 32;
2382 			ny = e->y;
2383 			g_hdb->_ai->_tileFroglickMiddleLR->drawMasked(nx - mx, ny - my);
2384 			g_hdb->_ai->_tileFroglickWiggleRight[2]->drawMasked(nx + 32 - mx, ny - my);
2385 			break;
2386 		default:
2387 			break;
2388 		}
2389 		break;
2390 	default:
2391 		break;
2392 	}
2393 }
2394 
2395 //-------------------------------------------------------------------
2396 //
2397 //	GOODFAIRY
2398 //
2399 //-------------------------------------------------------------------
2400 
aiGoodFairyInit(AIEntity * e)2401 void aiGoodFairyInit(AIEntity *e) {
2402 	e->aiAction = aiGoodFairyAction;
2403 	e->sequence = 20;
2404 	e->blinkFrames = e->goalX = 0;
2405 }
2406 
aiGoodFairyInit2(AIEntity * e)2407 void aiGoodFairyInit2(AIEntity *e) {
2408 	e->draw = g_hdb->_ai->getStandFrameDir(e);
2409 }
2410 
aiGoodFairyAction(AIEntity * e)2411 void aiGoodFairyAction(AIEntity *e) {
2412 	static const AIState state[5] = {STATE_NONE, STATE_MOVEUP, STATE_MOVEDOWN, STATE_MOVELEFT, STATE_MOVERIGHT};
2413 	static const int xvAhead[5] = {9, 0, 0,-1, 1};
2414 	static const int yvAhead[5] = {9,-1, 1, 0, 0};
2415 
2416 	int	result;
2417 
2418 	if (e->sequence) {
2419 		e->sequence--;
2420 
2421 		// look around...
2422 		switch (e->sequence) {
2423 		case 19:
2424 			e->state = STATE_MOVEDOWN;
2425 			break;
2426 		case 0:
2427 			{
2428 				// Create a GEM?
2429 				if (g_hdb->_rnd->getRandomNumber(99) > 98) {
2430 					// spawn a gem in a random direction
2431 					int	d = g_hdb->_rnd->getRandomNumber(3) + 1;
2432 					int xv = xvAhead[d];
2433 					int yv = yvAhead[d];
2434 
2435 					e->sequence = 30;
2436 					e->state = STATE_MOVEDOWN;
2437 					// is something there already?
2438 					if ((g_hdb->_ai->findEntityType(AI_CRATE, e->tileX + xv, e->tileY + yv) != nullptr) ||
2439 						(g_hdb->_ai->findEntityType(AI_LIGHTBARREL, e->tileX + xv, e->tileY + yv) != nullptr))
2440 						return;
2441 					int spawnOK;
2442 					AIEntity *hit = g_hdb->_ai->legalMove(e->tileX + xv, e->tileY + yv, e->level, &spawnOK);
2443 					uint32 bg_flags = g_hdb->_map->getMapBGTileFlags(e->tileX + xv, e->tileY + yv);
2444 					if (hit || !spawnOK || (bg_flags & kFlagSpecial))
2445 						return;
2446 
2447 					g_hdb->_ai->spawn(ITEM_GEM_WHITE, e->dir, e->tileX + xv, e->tileY + yv, nullptr, nullptr, nullptr, DIR_NONE, e->level, 0, 0, 1);
2448 					g_hdb->_ai->addAnimateTarget(e->x + xv * kTileWidth, e->y + yv * kTileHeight, 0, 3, ANIM_NORMAL, false, false, GEM_FLASH);
2449 					if (e->onScreen) {
2450 						g_hdb->_sound->playSound(SND_GET_GEM);
2451 						g_hdb->_sound->playSound(SND_GOOD_FAERIE_SPELL);
2452 					}
2453 					return;
2454 				}
2455 
2456 				int	tries = 4;
2457 				do {
2458 					// pick a random direction, then a random # of tiles in that direction
2459 					AIDir d = (AIDir)(g_hdb->_rnd->getRandomNumber(3) + 1);
2460 					int walk = g_hdb->_rnd->getRandomNumber(4) + 1;
2461 					AIEntity *p = g_hdb->_ai->getPlayer();
2462 
2463 					// if player is within 3 tiles, move closer
2464 					if (abs(p->tileX - e->tileX) < 3 && abs(p->tileY - e->tileY) < 3) {
2465 						if (abs(p->tileX - e->tileX) > abs(p->tileY - e->tileY)) {
2466 						testx:
2467 							if (p->tileX != e->tileX) {
2468 								if (p->tileX < e->tileX)
2469 									d = DIR_LEFT;
2470 								else
2471 									d = DIR_RIGHT;
2472 							} else if (p->tileY != e->tileY)
2473 								goto testy;
2474 						} else {
2475 						testy:
2476 							if (p->tileY != e->tileY) {
2477 								if (p->tileY <= e->tileY)
2478 									d = DIR_UP;
2479 								else
2480 									d = DIR_DOWN;
2481 							} else if (p->tileX != e->tileX)
2482 								goto testx;
2483 						}
2484 					}
2485 
2486 					// special case: if player is exactly 2 tiles away, move out of the way
2487 					if (abs(p->tileX - e->tileX) == 2 && p->tileY == e->tileY) {
2488 						int	move_ok;
2489 						d = DIR_UP;
2490 						AIEntity *h = g_hdb->_ai->legalMoveOverWater(e->tileX, e->tileY - 1, e->level, &move_ok);
2491 						if (h || !move_ok)
2492 							d = DIR_DOWN;
2493 					} else if (abs(p->tileY - e->tileY) == 2 && p->tileX == e->tileX) {
2494 						int	move_ok;
2495 						d = DIR_LEFT;
2496 						AIEntity *h = g_hdb->_ai->legalMoveOverWater(e->tileX - 1, e->tileY, e->level, &move_ok);
2497 						if (h || !move_ok)
2498 							d = DIR_RIGHT;
2499 					}
2500 
2501 					e->dir = d;
2502 					int tmpDir = (int)d;
2503 					e->state = state[tmpDir];
2504 					int xv = xvAhead[tmpDir] * walk;
2505 					if (e->tileX + xv < 1)
2506 						xv = -e->tileX + 1;
2507 					if (e->tileX + xv > g_hdb->_map->_width)
2508 						xv = g_hdb->_map->_width - e->tileX - 1;
2509 
2510 					int yv = yvAhead[d] * walk;
2511 					if (e->tileY + yv < 1)
2512 						yv = -e->tileY + 1;
2513 					if (e->tileY + yv > g_hdb->_map->_height)
2514 						yv = g_hdb->_map->_height - e->tileY - 1;
2515 
2516 					e->value1 = xvAhead[d];
2517 					e->value2 = yvAhead[d];
2518 					e->moveSpeed = kPlayerMoveSpeed;
2519 
2520 					// make sure we can move over water & white gems, but not fg_hdb->_ai->y blockers and solids
2521 					AIEntity *hit = g_hdb->_ai->legalMoveOverWater(e->tileX + e->value1, e->tileY + e->value2, e->level, &result);
2522 					if (hit && ((hit->type == ITEM_GEM_WHITE) || (hit->type == AI_GUY)))
2523 						hit = nullptr;
2524 					uint32 bg_flags = g_hdb->_map->getMapBGTileFlags(e->tileX + e->value1, e->tileY + e->value2);
2525 					if (result && !hit && !(bg_flags & kFlagSpecial)) {
2526 						g_hdb->_ai->setEntityGoal(e, e->tileX + xv, e->tileY + yv);
2527 						if (e->onScreen && !g_hdb->_rnd->getRandomNumber(29))
2528 							g_hdb->_sound->playSound(SND_GOOD_FAERIE_AMBIENT);
2529 						g_hdb->_ai->animateEntity(e);
2530 						return;
2531 					}
2532 					tries--;		// don't lock the system if the fg_hdb->_ai->y is cornered
2533 				} while (!result && tries);
2534 
2535 				// couldn't find a place to move so just sit here for a sec & try agg_hdb->_ai->
2536 				e->dir = DIR_NONE;
2537 				e->state = STATE_MOVEDOWN;
2538 				e->sequence = 1;
2539 				e->value1 = e->value2 = e->xVel = e->yVel = 0;
2540 			}
2541 			break;
2542 		default:
2543 			break;
2544 		}
2545 		g_hdb->_ai->animEntFrames(e);
2546 		return;
2547 	}
2548 
2549 	// in the process of moving around...
2550 	if (e->goalX) {
2551 		// did we run into a wall, entity, water, slime etc?
2552 		// if so, pick a new direction!
2553 		if (onEvenTile(e->x, e->y)) {
2554 			// did we hit a Fg_hdb->_ai->YSTONE??? if so - teleport the thing at the other end to here!
2555 			int index = g_hdb->_ai->checkFairystones(e->tileX, e->tileY);
2556 			if (index >= 0) {
2557 				int	sx, sy;
2558 				g_hdb->_ai->getFairystonesSrc(index, &sx, &sy);
2559 				AIEntity *hit = g_hdb->_ai->findEntity(sx, sy);
2560 				if (hit && (hit != g_hdb->_ai->getPlayer())) {
2561 					hit->tileX = e->tileX;
2562 					hit->tileY = e->tileY;
2563 					hit->x = hit->tileX * kTileWidth;
2564 					hit->y = hit->tileY * kTileHeight;
2565 					hit->goalX = hit->goalY = 0;
2566 					g_hdb->_ai->addAnimateTarget(e->x, e->y, 0, 7, ANIM_NORMAL, false, false, TELEPORT_FLASH);
2567 					g_hdb->_ai->addAnimateTarget(sx * kTileWidth, sy * kTileHeight, 0, 7, ANIM_NORMAL, false, false, TELEPORT_FLASH);
2568 					if (e->onScreen)
2569 						g_hdb->_sound->playSound(SND_TELEPORT);
2570 					if (hit->onScreen)
2571 						g_hdb->_sound->playSound(SND_TELEPORT);
2572 				}
2573 			}
2574 
2575 			// see if we're about to move to a bad spot, which means:
2576 			// (1) we're gonna hit a solid wall; ok to move over water/slime
2577 			// (2) ok to move thru white gems
2578 			// (3) cannot move thru SPECIAL flagged tiles (fg_hdb->_ai->y blockers)
2579 			AIEntity *hit = g_hdb->_ai->legalMoveOverWater(e->tileX + e->value1, e->tileY + e->value2, e->level, &result);
2580 			uint32 bg_flags = g_hdb->_map->getMapBGTileFlags(e->tileX + e->value1, e->tileY + e->value2);
2581 			if (!result || (hit && hit->type != ITEM_GEM_WHITE && hit->type != AI_GUY) || (bg_flags & kFlagSpecial)) {
2582 				g_hdb->_ai->stopEntity(e);
2583 				e->value1 = e->value2 = 0;
2584 				e->state = STATE_MOVEDOWN;
2585 				e->sequence = 20;
2586 				return;
2587 			}
2588 		}
2589 		g_hdb->_ai->animateEntity(e);
2590 	} else {
2591 		// if not, start looking around!
2592 		e->sequence = 20;
2593 	}
2594 }
2595 
2596 //-------------------------------------------------------------------
2597 //
2598 //	BADFAIRY
2599 //
2600 //-------------------------------------------------------------------
2601 
aiBadFairyInit(AIEntity * e)2602 void aiBadFairyInit(AIEntity *e) {
2603 	e->aiAction = aiBadFairyAction;
2604 	e->sequence = 20;
2605 	e->blinkFrames = e->goalX = 0;
2606 }
2607 
aiBadFairyInit2(AIEntity * e)2608 void aiBadFairyInit2(AIEntity *e) {
2609 	e->draw = g_hdb->_ai->getStandFrameDir(e);
2610 }
2611 
aiBadFairyAction(AIEntity * e)2612 void aiBadFairyAction(AIEntity *e) {
2613 	static const AIState state[5] = {STATE_NONE, STATE_MOVEUP, STATE_MOVEDOWN, STATE_MOVELEFT, STATE_MOVERIGHT};
2614 	static const AIDir opposite[5] = {DIR_NONE, DIR_DOWN, DIR_UP, DIR_RIGHT, DIR_LEFT};
2615 	static const int xvAhead[5] = {9, 0, 0,-1, 1};
2616 	static const int yvAhead[5] = {9,-1, 1, 0, 0};
2617 
2618 	if (e->sequence) {
2619 		e->sequence--;
2620 
2621 		// look around...
2622 		switch (e->sequence) {
2623 		case 19:
2624 			e->state = STATE_MOVEDOWN;
2625 			break;
2626 		case 0:
2627 			{
2628 				// Create a GATE PUDDLE?
2629 				if (e->onScreen && (g_hdb->_rnd->getRandomNumber(99) > 90) && g_hdb->getActionMode() && (g_hdb->_ai->getGatePuddles() < kMaxGatePuddles)) {
2630 					if (e->onScreen)
2631 						g_hdb->_sound->playSound(SND_BADFAIRY_SPELL);
2632 
2633 					e->sequence = 30;
2634 					e->state = STATE_MOVEUP;
2635 					g_hdb->_ai->spawn(AI_GATEPUDDLE, opposite[e->dir], e->tileX, e->tileY, nullptr, nullptr, nullptr, DIR_NONE, e->level, 0, 0, 1);
2636 					g_hdb->_ai->addAnimateTarget(e->x, e->y, 0, 7, ANIM_NORMAL, false, false, TELEPORT_FLASH);
2637 					g_hdb->_ai->addGatePuddle(1);
2638 					if (e->onScreen)
2639 						g_hdb->_sound->playSound(SND_GATEPUDDLE_SPAWN);
2640 					return;
2641 				}
2642 
2643 				int	tries = 4;
2644 				int	result;
2645 				do {
2646 					// pick a random direction, then a random # of tiles in that direction
2647 					int d = g_hdb->_rnd->getRandomNumber(3) + 1;
2648 					int	walk = g_hdb->_rnd->getRandomNumber(4) + 1;
2649 					AIEntity *p = g_hdb->_ai->getPlayer();
2650 
2651 					e->dir = (AIDir)d;
2652 					e->state = state[d];
2653 					int xv = xvAhead[d] * walk;
2654 					if (e->tileX + xv < 1)
2655 						xv = -e->tileX + 1;
2656 					if (e->tileX + xv > g_hdb->_map->_width)
2657 						xv = g_hdb->_map->_width - e->tileX - 1;
2658 
2659 					int yv = yvAhead[d] * walk;
2660 					if (e->tileY + yv < 1)
2661 						yv = -e->tileY + 1;
2662 					if (e->tileY + yv > g_hdb->_map->_height)
2663 						yv = g_hdb->_map->_height - e->tileY - 1;
2664 
2665 					e->value1 = xvAhead[d];
2666 					e->value2 = yvAhead[d];
2667 					e->moveSpeed = kPlayerMoveSpeed;
2668 					if (!g_hdb->getActionMode())
2669 						e->moveSpeed >>= 1;
2670 
2671 					AIEntity *hit = g_hdb->_ai->legalMoveOverWater(e->tileX + e->value1, e->tileY + e->value2, e->level, &result);
2672 					uint32 bg_flags = g_hdb->_map->getMapBGTileFlags(e->tileX + e->value1, e->tileY + e->value2);
2673 					if (hit == p && !g_hdb->_ai->playerDead()) {
2674 						g_hdb->_ai->killPlayer(DEATH_FRIED);
2675 						hit = nullptr;
2676 					}
2677 
2678 					if (!hit && result && !(bg_flags & kFlagSpecial)) {
2679 						g_hdb->_ai->setEntityGoal(e, e->tileX + xv, e->tileY + yv);
2680 						g_hdb->_ai->animateEntity(e);
2681 						if (e->onScreen && !g_hdb->_rnd->getRandomNumber(19))
2682 							g_hdb->_sound->playSound(SND_BADFAIRY_AMBIENT);
2683 						return;
2684 					}
2685 					tries--;		// don't lock the system if the player gets the fairy cornered
2686 				} while (!result && tries);
2687 
2688 				// couldn't find a place to move so just sit here for a sec & try again
2689 				e->dir = DIR_NONE;
2690 				e->state = STATE_MOVEDOWN;
2691 				e->sequence = 1;
2692 				e->value1 = e->value2 = e->xVel = e->yVel = 0;
2693 			}
2694 			break;
2695 		default:
2696 			break;
2697 		}
2698 		g_hdb->_ai->animEntFrames(e);
2699 		return;
2700 	}
2701 
2702 	// in the process of moving around...
2703 	if (e->goalX) {
2704 		// hit the player?
2705 		if (hitPlayer(e->x, e->y)) {
2706 			g_hdb->_ai->killPlayer(DEATH_FRIED);
2707 			g_hdb->_sound->playSound(SND_MBOT_DEATH);
2708 			return;
2709 		}
2710 
2711 		// did we run into a wall, entity, water, slime etc?
2712 		// if so, pick a new direction!
2713 		if (onEvenTile(e->x, e->y)) {
2714 			int	result;
2715 			AIEntity *hit = g_hdb->_ai->legalMoveOverWater(e->tileX + e->value1, e->tileY + e->value2, e->level, &result);
2716 			uint32 bg_flags = g_hdb->_map->getMapBGTileFlags(e->tileX + e->value1, e->tileY + e->value2);
2717 			if (!result || (hit && hit->type != AI_GUY) || (bg_flags & kFlagSpecial)) {
2718 				g_hdb->_ai->stopEntity(e);
2719 				e->state = STATE_MOVEDOWN;
2720 				e->sequence = 20;
2721 				return;
2722 			}
2723 		}
2724 		g_hdb->_ai->animateEntity(e);
2725 	} else {
2726 		// if not, start looking around!
2727 		e->sequence = 20;
2728 	}
2729 }
2730 
2731 //-------------------------------------------------------------------
2732 //
2733 //	BADFAIRY's GATE PUDDLE!
2734 //
2735 //-------------------------------------------------------------------
2736 
aiGatePuddleInit(AIEntity * e)2737 void aiGatePuddleInit(AIEntity *e) {
2738 	e->aiAction = aiGatePuddleAction;
2739 	e->value1 = 50;
2740 }
2741 
aiGatePuddleInit2(AIEntity * e)2742 void aiGatePuddleInit2(AIEntity *e) {
2743 }
2744 
aiGatePuddleAction(AIEntity * e)2745 void aiGatePuddleAction(AIEntity *e) {
2746 	static const int xva[5] = {9, 0, 0,-1, 1};
2747 	static const int yva[5] = {9,-1, 1, 0, 0};
2748 
2749 	AIEntity *p = g_hdb->_ai->getPlayer();
2750 
2751 	if (e->goalX) {
2752 		g_hdb->_ai->animateEntity(e);
2753 		if (hitPlayer(e->x, e->y)) {
2754 			for (int i = 0; i < kMaxTeleporters; i++) {
2755 				if (g_hdb->_ai->_teleporters[i].anim1 == 2) {	// PANIC ZONE?
2756 					p->tileX = g_hdb->_ai->_teleporters[i].x1;
2757 					p->tileY = g_hdb->_ai->_teleporters[i].y1;
2758 					p->x = p->tileX * kTileWidth;
2759 					p->y = p->tileY * kTileHeight;
2760 					p->xVel = p->yVel = 0;
2761 					p->goalX = p->goalY = 0;
2762 					p->animFrame = 0;
2763 					p->drawXOff = p->drawYOff = 0;
2764 					p->dir = g_hdb->_ai->_teleporters[i].dir1;
2765 					p->level = g_hdb->_ai->_teleporters[i].level1;
2766 					g_hdb->_ai->addAnimateTarget(p->x, p->y, 0, 7, ANIM_NORMAL, false, false, TELEPORT_FLASH);
2767 					g_hdb->_sound->playSound(SND_TELEPORT);
2768 					g_hdb->_ai->clearWaypoints();
2769 					g_hdb->_window->startPanicZone();
2770 					g_hdb->_map->centerMapXY(p->x + 16, p->y + 16);
2771 					switch (p->dir) {
2772 					case DIR_UP:
2773 						g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY - 1);
2774 						break;
2775 					case DIR_DOWN:
2776 						g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY + 1);
2777 						break;
2778 					case DIR_LEFT:
2779 						g_hdb->_ai->setEntityGoal(p, p->tileX - 1, p->tileY);
2780 						break;
2781 					case DIR_RIGHT:
2782 						g_hdb->_ai->setEntityGoal(p, p->tileX + 1, p->tileY);
2783 						break;
2784 					case DIR_NONE:
2785 					default:
2786 						break;
2787 					}
2788 					g_hdb->_ai->_playerEmerging = true;
2789 					break;
2790 				} else if (g_hdb->_ai->_teleporters[i].anim2 == 2) {	// PANIC ZONE?
2791 					p->tileX = g_hdb->_ai->_teleporters[i].x2;
2792 					p->tileY = g_hdb->_ai->_teleporters[i].y2;
2793 					p->x = p->tileX * kTileWidth;
2794 					p->y = p->tileY * kTileHeight;
2795 					p->xVel = p->yVel = 0;
2796 					p->goalX = p->goalY = 0;
2797 					p->animFrame = 0;
2798 					p->drawXOff = p->drawYOff = 0;
2799 					p->dir = g_hdb->_ai->_teleporters[i].dir2;
2800 					p->level = g_hdb->_ai->_teleporters[i].level2;
2801 					g_hdb->_ai->addAnimateTarget(p->x, p->y, 0, 7, ANIM_NORMAL, false, false, TELEPORT_FLASH);
2802 					g_hdb->_sound->playSound(SND_TELEPORT);
2803 					g_hdb->_ai->clearWaypoints();
2804 					g_hdb->_window->startPanicZone();
2805 					g_hdb->_map->centerMapXY(p->x + 16, p->y + 16);
2806 					switch (p->dir) {
2807 					case DIR_UP:
2808 						g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY - 1);
2809 						break;
2810 					case DIR_DOWN:
2811 						g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY + 1);
2812 						break;
2813 					case DIR_LEFT:
2814 						g_hdb->_ai->setEntityGoal(p, p->tileX - 1, p->tileY);
2815 						break;
2816 					case DIR_RIGHT:
2817 						g_hdb->_ai->setEntityGoal(p, p->tileX + 1, p->tileY);
2818 						break;
2819 					case DIR_NONE:
2820 					default:
2821 						break;
2822 					}
2823 					g_hdb->_ai->_playerEmerging = true;
2824 					break;
2825 				}
2826 			}
2827 		}
2828 	} else {
2829 		int rnd = g_hdb->_rnd->getRandomNumber(3) + 1;
2830 		e->dir = (AIDir)rnd;
2831 		int nx = e->tileX + xva[rnd];
2832 		int ny = e->tileY + yva[rnd];
2833 
2834 		int move_ok;
2835 		AIEntity *hit = g_hdb->_ai->legalMoveOverWater(nx, ny, e->level, &move_ok);
2836 		if (hit == p)
2837 			hit = nullptr;
2838 
2839 		if (!hit && move_ok) {
2840 			uint32	bg_flags = g_hdb->_map->getMapBGTileFlags(nx, ny);
2841 			// Gate Puddles can't go over METAL!!! It's in their genes...
2842 			if (!(bg_flags & kFlagMetal)) {
2843 				if (e->onScreen)
2844 					g_hdb->_sound->playSound(SND_GATEPUDDLE_AMBIENT);
2845 
2846 				g_hdb->_ai->setEntityGoal(e, nx, ny);
2847 				e->state = STATE_MOVEDOWN;
2848 				g_hdb->_ai->animateEntity(e);
2849 			}
2850 		}
2851 
2852 		// can only move 50 spaces or collisions
2853 		e->value1--;
2854 		if (!e->value1) {
2855 			g_hdb->_ai->addGatePuddle(-1);
2856 			g_hdb->_ai->addAnimateTarget(e->x, e->y, 0, 7, ANIM_NORMAL, false, false, TELEPORT_FLASH);
2857 			if (e->onScreen)
2858 				g_hdb->_sound->playSound(SND_GATEPUDDLE_DISSIPATE);
2859 			g_hdb->_ai->removeEntity(e);
2860 		}
2861 	}
2862 }
2863 
2864 //-------------------------------------------------------------------
2865 //
2866 //	ICEPUFF : Little icy dude peeks out of the ground and pops up and
2867 //		throws a snowball at you if he sees you....then he blasts back
2868 //		into the snow and hides for a while....
2869 //
2870 // Variables used specially:
2871 //	value1, value2	: x,y of snowball
2872 //	dir2			: direction of snowball. DIR_NONE = no snowball
2873 //	sequence	: timer for peeking
2874 //
2875 //-------------------------------------------------------------------
2876 
aiIcePuffSnowballInit(AIEntity * e)2877 void aiIcePuffSnowballInit(AIEntity *e) {
2878 	// which direction are we throwing in? Load the graphic if we need to
2879 	switch (e->dir) {
2880 	case DIR_DOWN:
2881 		e->value1 = e->x + 12;
2882 		e->value2 = e->y + 32;
2883 		break;
2884 	case DIR_LEFT:
2885 		e->value1 = e->x - 4;
2886 		e->value2 = e->y + 16;
2887 		break;
2888 	case DIR_RIGHT:
2889 		e->value1 = e->x + 32;
2890 		e->value2 = e->y + 16;
2891 		break;
2892 	default:
2893 		break;
2894 	}
2895 	e->aiDraw = aiIcePuffSnowballDraw;
2896 }
2897 
aiIcePuffSnowballAction(AIEntity * e)2898 void aiIcePuffSnowballAction(AIEntity *e) {
2899 	// check for hit BEFORE moving so snowball is closer to object
2900 	// NOTE: Need to do logic in this draw routine just in case the ICEPUFF gets stunned!
2901 	int result;
2902 	AIEntity *hit = g_hdb->_ai->legalMoveOverWater(e->value1 / kTileWidth, e->value2 / kTileHeight, e->level, &result);
2903 	if (hit && hit->type == AI_GUY && !g_hdb->_ai->playerDead()) {
2904 		g_hdb->_ai->killPlayer(DEATH_NORMAL);
2905 		g_hdb->_ai->addAnimateTarget(hit->x, hit->y, 0, 3, ANIM_NORMAL, false, false, GROUP_WATER_SPLASH_SIT);
2906 		result = 0; // fall thru...
2907 	}
2908 
2909 	// hit something solid - kill the snowball
2910 	if (!result) {
2911 		e->dir2 = DIR_NONE;
2912 		e->aiDraw = nullptr;
2913 		return;
2914 	}
2915 
2916 	int speed = kPlayerMoveSpeed;
2917 	if (!g_hdb->getActionMode())
2918 		speed >>= 1;
2919 
2920 	switch (e->dir2) {
2921 	case DIR_DOWN:
2922 		e->value2 += speed;
2923 		break;
2924 	case DIR_LEFT:
2925 		e->value1 -= speed;
2926 		break;
2927 	case DIR_RIGHT:
2928 		e->value1 += speed;
2929 		break;
2930 	default:
2931 		break;
2932 	}
2933 }
2934 
aiIcePuffSnowballDraw(AIEntity * e,int mx,int my)2935 void aiIcePuffSnowballDraw(AIEntity *e, int mx, int my) {
2936 	// did we throw a snowball?  make it move!
2937 	if (e->dir2 != DIR_NONE)
2938 		aiIcePuffSnowballAction(e);
2939 
2940 	switch (e->dir2) {
2941 	case DIR_DOWN:
2942 		if (!g_hdb->_ai->_icepSnowballGfxDown)
2943 			g_hdb->_ai->_icepSnowballGfxDown = g_hdb->_gfx->loadPic(ICEPUFF_SNOWBALL_DOWN);
2944 		g_hdb->_ai->_icepSnowballGfxDown->drawMasked(e->value1 - mx, e->value2 - my);
2945 		break;
2946 	case DIR_LEFT:
2947 		if (!g_hdb->_ai->_icepSnowballGfxLeft)
2948 			g_hdb->_ai->_icepSnowballGfxLeft = g_hdb->_gfx->loadPic(ICEPUFF_SNOWBALL_LEFT);
2949 		g_hdb->_ai->_icepSnowballGfxLeft->drawMasked(e->value1 - mx, e->value2 - my);
2950 		break;
2951 	case DIR_RIGHT:
2952 		if (!g_hdb->_ai->_icepSnowballGfxRight)
2953 			g_hdb->_ai->_icepSnowballGfxRight = g_hdb->_gfx->loadPic(ICEPUFF_SNOWBALL_RIGHT);
2954 		g_hdb->_ai->_icepSnowballGfxRight->drawMasked(e->value1 - mx, e->value2 - my);
2955 		break;
2956 	default:
2957 		break;
2958 	}
2959 }
2960 
aiIcePuffInit(AIEntity * e)2961 void aiIcePuffInit(AIEntity *e) {
2962 	// PEEK - but no head up yet
2963 	e->sequence = 30;				// timed sequence for peeking
2964 	e->state = STATE_ICEP_PEEK;			// start in PEEK mode
2965 	e->dir2 = DIR_NONE;					// no snowball out
2966 	e->aiAction = aiIcePuffAction;
2967 }
2968 
aiIcePuffInit2(AIEntity * e)2969 void aiIcePuffInit2(AIEntity *e) {
2970 	// empty frame
2971 	e->draw = e->blinkGfx[3];
2972 }
2973 
aiIcePuffAction(AIEntity * e)2974 void aiIcePuffAction(AIEntity *e) {
2975 	AIEntity *p = g_hdb->_ai->getPlayer();
2976 
2977 	switch (e->state) {
2978 	case STATE_ICEP_PEEK:
2979 		e->sequence--;
2980 		switch (e->sequence) {
2981 		case 20: // underground
2982 			e->draw = e->blinkGfx[0];
2983 			break;
2984 		case 16: // peek - looking
2985 			e->draw = e->blinkGfx[1];
2986 			break;
2987 		case 12: // peek - blinking
2988 			e->draw = e->blinkGfx[2];
2989 			break;
2990 		case 8: // peek - looking
2991 			e->draw = e->blinkGfx[1];
2992 			break;
2993 		case 4: // peek - looking
2994 			e->draw = e->blinkGfx[0];
2995 			break;
2996 		case 3:
2997 			if (e->onScreen && !g_hdb->_rnd->getRandomNumber(5))
2998 				g_hdb->_sound->playSound(SND_ICEPUFF_WARNING);
2999 			break;
3000 		case 0:
3001 			// underground
3002 			e->draw = e->blinkGfx[3];
3003 			e->sequence = 30;
3004 			break;
3005 		default:
3006 			break;
3007 		}
3008 
3009 		// can we see the player? (and no snowball is out)
3010 		if (e->sequence <= 20 && !g_hdb->_ai->playerDead() && e->onScreen) {
3011 			if (p->tileX == e->tileX && p->tileY > e->tileY && e->dir2 == DIR_NONE) {
3012 				e->dir = DIR_DOWN;
3013 				e->state = STATE_ICEP_APPEAR;
3014 				e->animFrame = 0;
3015 				if (e->onScreen)
3016 					g_hdb->_sound->playSound(SND_ICEPUFF_APPEAR);
3017 			} else if (p->tileY == e->tileY && e->dir2 == DIR_NONE) {
3018 				p->tileX < e->tileX ? e->dir = DIR_LEFT : e->dir = DIR_RIGHT;
3019 				e->state = STATE_ICEP_APPEAR;
3020 				e->animFrame = 0;
3021 				if (e->onScreen)
3022 					g_hdb->_sound->playSound(SND_ICEPUFF_APPEAR);
3023 			}
3024 		}
3025 		break;
3026 
3027 	case STATE_ICEP_APPEAR:
3028 		e->draw = e->standupGfx[e->animFrame];
3029 
3030 		// cycle animation frames
3031 		if (e->animDelay-- > 0)
3032 			return;
3033 		e->animDelay = e->animCycle;
3034 
3035 		e->animFrame++;
3036 		if (e->animFrame == e->standupFrames) {
3037 			e->animFrame = 0;
3038 			switch (e->dir) {
3039 			case DIR_DOWN:
3040 				e->state = STATE_ICEP_THROWDOWN;
3041 				g_hdb->_sound->playSound(SND_ICEPUFF_THROW);
3042 				break;
3043 			case DIR_LEFT:
3044 				e->state = STATE_ICEP_THROWLEFT;
3045 				g_hdb->_sound->playSound(SND_ICEPUFF_THROW);
3046 				break;
3047 			case DIR_RIGHT:
3048 				e->state = STATE_ICEP_THROWRIGHT;
3049 				g_hdb->_sound->playSound(SND_ICEPUFF_THROW);
3050 				break;
3051 			default:
3052 				break;
3053 			}
3054 		}
3055 		break;
3056 
3057 	case STATE_ICEP_THROWDOWN:
3058 		e->draw = e->standdownGfx[e->animFrame];
3059 
3060 		// cycle animation frames
3061 		if (e->animDelay-- > 0)
3062 			return;
3063 		e->animDelay = e->animCycle;
3064 
3065 		e->animFrame++;
3066 		if (e->animFrame == e->standdownFrames && e->state != STATE_ICEP_DISAPPEAR) {
3067 			// dir2 = direction snowball is moving
3068 			e->dir2 = e->dir;
3069 			// throw it!
3070 			aiIcePuffSnowballInit(e);
3071 			e->animFrame = 0;
3072 			e->state = STATE_ICEP_DISAPPEAR;
3073 		} else if (e->animFrame == e->special1Frames) {
3074 			e->state = STATE_ICEP_PEEK;
3075 			e->draw = e->blinkGfx[3];
3076 			e->sequence = g_hdb->_rnd->getRandomNumber(99) + 30;
3077 		}
3078 		break;
3079 
3080 	case STATE_ICEP_THROWLEFT:
3081 		e->draw = e->standleftGfx[e->animFrame];
3082 
3083 		// cycle animation frames
3084 		if (e->animDelay-- > 0)
3085 			return;
3086 		e->animDelay = e->animCycle;
3087 
3088 		e->animFrame++;
3089 		if (e->animFrame == e->standdownFrames && e->state != STATE_ICEP_DISAPPEAR) {
3090 			// dir2 = direction snowball is moving
3091 			e->dir2 = e->dir;
3092 			// throw it!
3093 			aiIcePuffSnowballInit(e);
3094 			e->animFrame = 0;
3095 			e->state = STATE_ICEP_DISAPPEAR;
3096 		} else if (e->animFrame == e->special1Frames) {
3097 			e->state = STATE_ICEP_PEEK;
3098 			e->draw = e->blinkGfx[3];
3099 			e->sequence = g_hdb->_rnd->getRandomNumber(99) + 30;
3100 		}
3101 		break;
3102 
3103 	case STATE_ICEP_THROWRIGHT:
3104 		e->draw = e->standrightGfx[e->animFrame];
3105 
3106 		// cycle animation frames
3107 		if (e->animDelay-- > 0)
3108 			return;
3109 		e->animDelay = e->animCycle;
3110 
3111 		e->animFrame++;
3112 		if (e->animFrame == e->standdownFrames && e->state != STATE_ICEP_DISAPPEAR) {
3113 			// dir2 = direction snowball is moving
3114 			e->dir2 = e->dir;
3115 			// throw it!
3116 			aiIcePuffSnowballInit(e);
3117 			e->animFrame = 0;
3118 			e->state = STATE_ICEP_DISAPPEAR;
3119 		} else if (e->animFrame == e->special1Frames) {
3120 			e->state = STATE_ICEP_PEEK;
3121 			e->draw = e->blinkGfx[3];
3122 			e->sequence = g_hdb->_rnd->getRandomNumber(99) + 30;
3123 		}
3124 		break;
3125 
3126 	case STATE_ICEP_DISAPPEAR:
3127 		e->draw = e->special1Gfx[e->animFrame];
3128 	default:
3129 		break;
3130 	}
3131 }
3132 
3133 //-------------------------------------------------------------------
3134 //
3135 //	BUZZFLY : Simply flies around on paths.... kills you if you touch him.
3136 //		He pauses at corners, too.
3137 //
3138 //-------------------------------------------------------------------
3139 
aiBuzzflyInit(AIEntity * e)3140 void aiBuzzflyInit(AIEntity *e) {
3141 	e->aiAction = aiBuzzflyAction;
3142 	e->sequence = 0;
3143 
3144 	g_hdb->_ai->findPath(e);
3145 }
3146 
aiBuzzflyInit2(AIEntity * e)3147 void aiBuzzflyInit2(AIEntity *e) {
3148 	e->draw = g_hdb->_ai->getStandFrameDir(e);
3149 	for (int i = 0; i < e->movedownFrames; i++) {
3150 		e->standdownGfx[i] = e->movedownGfx[i];
3151 		e->standupGfx[i] = e->moveupGfx[i];
3152 		e->standleftGfx[i] = e->moveleftGfx[i];
3153 		e->standrightGfx[i] = e->moverightGfx[i];
3154 	}
3155 	e->standdownFrames = e->movedownFrames;
3156 	e->standupFrames = e->moveupFrames;
3157 	e->standleftFrames = e->moveleftFrames;
3158 	e->standrightFrames = e->moverightFrames;
3159 }
3160 
aiBuzzflyAction(AIEntity * e)3161 void aiBuzzflyAction(AIEntity *e) {
3162 	if (!e->goalX) {
3163 		switch (e->sequence) {
3164 		case 0:
3165 		case 1:
3166 		case 2:
3167 		case 3:
3168 		case 4:
3169 			if (!e->animFrame && e->animDelay == e->animCycle)
3170 				e->sequence++;
3171 
3172 			e->draw = e->standdownGfx[e->animFrame];
3173 
3174 			// cycle animation frames
3175 			if (e->animDelay-- > 0)
3176 				return;
3177 			e->animDelay = e->animCycle;
3178 			e->animFrame++;
3179 			if (e->animFrame == e->standdownFrames)
3180 				e->animFrame = 0;
3181 
3182 			break;
3183 
3184 		case 5:
3185 			g_hdb->_ai->findPath(e);
3186 			if (e->onScreen)
3187 				g_hdb->_sound->playSound(SND_BUZZFLY_FLY);
3188 			e->sequence = 0;
3189 			break;
3190 
3191 		default:
3192 			break;
3193 		}
3194 	} else {
3195 		g_hdb->_ai->animateEntity(e);
3196 		if (g_hdb->_ai->checkPlayerCollision(e->x, e->y, 6) && !g_hdb->_ai->playerDead()) {
3197 			g_hdb->_sound->playSound(SND_BUZZFLY_STING);
3198 			g_hdb->_ai->killPlayer(DEATH_GRABBED);
3199 		}
3200 	}
3201 }
3202 
3203 //-------------------------------------------------------------------
3204 //
3205 //	DRAGON
3206 //
3207 //-------------------------------------------------------------------
3208 
aiDragonInit(AIEntity * e)3209 void aiDragonInit(AIEntity *e) {
3210 	e->state = STATE_STANDDOWN;
3211 	e->sequence = 0;	// 0 = sleeping
3212 	e->aiAction = aiDragonAction;
3213 	e->aiDraw = aiDragonDraw;
3214 	e->animCycle = 10;	// time between flaps
3215 
3216 	// need to save the dragon's coords and type in the blocking entity for gem-hit-blocking detection
3217 	AIEntity *block = spawnBlocking(e->tileX - 1, e->tileY, e->level);
3218 	block->value1 = (int)AI_DRAGON;
3219 	sprintf(block->luaFuncUse, "%03d%03d", e->tileX, e->tileY);
3220 	block = spawnBlocking(e->tileX + 1, e->tileY, e->level);
3221 	block->value1 = (int)AI_DRAGON;
3222 	sprintf(block->luaFuncUse, "%03d%03d", e->tileX, e->tileY);
3223 	block = spawnBlocking(e->tileX - 1, e->tileY - 1, e->level);
3224 	block->value1 = (int)AI_DRAGON;
3225 	sprintf(block->luaFuncUse, "%03d%03d", e->tileX, e->tileY);
3226 	block = spawnBlocking(e->tileX + 1, e->tileY - 1, e->level);
3227 	block->value1 = (int)AI_DRAGON;
3228 	sprintf(block->luaFuncUse, "%03d%03d", e->tileX, e->tileY);
3229 	block = spawnBlocking(e->tileX - 1, e->tileY - 2, e->level);
3230 	block->value1 = (int)AI_DRAGON;
3231 	sprintf(block->luaFuncUse, "%03d%03d", e->tileX, e->tileY);
3232 	block = spawnBlocking(e->tileX + 1, e->tileY - 2, e->level);
3233 	block->value1 = (int)AI_DRAGON;
3234 	sprintf(block->luaFuncUse, "%03d%03d", e->tileX, e->tileY);
3235 }
3236 
aiDragonInit2(AIEntity * e)3237 void aiDragonInit2(AIEntity *e) {
3238 	e->draw = nullptr;
3239 	if (!g_hdb->_ai->_gfxDragonAsleep) {
3240 		g_hdb->_ai->_gfxDragonAsleep = g_hdb->_gfx->loadPic(DRAGON_ASLEEP);
3241 		g_hdb->_ai->_gfxDragonFlap[0] = g_hdb->_gfx->loadPic(DRAGON_FLAP1);
3242 		g_hdb->_ai->_gfxDragonFlap[1] = g_hdb->_gfx->loadPic(DRAGON_FLAP2);
3243 		g_hdb->_ai->_gfxDragonBreathe[0] = g_hdb->_gfx->loadPic(DRAGON_BREATHE_START);
3244 		g_hdb->_ai->_gfxDragonBreathe[1] = g_hdb->_gfx->loadPic(DRAGON_BREATHING_1);
3245 		g_hdb->_ai->_gfxDragonBreathe[2] = g_hdb->_gfx->loadPic(DRAGON_BREATHING_2);
3246 	}
3247 }
3248 
aiDragonWake(AIEntity * e)3249 void aiDragonWake(AIEntity *e) {
3250 	// woke up, start flapping and breathing!
3251 	e->sequence = 1;
3252 	e->animFrame = 0;
3253 	e->animDelay = e->animCycle;
3254 }
3255 
aiDragonUse(AIEntity * e)3256 void aiDragonUse(AIEntity *e) {
3257 	aiDragonWake(e);
3258 }
3259 
aiDragonAction(AIEntity * e)3260 void aiDragonAction(AIEntity *e) {
3261 	AIEntity *p = g_hdb->_ai->getPlayer();
3262 
3263 	switch (e->sequence) {
3264 	// Sleeping, waiting for the player to wake him up
3265 	case 0:
3266 		if (e->onScreen &&
3267 			p->tileX >= e->tileX - 1 &&
3268 			p->tileX <= e->tileX + 1 &&
3269 			p->tileY <= e->tileY + 1 &&
3270 			p->tileY >= e->tileY - 3) {
3271 			if ((p->state >= STATE_ATK_CLUB_UP &&
3272 				p->state <= STATE_ATK_SLUG_RIGHT) || g_hdb->_window->inPanicZone()) {
3273 				aiDragonWake(e);
3274 				if (e->onScreen)
3275 					g_hdb->_sound->playSound(SND_DRAGON_WAKE);
3276 			}
3277 		}
3278 		break;
3279 
3280 	// Woke up - flapping wings 3 times!
3281 	case 1:
3282 		e->animDelay--;
3283 
3284 		if (e->animDelay < 1) {
3285 			if (e->onScreen)
3286 				g_hdb->_sound->playSound(SND_DRAGON_WAKE);
3287 			e->animDelay = e->animCycle;
3288 			e->animFrame++;
3289 			if (e->animFrame >= 8) {
3290 				e->animFrame = 0;
3291 				e->sequence = 2;
3292 				e->animCycle = 2;
3293 			}
3294 		}
3295 		break;
3296 
3297 	// Start breathing fire!
3298 	case 2:
3299 		e->animDelay--;
3300 
3301 		if (e->onScreen)
3302 			g_hdb->_sound->playSound(SND_DRAGON_BREATHEFIRE);
3303 		if (e->animDelay < 1) {
3304 			e->animDelay = e->animCycle;
3305 			e->animFrame++;
3306 			if (e->animFrame >= 1) {
3307 				e->animFrame = 0;
3308 				e->sequence = 3;
3309 				e->animCycle = 2;		// time between flaps
3310 			}
3311 		}
3312 
3313 		break;
3314 
3315 	// Breathing fire!
3316 	case 3:
3317 		{
3318 			if (hitPlayer(e->x, e->y + 32)) {
3319 				g_hdb->_ai->killPlayer(DEATH_FRIED);
3320 				return;
3321 			}
3322 
3323 			// whatever entity is in front of the dragon is gettin' USED!
3324 			AIEntity *hit = g_hdb->_ai->findEntity(e->tileX, e->tileY + 1);
3325 			if (hit) {
3326 				switch (hit->type) {
3327 				case AI_CHICKEN:
3328 					g_hdb->_ai->addAnimateTarget(hit->tileX * kTileWidth, hit->tileY * kTileHeight, 0, 2, ANIM_NORMAL, false, false, GROUP_ENT_CHICKEN_DIE);
3329 					g_hdb->_sound->playSound(SND_CHICKEN_DEATH);
3330 					g_hdb->_ai->removeEntity(hit);
3331 					e->sequence = 4;
3332 					break;
3333 				case AI_MAGIC_EGG:
3334 				case AI_ICE_BLOCK:
3335 					aiMagicEggUse(hit);
3336 					break;
3337 				default:
3338 					if (hit->aiUse)
3339 						hit->aiUse(hit);
3340 					if (hit->luaFuncUse[0])
3341 						g_hdb->_lua->callFunction(hit->luaFuncUse, 0);
3342 				}
3343 			}
3344 
3345 			e->animDelay--;
3346 
3347 			if (e->animDelay < 1) {
3348 				if (e->onScreen && !(e->animFrame & 7))
3349 					g_hdb->_sound->playSound(SND_DRAGON_BREATHEFIRE);
3350 
3351 				e->animDelay = e->animCycle;
3352 				e->animFrame++;
3353 				if (e->animFrame >= 30) {
3354 					e->animFrame = 0;
3355 					e->sequence = 4;
3356 					e->animCycle = 10;		// time between flaps
3357 				}
3358 			}
3359 		}
3360 
3361 		break;
3362 
3363 	// Done burning - flapping wings 3 times
3364 	case 4:
3365 		e->animDelay--;
3366 
3367 		if (e->animDelay < 1) {
3368 			e->animDelay = e->animCycle;
3369 			e->animFrame++;
3370 			if (e->animFrame >= 8) {
3371 				e->animFrame = 0;
3372 				e->sequence = 0;
3373 				if (e->onScreen)
3374 					g_hdb->_sound->playSound(SND_DRAGON_FALLASLEEP);
3375 			}
3376 		}
3377 		break;
3378 
3379 	default:
3380 		break;
3381 	}
3382 }
3383 
aiDragonDraw(AIEntity * e,int mx,int my)3384 void aiDragonDraw(AIEntity *e, int mx, int my) {
3385 	switch (e->sequence) {
3386 	// sleeping
3387 	case 0:
3388 		g_hdb->_ai->_gfxDragonAsleep->drawMasked(e->x - 32 - mx, e->y - 96 - my);
3389 		break;
3390 	// flapping 3 times
3391 	case 1:
3392 		g_hdb->_ai->_gfxDragonFlap[e->animFrame & 1]->drawMasked(e->x - 32 - mx, e->y - 96 - my);
3393 		break;
3394 	// start breathing (very short)
3395 	case 2:
3396 		g_hdb->_ai->_gfxDragonBreathe[0]->drawMasked(e->x - 32 - mx, e->y - 96 - my);
3397 		break;
3398 	// breathing
3399 	case 3:
3400 		g_hdb->_ai->_gfxDragonBreathe[(e->animFrame & 1) + 1]->drawMasked(e->x - 32 - mx, e->y - 96 - my);
3401 		break;
3402 	// flapping 3 times
3403 	case 4:
3404 		g_hdb->_ai->_gfxDragonBreathe[e->animFrame & 1]->drawMasked(e->x - 32 - mx, e->y - 96 - my);
3405 		break;
3406 	default:
3407 		break;
3408 	}
3409 }
3410 
3411 } // End of Namespace
3412