fixed alot of warnings, and some bugs, so it can be compiled with the current vccrun
[k8vacspelynky.git] / mapent / enemies / olmec.vc
blob9b7429f9d49f84850c5babdc4c62ce5b2a9ad71c
1 /**********************************************************************************
2  * Copyright (c) 2008, 2009 Derek Yu and Mossmouth, LLC
3  * Copyright (c) 2010, Moloch
4  * Copyright (c) 2018, Ketmar Dark
5  *
6  * This file is part of Spelunky.
7  *
8  * You can redistribute and/or modify Spelunky, including its source code, under
9  * the terms of the Spelunky User License.
10  *
11  * Spelunky is distributed in the hope that it will be entertaining and useful,
12  * but WITHOUT WARRANTY.  Please see the Spelunky User License for more details.
13  *
14  * The Spelunky User License should be available in "Game Information", which
15  * can be found in the Resource Explorer, or as an external file called COPYING.
16  * If not, please obtain a new copy of Spelunky from <http://spelunkyworld.com/>
17  *
18  **********************************************************************************/
19 class EnemyOlmec['oOlmec'] : MapEnemy;
22 START2 = -2;
23 START1 = -1;
24 IDLE = 0;
25 BOUNCE = 1;
26 RECOVER = 2;
27 WALK = 3;
28 DROWNING = 4;
29 PREPARE = 5;
30 SLAM = 6;
31 CREATE = 7;
32 DEAD = 99;
34 bool firstThink = true;
35 bool toggle;
36 bool slammed;
38 int alarmEnablePlayer;
39 int alarmBossAwake;
40 int bossAwakePhase;
41 int alarmAlienEjector;
44 override bool initialize () {
45   if (!::initialize()) return false;
46   setSprite('sOlmecStart1');
47   dir = Dir.Right;
48   //dir = global.randRoom(0, 1);
49   return true;
53 override void onBulletHit (ObjBullet bullet) {
57 override bool onTouchedByPlayer (PlayerPawn plr) {
58   return false; // don't skip thinker
62 // return `false` to do standard weapon processing
63 override bool onTouchedByPlayerWeapon (PlayerPawn plr, PlayerWeapon wpn) {
64   return true;
68 override void thinkFrame () {
69   auto plr = level.player;
71   if (firstThink) {
72     firstThink = false;
73     plr.movementBlocked = true;
74   }
76   if (alarmEnablePlayer > 0) {
77     if (--alarmEnablePlayer == 0) {
78       /+!
79       view_hborder[0] = 128;
80       view_vborder[0] = 64;
81       view_xview[0] = 0;
82       view_object[0] = oPlayer1;
83       +/
84       level.cameraReturnToPlayer();
85       global.forceAllSoundsAtPlayer = false;
86       plr.movementBlocked = false;
87       status = IDLE;
88       counter = 100;
89       global.playMusic('musBoss');
90       if (global.config.bizarre && global.config.bizarrePlus) alarmAlienEjector = global.randOther(150, 300);
91       //playSound(global.sndBoss);
92     }
93   }
95   if (alarmBossAwake > 0) {
96     if (--alarmBossAwake == 0) {
97       switch (bossAwakePhase++) {
98         case 0:
99           setSprite(global.config.bizarre ? 'sOlmecStart2B' : 'sOlmecStart2');
100           foreach (; 0..6) {
101             auto debris = level.MakeMapObject(ix+global.randOther(0, 32), iy+global.randOther(0, 32), 'oOlmecDebris');
102             if (debris) {
103               debris.xVel = -global.randOther(1, 4);
104               debris.yVel = -global.randOther(1, 3);
105             }
106           }
107           /*plr.*/playSound('sndThump'); // make it local to player, 'cause camera is panned
108           level.forEachObject(delegate bool (MapObject o) {
109             if (o isa ObjHawkmanWorship) {
110               auto hm = level.MakeMapObject(o.ix, o.iy, 'oHawkman');
111               hm.status = STUNNED;
112               hm.hp = 1;
113               hm.xVel = -3;
114               hm.yVel = -5;
115               hm.counter = 300;
116               o.instanceRemove();
117               return false;
118             }
119             if (o isa ObjCavemanWorship) {
120               o.imageFrame = 0;
121               o.imageSpeed = 0;
122             }
123             return false;
124           }, allowSpectrals:true);
125           alarmBossAwake = 50;
126           break;
127         case 1:
128           setSprite(global.config.bizarre ? 'sOlmecStart3B' : 'sOlmecStart3');
129           foreach (; 0..6) {
130             auto debris = level.MakeMapObject(ix+32+global.randOther(0, 32), iy+global.randOther(0, 32), 'oOlmecDebris');
131             if (debris) {
132               debris.xVel = global.randOther(1, 4);
133               debris.yVel = -global.randOther(1, 3);
134             }
135           }
136           /*plr.*/playSound('sndThump'); // make it local to player, 'cause camera is panned
137           alarmBossAwake = 50;
138           break;
139         case 2:
140           setSprite(global.config.bizarre ? 'sOlmec2' : 'sOlmec');
141           foreach (; 0..12) {
142             auto debris = level.MakeMapObject(ix+32+global.randOther(0, 32), iy+global.randOther(0, 32), 'oOlmecDebris');
143             if (debris) {
144               do {
145                 debris.xVel = global.randOther(-4, 4);
146               } while (debris.xVel == 0);
147               debris.yVel = -global.randOther(1, 3);
148             }
149           }
150           /*plr.*/playSound('sndThump'); // make it local to player, 'cause camera is panned
151           alarmBossAwake = 50;
152           break;
153         case 3:
154           toggle = true;
155           status = BOUNCE;
156           /*plr.*/playSound('sndBigJump'); // make it local to player, 'cause camera is panned
157           /*plr.*/playSound('sndAlert'); // make it local to player, 'cause camera is panned
158           alarmBossAwake = 20;
159           break;
160         case 4:
161           level.forEachObject(delegate bool (MapObject o) {
162             if (o isa ObjCavemanWorship) {
163               auto obj = level.MakeMapObject(o.ix, o.iy, 'oCaveman');
164               obj.dir = Dir.Right;
165               obj.status = ATTACK;
166               o.instanceRemove();
167             }
168             return false;
169           }, allowSpectrals:true);
170           break;
171       }
172     }
173   }
175   if (alarmAlienEjector > 0) {
176     if (--alarmAlienEjector == 0) {
177       if (global.config.bizarre && global.config.bizarrePlus) {
178         /+
179         if (view_yview[0] &gt; 288 and view_yview[0] &lt; 416) {
180           instance_activate_object(oAlien);
181           instance_activate_object(oAlienEject);
182           if (instance_number(oAlien)+instance_number(oAlienEject) &lt; 48) {
183             instance_create(24+rand(1, 39)*8, view_yview[0]-8, oAlienEject);
184           }
185         }
186         +/
187         alarmAlienEjector = global.randOther(30, 300);
188       }
189     }
190   }
192   spectral = true;
194   auto oldFX = fltx, oldFY = flty;
195   bool fixCarry = (carryPlayer || plr.isRectHitSimple(ix-1, iy, 68, 63));
196   moveRel(xVel, yVel);
198   yVel = fmin(yVel+myGrav, 6);
200   if (isCollisionTop(1)) {
201     level.MakeMapObject(ix, iy-16, 'oOlmecSlam');
202     flty = iy+1;
203     if (yVel < 0) yVel = -yVel*0.8;
204   }
206   if (isCollisionLeft(1)) {
207     fltx = ix+1;
208     xVel = 0;
209     //if (xVel &lt; 0) xVel = -xVel * 0.8;
210   }
211   if (isCollisionRight(1)) {
212     fltx = ix-1;
213     xVel = 0;
214     //if (xVel &gt; 0) xVel = -xVel * 0.8;
215   }
217   if (level.isLavaAtPoint(ix, iy+64)) status = DROWNING;
219   if (level.isLavaAtPoint(ix, iy-2)) {
220     /*
221     if (!global.customLevel) {
222       global.enemyKills[21] += 1;
223       oFinalBoss.olmecDead = true;
224       global.kills += 1;
225     }
226     if (isRoom("rLoadLevel")) global.shake = 0;
227     */
228     level.onOlmecDead(self);
229     instanceRemove();
230     return;
231   }
233   //auto dist = distanceToEntityCenter(plr)+32;
235   carryPlayer = plr.isRectHitSimple(ix, iy-2, 65, 67);
236   //if (collision_rectangle(x, y-2, x+64, y+64, oPlayer1, 0, 0)) carryPlayer = true; else carryPlayer = false;
238   if (status == START1) {
239     /+!
240     if (global.scumWholeLevel) {
241       alarm[1] = 100;
242       status = START2;
243     } else {
244       if (view_xview[0] < 176) {
245         view_xview[0] += 2;
246       } else {
247         alarm[1] = 100;
248         status = START2;
249       }
250     }
251     +/
252     level.cameraSlideToPoint(176, 0, 2, 0);
253     global.forceAllSoundsAtPlayer = true;
254     alarmBossAwake = 100;
255     status = START2; //!!!
256     if (isCollisionBottom(1)) yVel = 0;
257   } else if (status == START2) {
258     if (isCollisionBottom(1)) yVel = 0;
259   } else if (status == IDLE) {
260     if (counter > 0) --counter;
261     if (counter == 0) status = BOUNCE;
262     if (isCollisionBottom(1)) yVel = 0;
263     toggle = true;
264   } else if (status == CREATE) {
265     foreach (; 0..6) {
266       level.MakeMapObject(ix+32+global.randOther(0, 32)-global.randOther(0, 32), iy+14+global.randOther(0, 32)-global.randOther(0, 32), 'oPsychicCreate2');
267     }
268     level.MakeMapObject(ix+32, iy+16, 'oYellowBall');
269     level.MakeMapObject(ix+32, iy+16, 'oYellowBall');
270     level.MakeMapObject(ix+32, iy+16, 'oYellowBall');
271     playSound('sndPsychic');
272     status = IDLE;
273   } else if (status == RECOVER) {
274     if (isCollisionBottom(1)) {
275       playSound('sndThump');
276       status = IDLE;
277       xVel = 0;
278       yVel = 0;
279       counter = global.randOther(40, (global.config.gameMode == GameConfig::GameMode.Vanilla ? 100 : 60));
280     } else {
281       if (counter > 1) {
282         --counter;
283       } else if (counter == 1) {
284              if (plr.ix < ix) xVel = -0.25;
285         else if (plr.ix > ix+64) xVel = 0.25;
286         else xVel = 0;
287         --counter;
288       } else {
289         if (xVel < 0 && toggle) xVel -= 0.25; else if (xVel < 0 && !toggle) xVel += 0.25;
290         if (xVel > 0 && toggle) xVel += 0.25; else if (xVel > 0 && !toggle) xVel -= 0.25;
291         if (xVel <= -2 || xVel >= 2) toggle = !toggle;
292       }
294       //if ((not oPlayer1.active and yVel &gt;= 0) or
295       //    (oPlayer1.y &gt; y and abs(oPlayer1.x - (x+32)) &lt; 32 and xVel &gt; -1))
296       if ((plr.movementBlocked && yVel >= 0) ||
297           (plr.iy > iy && abs(plr.ix-(ix+32)) < 32 && xVel > -1 && !plr.dead))
298       {
299         status = PREPARE;
300         yVel = 0;
301         xVel = 0;
302         myGrav = 0;
303         counter = 20;
304       }
305     }
306   } else if (status == BOUNCE) {
307     if (isCollisionBottom(1)) {
308       yVel = -4;
309     } else {
310       counter = 10;
311       status = RECOVER;
312       playSound('sndBigJump');
313     }
314   } else if (status == PREPARE) {
315     if (counter > 0) {
316       --counter;
317     } else {
318       yVel = 5;
319       myGrav = 0.2;
320       status = SLAM;
321       slammed = false;
322     }
323   } else if (status == SLAM) {
324     carryPlayer = false;
325     fixCarry = false;
326     if (isCollisionBottom(1)) {
327       if (!slammed) {
328         level.MakeMapObject(ix, iy+64, 'oOlmecSlam');
329         slammed = true;
330         level.scrShake(5);
331       } else {
332         if (plr.movementBlocked || global.randOther(1, 2) == 1) status = IDLE; else status = CREATE;
333         xVel = 0;
334         yVel = 0;
335         counter = (global.config.gameMode == GameConfig::GameMode.Vanilla ? 60 : 40);
336         if (plr.movementBlocked) alarmEnablePlayer = 50;
337       }
338     }
339   } else if (status == DROWNING) {
340     xVel = 0;
341     yVel = 0.1;
342     myGrav = 0;
343     level.scrShake(10);
344     if (!sndIsPlaying('sndFlame')) playSound('sndFlame');
345     depth = 1100; // hide behind lava
346   }
348   if (isCollisionTop(1)) yVel = 1;
349   if (isCollisionLeft(1) || isCollisionRight(1)) xVel = -xVel;
350   if (isCollision()) flty = iy-2;
352   if (fixCarry) {
353     float dx = fltx-oldFX;
354     float dy = flty-oldFY;
355     plr.fltx += dx;
356     plr.flty += dy;
357     // for interpolator
358     plr.prevFltX += dx;
359     plr.prevFltY += dy;
360   }
362   spectral = false;
366 defaultproperties {
367   objName = 'Olmec';
368   desc = "Olmec";
369   desc2 = "A construct built by an ancient civilization, equipped with tremendous crushing force and powerful summoning magic.";
371   depth = 40;
373   setCollisionBounds(2, 0, 62, 64);
374   xVel = 0;
375   yVel = 0;
376   yDelta = -0.4;
377   myGrav = 0.2;
378   invincible = true;
379   //viscidTop = 1;
380   carryPlayer = false;
381   walkableSolid = true;
382   imageSpeed = 0.4;
383   bossAwakePhase = 0;
385   /*
386   if (global.customLevel) {
387     status = 0;
388     sprite_index = sOlmec;
389   } else {
390     status = -1;
391   }
392   */
393   status = START1;
395   counter = 0;
396   //bounceCounter = 0;
397   slammed = false;
399   /+
400   if (!global.customLevel) {
401     view_hborder[0] = 0;
402     if (global.bizarre) view_vborder[0] = 48;/*0*/ else view_vborder[0] = 0;
403     if (global.bizarre) view_yview[0] = 272; else view_yview[0] = 400;
404     view_object[0] = oOlmec;
405   }
406   +/
410 // ////////////////////////////////////////////////////////////////////////// //
411 class ObjOlmecDebris['oOlmecDebris'] : MapObject;
414 override bool initialize () {
415   if (!::initialize()) return false;
416   switch (global.randOther(1, 3)) {
417     case 1: setSprite('sOlmecDebris'); break;
418     case 2: setSprite('sOlmecDebris2'); break;
419     case 3: setSprite('sOlmecDebris3'); break;
420   }
421   xVel = global.randOtherFloat(4)-global.randOtherFloat(4);
422   yVel = -1-global.randOtherFloat(2);
423   return true;
427 override void thinkFrame () {
428   //x += xVel;
429   //y += yVel;
430   shiftXY(xVel, yVel);
432   if (bounce) {
433     yVel = fmin(yVel+grav, 6);
434     if (level.checkTileAtPoint(ix, iy+4, delegate bool (MapTile t) { return (t.objType == 'oTemple'); })) {
435       // bounce
436       if (yVel > 1) {
437         yVel = -yVel*0.4;
438       } else {
439         level.MakeMapObject(ix, iy, 'oSmokePuff');
440         instanceRemove();
441         return;
442       }
443       // friction
444       if (fabs(xVel) < 0.1) xVel = 0; else if (fabs(xVel) != 0) xVel *= 0.3;
445     }
446   }
450 defaultproperties {
451   objName = 'Olmec Debris';
452   desc = "Debris";
453   desc2 = "Debris.";
454   depth = 1;
455   spectral = true;
457   setCollisionBounds(-4, -4, 4, 4);
458   grav = 0.6;
459   //invincible = true;
460   bounce = true;
464 // ////////////////////////////////////////////////////////////////////////// //
465 class ObjOlmecSlam['oOlmecSlam'] : MapObject;
467 //int alarmDeath = 1;
470 override bool initialize () {
471   if (!::initialize()) return false;
472   setSprite('sOlmecSlam');
473   playSound('sndSlam');
474   return true;
478 override void drawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
482 override void thinkFrame () {
483   level.checkTilesInRect(ix, iy, 64, 8, delegate bool (MapTile t) {
484     if (!t.isInstanceAlive) return false;
485     //writeln("tile: <", GetClassName(t.Class), ">; objType='", t.objType, "'");
486     //if (t.invincible) return false;
487     if (t.solid) {
488       t.smashMe();
489       //instanceRemove();
490       //return true;
491     }
492     return false;
493   });
494   //if (--alarmDeath == 0) { instanceRemove(); return; }
495   instanceRemove();
499 defaultproperties {
500   objName = 'Olmec Slam';
501   depth = 0;
502   spectral = true;
506 // ////////////////////////////////////////////////////////////////////////// //
507 class ObjCavemanWorship['oCavemanWorship'] : MapObject;
510 override bool initialize () {
511   if (!::initialize()) return false;
512   setSprite('sCavemanWorshipR');
513   return true;
517 override void thinkFrame () {
521 defaultproperties {
522   objName = 'Caveman';
523   desc = "Caveman";
524   desc2 = "A caveman, bent over in worship of the statue.";
525   depth = 66;
526   spectral = true;
527   imageSpeed = 0.25;
531 // ////////////////////////////////////////////////////////////////////////// //
532 class ObjHawkmanWorship['oHawkmanWorship'] : MapObject;
535 override bool initialize () {
536   if (!::initialize()) return false;
537   setSprite('sHawkLeft');
538   return true;
542 override void thinkFrame () {
546 defaultproperties {
547   objName = 'Hawkman';
548   desc = "Hawkman";
549   desc2 = "One of Kali's followers, this hawkman believes the statue in front of him is a sign from his goddess. He is sadly mistaken.";
550   depth = 0;
551   spectral = true;
552   imageSpeed = 1;
556 // ////////////////////////////////////////////////////////////////////////// //
557 class ItemSfxPsychicCreate2['oPsychicCreate2'] : MapObject;
559 float dirAngle;
561 override bool initialize () {
562   if (!::initialize()) return false;
563   setSprite('sPsychicCreate');
564   level.forEachObject(delegate bool (MapObject o) {
565     if (o isa EnemyOlmec) {
566       dirAngle = pointDirection(ix, iy, o.ix+32, o.iy+16);
567       return true;
568     }
569     return false;
570   });
571   return true;
575 override void onAnimationLooped () {
576   instanceRemove();
580 override void thinkFrame () {
581   shiftXY(2*cos(dirAngle), -2*sin(dirAngle));
585 defaultproperties {
586   objName = 'Psychic Wave';
587   yAcc = 0.6;
588   imageSpeed = 0.4;
589   spectral = true;
590   depth = 1;
594 // ////////////////////////////////////////////////////////////////////////// //
595 class ItemSfxYellowBall['oYellowBall'] : MapObject;
597 int trailTimer = 1;
599 override bool initialize () {
600   if (!::initialize()) return false;
601   setSprite('sYellowBall');
602   yVel = -1*(global.randOtherFloat(3)+4);
603   xVel = global.randOther(2, 5);
604   if (global.randOther(1, 2) == 1) xVel *= -1;
605   return true;
610 override void onAnimationLooped () {
611   instanceRemove();
616 override void thinkFrame () {
617   if (isOutsideOfLevel()) { instanceRemove(); return; }
619   if (--trailTimer == 0) {
620     level.MakeMapObject(ix, iy, 'oYellowTrail');
621     trailTimer = 4;
622   }
624   //shiftY(yVel);
625   shiftXY(xVel, yVel);
627   if (level.checkTilesInRect(ix-8, iy-8, 17, 17) &&
628       !level.isObjectInRect(ix-8, iy-8, 17, 17, delegate bool (MapObject o) { return (o isa EnemyOlmec); }))
629   {
630     shiftXY(-xVel, -yVel);
631     MapObject obj;
632     if (global.config.bizarre) {
633       switch (global.randOther(1, 13)) {
634         case 1: level.MakeMapObject(ix-8, iy-8, 'oBat'); break;
635         case 2: level.MakeMapObject(ix-8, iy-8, 'oSpider'); break;
636         case 3: level.MakeMapObject(ix-8, iy-8, 'oSnake'); break;
637         case 4: level.MakeMapObject(ix-8, iy-8, 'oFrog'); break;
638         case 5: level.MakeMapObject(ix-8, iy-8, 'oZombie'); break;
639         case 6: level.MakeMapObject(ix-8, iy-8, 'oFireFrog'); break; // oSkeleton
640         case 7: obj = level.MakeMapObject(ix-8, iy-8, 'oMonkey'); if (obj) obj.status = IDLE; break;
641         case 8:
642           obj = level.MakeMapObject(ix, iy-16, 'oMagma');
643           if (obj) {
644             obj.persist = true;
645             obj.hp = 10;
646           }
647           break;
648         case 9: level.MakeMapObject(ix-8, iy-8, 'oFireFrog'); break;
649         case 10: level.MakeMapObject(ix-8, iy-8, 'oCobra'); break;
650         case 11: level.MakeMapObject(ix-8, iy-8, 'oGreenFrog'); break;
651         case 12: level.MakeMapObject(ix-8, iy-10, 'oGreenSpider'); break;
652         case 13: {
653           /+!!!
654           auto di = distanceToEntityCenter(level.player)+16;
655           if (di > 48) {
656             obj = level.MakeMapObject(ix-8, iy-8, 'oFloater');
657             if (obj) {
658               obj.explosive = true;
659               obj.setSprite('sFloaterEvil');
660               obj.iVel = 2;
661               obj.slow = 0.01;
662               obj.waitMin = 1;
663               obj.waitMax = 2;
664               obj.range = 8;
665               /+!!!
666               if (rand(1,4) == 1) obj.carries = "Bomb Bag";
667               else obj.carries = choose("Big Sapphire", "Big Emerald", "Big Ruby");
668               +/
669             }
670           } else {
671             level.MakeMapObject(ix-8, iy-8, 'oFireFrog');
672           }
673           +/
674           break;
675         }
676       }
677     } else {
678       switch (global.randOther(1, 4)) {
679         case 1: level.MakeMapObject(ix-8, iy-8, 'oBat'); break;
680         case 2: level.MakeMapObject(ix-8, iy-8, 'oSpider'); break;
681         case 3: level.MakeMapObject(ix-8, iy-8, 'oSnake'); break;
682         case 4: level.MakeMapObject(ix-8, iy-8, 'oFrog'); break;
683       }
684     }
686     instanceRemove();
687     return;
688   }
690   yVel = fmin(yVel+0.15, 6);
694 defaultproperties {
695   objName = 'Yellow Ball';
696   yAcc = 0.6;
697   imageSpeed = 0.4;
698   spectral = true;
699   depth = 1;