fixed alot of warnings, and some bugs, so it can be compiled with the current vccrun
[k8vacspelynky.git] / mapent / enemies / jaws.vc
blob4519b195bde6601927da4a3653b8813ebb1f7c12
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 EnemyJaws['oJaws'] : MapEnemy;
21 const int bubbleTimerMax = 40;
22 int bubbleTimer;
23 MapObject crates[4];
24 float dirAngle;
27 override bool initialize () {
28   if (!::initialize()) return false;
29   setSprite('sJawsLeft');
30   foreach (ref auto n; crates) {
31     auto obj = level.MakeMapObject(ix, iy, 'oJawsCrate');
32     if (!obj) continue;
33     obj.active = false;
34     obj.spectral = true;
35     obj.visible = false;
36     n = obj;
37   }
38   // unstuck
39   if (isCollision()) {
40     writeln("*** JAWS STUCK!");
41     while (isCollision() && !isCollisionTop(1)) shiftY(-1);
42   }
43   return true;
47 override void onAnimationLooped () {
48   if (spriteLName == 'sJawsTurnL') {
49     dirAngle = 180;
50     setSprite('sJawsLeft');
51     status = PAUSE;
52     counter = 40;
53     setCollisionBounds(0, 0, 64, 32);
54   } else if (spriteLName == 'sJawsTurnR') {
55     dirAngle = 0;
56     setSprite('sJawsRight');
57     status = PAUSE;
58     counter = 40;
59     shiftX(48);
60     prevFltX += 48;
61     setCollisionBounds(-48, 0, 16, 32);
62   }
66 // ////////////////////////////////////////////////////////////////////////// //
67 // for bullets and whip: always use bounding box
68 //TODO: pixel-perfect with head and body
69 override bool collidesWith (MapEntity e, optional bool ignoreDims) {
70   if (!e || width < 1 || height < 1) return false;
71   if (e == self) return false; // never
72   //if (e isa WeaponWhipBase) writeln("collides with whip: ", isRectHitSimple(e.x0, e.y0, e.width, e.height));
73   //if (e isa PlayerPawn) writeln("collides with player: ", isRectHitSimple(e.x0, e.y0, e.width, e.height));
74   return isRectHitSimple(e.x0, e.y0, e.width, e.height);
78 override bool onTouchedByPlayer (PlayerPawn plr) {
79   //writeln("*** MEGAMOUTH TOUCHED THE PLAYER!");
80   if (dead || status >= STUNNED || plr.dead) return false;
82   int x = ix;
83   //int y = iy;
84   int plrx = plr.ix;
85   //int plry = plr.iy;
87   if (plr.invincible == 0) {
88     plr.blink = 30;
89     plr.invincible = 30;
90     if (plr.status != CLIMBING) {
91       plr.xVel = (plrx < x ? -6 : 6);
92     }
93     if (global.plife > 0) {
94       global.plife -= damage;
95       if (global.plife < 0) level.addDeath(objName);
96     }
97     plr.playSound('sndHurt');
98   }
99   return false; // don't skip thinker
103 // ////////////////////////////////////////////////////////////////////////// //
104 override void drawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
105   int xi, yi;
106   getInterpCoords(currFrameDelta, scale, out xi, out yi);
108   bool doMirror;
109   int fx0, fy0, fx1, fy1;
110   auto spf = getSpriteFrame(out doMirror, out fx0, out fy0, out fx1, out fy1);
111   if (!spf) return;
113   auto oclr = GLVideo.color;
114   GLVideo.color = oclr|(trunci(fclamp(255.0-255*imageAlpha, 0.0, 255.0))<<24);
116   fx0 = xi+fx0*scale-xpos;
117   fy0 = yi+fy0*scale-ypos;
118   fx1 = xi+fx1*scale-xpos;
119   fy1 = yi+fy1*scale-ypos;
120   if (!doMirror) {
121     spf.tex.blitExt(fx0, fy0, fx1, fy1, 0, 0, spf.width, spf.height, angle:imageAngle);
122   } else {
123     spf.tex.blitExt(fx0, fy0, fx1, fy1, spf.width, 0, 0, spf.height, angle:imageAngle);
124   }
126   name jbody;
127   int xsign = 0;
129   if (spriteLName == 'sJawsLeft') {
130     xsign = 16;
131          if (hp < 10) jbody = 'sJawsBody3L';
132     else if (hp < 20) jbody = 'sJawsBody2L';
133     else jbody = 'sJawsBody1L';
134   } else if (spriteLName == 'sJawsRight') {
135     xsign = -48;
136          if (hp < 10) jbody = 'sJawsBody3R';
137     else if (hp < 20) jbody = 'sJawsBody2R';
138     else jbody = 'sJawsBody1R';
139   }
141   if (xsign) {
142     auto spr = level.sprStore[jbody];
143     if (spr) {
144       // it has only one frame, nobody cares
145       spf = spr.frames[0];
146       spf.blitAt(fx0+xsign*scale, fy0, scale:scale, angle:imageAngle);
147     }
148   }
150   GLVideo.color = oclr;
152   if (false) {
153     oclr = GLVideo.color;
154     GLVideo.color = 0xff_ff_00;
155     GLVideo.drawRect(x0*scale-xpos, y0*scale-ypos, width*scale, height*scale);
156     GLVideo.color = 0x00_ff_00;
157     GLVideo.drawRect(ix*scale-xpos, iy*scale-ypos, 2, 2);
159     if (isCollision()) {
160       GLVideo.color = 0x3f_ff_00_00;
161       GLVideo.fillRect(x0*scale-xpos, y0*scale-ypos, width*scale, height*scale);
162     }
164     GLVideo.color = oclr;
165   }
169 // ////////////////////////////////////////////////////////////////////////// //
170 // WARNING: no checks!
171 void turnLeft () {
172   status = TURN;
173   dirAngle = 180;
174   fltx -= 48;
175   prevFltX -= 48;
176   setCollisionBounds(0, 0, 64, 32);
177   setSprite('sJawsTurnL');
178   imageFrame = 0;
182 // WARNING: no checks!
183 void turnRight () {
184   status = TURN;
185   dirAngle = 0;
186   setSprite('sJawsTurnR');
187   imageFrame = 0;
191 // ////////////////////////////////////////////////////////////////////////// //
192 override void thinkFrame () {
193   //::thinkFrame();
194   //if (!isInstanceAlive) return;
196   if (!level.isWaterAtPoint(ix+8, iy+16)) hp -= 1;
198   if (hp < 1) {
199     if (countsAsKill) level.addKill(objName);
200     foreach (; 0..4) scrCreateBlood(ix+22+global.randOther(0, 4), iy+14+global.randOther(0, 4), 1);
201     foreach (; 0..4) level.MakeMapObject(ix+22+global.randOther(0, 4), iy+14+global.randOther(0, 6), 'oBone');
202     // drop crates
203     foreach (ref auto cobj; crates) {
204       if (!cobj) continue;
205       auto obj = cobj;
206       cobj = none;
207       obj.fltx = ix+16;
208       obj.flty = iy+16;
209       obj.xVel = global.randOther(0, 3)-global.randOther(0, 3);
210       obj.yVel = -global.randOther(1, 2);
211       obj.active = true;
212       obj.spectral = false;
213       obj.visible = true;
214     }
215     /*
216     if (carries != "") {
217       if (holds != "") scrMakeItem(carries, x+16, y+16, 1); else scrMakeItem(carries, x+16, y+16);
218     }
219     */
220     //!!with (oBossBlock) dying = true;
221     instanceRemove();
222     return;
223   }
225   //auto dist = pointDistance(ix, iy, level.player.ix, level.player.iy);
227   if (status == IDLE) {
228     if (dirAngle == 0) {
229       // right
230       if (level.isWaterAtPoint(ix+18, iy+16) && !level.isSolidAtPoint(ix+18, iy+16)) {
231         moveRel(2, 0);
232       } else if (!level.checkTilesInRect(ix-32, iy, 33, 33)) {
233         turnLeft();
234       }
235     } else {
236       if (level.isWaterAtPoint(ix-2, iy+16) && !level.isSolidAtPoint(ix-2, iy+16)) {
237         moveRel(-2, 0);
238       } else if (!level.checkTilesInRect(ix+16, iy, 33, 33)) {
239         turnRight();
240       }
241     }
242     if (!isCollisionBottom(2)) flty += 1;
243     if (level.player.swimming && !level.player.dead) status = ATTACK;
244   } else if (status == PAUSE) {
245     if (counter > 0) {
246       --counter;
247     } else {
248       status = IDLE;
249       dirAngle = (dirAngle > 90 && dirAngle < 270 ? 180 : 0);
250     }
251   } else if (status == ATTACK) {
252     if (level.player.swimming && !level.player.dead) {
253       if (spriteLName == 'sJawsLeft' || spriteLName == 'sJawsRight') {
254         dirAngle = pointDirection(ix+8, iy+16, level.player.ix, level.player.iy-8);
255       }
256       bool turn = false;
257       if (level.player.ix < ix+8) {
258         if (spriteLName == 'sJawsRight' && !level.checkTilesInRect(ix-32, iy, 33, 33)) {
259           turnLeft();
260           turn = true;
261         }
262       } else {
263         if (spriteLName == 'sJawsLeft' && !level.isSolidAtPoint(ix-2, iy+16)) {
264           turnRight();
265           turn = true;
266         }
267       }
268       if (!turn) {
269         if (level.isWaterAtPoint(roundi(fltx+cos(dirAngle)), roundi(flty-sin(dirAngle))) &&
270             !level.isSolidAtPoint(roundi(fltx+cos(dirAngle)), roundi(flty-sin(dirAngle))))
271         {
272           moveRel(3*cos(dirAngle), -3*sin(dirAngle));
273         }
274       }
275     } else {
276       status = IDLE;
277       dirAngle = (dirAngle > 90 && dirAngle < 270 ? 180 : 0);
278     }
279   }
281   if (bubbleTimer > 0) {
282     --bubbleTimer;
283   } else {
284     if (level.isWaterAtPoint(ix, (iy&~0x0f)-8)) level.MakeMapObject(ix, iy+16, 'oBubble');
285     bubbleTimer = bubbleTimerMax;
286   }
288        if (spriteLName == 'sJawsLeft') setCollisionBounds(0, 0, 64, 32);
289   else if (spriteLName == 'sJawsRight') setCollisionBounds(-48, 0, 16, 32);
293 defaultproperties {
294   objName = 'Megamouth';
295   desc = "Megamouth";
296   desc2 = "A rare, enormous variety of piranha with fangs as large as human arm bones. It can eat entire boats, the contents of which are sometimes found in its stomach.";
297   setCollisionBounds(0, 0, 48, 32);
298   imageSpeed = 0.5;
299   //origX = 0;
300   //origY = 0;
301   xVel = 0;
302   yVel = 0;
303   xAcc = 0.2;
304   yAcc = 0.2;
305   dirAngle = 180;
307   bubbleTimer = 0;
309   status = IDLE;
310   hp = 40;
312   canPickUp = false;
314   doBasicPhysics = false;
315   flying = true; // actually, we are swimming, but meh
316   liveInWater = true;
318   countsAsKill = true;
319   leavesBody = true; // we will do our own death sequence
321   depth = 40;