1 /*
2 Copyright (C) 2009-2021 Parallel Realities
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 */
19 
20 #include "headers.h"
21 
22 #include "audio/audio.h"
23 #include "audio/music.h"
24 #include "collisions.h"
25 #include "custom_actions.h"
26 #include "enemy/rock.h"
27 #include "entity.h"
28 #include "event/script.h"
29 #include "game.h"
30 #include "geometry.h"
31 #include "graphics/animation.h"
32 #include "graphics/decoration.h"
33 #include "graphics/gib.h"
34 #include "hud.h"
35 #include "inventory.h"
36 #include "item/item.h"
37 #include "map.h"
38 #include "medal.h"
39 #include "player.h"
40 #include "projectile.h"
41 #include "system/error.h"
42 #include "system/properties.h"
43 #include "system/random.h"
44 
45 extern Entity player, playerShield, playerWeapon;
46 extern Entity *self;
47 extern Input input;
48 extern Game game;
49 
50 static void takeDamage(Entity *, int);
51 static void attackFinish(void);
52 static void resetPlayer(void);
53 static void fallout(void);
54 static void slimeFallout(void);
55 static void falloutPause(void);
56 static void resetPause(void);
57 static void dialogWait(void);
58 static void playerDie(void);
59 static void alignAnimations(Entity *);
60 static void gameOverTimeOut(void);
61 static void touch(Entity *other);
62 static void applySlime(void);
63 static void swingSword(void);
64 static void drawBow(void);
65 static void fireArrow(void);
66 static int usingBow(void);
67 static void playerWait(void);
68 static void applyIce(void);
69 static void applyWebbing(void);
70 static void lightningSwordTouch(Entity *);
71 static void lightningSwordChangeToBasic(void);
72 static void weaponTouch(Entity *);
73 static void resurrectionTimeOut(void);
74 static void resurrectionParticleWait(void);
75 static void resurrectionWait(void);
76 static void confirmWait(void);
77 static void creditsMove(void);
78 static void creditsWait(void);
79 static void shieldAttackInit(void);
80 static void shieldAttack(void);
81 static void shieldAttackMove(void);
82 static void shieldTouch(Entity *);
83 static void shieldSideAttackInit(void);
84 static void starWait(void);
85 static void applyPetrification(void);
86 static void applyAsh(void);
87 static void becomeAsh(void);
88 
loadPlayer(int x,int y,char * name)89 Entity *loadPlayer(int x, int y, char *name)
90 {
91 	loadProperties(name == NULL ? "edgar/edgar" : name, &player);
92 
93 	if (player.inUse != TRUE)
94 	{
95 		player.inUse = TRUE;
96 		player.x = x;
97 		player.y = y;
98 		player.dirX = player.dirY = 0;
99 		player.face = LEFT;
100 		player.flags = 0;
101 		player.type = PLAYER;
102 		player.frameSpeed = 1;
103 
104 		player.thinkTime = 0;
105 
106 		player.maxHealth = player.health = 5;
107 
108 		#if DEV == 1
109 			player.maxHealth = player.health = 5;
110 		#endif
111 
112 		setEntityAnimationByID(&player, 0);
113 
114 		player.draw = &drawLoopingAnimationToMap;
115 
116 		player.takeDamage = &takeDamage;
117 
118 		playerShield.parent = &player;
119 		playerWeapon.parent = &player;
120 
121 		playerShield.name[0] = '\0';
122 		playerWeapon.name[0] = '\0';
123 
124 		playerWeapon.face = playerShield.face = LEFT;
125 	}
126 
127 	else
128 	{
129 		player.flags = 0;
130 
131 		playerWeapon.inUse = FALSE;
132 		playerShield.inUse = FALSE;
133 
134 		loadInventoryItems();
135 
136 		setEntityAnimationByID(&player, 0);
137 
138 		setPlayerLocation(x, y);
139 
140 		centerMapOnEntity(&player);
141 
142 		cameraSnapToTargetEntity();
143 	}
144 
145 	playerWeapon.flags = 0;
146 	playerShield.flags = 0;
147 
148 	playerWeapon.animationCallback = NULL;
149 	playerShield.animationCallback = NULL;
150 
151 	clearCustomActions(&player);
152 
153 	player.thinkTime = 0;
154 
155 	player.alpha = 255;
156 
157 	player.element = NO_ELEMENT;
158 
159 	player.weight = 1;
160 
161 	player.originalWeight = player.weight;
162 
163 	player.fallout = &fallout;
164 
165 	player.die = &playerDie;
166 
167 	player.action = NULL;
168 
169 	player.touch = &touch;
170 
171 	centerMapOnEntity(&player);
172 
173 	player.creditsAction = creditsMove;
174 
175 	player.maxThinkTime = 0;
176 
177 	player.standingOn = NULL;
178 
179 	setCheckpoint(player.x, player.y);
180 
181 	setSlimeTimerValue(-1);
182 
183 	return &player;
184 }
185 
setPlayerLocation(int x,int y)186 void setPlayerLocation(int x, int y)
187 {
188 	player.x = x;
189 	player.y = y;
190 
191 	player.draw = &drawLoopingAnimationToMap;
192 
193 	player.inUse = TRUE;
194 }
195 
setPlayerWeaponName(char * name)196 void setPlayerWeaponName(char *name)
197 {
198 	STRNCPY(playerWeapon.name, name, sizeof(playerWeapon.name));
199 
200 	playerWeapon.inUse = TRUE;
201 }
202 
setPlayerShieldName(char * name)203 void setPlayerShieldName(char *name)
204 {
205 	STRNCPY(playerShield.name, name, sizeof(playerShield.name));
206 
207 	playerShield.inUse = TRUE;
208 }
209 
doPlayer()210 void doPlayer()
211 {
212 	int i, j;
213 	int left, right, up, down, attack, block;
214 	long travelled;
215 
216 	travelled = game.distanceTravelled;
217 
218 	self = &player;
219 
220 	if (!(self->flags & TELEPORTING))
221 	{
222 		/* Gravity always pulls the player down */
223 
224 		if (!(self->flags & FLY))
225 		{
226 			switch (self->environment)
227 			{
228 				case WATER:
229 				case SLIME:
230 				case LAVA:
231 					self->dirY += GRAVITY_SPEED * 0.25 * self->weight;
232 
233 					if (self->dirY < -2)
234 					{
235 						self->dirY = -2;
236 					}
237 
238 					else if (self->dirY >= MAX_WATER_SPEED)
239 					{
240 						self->dirY = MAX_WATER_SPEED;
241 					}
242 				break;
243 
244 				default:
245 					self->dirY += GRAVITY_SPEED * self->weight;
246 
247 					if (self->weight < 0 && self->dirY < -2)
248 					{
249 						self->dirY = -2;
250 					}
251 
252 					else if (self->dirY >= MAX_AIR_SPEED)
253 					{
254 						self->dirY = MAX_AIR_SPEED;
255 					}
256 
257 					else if (self->dirY > 0 && self->dirY < 1)
258 					{
259 						self->dirY = 1;
260 					}
261 				break;
262 			}
263 		}
264 
265 		else
266 		{
267 			self->dirY = 0;
268 		}
269 
270 		playerWeapon.frameSpeed = self->frameSpeed;
271 		playerShield.frameSpeed = self->frameSpeed;
272 
273 		if (self->action == NULL)
274 		{
275 			self->flags &= ~(HELPLESS|INVULNERABLE|FLASH|ATTRACTED|CONFUSED);
276 
277 			for (i=0;i<MAX_CUSTOM_ACTIONS;i++)
278 			{
279 				if (self->customAction[i].thinkTime > 0)
280 				{
281 					doCustomAction(&self->customAction[i]);
282 				}
283 			}
284 
285 			if (!(self->flags & HELPLESS))
286 			{
287 				/* Don't reset the speed if being pulled towards target or on ice */
288 
289 				if (!(self->flags & ATTRACTED) && !(self->flags & FRICTIONLESS))
290 				{
291 					self->dirX = 0;
292 				}
293 
294 				/* If confused, then swap around all the controls */
295 
296 				left = right = 0;
297 				up = down = 0;
298 				attack = block = 0;
299 
300 				if (self->flags & CONFUSED)
301 				{
302 					if (input.left == 1)
303 					{
304 						right = 1;
305 					}
306 
307 					else if (input.right == 1)
308 					{
309 						left = 1;
310 					}
311 
312 					if (input.up == 1)
313 					{
314 						down = 1;
315 					}
316 
317 					else if (input.down == 1)
318 					{
319 						up = 1;
320 					}
321 
322 					if (input.attack == 1)
323 					{
324 						block = 1;
325 					}
326 
327 					if (input.block == 1)
328 					{
329 						attack = 1;
330 					}
331 				}
332 
333 				else
334 				{
335 					left = input.left;
336 					right = input.right;
337 					up = input.up;
338 					down = input.down;
339 					attack = input.attack;
340 					block = input.block;
341 				}
342 
343 				if (!(self->flags & ON_GROUND) || (self->standingOn != NULL && self->standingOn->dirY != 0))
344 				{
345 					self->flags &= ~GRABBING;
346 
347 					if (self->target != NULL)
348 					{
349 						self->target->flags &= ~GRABBED;
350 
351 						self->target = NULL;
352 					}
353 				}
354 
355 				if (self->standingOn != NULL)
356 				{
357 					if (!(self->flags & ATTRACTED))
358 					{
359 						self->dirX = self->standingOn->dirX;
360 					}
361 
362 					if (self->standingOn->dirY > 0)
363 					{
364 						self->dirY = self->standingOn->dirY + 1;
365 					}
366 
367 					self->flags |= ON_GROUND;
368 				}
369 
370 				if (left == 1)
371 				{
372 					if (self->flags & BLOCKING)
373 					{
374 						playerWeapon.face = playerShield.face = self->face = LEFT;
375 					}
376 
377 					else
378 					{
379 						if ((playerWeapon.flags & ATTACKING) && !(self->flags & ON_GROUND))
380 						{
381 							if (self->flags & FRICTIONLESS)
382 							{
383 								self->dirX -= self->speed * 0.01;
384 
385 								if (self->dirX < -self->speed)
386 								{
387 									self->dirX = -self->speed;
388 								}
389 							}
390 
391 							else
392 							{
393 								self->dirX -= self->speed;
394 							}
395 						}
396 
397 						else if (!(playerWeapon.flags & ATTACKING))
398 						{
399 							if (self->flags & FRICTIONLESS)
400 							{
401 								self->dirX -= self->speed * 0.01;
402 
403 								if (self->dirX < -self->speed)
404 								{
405 									self->dirX = -self->speed;
406 								}
407 							}
408 
409 							else
410 							{
411 								self->dirX -= self->speed;
412 							}
413 
414 							/* Only pull the target */
415 
416 							if ((self->flags & GRABBING) && self->target != NULL)
417 							{
418 								if (self->target->x > self->x)
419 								{
420 									self->target->dirX = self->dirX;
421 
422 									self->target->frameSpeed = -1;
423 								}
424 
425 								else
426 								{
427 									self->target->dirX = 0;
428 
429 									self->target->frameSpeed = 0;
430 								}
431 							}
432 
433 							else
434 							{
435 								playerWeapon.face = playerShield.face = self->face = LEFT;
436 							}
437 
438 							setEntityAnimation(self, "WALK");
439 							setEntityAnimation(&playerShield, "WALK");
440 							setEntityAnimation(&playerWeapon, "WALK");
441 						}
442 					}
443 				}
444 
445 				else if (right == 1)
446 				{
447 					if (self->flags & BLOCKING)
448 					{
449 						playerWeapon.face = playerShield.face = self->face = RIGHT;
450 					}
451 
452 					else
453 					{
454 						if ((playerWeapon.flags & ATTACKING) && !(self->flags & ON_GROUND))
455 						{
456 							if (self->flags & FRICTIONLESS)
457 							{
458 								self->dirX += self->speed * 0.01;
459 
460 								if (self->dirX > self->speed)
461 								{
462 									self->dirX = self->speed;
463 								}
464 							}
465 
466 							else
467 							{
468 								self->dirX += self->speed;
469 							}
470 						}
471 
472 						else if (!(playerWeapon.flags & ATTACKING))
473 						{
474 							if (self->flags & FRICTIONLESS)
475 							{
476 								self->dirX += self->speed * 0.01;
477 
478 								if (self->dirX > self->speed)
479 								{
480 									self->dirX = self->speed;
481 								}
482 							}
483 
484 							else
485 							{
486 								self->dirX += self->speed;
487 							}
488 
489 							/* Only pull the target */
490 
491 							if ((self->flags & GRABBING) && self->target != NULL)
492 							{
493 								if (self->target->x < self->x)
494 								{
495 									self->target->dirX = self->dirX;
496 
497 									self->target->frameSpeed = 1;
498 								}
499 
500 								else
501 								{
502 									self->target->dirX = 0;
503 
504 									self->target->frameSpeed = 0;
505 								}
506 							}
507 
508 							else
509 							{
510 								playerWeapon.face = playerShield.face = self->face = RIGHT;
511 							}
512 
513 							setEntityAnimation(self, "WALK");
514 							setEntityAnimation(&playerShield, "WALK");
515 							setEntityAnimation(&playerWeapon, "WALK");
516 						}
517 					}
518 				}
519 
520 				else if (left == 0 && right == 0 && !(self->flags & BLOCKING) && !(playerWeapon.flags & ATTACKING))
521 				{
522 					setEntityAnimation(self, "STAND");
523 					setEntityAnimation(&playerShield, "STAND");
524 					setEntityAnimation(&playerWeapon, "STAND");
525 
526 					if ((self->flags & GRABBING) && self->target != NULL)
527 					{
528 						self->target->dirX = 0;
529 
530 						self->target->frameSpeed = 0;
531 					}
532 				}
533 
534 				if (up == 1)
535 				{
536 					if (self->element == WATER && self->environment == WATER)
537 					{
538 						self->dirY = -self->speed;
539 					}
540 
541 					else if (self->flags & FLY)
542 					{
543 						self->dirY = -self->speed;
544 					}
545 
546 					else
547 					{
548 						if (self->standingOn != NULL)
549 						{
550 							if (self->standingOn->activate != NULL)
551 							{
552 								self = self->standingOn;
553 
554 								self->activate(1);
555 
556 								self = &player;
557 							}
558 						}
559 
560 						input.up = 0;
561 						input.down = 0;
562 					}
563 				}
564 
565 				if (down == 1)
566 				{
567 					if (self->environment == WATER || (self->flags & FLY))
568 					{
569 						self->dirY = self->speed;
570 					}
571 
572 					else
573 					{
574 						if (self->standingOn != NULL)
575 						{
576 							if (self->standingOn->activate != NULL)
577 							{
578 								self = self->standingOn;
579 
580 								self->activate(-1);
581 
582 								self = &player;
583 
584 								self->dirY = self->standingOn->speed;
585 							}
586 
587 							input.down = 0;
588 							input.up = 0;
589 						}
590 					}
591 				}
592 
593 				if (attack == 1 && !(self->flags & GRABBED))
594 				{
595 					if (!(self->flags & BLOCKING))
596 					{
597 						if (playerWeapon.inUse == TRUE && !(playerWeapon.flags & ATTACKING))
598 						{
599 							playerWeapon.flags |= ATTACKING;
600 
601 							playerWeapon.action();
602 						}
603 					}
604 
605 					else
606 					{
607 						if (playerShield.inUse == TRUE && playerShield.health >= 4 && strcmpignorecase(playerShield.objectiveName, "Disintegration Shield") == 0)
608 						{
609 							setEntityAnimation(self, "STAND");
610 							setEntityAnimation(&playerShield, "STAND");
611 							setEntityAnimation(&playerWeapon, "STAND");
612 
613 							playerShield.action = &shieldAttack;
614 
615 							playerShield.action();
616 						}
617 					}
618 
619 					if (self->flags & CONFUSED)
620 					{
621 						input.block = 0;
622 					}
623 
624 					else
625 					{
626 						input.attack = 0;
627 					}
628 				}
629 
630 				if (input.interact == 1)
631 				{
632 					interactWithEntity(self->x, self->y, self->w, self->h);
633 
634 					input.interact = 0;
635 				}
636 
637 				if (input.grabbing == 1 && !(self->flags & BLOCKING))
638 				{
639 					self->flags |= GRABBING;
640 				}
641 
642 				else
643 				{
644 					self->flags &= ~GRABBING;
645 
646 					if (self->target != NULL)
647 					{
648 						self->target->flags &= ~GRABBED;
649 
650 						self->target->frameSpeed = 0;
651 
652 						self->target = NULL;
653 					}
654 				}
655 
656 				if (block == 1 && (self->flags & ON_GROUND) && playerShield.inUse == TRUE && !(playerWeapon.flags & ATTACKING) && !(self->flags & GRABBED))
657 				{
658 					self->flags |= BLOCKING;
659 
660 					setEntityAnimation(&playerWeapon, "STAND");
661 					setEntityAnimation(self, "BLOCK");
662 					setEntityAnimation(&playerShield, "BLOCK");
663 
664 					playerShield.mental++;
665 				}
666 
667 				else if ((block == 0 && (self->flags & BLOCKING)))
668 				{
669 					self->flags &= ~BLOCKING;
670 
671 					setEntityAnimation(self, "STAND");
672 					setEntityAnimation(&playerWeapon, "STAND");
673 					setEntityAnimation(&playerShield, "STAND");
674 
675 					playerShield.mental = 0;
676 				}
677 
678 				if (input.activate == 1)
679 				{
680 					useInventoryItem();
681 
682 					input.activate = 0;
683 				}
684 
685 				if (input.next == 1 || input.previous == 1)
686 				{
687 					nextInventoryItem(input.next == 1 ? 1 : -1);
688 
689 					input.next = input.previous = 0;
690 				}
691 
692 				if (input.jump == 1 && !(self->flags & (BLOCKING|GRABBED)))
693 				{
694 					if (self->element == WATER && self->environment == WATER)
695 					{
696 						self->dirY = -self->speed;
697 					}
698 
699 					else if (self->flags & ON_GROUND)
700 					{
701 						if (self->element == WATER)
702 						{
703 							if (prand() % 3 == 0)
704 							{
705 								playSoundToMap("sound/enemy/jumping_slime/jump2", EDGAR_CHANNEL, self->x, self->y, 0);
706 							}
707 
708 							else
709 							{
710 								playSoundToMap("sound/enemy/jumping_slime/jump1", EDGAR_CHANNEL, self->x, self->y, 0);
711 							}
712 						}
713 
714 						if (!(player.flags & GROUNDED))
715 						{
716 							self->dirY = -JUMP_HEIGHT;
717 						}
718 					}
719 
720 					if (!(self->element == WATER && self->environment == WATER))
721 					{
722 						input.jump = 0;
723 					}
724 				}
725 			}
726 
727 			else
728 			{
729 				self->flags &= ~GRABBING;
730 
731 				if (self->target != NULL)
732 				{
733 					self->target->flags &= ~GRABBED;
734 
735 					self->target = NULL;
736 				}
737 
738 				if (self->standingOn != NULL)
739 				{
740 					if (self->standingOn->dirY > 0)
741 					{
742 						self->dirY = self->standingOn->dirY + 1;
743 					}
744 				}
745 			}
746 
747 			i = self->environment;
748 
749 			if (!(self->flags & TELEPORTING))
750 			{
751 				checkToMap(self);
752 
753 				if (self->flags & ON_GROUND)
754 				{
755 					self->flags &= ~FRICTIONLESS;
756 				}
757 
758 				if (self->environment == AIR && i == WATER && self->dirY < 0)
759 				{
760 					self->dirY = -JUMP_HEIGHT;
761 				}
762 			}
763 
764 			self->standingOn = NULL;
765 
766 			i = mapTileAt(self->x / TILE_SIZE, (self->y + self->h + 5) / TILE_SIZE);
767 			j = mapTileAt((self->x + self->w - 1) / TILE_SIZE, (self->y + self->h + 5) / TILE_SIZE);
768 
769 			if ((self->flags & ON_GROUND) && i > BLANK_TILE && i < BACKGROUND_TILE_START && j > BLANK_TILE
770 				&& j < BACKGROUND_TILE_START && self->environment != WATER)
771 			{
772 				setCheckpoint(self->x, self->y);
773 			}
774 		}
775 
776 		else
777 		{
778 			self->action();
779 		}
780 
781 		addToGrid(self);
782 
783 		if (playerWeapon.flags & ATTACKING)
784 		{
785 			addToGrid(&playerWeapon);
786 		}
787 
788 		playerWeapon.alpha = player.alpha;
789 		playerShield.alpha = player.alpha;
790 
791 		playerShield.thinkTime--;
792 
793 		if (playerShield.thinkTime <= 0)
794 		{
795 			playerShield.thinkTime = 0;
796 		}
797 
798 		playerWeapon.thinkTime--;
799 
800 		if (playerWeapon.thinkTime <= 0)
801 		{
802 			playerWeapon.thinkTime = 0;
803 		}
804 
805 		game.distanceTravelled += fabs(self->dirX);
806 
807 		if (travelled < 2250000 && game.distanceTravelled >= 2250000)
808 		{
809 			addMedal("50km");
810 		}
811 	}
812 
813 	else
814 	{
815 		doTeleport();
816 	}
817 }
818 
playerWaitForDialog()819 void playerWaitForDialog()
820 {
821 	player.animationCallback = NULL;
822 	playerShield.animationCallback = NULL;
823 	playerWeapon.animationCallback = NULL;
824 
825 	playerWeapon.flags &= ~(ATTACKING|ATTACK_SUCCESS);
826 
827 	setEntityAnimation(&player, "STAND");
828 	setEntityAnimation(&playerShield, "STAND");
829 	setEntityAnimation(&playerWeapon, "STAND");
830 
831 	if (playerWeapon.mental == -1)
832 	{
833 		lightningSwordChangeToBasic();
834 	}
835 
836 	if (player.target != NULL)
837 	{
838 		player.face = player.x < player.target->x ? RIGHT : LEFT;
839 
840 		playerWeapon.face = player.face;
841 		playerShield.face = player.face;
842 	}
843 
844 	player.dirX = 0;
845 
846 	player.flags |= INVULNERABLE;
847 
848 	if (player.maxThinkTime != 1)
849 	{
850 		player.action = &dialogWait;
851 	}
852 }
853 
playerResumeNormal()854 void playerResumeNormal()
855 {
856 	if (player.health != 0)
857 	{
858 		player.target = NULL;
859 
860 		if (player.maxThinkTime != 1)
861 		{
862 			player.action = NULL;
863 		}
864 
865 		player.flags &= ~INVULNERABLE;
866 	}
867 }
868 
dialogWait()869 static void dialogWait()
870 {
871 	if (scriptWaiting() == TRUE)
872 	{
873 		readNextScriptLine();
874 
875 		input.interact = 0;
876 		input.jump = 0;
877 		input.attack = 0;
878 		input.activate = 0;
879 		input.block = 0;
880 	}
881 
882 	else if (input.interact == 1 || input.jump == 1 || input.attack == 1 || input.activate == 1 || input.block == 1)
883 	{
884 		readNextScriptLine();
885 
886 		input.interact = 0;
887 		input.jump = 0;
888 		input.attack = 0;
889 		input.activate = 0;
890 		input.block = 0;
891 	}
892 
893 	checkToMap(&player);
894 
895 	if (player.target != NULL)
896 	{
897 		player.face = player.x < player.target->x ? RIGHT : LEFT;
898 
899 		playerShield.face = player.face;
900 		playerWeapon.face = player.face;
901 	}
902 
903 	if (self->standingOn != NULL)
904 	{
905 		self->dirX = self->standingOn->dirX;
906 
907 		if (self->standingOn->dirY > 0)
908 		{
909 			self->dirY = self->standingOn->dirY + 1;
910 		}
911 
912 		self->flags |= ON_GROUND;
913 	}
914 
915 	if (self->flags & ON_GROUND)
916 	{
917 		self->dirX = 0;
918 	}
919 }
920 
playerWaitForConfirm()921 void playerWaitForConfirm()
922 {
923 	player.action = &confirmWait;
924 }
925 
confirmWait()926 static void confirmWait()
927 {
928 	doScriptMenu();
929 
930 	checkToMap(&player);
931 
932 	if (player.target != NULL)
933 	{
934 		player.face = player.x < player.target->x ? RIGHT : LEFT;
935 
936 		playerShield.face = player.face;
937 		playerWeapon.face = player.face;
938 	}
939 }
940 
attackFinish()941 static void attackFinish()
942 {
943 	player.animationCallback = NULL;
944 	playerShield.animationCallback = NULL;
945 	playerWeapon.animationCallback = NULL;
946 
947 	playerWeapon.flags &= ~(ATTACKING|ATTACK_SUCCESS);
948 
949 	setEntityAnimation(&player, "STAND");
950 	setEntityAnimation(&playerShield, "STAND");
951 	setEntityAnimation(&playerWeapon, "STAND");
952 
953 	if (playerWeapon.mental == -1)
954 	{
955 		lightningSwordChangeToBasic();
956 	}
957 
958 	playerWeapon.x = playerShield.x = player.x;
959 	playerWeapon.y = playerShield.y = player.y;
960 }
961 
drawPlayer()962 void drawPlayer()
963 {
964 	int hasBow = usingBow();
965 
966 	if (!(player.flags & NO_DRAW))
967 	{
968 		/* Draw the weapon if it's not a firing bow */
969 
970 		self = &playerWeapon;
971 
972 		if ((self->inUse == TRUE && hasBow == FALSE) || (hasBow == TRUE && !(self->flags & ATTACKING)))
973 		{
974 			if (!(player.flags & BLOCKING))
975 			{
976 				if (self->inUse == TRUE)
977 				{
978 					self->x = player.x;
979 					self->y = player.y;
980 
981 					self->draw();
982 				}
983 			}
984 		}
985 
986 		/* Draw the player */
987 
988 		self = &player;
989 
990 		self->draw();
991 
992 		self = &playerWeapon;
993 
994 		/* Draw the bow on top if it's being fired */
995 
996 		if (hasBow == TRUE && (self->flags & ATTACKING))
997 		{
998 			self = &playerWeapon;
999 
1000 			self->x = player.x;
1001 			self->y = player.y;
1002 
1003 			self->draw();
1004 		}
1005 
1006 		else
1007 		{
1008 			/* Draw the shield */
1009 
1010 			self = &playerShield;
1011 
1012 			if (self->inUse == TRUE)
1013 			{
1014 				self->x = player.x;
1015 				self->y = player.y;
1016 
1017 				self->draw();
1018 			}
1019 		}
1020 	}
1021 
1022 	self = &player;
1023 
1024 	if (self->health <= 0 && self->thinkTime == 0)
1025 	{
1026 		initGameOver();
1027 	}
1028 }
1029 
setPlayerShield(int val)1030 void setPlayerShield(int val)
1031 {
1032 	if (player.element == WATER)
1033 	{
1034 		setInventoryDialogMessage(_("Cannot equip items whilst transmogrified"));
1035 
1036 		return;
1037 	}
1038 
1039 	/* Don't allow the player to change shields whilst attacking */
1040 
1041 	if (playerWeapon.flags & ATTACKING)
1042 	{
1043 		if (game.status == IN_INVENTORY)
1044 		{
1045 			setInventoryDialogMessage(_("Cannot equip items whilst attacking"));
1046 		}
1047 
1048 		else
1049 		{
1050 			setInfoBoxMessage(120, 255, 255, 255, _("Cannot equip items whilst attacking"));
1051 		}
1052 
1053 		return;
1054 	}
1055 
1056 	if (usingBow() == TRUE)
1057 	{
1058 		playerWeapon.animationCallback = NULL;
1059 
1060 		setEntityAnimation(&player, "STAND");
1061 
1062 		/* Unequip the bow */
1063 
1064 		playerWeapon.inUse = FALSE;
1065 	}
1066 
1067 	playerShield = *self;
1068 
1069 	alignAnimations(&playerShield);
1070 
1071 	if (game.status == IN_INVENTORY)
1072 	{
1073 		setInventoryDialogMessage(_("Equipped %s"), _(playerShield.objectiveName));
1074 	}
1075 }
1076 
setPlayerWeapon(int val)1077 void setPlayerWeapon(int val)
1078 {
1079 	if (player.element == WATER)
1080 	{
1081 		setInventoryDialogMessage(_("Cannot equip items whilst transmogrified"));
1082 
1083 		return;
1084 	}
1085 
1086 	/* Don't allow the player to change weapons whilst attacking */
1087 
1088 	if (playerWeapon.flags & ATTACKING)
1089 	{
1090 		if (game.status == IN_INVENTORY)
1091 		{
1092 			setInventoryDialogMessage(_("Cannot equip items whilst attacking"));
1093 		}
1094 
1095 		else
1096 		{
1097 			setInfoBoxMessage(120, 255, 255, 255, _("Cannot equip items whilst attacking"));
1098 		}
1099 
1100 		return;
1101 	}
1102 
1103 	playerWeapon.animationCallback = NULL;
1104 
1105 	playerWeapon = *self;
1106 
1107 	playerWeapon.head = self;
1108 
1109 	playerWeapon.touch = &weaponTouch;
1110 
1111 	alignAnimations(&playerWeapon);
1112 
1113 	/* Unset the shield */
1114 
1115 	if (strcmpignorecase(playerWeapon.name, "weapon/bow") == 0)
1116 	{
1117 		playerWeapon.action = &drawBow;
1118 
1119 		playerShield.name[0] = '\0';
1120 
1121 		playerShield.inUse = FALSE;
1122 	}
1123 
1124 	else if (strcmpignorecase(playerWeapon.name, "weapon/lightning_sword") == 0)
1125 	{
1126 		playerWeapon.action = &swingSword;
1127 
1128 		if (playerWeapon.mental > 0)
1129 		{
1130 			playerWeapon.touch = &lightningSwordTouch;
1131 		}
1132 	}
1133 
1134 	else
1135 	{
1136 		playerWeapon.action = &swingSword;
1137 	}
1138 
1139 	if (game.status == IN_INVENTORY)
1140 	{
1141 		setInventoryDialogMessage(_("Equipped %s"), _(playerWeapon.objectiveName));
1142 	}
1143 }
1144 
autoSetPlayerWeapon(Entity * newWeapon)1145 void autoSetPlayerWeapon(Entity *newWeapon)
1146 {
1147 	if (player.element == WATER)
1148 	{
1149 		return;
1150 	}
1151 
1152 	if (strcmpignorecase(newWeapon->name, "weapon/normal_arrow") == 0)
1153 	{
1154 		return;
1155 	}
1156 
1157 	if (playerWeapon.inUse == FALSE)
1158 	{
1159 		/* Don't auto set the bow if the shield is in use */
1160 
1161 		if (strcmpignorecase(newWeapon->name, "weapon/bow") == 0 && playerShield.inUse == TRUE)
1162 		{
1163 			return;
1164 		}
1165 
1166 		playerWeapon = *newWeapon;
1167 
1168 		playerWeapon.head = newWeapon;
1169 
1170 		if (strcmpignorecase(playerWeapon.name, "weapon/bow") == 0)
1171 		{
1172 			playerWeapon.action = &drawBow;
1173 
1174 			playerShield.inUse = FALSE;
1175 		}
1176 
1177 		else if (strcmpignorecase(playerWeapon.name, "weapon/lightning_sword") == 0)
1178 		{
1179 			playerWeapon.action = &swingSword;
1180 
1181 			if (playerWeapon.mental > 0)
1182 			{
1183 				playerWeapon.touch = &lightningSwordTouch;
1184 			}
1185 		}
1186 
1187 		else
1188 		{
1189 			playerWeapon.action = &swingSword;
1190 		}
1191 
1192 		alignAnimations(&playerWeapon);
1193 	}
1194 }
1195 
autoSetPlayerShield(Entity * newWeapon)1196 void autoSetPlayerShield(Entity *newWeapon)
1197 {
1198 	if (player.element == WATER || usingBow() == TRUE)
1199 	{
1200 		return;
1201 	}
1202 
1203 	if (playerShield.inUse == FALSE)
1204 	{
1205 		playerShield = *newWeapon;
1206 
1207 		alignAnimations(&playerShield);
1208 	}
1209 }
1210 
alignAnimations(Entity * e)1211 static void alignAnimations(Entity *e)
1212 {
1213 	e->parent = &player;
1214 
1215 	e->face = player.face;
1216 
1217 	e->inUse = TRUE;
1218 
1219 	setEntityAnimation(e, getAnimationTypeAtIndex(&player));
1220 
1221 	e->currentFrame = player.currentFrame;
1222 
1223 	e->frameTimer = player.frameTimer;
1224 }
1225 
takeDamage(Entity * other,int damage)1226 static void takeDamage(Entity *other, int damage)
1227 {
1228 	int shieldHealth;
1229 	Entity *temp;
1230 	char shieldName[MAX_VALUE_LENGTH], oldShieldName[MAX_VALUE_LENGTH];
1231 
1232 	if (player.health <= 0 || other->parent == &playerWeapon)
1233 	{
1234 		return;
1235 	}
1236 
1237 	if ((player.flags & BLOCKING) && !(other->flags & UNBLOCKABLE) && (other->element == NO_ELEMENT || (playerShield.element == other->element)))
1238 	{
1239 		if (other->type == PROJECTILE)
1240 		{
1241 			if ((other->dirX > 0 && player.face == LEFT) || (other->dirX < 0 && player.face == RIGHT))
1242 			{
1243 				player.dirX = other->dirX < 0 ? -2 : 2;
1244 
1245 				other->x = other->dirX > 0 ? player.x - other->w : player.x + player.w;
1246 
1247 				setCustomAction(&player, &helpless, 2, 0, 0);
1248 
1249 				if (playerShield.thinkTime <= 0)
1250 				{
1251 					if (other->element != DRAGON_FIRE)
1252 					{
1253 						playSoundToMap("sound/edgar/block", EDGAR_CHANNEL, player.x, player.y, 0);
1254 					}
1255 
1256 					playerShield.thinkTime = 5;
1257 				}
1258 
1259 				game.attacksBlocked++;
1260 
1261 				if (game.attacksBlocked == 2000)
1262 				{
1263 					addMedal("blocked");
1264 				}
1265 
1266 				if (other->reactToBlock != NULL)
1267 				{
1268 					temp = self;
1269 
1270 					self = other;
1271 
1272 					self->reactToBlock(&playerShield);
1273 
1274 					self = temp;
1275 
1276 					return;
1277 				}
1278 
1279 				temp = self;
1280 
1281 				self = other;
1282 
1283 				self->die();
1284 
1285 				self = temp;
1286 
1287 				return;
1288 			}
1289 		}
1290 
1291 		else if ((other->dirX > 0 && player.face == LEFT) || (other->dirX < 0 && player.face == RIGHT)
1292 			|| ((other->flags & ATTACKING) && ((other->x < player.x && player.face == LEFT) || (other->x > player.x && player.face == RIGHT))))
1293 		{
1294 			if (other->dirX != 0)
1295 			{
1296 				player.dirX = other->dirX < 0 ? -2 : 2;
1297 
1298 				other->x = other->dirX > 0 ? player.x - other->w : player.x + player.w;
1299 			}
1300 
1301 			else
1302 			{
1303 				player.dirX = player.x < other->x ? -6 : 6;
1304 
1305 				other->x = other->x < player.x ? player.x - other->w : player.x + player.w;
1306 			}
1307 
1308 			setCustomAction(&player, &helpless, 2, 0, 0);
1309 
1310 			if (other->reactToBlock == NULL)
1311 			{
1312 				other->dirX = 0;
1313 			}
1314 
1315 			else
1316 			{
1317 				temp = self;
1318 
1319 				self = other;
1320 
1321 				self->reactToBlock(&playerShield);
1322 
1323 				self = temp;
1324 			}
1325 
1326 			if (playerShield.thinkTime <= 0)
1327 			{
1328 				playSoundToMap("sound/edgar/block", EDGAR_CHANNEL, player.x, player.y, 0);
1329 
1330 				playerShield.thinkTime = 5;
1331 
1332 				if (strcmpignorecase(other->objectiveName, "SORCEROR_DISINTEGRATION_SPELL") == 0)
1333 				{
1334 					if (strcmpignorecase(playerShield.objectiveName, "Disintegration Shield") == 0)
1335 					{
1336 						playerShield.health++;
1337 
1338 						if (playerShield.health >= 4)
1339 						{
1340 							setInfoBoxMessage(3600, 255, 255, 255, _("Press Attack whilst Blocking to use your shield..."));
1341 
1342 							playerShield.health = 4;
1343 						}
1344 
1345 						else
1346 						{
1347 							setInfoBoxMessage(180, 255, 255, 255, _("Your shield is increasing in power..."));
1348 						}
1349 
1350 						shieldHealth = playerShield.health;
1351 
1352 						STRNCPY(oldShieldName, playerShield.name, sizeof(oldShieldName));
1353 
1354 						SNPRINTF(shieldName, MAX_VALUE_LENGTH, "weapon/disintegration_shield_%d", shieldHealth);
1355 
1356 						loadProperties(shieldName, &playerShield);
1357 
1358 						replaceInventoryItemWithName(oldShieldName, &playerShield);
1359 
1360 						playerShield.thinkTime = 5;
1361 
1362 						playerShield.health = shieldHealth;
1363 
1364 						alignAnimations(&playerShield);
1365 					}
1366 				}
1367 			}
1368 
1369 			game.attacksBlocked++;
1370 
1371 			if (game.attacksBlocked == 2000)
1372 			{
1373 				addMedal("blocked");
1374 			}
1375 
1376 			return;
1377 		}
1378 	}
1379 
1380 	if (!(player.flags & INVULNERABLE))
1381 	{
1382 		if (game.infiniteEnergy == FALSE)
1383 		{
1384 			player.health -= damage;
1385 		}
1386 
1387 		player.animationCallback = NULL;
1388 		playerShield.animationCallback = NULL;
1389 		playerWeapon.animationCallback = NULL;
1390 
1391 		playerWeapon.flags &= ~(ATTACKING|ATTACK_SUCCESS);
1392 
1393 		if (strcmpignorecase(player.animationName, "DIE") != 0)
1394 		{
1395 			setEntityAnimation(&player, "STAND");
1396 			setEntityAnimation(&playerShield, "STAND");
1397 			setEntityAnimation(&playerWeapon, "STAND");
1398 		}
1399 
1400 		if (playerWeapon.mental == -1)
1401 		{
1402 			lightningSwordChangeToBasic();
1403 		}
1404 
1405 		if (other->type == PROJECTILE)
1406 		{
1407 			temp = self;
1408 
1409 			self = other;
1410 
1411 			self->die();
1412 
1413 			self = temp;
1414 		}
1415 
1416 		if (player.health > 0)
1417 		{
1418 			if (self->action != NULL)
1419 			{
1420 				self->action = NULL;
1421 
1422 				if (self->target != NULL)
1423 				{
1424 					self->target = NULL;
1425 				}
1426 			}
1427 
1428 			setCustomAction(&player, &helpless, 10, 0, 0);
1429 
1430 			setCustomAction(&player, &invulnerable, 60, 0, 0);
1431 
1432 			if (player.dirX == 0)
1433 			{
1434 				if (other->dirX == 0)
1435 				{
1436 					player.dirX = other->x > player.x ? -6 : 6;
1437 				}
1438 
1439 				else
1440 				{
1441 					player.dirX = other->dirX < 0 ? -6 : 6;
1442 				}
1443 			}
1444 
1445 			else
1446 			{
1447 				player.dirX = player.dirX < 0 ? 6 : -6;
1448 			}
1449 		}
1450 
1451 		else
1452 		{
1453 			player.die();
1454 		}
1455 	}
1456 }
1457 
getDistanceFromPlayer(Entity * e)1458 int getDistanceFromPlayer(Entity *e)
1459 {
1460 	return getDistance(player.x, player.y, e->x, e->y);
1461 }
1462 
writePlayerMapStartToFile(FILE * fp)1463 void writePlayerMapStartToFile(FILE *fp)
1464 {
1465 	self = &player;
1466 
1467 	fprintf(fp, "{\n");
1468 	fprintf(fp, "TYPE PLAYER\n");
1469 	fprintf(fp, "NAME PLAYER\n");
1470 	fprintf(fp, "START_X %d\n", (int)self->x);
1471 	fprintf(fp, "START_Y %d\n", (int)self->y);
1472 	fprintf(fp, "}\n\n");
1473 }
1474 
writePlayerToFile(FILE * fp)1475 void writePlayerToFile(FILE *fp)
1476 {
1477 	self = &player;
1478 
1479 	fprintf(fp, "{\n");
1480 	fprintf(fp, "TYPE PLAYER\n");
1481 	fprintf(fp, "NAME PLAYER\n");
1482 	fprintf(fp, "START_X %d\n", (int)self->x);
1483 	fprintf(fp, "START_Y %d\n", (int)self->y);
1484 	fprintf(fp, "END_X %d\n", (int)self->endX);
1485 	fprintf(fp, "END_Y %d\n", (int)self->endY);
1486 	fprintf(fp, "THINKTIME %d\n", self->thinkTime);
1487 	fprintf(fp, "HEALTH %d\n", self->health);
1488 	fprintf(fp, "MENTAL %d\n", self->mental);
1489 	fprintf(fp, "MAX_HEALTH %d\n", self->maxHealth);
1490 	fprintf(fp, "DAMAGE %d\n", self->damage);
1491 	/*fprintf(fp, "SPEED %0.2f\n", self->speed); Don't write the speed */
1492 	/*fprintf(fp, "WEIGHT %0.2f\n", self->weight); Don't write the weight */
1493 	fprintf(fp, "OBJECTIVE_NAME %s\n", self->objectiveName);
1494 	fprintf(fp, "REQUIRES %s\n", self->requires);
1495 	fprintf(fp, "ACTIVE %s\n", self->active == TRUE ? "TRUE" : "FALSE");
1496 	fprintf(fp, "FACE %s\n", self->face == RIGHT ? "RIGHT" : "LEFT");
1497 	fprintf(fp, "}\n\n");
1498 
1499 	self = &playerWeapon;
1500 
1501 	if (self->inUse == TRUE)
1502 	{
1503 		fprintf(fp, "{\n");
1504 		fprintf(fp, "TYPE PLAYER_WEAPON\n");
1505 		fprintf(fp, "NAME %s\n", self->name);
1506 		fprintf(fp, "START_X %d\n", (int)self->x);
1507 		fprintf(fp, "START_Y %d\n", (int)self->y);
1508 		fprintf(fp, "END_X %d\n", (int)self->endX);
1509 		fprintf(fp, "END_Y %d\n", (int)self->endY);
1510 		fprintf(fp, "THINKTIME %d\n", self->thinkTime);
1511 		fprintf(fp, "MENTAL %d\n", self->mental);
1512 		fprintf(fp, "HEALTH %d\n", self->health);
1513 		fprintf(fp, "MAX_HEALTH %d\n", self->maxHealth);
1514 		fprintf(fp, "DAMAGE %d\n", self->damage);
1515 		/*fprintf(fp, "SPEED %0.2f\n", self->speed); Don't write the speed */
1516 		fprintf(fp, "WEIGHT %0.2f\n", self->weight);
1517 		fprintf(fp, "OBJECTIVE_NAME %s\n", self->objectiveName);
1518 		fprintf(fp, "REQUIRES %s\n", self->requires);
1519 		fprintf(fp, "ACTIVE %s\n", self->active == TRUE ? "TRUE" : "FALSE");
1520 		fprintf(fp, "FACE %s\n", self->face == RIGHT ? "RIGHT" : "LEFT");
1521 		fprintf(fp, "}\n\n");
1522 	}
1523 
1524 	self = &playerShield;
1525 
1526 	if (self->inUse == TRUE)
1527 	{
1528 		fprintf(fp, "{\n");
1529 		fprintf(fp, "TYPE PLAYER_SHIELD\n");
1530 		fprintf(fp, "NAME %s\n", self->name);
1531 		fprintf(fp, "START_X %d\n", (int)self->x);
1532 		fprintf(fp, "START_Y %d\n", (int)self->y);
1533 		fprintf(fp, "END_X %d\n", (int)self->endX);
1534 		fprintf(fp, "END_Y %d\n", (int)self->endY);
1535 		fprintf(fp, "THINKTIME %d\n", self->thinkTime);
1536 		fprintf(fp, "MENTAL %d\n", self->mental);
1537 		fprintf(fp, "MAX_HEALTH %d\n", self->maxHealth);
1538 		fprintf(fp, "HEALTH %d\n", self->health);
1539 		fprintf(fp, "DAMAGE %d\n", self->damage);
1540 		/*fprintf(fp, "SPEED %0.2f\n", self->speed); Don't write the speed */
1541 		fprintf(fp, "WEIGHT %0.2f\n", self->weight);
1542 		fprintf(fp, "OBJECTIVE_NAME %s\n", self->objectiveName);
1543 		fprintf(fp, "REQUIRES %s\n", self->requires);
1544 		fprintf(fp, "ACTIVE %s\n", self->active == TRUE ? "TRUE" : "FALSE");
1545 		fprintf(fp, "FACE %s\n", self->face == RIGHT ? "RIGHT" : "LEFT");
1546 		fprintf(fp, "}\n\n");
1547 	}
1548 }
1549 
slimeFallout()1550 static void slimeFallout()
1551 {
1552 	if (player.environment != WATER)
1553 	{
1554 		fallout();
1555 	}
1556 }
1557 
fallout()1558 static void fallout()
1559 {
1560 	if (!(player.flags & HELPLESS))
1561 	{
1562 		centerMapOnEntity(NULL);
1563 
1564 		player.maxThinkTime = 1;
1565 
1566 		player.thinkTime = 120;
1567 
1568 		player.dirX = 0;
1569 
1570 		player.flags |= HELPLESS|INVULNERABLE;
1571 
1572 		player.action = &falloutPause;
1573 
1574 		player.touch = NULL;
1575 
1576 		setEntityAnimation(&player, "STAND");
1577 		setEntityAnimation(&playerShield, "STAND");
1578 		setEntityAnimation(&playerWeapon, "STAND");
1579 
1580 		checkToMap(&player);
1581 	}
1582 }
1583 
falloutPause()1584 static void falloutPause()
1585 {
1586 	player.thinkTime--;
1587 
1588 	if (player.flags & NO_DRAW)
1589 	{
1590 		player.dirY = 0;
1591 
1592 		player.flags |= FLY;
1593 	}
1594 
1595 	if (player.thinkTime <= 0)
1596 	{
1597 		player.thinkTime = 60;
1598 
1599 		player.action = &resetPause;
1600 	}
1601 
1602 	checkToMap(&player);
1603 }
1604 
resetPause()1605 static void resetPause()
1606 {
1607 	int minHealth;
1608 
1609 	switch (player.environment)
1610 	{
1611 		case LAVA:
1612 			minHealth = game.lavaIsFatal == FALSE ? 2 : 99999;
1613 		break;
1614 
1615 		case SLIME:
1616 			minHealth = 2;
1617 		break;
1618 
1619 		default:
1620 			minHealth = 1;
1621 		break;
1622 	}
1623 
1624 	if ((player.health <= minHealth && getInventoryItemByObjectiveName("Amulet of Resurrection") == NULL)
1625 		|| (player.environment == LAVA && game.lavaIsFatal == TRUE))
1626 	{
1627 		player.health = 0;
1628 
1629 		if (player.environment != AIR)
1630 		{
1631 			checkToMap(&player);
1632 		}
1633 
1634 		player.die();
1635 	}
1636 
1637 	else
1638 	{
1639 		player.thinkTime--;
1640 
1641 		game.drawScreen = FALSE;
1642 
1643 		if (player.thinkTime <= 0)
1644 		{
1645 			player.action = &resetPlayer;
1646 		}
1647 
1648 		player.dirY = 0;
1649 	}
1650 }
1651 
resetPlayer()1652 static void resetPlayer()
1653 {
1654 	game.drawScreen = TRUE;
1655 
1656 	player.draw = &drawLoopingAnimationToMap;
1657 
1658 	getCheckpoint(&player.x, &player.y);
1659 
1660 	if (game.canContinue == FALSE)
1661 	{
1662 		centerMapOnEntity(&player);
1663 
1664 		cameraSnapToTargetEntity();
1665 	}
1666 
1667 	player.maxThinkTime = 0;
1668 
1669 	player.action = NULL;
1670 
1671 	player.touch = &touch;
1672 
1673 	playerWeapon.flags &= ~(ATTACKING|ATTACK_SUCCESS);
1674 
1675 	if (game.infiniteEnergy == FALSE)
1676 	{
1677 		player.health -= (player.environment == SLIME ? 2 : 1);
1678 	}
1679 
1680 	if (player.health <= 0)
1681 	{
1682 		removeInventoryItemByObjectiveName("Amulet of Resurrection");
1683 
1684 		player.health = player.maxHealth;
1685 
1686 		setInfoBoxMessage(60, 255, 255, 255, _("Used Amulet of Resurrection"));
1687 	}
1688 
1689 	player.flags &= ~(HELPLESS|NO_DRAW|FLY);
1690 
1691 	player.dirX = player.dirY = 0;
1692 
1693 	player.y--;
1694 
1695 	setCustomAction(&player, &invulnerable, 60, 0, 0);
1696 }
1697 
increasePlayerMaxHealth()1698 void increasePlayerMaxHealth()
1699 {
1700 	player.maxHealth++;
1701 
1702 	player.health = player.maxHealth;
1703 
1704 	setInfoBoxMessage(60, 255, 255, 255, _("Maximum health has increased!"));
1705 
1706 	if (player.health == 10)
1707 	{
1708 		addMedal("10_hp");
1709 	}
1710 
1711 	else if (player.health == 20)
1712 	{
1713 		addMedal("20_hp");
1714 	}
1715 }
1716 
syncWeaponShieldToPlayer()1717 void syncWeaponShieldToPlayer()
1718 {
1719 	playerWeapon.face = playerShield.face = player.face;
1720 	playerWeapon.face = playerShield.face = player.face;
1721 
1722 	setEntityAnimation(&playerWeapon, getAnimationTypeAtIndex(&player));
1723 	setEntityAnimation(&playerShield, getAnimationTypeAtIndex(&player));
1724 }
1725 
playerDie()1726 static void playerDie()
1727 {
1728 	/* Change back to Edgar */
1729 
1730 	if (player.element == WATER)
1731 	{
1732 		becomeEdgar();
1733 	}
1734 
1735 	setEntityAnimation(&player, "DIE");
1736 	setEntityAnimation(&playerShield, "DIE");
1737 	setEntityAnimation(&playerWeapon, "DIE");
1738 
1739 	player.frameSpeed = 1;
1740 
1741 	player.health = 0;
1742 
1743 	player.flags |= HELPLESS;
1744 
1745 	playerWeapon.flags &= ~(ATTACKING|FLY);
1746 
1747 	player.dirX = 0;
1748 
1749 	player.thinkTime = 120;
1750 
1751 	if (player.element == ICE || player.environment == LAVA || player.environment == SLIME)
1752 	{
1753 		player.flags |= NO_DRAW;
1754 		playerShield.flags |= NO_DRAW;
1755 		playerWeapon.flags |= NO_DRAW;
1756 
1757 		if (player.environment != SLIME)
1758 		{
1759 			removeInventoryItemByObjectiveName("Amulet of Resurrection");
1760 		}
1761 	}
1762 
1763 	if (getInventoryItemByObjectiveName("Amulet of Resurrection") == NULL)
1764 	{
1765 		player.action = &gameOverTimeOut;
1766 
1767 		doGameOver();
1768 
1769 		loadGameOverMusic();
1770 	}
1771 
1772 	else
1773 	{
1774 		player.action = &resurrectionTimeOut;
1775 	}
1776 }
1777 
resurrectionTimeOut()1778 static void resurrectionTimeOut()
1779 {
1780 	int i;
1781 	Entity *e;
1782 
1783 	player.thinkTime--;
1784 
1785 	player.flags |= INVULNERABLE;
1786 
1787 	if (player.thinkTime == 0)
1788 	{
1789 		for (i=0;i<6;i++)
1790 		{
1791 			e = getFreeEntity();
1792 
1793 			if (e == NULL)
1794 			{
1795 				showErrorAndExit("No free slots to add the Resurrection particle");
1796 			}
1797 
1798 			loadProperties("boss/awesome_fireball_particle", e);
1799 
1800 			setEntityAnimation(e, "STAND");
1801 
1802 			e->head = &player;
1803 
1804 			e->x = player.x + player.w / 2 - e->w / 2;
1805 			e->y = player.y + player.h / 2 - e->h / 2;
1806 
1807 			e->startX = e->x;
1808 			e->startY = e->y;
1809 
1810 			e->draw = &drawLoopingAnimationToMap;
1811 
1812 			e->mental = 180;
1813 
1814 			e->health = i * 60;
1815 
1816 			e->action = &resurrectionParticleWait;
1817 		}
1818 
1819 		player.action = &resurrectionWait;
1820 
1821 		player.mental = 6;
1822 
1823 		player.health = player.maxHealth;
1824 	}
1825 
1826 	checkToMap(self);
1827 }
1828 
resurrectionWait()1829 static void resurrectionWait()
1830 {
1831 	if (player.mental == 0)
1832 	{
1833 		setInfoBoxMessage(60, 255, 255, 255, _("Used Amulet of Resurrection"));
1834 
1835 		removeInventoryItemByObjectiveName("Amulet of Resurrection");
1836 
1837 		setEntityAnimation(&player, "STAND");
1838 		setEntityAnimation(&playerShield, "STAND");
1839 		setEntityAnimation(&playerWeapon, "STAND");
1840 
1841 		player.action = NULL;
1842 
1843 		setCustomAction(&player, &invulnerable, 180, 0, 0);
1844 	}
1845 
1846 	checkToMap(self);
1847 }
1848 
gameOverTimeOut()1849 static void gameOverTimeOut()
1850 {
1851 	player.thinkTime--;
1852 
1853 	checkToMap(&player);
1854 }
1855 
freePlayer()1856 void freePlayer()
1857 {
1858 	memset(&player, 0, sizeof(Entity));
1859 	memset(&playerWeapon, 0, sizeof(Entity));
1860 	memset(&playerShield, 0, sizeof(Entity));
1861 }
1862 
touch(Entity * other)1863 static void touch(Entity *other)
1864 {
1865 
1866 }
1867 
playerGib()1868 EntityList *playerGib()
1869 {
1870 	EntityList *list = NULL;
1871 
1872 	/* Don't multigib */
1873 
1874 	if (player.health > 0)
1875 	{
1876 		/* Don't allow resurrecting */
1877 
1878 		removeInventoryItemByObjectiveName("Amulet of Resurrection");
1879 
1880 		list = throwGibs("edgar/edgar_gibs", 6);
1881 
1882 		player.inUse = TRUE;
1883 
1884 		playerDie();
1885 
1886 		player.flags |= NO_DRAW;
1887 	}
1888 
1889 	return list;
1890 }
1891 
facePlayer()1892 void facePlayer()
1893 {
1894 	self->face = player.x < self->x ? LEFT : RIGHT;
1895 }
1896 
setPlayerStunned(int thinkTime)1897 void setPlayerStunned(int thinkTime)
1898 {
1899 	if (player.health <= 0)
1900 	{
1901 		return;
1902 	}
1903 
1904 	/* Change back to Edgar */
1905 
1906 	if (player.element == WATER)
1907 	{
1908 		becomeEdgar();
1909 	}
1910 
1911 	if (player.action != NULL)
1912 	{
1913 		player.action = NULL;
1914 
1915 		if (player.target == NULL)
1916 		{
1917 			player.target = NULL;
1918 		}
1919 	}
1920 
1921 	player.animationCallback = NULL;
1922 	playerShield.animationCallback = NULL;
1923 	playerWeapon.animationCallback = NULL;
1924 
1925 	playerWeapon.flags &= ~(ATTACKING|ATTACK_SUCCESS);
1926 
1927 	player.flags &= ~BLOCKING;
1928 
1929 	setCustomAction(&player, &dizzy, thinkTime, 0, 0);
1930 
1931 	setEntityAnimation(&player, "DIE");
1932 	setEntityAnimation(&playerShield, "DIE");
1933 	setEntityAnimation(&playerWeapon, "DIE");
1934 
1935 	player.dirX = 0;
1936 }
1937 
doStunned()1938 void doStunned()
1939 {
1940 	player.flags &= ~BLOCKING;
1941 
1942 	setEntityAnimation(&player, "DIE");
1943 	setEntityAnimation(&playerShield, "DIE");
1944 	setEntityAnimation(&playerWeapon, "DIE");
1945 }
1946 
setPlayerSlimed(int thinkTime)1947 void setPlayerSlimed(int thinkTime)
1948 {
1949 	Entity *e = NULL;
1950 
1951 	if (player.health <= 0)
1952 	{
1953 		return;
1954 	}
1955 
1956 	e = getFreeEntity();
1957 
1958 	if (e == NULL)
1959 	{
1960 		showErrorAndExit("No free slots to add Slimed Player");
1961 	}
1962 
1963 	/* Change back to Edgar */
1964 
1965 	if (player.element == WATER)
1966 	{
1967 		becomeEdgar();
1968 	}
1969 
1970 	loadProperties("edgar/edgar_slimed", e);
1971 
1972 	e->x = player.x;
1973 	e->y = player.y;
1974 
1975 	e->type = ENEMY;
1976 
1977 	e->face = player.face;
1978 
1979 	e->action = &applySlime;
1980 	e->touch = touch;
1981 
1982 	e->draw = &drawLoopingAnimationToMap;
1983 
1984 	setEntityAnimation(e, "STAND");
1985 
1986 	e->thinkTime = thinkTime;
1987 
1988 	player.dirX = 0;
1989 
1990 	setCustomAction(&player, &helpless, thinkTime, 0, 0);
1991 
1992 	player.flags &= ~BLOCKING;
1993 
1994 	setEntityAnimation(&player, "STAND");
1995 	setEntityAnimation(&playerWeapon, "STAND");
1996 	setEntityAnimation(&playerShield, "STAND");
1997 
1998 	player.animationCallback = NULL;
1999 	playerShield.animationCallback = NULL;
2000 	playerWeapon.animationCallback = NULL;
2001 
2002 	playerWeapon.flags &= ~(ATTACKING|ATTACK_SUCCESS);
2003 }
2004 
applySlime()2005 static void applySlime()
2006 {
2007 	if (player.action == NULL)
2008 	{
2009 		self->thinkTime--;
2010 	}
2011 
2012 	self->face = player.face;
2013 
2014 	player.dirX = 0;
2015 
2016 	self->x = player.x;
2017 	self->y = player.y;
2018 
2019 	if (self->thinkTime <= 0 || player.health <= 0)
2020 	{
2021 		self->inUse = FALSE;
2022 	}
2023 }
2024 
setPlayerConfused(int thinkTime)2025 void setPlayerConfused(int thinkTime)
2026 {
2027 	int i;
2028 	Entity *e;
2029 
2030 	if (player.health <= 0)
2031 	{
2032 		return;
2033 	}
2034 
2035 	/* Change back to Edgar */
2036 
2037 	if (player.element == WATER)
2038 	{
2039 		becomeEdgar();
2040 	}
2041 
2042 	if (!(player.flags & CONFUSED))
2043 	{
2044 		for (i=0;i<2;i++)
2045 		{
2046 			e = getFreeEntity();
2047 
2048 			if (e == NULL)
2049 			{
2050 				showErrorAndExit("No free slots to add Edgar's Star");
2051 			}
2052 
2053 			loadProperties("boss/armour_boss_star", e);
2054 
2055 			e->x = player.x;
2056 			e->y = player.y;
2057 
2058 			e->action = &starWait;
2059 
2060 			e->draw = &drawLoopingAnimationToMap;
2061 
2062 			e->thinkTime = thinkTime;
2063 
2064 			e->head = &player;
2065 
2066 			setEntityAnimation(e, "STAND");
2067 
2068 			e->currentFrame = (i == 0 ? 0 : 6);
2069 
2070 			e->x = player.x + player.w / 2 - e->w / 2;
2071 
2072 			e->y = player.y - e->h;
2073 		}
2074 	}
2075 
2076 	setCustomAction(&player, &confused, thinkTime, 0, 0);
2077 }
2078 
starWait()2079 static void starWait()
2080 {
2081 	self->thinkTime--;
2082 
2083 	if (self->thinkTime <= 0 || self->head->health <= 0)
2084 	{
2085 		self->inUse = FALSE;
2086 	}
2087 
2088 	self->x = self->head->x + self->head->w / 2 - self->w / 2;
2089 
2090 	self->y = self->head->y - self->h - 8;
2091 
2092 	setInfoBoxMessage(0, 255, 255, 255, _("Your controls have been reversed!"));
2093 }
2094 
swingSword()2095 static void swingSword()
2096 {
2097 	setEntityAnimation(self, "ATTACK_1");
2098 	setEntityAnimation(&playerShield, "ATTACK_1");
2099 	setEntityAnimation(&playerWeapon, "ATTACK_1");
2100 
2101 	playSoundToMap("sound/edgar/swing", EDGAR_CHANNEL, self->x, self->y, 0);
2102 
2103 	playerWeapon.animationCallback = &attackFinish;
2104 }
2105 
drawBow()2106 static void drawBow()
2107 {
2108 	if (!(player.flags & ON_GROUND))
2109 	{
2110 		fireArrow();
2111 	}
2112 
2113 	else
2114 	{
2115 		setEntityAnimation(&player, "ATTACK_2");
2116 		setEntityAnimation(&playerShield, "ATTACK_1");
2117 		setEntityAnimation(&playerWeapon, "ATTACK_1");
2118 
2119 		playerWeapon.animationCallback = &fireArrow;
2120 	}
2121 }
2122 
fireArrow()2123 static void fireArrow()
2124 {
2125 	Entity *arrow, *e;
2126 
2127 	setEntityAnimation(&player, "ATTACK_2");
2128 	setEntityAnimation(&playerShield, "ATTACK_1");
2129 	setEntityAnimation(&playerWeapon, "ATTACK_2");
2130 
2131 	arrow = getInventoryItemByObjectiveName(playerWeapon.requires);
2132 
2133 	if (arrow != NULL)
2134 	{
2135 		e = addProjectile(arrow->name, &playerWeapon, playerWeapon.x + (player.face == RIGHT ? 0 : player.w), player.y + player.offsetY, player.face == RIGHT ? arrow->speed : -arrow->speed, 0);
2136 
2137 		if (e->face == LEFT)
2138 		{
2139 			e->x -= e->w;
2140 		}
2141 
2142 		playSoundToMap("sound/edgar/arrow", EDGAR_CHANNEL, player.x, player.y, 0);
2143 
2144 		e->reactToBlock = &bounceOffShield;
2145 
2146 		e->face = player.face;
2147 
2148 		e->flags |= FLY|ATTACKING;
2149 
2150 		if (game.infiniteArrows == FALSE)
2151 		{
2152 			arrow->health--;
2153 		}
2154 
2155 		if (arrow->health <= 0)
2156 		{
2157 			removeInventoryItemByObjectiveName(playerWeapon.requires);
2158 		}
2159 
2160 		game.arrowsFired++;
2161 
2162 		if (game.arrowsFired == 250)
2163 		{
2164 			addMedal("arrow_250");
2165 		}
2166 
2167 		else if (game.arrowsFired == 500)
2168 		{
2169 			addMedal("arrow_500");
2170 		}
2171 	}
2172 
2173 	else
2174 	{
2175 		setInfoBoxMessage(30, 255, 255, 255, _("Out of arrows!"));
2176 	}
2177 
2178 	playerWeapon.animationCallback = &attackFinish;
2179 }
2180 
shieldAttack()2181 static void shieldAttack()
2182 {
2183 	Entity *shield = removePlayerShield();
2184 
2185 	if (shield == NULL)
2186 	{
2187 		return;
2188 	}
2189 
2190 	shield->target = getEntityByObjectiveName("SORCEROR");
2191 
2192 	if (shield->target == NULL)
2193 	{
2194 		showErrorAndExit("Shield could not find Sorceror");
2195 	}
2196 
2197 	shield->thinkTime = 60;
2198 
2199 	shield->action = &shieldAttackInit;
2200 
2201 	shield->touch = &shieldTouch;
2202 
2203 	shield->mental = 0;
2204 
2205 	shield->flags |= FLY;
2206 
2207 	shield->layer = FOREGROUND_LAYER;
2208 
2209 	if (player.face == LEFT)
2210 	{
2211 		shield->x = player.x + player.w - shield->w - shield->offsetX;
2212 	}
2213 
2214 	else
2215 	{
2216 		shield->x = player.x + shield->offsetX;
2217 	}
2218 
2219 	shield->y = player.y + shield->offsetY;
2220 
2221 	setEntityAnimation(shield, "SHIELD_ATTACK");
2222 
2223 	setCustomAction(shield, &spriteTrail, 3600, 0, 0);
2224 }
2225 
shieldAttackInit()2226 static void shieldAttackInit()
2227 {
2228 	float x, y, radians;
2229 
2230 	if (self->target->startX == 2)
2231 	{
2232 		self->thinkTime--;
2233 	}
2234 
2235 	if (self->thinkTime <= 0)
2236 	{
2237 		self->x = player.x + player.w / 2 - self->w / 2;
2238 		self->y = player.y + player.h / 2 - self->h / 2;
2239 
2240 		self->targetX = self->target->x + self->target->w / 2 - self->w / 2;
2241 		self->targetY = self->target->y + self->target->h / 2 - self->h / 2;
2242 
2243 		calculatePath(self->x, self->y, self->targetX, self->targetY, &self->dirX, &self->dirY);
2244 
2245 		self->dirX *= self->speed * 2;
2246 		self->dirY *= self->speed * 2;
2247 
2248 		self->action = &shieldAttackMove;
2249 
2250 		self->thinkTime = 300;
2251 
2252 		self->mental = 0;
2253 
2254 		self->health = 30;
2255 
2256 		self->maxThinkTime = 30;
2257 	}
2258 
2259 	else
2260 	{
2261 		x = 0;
2262 		y = 64;
2263 
2264 		self->startX += self->speed / 2;
2265 
2266 		if (self->startX >= 360)
2267 		{
2268 			self->startX = 0;
2269 		}
2270 
2271 		radians = DEG_TO_RAD(self->startX);
2272 
2273 		self->x = (x * cos(radians) - y * sin(radians));
2274 		self->y = (x * sin(radians) + y * cos(radians));
2275 
2276 		self->x += player.x;
2277 		self->y += player.y;
2278 
2279 		self->x += (player.w - self->w) / 2;
2280 		self->y += (player.h - self->h) / 2;
2281 	}
2282 }
2283 
shieldAttackMove()2284 static void shieldAttackMove()
2285 {
2286 	self->x += self->dirX;
2287 	self->y += self->dirY;
2288 
2289 	self->thinkTime--;
2290 
2291 	if (self->thinkTime <= 0 && self->mental == 0)
2292 	{
2293 		self->fallout();
2294 	}
2295 }
2296 
shieldAttackVanish()2297 static void shieldAttackVanish()
2298 {
2299 	Entity *temp;
2300 
2301 	self->x += self->dirX;
2302 	self->y += self->dirY;
2303 
2304 	self->thinkTime--;
2305 
2306 	if (self->thinkTime <= 0)
2307 	{
2308 		self->flags |= NO_DRAW;
2309 
2310 		self->maxThinkTime -= 5;
2311 
2312 		self->thinkTime = self->maxThinkTime;
2313 
2314 		self->action = &shieldSideAttackInit;
2315 
2316 		self->health--;
2317 
2318 		if (self->health <= 0)
2319 		{
2320 			temp = self;
2321 
2322 			self = self->target;
2323 
2324 			clearCustomAction(self, &helpless);
2325 
2326 			self->die();
2327 
2328 			self = temp;
2329 
2330 			clearCustomAction(self, &spriteTrail);
2331 
2332 			setEntityAnimation(self, "STAND");
2333 
2334 			self->fallout();
2335 		}
2336 	}
2337 }
2338 
shieldSideAttackInit()2339 static void shieldSideAttackInit()
2340 {
2341 	self->thinkTime--;
2342 
2343 	if (self->thinkTime <= 0)
2344 	{
2345 		self->x = self->target->x + self->target->w / 2 - self->w / 2;
2346 		self->y = self->target->y + self->target->h / 2 - self->h / 2;
2347 
2348 		switch (self->mental)
2349 		{
2350 			case 0:
2351 				self->x -= 32;
2352 			break;
2353 
2354 			case 1:
2355 				self->x += 32;
2356 			break;
2357 
2358 			case 2:
2359 				self->y -= 32;
2360 			break;
2361 
2362 			case 3:
2363 				self->y += 32;
2364 			break;
2365 
2366 			case 4:
2367 				self->x -= 32;
2368 				self->y -= 32;
2369 			break;
2370 
2371 			case 5:
2372 				self->x += 32;
2373 				self->y -= 32;
2374 			break;
2375 
2376 			case 6:
2377 				self->x += 32;
2378 				self->y += 32;
2379 			break;
2380 
2381 			default:
2382 				self->x -= 32;
2383 				self->y += 32;
2384 			break;
2385 		}
2386 
2387 		self->targetX = self->target->x + self->target->w / 2 - self->w / 2;
2388 		self->targetY = self->target->y + self->target->h / 2 - self->h / 2;
2389 
2390 		calculatePath(self->x, self->y, self->targetX, self->targetY, &self->dirX, &self->dirY);
2391 
2392 		self->dirX *= self->speed;
2393 		self->dirY *= self->speed;
2394 
2395 		self->action = &shieldAttackMove;
2396 
2397 		self->touch = &shieldTouch;
2398 
2399 		self->thinkTime = 300;
2400 
2401 		self->flags &= ~NO_DRAW;
2402 
2403 		self->mental++;
2404 
2405 		if (self->mental >= 8)
2406 		{
2407 			self->mental = 0;
2408 		}
2409 	}
2410 }
2411 
shieldTouch(Entity * other)2412 static void shieldTouch(Entity *other)
2413 {
2414 	if (other == self->target)
2415 	{
2416 		other->dirX = 0;
2417 		other->dirY = 0;
2418 
2419 		setCustomAction(other, &helpless, 300, 0, 0);
2420 
2421 		self->action = &shieldAttackVanish;
2422 
2423 		self->touch = NULL;
2424 
2425 		self->thinkTime = 10;
2426 
2427 		playSoundToMap("sound/edgar/shield", BOSS_CHANNEL, player.x, player.y, 0);
2428 
2429 		other->health = 0;
2430 
2431 		fadeFromColour(255, 0, 0, 15);
2432 	}
2433 }
2434 
setBowAmmo(int val)2435 void setBowAmmo(int val)
2436 {
2437 	Entity *bow = getInventoryItemByObjectiveName("Bow");
2438 
2439 	if (bow != NULL)
2440 	{
2441 		if (game.status == IN_INVENTORY)
2442 		{
2443 			setInventoryDialogMessage(_("Bow will now fire %s"), _(self->objectiveName));
2444 		}
2445 
2446 		else
2447 		{
2448 			setInfoBoxMessage(60, 255, 255, 255, _("Bow will now fire %s"), _(self->objectiveName));
2449 		}
2450 
2451 		STRNCPY(bow->requires, self->objectiveName, sizeof(bow->requires));
2452 
2453 		self = bow;
2454 
2455 		setPlayerWeapon(0);
2456 	}
2457 
2458 	else
2459 	{
2460 		if (game.status == IN_INVENTORY)
2461 		{
2462 			setInventoryDialogMessage(_("A bow is required to use this item"));
2463 		}
2464 
2465 		else
2466 		{
2467 			setInfoBoxMessage(60, 255, 255, 255, _("A bow is required to use this item"));
2468 		}
2469 	}
2470 }
2471 
usingBow()2472 static int usingBow()
2473 {
2474 	if (playerWeapon.inUse == TRUE && strcmpignorecase(playerWeapon.name, "weapon/bow") == 0)
2475 	{
2476 		return TRUE;
2477 	}
2478 
2479 	return FALSE;
2480 }
2481 
becomeJumpingSlime(int seconds)2482 void becomeJumpingSlime(int seconds)
2483 {
2484 	int health, originalX, originalY, maxHealth;
2485 
2486 	originalX = player.x;
2487 	originalY = player.y;
2488 
2489 	player.x = player.x + player.w / 2;
2490 	player.y = player.y + player.h / 2;
2491 
2492 	health = player.health;
2493 	maxHealth = player.maxHealth;
2494 
2495 	loadProperties("enemy/purple_jumping_slime", &player);
2496 
2497 	player.x -= player.w / 2;
2498 	player.y -= player.h / 2;
2499 
2500 	/* If there wasn't enough space to transform then don't do anything */
2501 
2502 	if (isValidOnMap(&player) == FALSE || isNearObstacle(&player) == TRUE)
2503 	{
2504 		loadProperties("edgar/edgar", &player);
2505 
2506 		player.x = originalX;
2507 		player.y = originalY;
2508 
2509 		setInfoBoxMessage(60, 255, 255, 255, _("Cannot transmogrify here..."));
2510 
2511 		player.type = PLAYER;
2512 	}
2513 
2514 	else
2515 	{
2516 		player.element = WATER;
2517 
2518 		player.type = PLAYER;
2519 
2520 		playSoundToMap("sound/common/teleport", EDGAR_CHANNEL, player.x, player.y, 0);
2521 
2522 		addParticleExplosion(player.x + player.w / 2, player.y + player.h / 2);
2523 
2524 		playerWeapon.inUse = FALSE;
2525 		playerShield.inUse = FALSE;
2526 
2527 		player.fallout = &slimeFallout;
2528 
2529 		setCustomAction(&player, &slimeTimeout, 60 * seconds, 0, 0);
2530 	}
2531 
2532 	player.health = health;
2533 	player.maxHealth = maxHealth;
2534 }
2535 
becomeEdgar()2536 void becomeEdgar()
2537 {
2538 	int bottom, midX;
2539 
2540 	midX = player.x + player.w / 2;
2541 
2542 	bottom = player.y + player.h;
2543 
2544 	addParticleExplosion(player.x + player.w / 2, player.y + player.h / 2);
2545 
2546 	loadProperties("edgar/edgar", &player);
2547 
2548 	player.x = midX;
2549 
2550 	player.x -= player.w / 2;
2551 
2552 	player.type = PLAYER;
2553 
2554 	player.element = NO_ELEMENT;
2555 
2556 	player.environment = AIR;
2557 
2558 	playSoundToMap("sound/common/teleport", EDGAR_CHANNEL, player.x, player.y, 0);
2559 
2560 	player.fallout = &fallout;
2561 
2562 	clearCustomAction(&player, &slimeTimeout);
2563 
2564 	setSlimeTimerValue(-1);
2565 
2566 	player.y = bottom - player.h;
2567 
2568 	if (player.health > 0)
2569 	{
2570 		if (strlen(playerWeapon.name) > 0)
2571 		{
2572 			playerWeapon.inUse = TRUE;
2573 		}
2574 
2575 		if (strlen(playerShield.name) > 0)
2576 		{
2577 			playerShield.inUse = TRUE;
2578 		}
2579 	}
2580 }
2581 
setPlayerLocked(int lock)2582 void setPlayerLocked(int lock)
2583 {
2584 	if (lock == TRUE)
2585 	{
2586 		player.action = &playerWait;
2587 
2588 		player.animationCallback = NULL;
2589 		playerShield.animationCallback = NULL;
2590 		playerWeapon.animationCallback = NULL;
2591 
2592 		playerWeapon.flags &= ~(ATTACKING|ATTACK_SUCCESS);
2593 
2594 		setEntityAnimation(&player, "STAND");
2595 		setEntityAnimation(&playerShield, "STAND");
2596 		setEntityAnimation(&playerWeapon, "STAND");
2597 
2598 		player.dirX = 0;
2599 	}
2600 
2601 	else
2602 	{
2603 		if (player.health > 0)
2604 		{
2605 			player.action = NULL;
2606 		}
2607 	}
2608 }
2609 
isPlayerLocked()2610 int isPlayerLocked()
2611 {
2612 	return player.action == NULL ? FALSE : TRUE;
2613 }
2614 
playerWait()2615 static void playerWait()
2616 {
2617 	checkToMap(self);
2618 }
2619 
removePlayerWeapon()2620 Entity *removePlayerWeapon()
2621 {
2622 	Entity *e;
2623 
2624 	if (playerWeapon.inUse == TRUE)
2625 	{
2626 		removeInventoryItemByObjectiveName(playerWeapon.objectiveName);
2627 
2628 		e = addPermanentItem(playerWeapon.name, self->x, self->y);
2629 
2630 		e->mental = playerWeapon.mental;
2631 
2632 		playerWeapon.inUse = FALSE;
2633 	}
2634 
2635 	else
2636 	{
2637 		e = NULL;
2638 	}
2639 
2640 	return e;
2641 }
2642 
removePlayerShield()2643 Entity *removePlayerShield()
2644 {
2645 	Entity *e;
2646 
2647 	if (playerShield.inUse == TRUE)
2648 	{
2649 		removeInventoryItemByObjectiveName(playerShield.objectiveName);
2650 
2651 		e = addPermanentItem(playerShield.name, self->x, self->y);
2652 
2653 		e->health = playerShield.health;
2654 
2655 		playerShield.inUse = FALSE;
2656 	}
2657 
2658 	else
2659 	{
2660 		e = NULL;
2661 	}
2662 
2663 	return e;
2664 }
2665 
setPlayerFrozen(int thinkTime)2666 void setPlayerFrozen(int thinkTime)
2667 {
2668 	Entity *e = getFreeEntity();
2669 
2670 	if (e == NULL)
2671 	{
2672 		showErrorAndExit("No free slots to add Iced Player");
2673 	}
2674 
2675 	/* Change back to Edgar */
2676 
2677 	if (player.element == WATER)
2678 	{
2679 		becomeEdgar();
2680 	}
2681 
2682 	player.element = ICE;
2683 
2684 	loadProperties("edgar/edgar_frozen", e);
2685 
2686 	playSoundToMap("sound/common/freeze", EDGAR_CHANNEL, player.x, player.y, 0);
2687 
2688 	e->x = player.x + player.w / 2;
2689 	e->y = player.y + player.h / 2;
2690 
2691 	e->x -= e->w / 2;
2692 	e->y -= e->h / 2;
2693 
2694 	e->type = ENEMY;
2695 
2696 	e->face = player.face;
2697 
2698 	e->action = &applyIce;
2699 
2700 	e->draw = &drawLoopingAnimationToMap;
2701 
2702 	setEntityAnimation(e, "STAND");
2703 
2704 	e->thinkTime = thinkTime;
2705 
2706 	player.dirX = 0;
2707 
2708 	setCustomAction(&player, &helpless, thinkTime, 0, 0);
2709 
2710 	player.flags &= ~BLOCKING;
2711 
2712 	player.animationCallback = NULL;
2713 	playerShield.animationCallback = NULL;
2714 	playerWeapon.animationCallback = NULL;
2715 
2716 	playerWeapon.flags &= ~(ATTACKING|ATTACK_SUCCESS);
2717 
2718 	setEntityAnimation(&player, "STAND");
2719 	setEntityAnimation(&playerShield, "STAND");
2720 	setEntityAnimation(&playerWeapon, "STAND");
2721 }
2722 
applyIce()2723 static void applyIce()
2724 {
2725 	int i;
2726 	Entity *e;
2727 
2728 	self->thinkTime--;
2729 
2730 	self->face = player.face;
2731 
2732 	if (self->thinkTime <= 0 || player.health <= 0 || (player.flags & TELEPORTING))
2733 	{
2734 		for (i=0;i<8;i++)
2735 		{
2736 			e = addTemporaryItem("common/ice_piece", self->x, self->y, RIGHT, 0, 0);
2737 
2738 			e->x += self->w / 2 - e->w / 2;
2739 			e->y += self->h / 2 - e->h / 2;
2740 
2741 			e->dirX = (prand() % 10) * (prand() % 2 == 0 ? -1 : 1);
2742 			e->dirY = ITEM_JUMP_HEIGHT + (prand() % ITEM_JUMP_HEIGHT);
2743 
2744 			setEntityAnimationByID(e, i);
2745 
2746 			e->thinkTime = 60 + (prand() % 60);
2747 		}
2748 
2749 		playSoundToMap("sound/common/shatter", EDGAR_CHANNEL, player.x, player.y, 0);
2750 
2751 		self->inUse = FALSE;
2752 
2753 		player.element = NO_ELEMENT;
2754 	}
2755 
2756 	else
2757 	{
2758 		player.dirX = 0;
2759 
2760 		self->x = player.x + player.w / 2 - self->w / 2;
2761 		self->y = player.y + player.h / 2 - self->h / 2;
2762 	}
2763 }
2764 
setPlayerWrapped(int thinkTime)2765 void setPlayerWrapped(int thinkTime)
2766 {
2767 	Entity *e = getFreeEntity();
2768 
2769 	if (e == NULL)
2770 	{
2771 		showErrorAndExit("No free slots to add Wrapped Player");
2772 	}
2773 
2774 	/* Change back to Edgar */
2775 
2776 	if (player.element == WATER)
2777 	{
2778 		becomeEdgar();
2779 	}
2780 
2781 	loadProperties("edgar/edgar_wrapped", e);
2782 
2783 	e->x = player.x + player.w / 2;
2784 	e->y = player.y + player.h / 2;
2785 
2786 	e->x -= e->w / 2;
2787 	e->y -= e->h / 2;
2788 
2789 	e->type = ENEMY;
2790 
2791 	e->face = player.face;
2792 
2793 	e->action = &applyWebbing;
2794 	e->touch = &touch;
2795 
2796 	e->draw = &drawLoopingAnimationToMap;
2797 
2798 	setEntityAnimation(e, "STAND");
2799 
2800 	e->thinkTime = thinkTime;
2801 
2802 	player.dirX = 0;
2803 
2804 	setCustomAction(&player, &helpless, thinkTime, 0, 0);
2805 
2806 	player.flags |= WRAPPED;
2807 
2808 	player.flags &= ~BLOCKING;
2809 
2810 	player.animationCallback = NULL;
2811 	playerShield.animationCallback = NULL;
2812 	playerWeapon.animationCallback = NULL;
2813 
2814 	playerWeapon.flags &= ~(ATTACKING|ATTACK_SUCCESS);
2815 
2816 	setEntityAnimation(&player, "STAND");
2817 	setEntityAnimation(&playerShield, "STAND");
2818 	setEntityAnimation(&playerWeapon, "STAND");
2819 }
2820 
applyWebbing()2821 static void applyWebbing()
2822 {
2823 	int i;
2824 	Entity *e;
2825 
2826 	self->thinkTime--;
2827 
2828 	self->face = player.face;
2829 
2830 	player.dirX = 0;
2831 
2832 	self->x = player.x + player.w / 2;
2833 	self->y = player.y + player.h / 2;
2834 
2835 	self->x -= self->w / 2;
2836 	self->y -= self->h / 2;
2837 
2838 	if (self->thinkTime <= 0 || player.health <= 0)
2839 	{
2840 		for (i=0;i<4;i++)
2841 		{
2842 			e = addTemporaryItem("common/web_piece", self->x, self->y, RIGHT, 0, 0);
2843 
2844 			e->x += self->w / 2 - e->w / 2;
2845 			e->y += self->h / 2 - e->h / 2;
2846 
2847 			e->dirX = (prand() % 10) * (prand() % 2 == 0 ? -1 : 1);
2848 			e->dirY = ITEM_JUMP_HEIGHT + (prand() % ITEM_JUMP_HEIGHT);
2849 
2850 			setEntityAnimationByID(e, i);
2851 
2852 			e->thinkTime = 60 + (prand() % 60);
2853 		}
2854 
2855 		self->inUse = FALSE;
2856 
2857 		player.flags &= ~WRAPPED;
2858 	}
2859 
2860 	checkToMap(self);
2861 }
2862 
setPlayerPetrified()2863 void setPlayerPetrified()
2864 {
2865 	Entity *e = getFreeEntity();
2866 
2867 	if (e == NULL)
2868 	{
2869 		showErrorAndExit("No free slots to add Petrified Player");
2870 	}
2871 
2872 	loadProperties("edgar/edgar_petrify", e);
2873 
2874 	e->x = player.x;
2875 	e->y = player.y;
2876 
2877 	e->startX = e->x;
2878 
2879 	e->action = &applyPetrification;
2880 
2881 	e->face = player.face;
2882 
2883 	e->draw = &drawLoopingAnimationToMap;
2884 	e->touch = &entityTouch;
2885 	e->takeDamage = NULL;
2886 
2887 	e->type = ENEMY;
2888 
2889 	e->head = self;
2890 
2891 	e->maxThinkTime = 10;
2892 
2893 	e->thinkTime = e->maxThinkTime;
2894 
2895 	e->health = 4;
2896 
2897 	setEntityAnimationByID(e, e->health);
2898 
2899 	setPlayerLocked(TRUE);
2900 
2901 	player.flags &= ~NO_DRAW;
2902 
2903 	player.element = SLIME;
2904 }
2905 
applyPetrification()2906 static void applyPetrification()
2907 {
2908 	Entity *e;
2909 
2910 	if (self->thinkTime <= 0)
2911 	{
2912 		playSoundToMap("sound/item/crack", -1, self->x, self->y, 0);
2913 
2914 		self->health--;
2915 
2916 		self->thinkTime = self->maxThinkTime;
2917 
2918 		if (self->health < 0)
2919 		{
2920 			e = addSmallRock(self->x, self->y, "common/small_rock");
2921 
2922 			e->x += (self->w - e->w) / 2;
2923 			e->y -= e->h;
2924 
2925 			e->dirX = -3;
2926 			e->dirY = -8;
2927 
2928 			e->layer = FOREGROUND_LAYER;
2929 
2930 			e = addSmallRock(self->x, self->y, "common/small_rock");
2931 
2932 			e->x += (self->w - e->w) / 2;
2933 			e->y -= e->h;
2934 
2935 			e->dirX = 3;
2936 			e->dirY = -8;
2937 
2938 			e->layer = FOREGROUND_LAYER;
2939 
2940 			self->inUse = FALSE;
2941 
2942 			setPlayerLocked(FALSE);
2943 
2944 			playSoundToMap("sound/common/crumble", -1, self->x, self->y, 0);
2945 
2946 			player.element = NO_ELEMENT;
2947 		}
2948 
2949 		else
2950 		{
2951 			setEntityAnimationByID(self, self->health);
2952 		}
2953 	}
2954 
2955 	if (player.health > 0 && (input.up == 1 || input.down == 1 || input.right == 1 ||
2956 		 input.left == 1 || input.previous == 1 || input.next == 1 || input.jump == 1 ||
2957 		input.activate == 1 || input.attack == 1 || input.interact == 1 || input.block == 1))
2958 	{
2959 		player.x = self->startX + 1 * (prand() % 2 == 0 ? 1 : -1);
2960 		self->x = player.x;
2961 
2962 		playSoundToMap("sound/boss/gargoyle/petrify_shake", EDGAR_CHANNEL, self->x, self->y, 0);
2963 
2964 		self->thinkTime--;
2965 
2966 		input.up = 0;
2967 		input.down = 0;
2968 		input.right = 0;
2969 		input.left = 0;
2970 		input.previous = 0;
2971 		input.next = 0;
2972 		input.jump = 0;
2973 		input.activate = 0;
2974 		input.attack = 0;
2975 		input.interact = 0;
2976 		input.block = 0;
2977 
2978 		e = addSmallRock(self->x, self->y, "common/small_rock");
2979 
2980 		e->x += self->w / 2 - e->w / 2;
2981 		e->y += self->h / 2 - e->h / 2;
2982 
2983 		e->dirX = (2 + prand() % 6) * (prand() % 2 == 0 ? -1 : 1);
2984 		e->dirY = -8;
2985 
2986 		e->thinkTime = 60 + (prand() % 120);
2987 
2988 		e->layer = FOREGROUND_LAYER;
2989 	}
2990 
2991 	if (player.health > 0)
2992 	{
2993 		setInfoBoxMessage(0, 255, 255, 255, _("Press buttons to break the petrification!"));
2994 	}
2995 
2996 	else
2997 	{
2998 		player.flags |= NO_DRAW;
2999 
3000 		e = addSmallRock(self->x, self->y, "common/small_rock");
3001 
3002 		e->x += (self->w - e->w) / 2;
3003 		e->y -= e->h;
3004 
3005 		e->dirX = -3;
3006 		e->dirY = -8;
3007 
3008 		e->layer = FOREGROUND_LAYER;
3009 
3010 		e = addSmallRock(self->x, self->y, "common/small_rock");
3011 
3012 		e->x += (self->w - e->w) / 2;
3013 		e->y -= e->h;
3014 
3015 		e->dirX = 3;
3016 		e->dirY = -8;
3017 
3018 		e->layer = FOREGROUND_LAYER;
3019 
3020 		e = addSmallRock(self->x, self->y, "common/small_rock");
3021 
3022 		e->x += (self->w - e->w) / 2;
3023 		e->y -= e->h;
3024 
3025 		e->dirX = -3;
3026 		e->dirY = -4;
3027 
3028 		e->layer = FOREGROUND_LAYER;
3029 
3030 		e = addSmallRock(self->x, self->y, "common/small_rock");
3031 
3032 		e->x += (self->w - e->w) / 2;
3033 		e->y -= e->h;
3034 
3035 		e->dirX = 3;
3036 		e->dirY = -4;
3037 
3038 		e->layer = FOREGROUND_LAYER;
3039 
3040 		self->inUse = FALSE;
3041 
3042 		playSoundToMap("sound/common/crumble", -1, self->x, self->y, 0);
3043 	}
3044 
3045 	self->y = player.y;
3046 }
3047 
setPlayerAsh()3048 void setPlayerAsh()
3049 {
3050 	Entity *e = getFreeEntity();
3051 
3052 	if (e == NULL)
3053 	{
3054 		showErrorAndExit("No free slots to add an Ashed Player");
3055 	}
3056 
3057 	/* Change back to Edgar */
3058 
3059 	if (player.element == WATER)
3060 	{
3061 		becomeEdgar();
3062 	}
3063 
3064 	loadProperties("edgar/edgar_ash", e);
3065 
3066 	e->x = player.x;
3067 	e->y = player.y;
3068 
3069 	e->face = player.face;
3070 
3071 	e->action = &applyAsh;
3072 
3073 	e->draw = &drawLoopingAnimationToMap;
3074 
3075 	setEntityAnimation(e, "STAND");
3076 
3077 	e->mental = 0;
3078 
3079 	player.dirX = 0;
3080 	player.dirY = 0;
3081 }
3082 
applyAsh()3083 static void applyAsh()
3084 {
3085 	Entity *e;
3086 
3087 	self->x = player.x;
3088 	self->y = player.y;
3089 
3090 	if (player.mental == 0 && self->mental == 0)
3091 	{
3092 		self->mental = 1;
3093 
3094 		setEntityAnimation(self, "BECOME_ASH");
3095 
3096 		self->animationCallback = &becomeAsh;
3097 	}
3098 
3099 	if (self->mental == 2)
3100 	{
3101 		self->thinkTime--;
3102 
3103 		if (self->thinkTime <= 0)
3104 		{
3105 			e = addSmoke(self->x + prand() % self->w, self->y + self->h - prand() % 10, "decoration/dust");
3106 
3107 			if (e != NULL)
3108 			{
3109 				e->dirY = -(5 + prand() % 20);
3110 
3111 				e->dirY /= 10;
3112 			}
3113 
3114 			self->thinkTime = 5 + prand() % 10;
3115 		}
3116 	}
3117 }
3118 
becomeAsh()3119 static void becomeAsh()
3120 {
3121 	setEntityAnimation(self, "ASH_WAIT");
3122 
3123 	self->mental = 2;
3124 }
3125 
isAttacking()3126 int isAttacking()
3127 {
3128 	return (playerWeapon.flags & ATTACKING) ? TRUE : FALSE;
3129 }
3130 
playerStand()3131 void playerStand()
3132 {
3133 	player.animationCallback = NULL;
3134 	playerShield.animationCallback = NULL;
3135 	playerWeapon.animationCallback = NULL;
3136 
3137 	playerWeapon.flags &= ~(ATTACKING|ATTACK_SUCCESS);
3138 
3139 	setEntityAnimation(&player, "STAND");
3140 	setEntityAnimation(&playerShield, "STAND");
3141 	setEntityAnimation(&playerWeapon, "STAND");
3142 }
3143 
weaponTouch(Entity * other)3144 static void weaponTouch(Entity *other)
3145 {
3146 
3147 }
3148 
lightningSwordTouch(Entity * other)3149 static void lightningSwordTouch(Entity *other)
3150 {
3151 	if (other->takeDamage != NULL && self->thinkTime <= 0)
3152 	{
3153 		self->mental--;
3154 
3155 		self->head->mental = self->mental;
3156 
3157 		if (self->mental == 10)
3158 		{
3159 			freeMessageQueue();
3160 
3161 			setInfoBoxMessage(60, 255, 255, 255, _("10 charges remaining..."));
3162 		}
3163 
3164 		else if (self->mental <= 0)
3165 		{
3166 			freeMessageQueue();
3167 
3168 			setInfoBoxMessage(120, 255, 255, 255, _("%s is out of power"), self->objectiveName);
3169 
3170 			self->touch = NULL;
3171 
3172 			self->mental = -1;
3173 		}
3174 
3175 		self->thinkTime = HIT_INVULNERABLE_TIME;
3176 	}
3177 }
3178 
lightningSwordChangeToBasic()3179 static void lightningSwordChangeToBasic()
3180 {
3181 	loadProperties("weapon/lightning_sword_empty", &playerWeapon);
3182 
3183 	playerWeapon.touch = &weaponTouch;
3184 
3185 	alignAnimations(&playerWeapon);
3186 
3187 	playerWeapon.action = &swingSword;
3188 
3189 	playerWeapon.mental = -2;
3190 
3191 	replaceInventoryItemWithName("weapon/lightning_sword", &playerWeapon);
3192 }
3193 
addChargesToWeapon()3194 void addChargesToWeapon()
3195 {
3196 	int mental = 0;
3197 	Entity *e;
3198 
3199 	if (strcmpignorecase(playerWeapon.name, "weapon/lightning_sword") == 0 ||
3200 		strcmpignorecase(playerWeapon.name, "weapon/lightning_sword_empty") == 0)
3201 	{
3202 		mental = playerWeapon.mental;
3203 
3204 		if (playerWeapon.mental < 0)
3205 		{
3206 			playerWeapon.mental = 0;
3207 		}
3208 
3209 		playerWeapon.mental += self->health;
3210 
3211 		if (playerWeapon.mental > SWORD_MAX_CHARGE)
3212 		{
3213 			playerWeapon.mental = SWORD_MAX_CHARGE;
3214 		}
3215 
3216 		playerWeapon.head->mental = playerWeapon.mental;
3217 
3218 		/* Transform back into Lightning Sword */
3219 
3220 		if (mental == -2)
3221 		{
3222 			mental = playerWeapon.mental;
3223 
3224 			loadProperties("weapon/lightning_sword", &playerWeapon);
3225 
3226 			playerWeapon.mental = mental;
3227 
3228 			playerWeapon.touch = &lightningSwordTouch;
3229 
3230 			alignAnimations(&playerWeapon);
3231 
3232 			playerWeapon.action = &swingSword;
3233 
3234 			replaceInventoryItemWithName("weapon/lightning_sword_empty", &playerWeapon);
3235 
3236 			setInfoBoxMessage(60, 255, 255, 255, _("%s has regained power"), _(playerWeapon.objectiveName));
3237 		}
3238 
3239 		else
3240 		{
3241 			setInfoBoxMessage(60, 255, 255, 255, _("Picked up %s x %d"), _(self->objectiveName), self->health);
3242 		}
3243 	}
3244 
3245 	else
3246 	{
3247 		e = getInventoryItemByObjectiveName(self->requires);
3248 
3249 		if (e != NULL)
3250 		{
3251 			mental = e->mental;
3252 
3253 			if (e->mental < 0)
3254 			{
3255 				e->mental = 0;
3256 			}
3257 
3258 			e->mental += self->health;
3259 
3260 			if (e->mental > SWORD_MAX_CHARGE)
3261 			{
3262 				e->mental = SWORD_MAX_CHARGE;
3263 			}
3264 
3265 			/* Transform back into Lightning Sword */
3266 
3267 			if (mental == -2)
3268 			{
3269 				mental = e->mental;
3270 
3271 				loadProperties("weapon/lightning_sword", e);
3272 
3273 				e->mental = mental;
3274 
3275 				e->touch = &lightningSwordTouch;
3276 
3277 				setInfoBoxMessage(60, 255, 255, 255, _("%s has regained power"), _(e->objectiveName));
3278 			}
3279 
3280 			else
3281 			{
3282 				setInfoBoxMessage(60, 255, 255, 255, _("Picked up %s x %d"), _(self->objectiveName), self->health);
3283 			}
3284 		}
3285 	}
3286 }
3287 
resurrectionParticleWait()3288 static void resurrectionParticleWait()
3289 {
3290 	float radians;
3291 
3292 	self->mental -= 2;
3293 
3294 	if (self->mental <= 0)
3295 	{
3296 		self->head->mental--;
3297 
3298 		self->inUse = FALSE;
3299 	}
3300 
3301 	self->health += 8;
3302 
3303 	radians = DEG_TO_RAD(self->health);
3304 
3305 	self->x = (0 * cos(radians) - self->mental * sin(radians));
3306 	self->y = (0 * sin(radians) + self->mental * cos(radians));
3307 
3308 	self->x += self->startX;
3309 	self->y += self->startY;
3310 }
3311 
setWeaponFromScript(char * name)3312 void setWeaponFromScript(char *name)
3313 {
3314 	self = getInventoryItemByName(name);
3315 
3316 	if (self == NULL)
3317 	{
3318 		showErrorAndExit("Could not find inventory item %s\n", name);
3319 	}
3320 
3321 	setPlayerWeapon(1);
3322 }
3323 
setShieldFromScript(char * name)3324 void setShieldFromScript(char *name)
3325 {
3326 	self = getInventoryItemByName(name);
3327 
3328 	if (self == NULL)
3329 	{
3330 		showErrorAndExit("Could not find inventory item %s\n", name);
3331 	}
3332 
3333 	setPlayerShield(1);
3334 }
3335 
unsetWeapon()3336 void unsetWeapon()
3337 {
3338 	if (playerWeapon.inUse == TRUE)
3339 	{
3340 		playerWeapon.inUse = FALSE;
3341 	}
3342 }
3343 
unsetShield()3344 void unsetShield()
3345 {
3346 	if (playerShield.inUse == TRUE)
3347 	{
3348 		playerShield.inUse = FALSE;
3349 	}
3350 }
3351 
scriptAttack()3352 void scriptAttack()
3353 {
3354 	playerWeapon.flags |= ATTACKING;
3355 
3356 	playerWeapon.action();
3357 }
3358 
creditsMove()3359 static void creditsMove()
3360 {
3361 	self->thinkTime++;
3362 
3363 	setEntityAnimation(self, "WALK");
3364 	setEntityAnimation(&playerWeapon, "WALK");
3365 	setEntityAnimation(&playerShield, "WALK");
3366 
3367 	self->dirX = self->speed;
3368 
3369 	checkToMap(self);
3370 
3371 	if (self->dirX == 0 && self->mental != 0)
3372 	{
3373 		self->inUse = FALSE;
3374 	}
3375 
3376 	if (self->thinkTime == 180 && self->mental == 0)
3377 	{
3378 		self->thinkTime = 60;
3379 
3380 		self->creditsAction = &creditsWait;
3381 	}
3382 }
3383 
creditsWait()3384 static void creditsWait()
3385 {
3386 	setEntityAnimation(self, "STAND");
3387 	setEntityAnimation(&playerWeapon, "STAND");
3388 	setEntityAnimation(&playerShield, "STAND");
3389 
3390 	self->thinkTime--;
3391 
3392 	if (self->thinkTime <= 0)
3393 	{
3394 		self->face = self->face == LEFT ? RIGHT : LEFT;
3395 
3396 		playerWeapon.face = self->face;
3397 		playerShield.face = self->face;
3398 
3399 		self->mental++;
3400 
3401 		self->thinkTime = 60;
3402 
3403 		if (self->face == RIGHT)
3404 		{
3405 			self->creditsAction = &creditsMove;
3406 		}
3407 	}
3408 }
3409