fixes for API renaming
[k8vacspelynky.git] / mapent / tiles / arrowtraps.vc
blob131eeb1913606e698d4842422c158201aecb01ba
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 MapTileArrowTrap : MapTile abstract;
21 const int range = 90;
22 const int vel = 8;
23 //const name soundName = 'sndArrowTrap';
24 bool fired;
25 bool repeater;
26 int fireTimer = -1;
27 int recheckTimer = -1;
28 int checkerX, checkerY, checkerW; // checkerW == 0 means "setup it now"
29 const int checkerH = 16;
30 const int checkerDuckOfs = -2;
32 int dir = Dir.Left;
35 override bool smashMe () {
36   auto res = ::smashMe();
37   if (res && !isInstanceAlive && !fired) {
38     level.MakeMapObject(ix+8, iy+8, 'oArrow');
39   }
40   return res;
44 override void setupTile () {
45   if (global.cityOfGold == 1) {
46     ore = 2;
47   } else {
48     ore = 0;
49   }
50 /*!destroying
51     if (global.cityOfGold != 1)
52     {
53         if (smashed) scrSprayRubble(3, sRubbleTan, sRubbleTanSmall);
54         scrDropRubble(3, sRubbleTan, sRubbleTanSmall);
55         if (fired == 0) instance_create(x+8, y+8, projectile);
56     }
57     else
58     {
59         for (i = 0; i &lt; 3; i += 1)
60         {
61             gold = instance_create(x+8+rand(0,4)-rand(0,4), y+8+rand(0,4)-rand(0,4), oGoldChunk);
62             gold.xVel = rand(0,3) - rand(0,3);
63             gold.yVel = rand(2,4) * 1;
64         }
65         gold = instance_create(x+8+rand(0,4)-rand(0,4), y+8+rand(0,4)-rand(0,4), oGoldNugget);
66         gold.xVel = rand(0,3) - rand(0,3);
67         gold.yVel = rand(2,4) * 1;
68         if (fired == 0) instance_create(x+8, y+8, projectile);
69     }
75 override void drawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
76   ::drawWithOfs(xpos, ypos, scale, currFrameDelta);
77   if (!fired) {
78     auto oclr = GLVideo.color;
79     GLVideo.color = 0x7f_00_ff_00;
80     GLVideo.fillRect(checkerX*scale-xpos, checkerY*scale-ypos, checkerW*scale, checkerH*scale);
81     GLVideo.color = oclr;
82   }
87 transient MapTile nearestSolid;
89 final void setupChecker () {
90   // calculate how far to motion detect
91   //writeln("setting up sensor area for arrow trap at (", ix/16, ",", iy/16, ")");
92   nearestSolid = none;
93   if (dir == Dir.Right) {
94     level.checkTilesInRect(ix+width, iy, range, height, delegate bool (MapTile t) {
95       if (t == self || !t.solid || !t.visible) return false;
96       if (!nearestSolid || nearestSolid.ix-ix > t.ix-ix) nearestSolid = t;
97       return false;
98     });
99     checkerX = ix+width;
100     checkerY = iy;
101     checkerW = max(1, (nearestSolid ? nearestSolid.ix-checkerX : range));
102   } else {
103     level.checkTilesInRect(ix-range, iy, range, height, delegate bool (MapTile t) {
104       if (t == self || !t.solid || !t.visible) return false;
105       if (!nearestSolid || ix-nearestSolid.x1 > ix-t.x1) nearestSolid = t;
106       return false;
107     });
108     checkerX = (nearestSolid ? nearestSolid.x1+1 : ix-range);
109     checkerY = iy;
110     checkerW = max(1, ix-checkerX);
111   }
115 final void fireProjectile () {
116   if (fired) return;
117   fired = true;
118   playSound('sndArrowTrap');
119   auto arrow = level.MakeMapObject(ix+(dir == Dir.Left ? -2-3 : 18+3), iy+4, 'oArrow');
120   arrow.dir = dir;
121   float xvel = fclamp(vel, 0, 24);
122   arrow.xVel = (dir == Dir.Left ? -xvel : xvel);
123   if (repeater) {
124     fireTimer = global.randOther(100, 200);
125   }
129 override void thinkFrame () {
130   if (repeater) {
131     if (fireTimer < 0) fireTimer = global.randOther(100, 200);
132     if (--fireTimer <= 0) {
133       fired = false;
134       fireProjectile();
135     }
136     return;
137   }
139   if (fired) return; // come along, nothing to see here
141   if (level.someTilesRemoved && recheckTimer < 0) recheckTimer = global.randOther(4, 8);
142   if (recheckTimer >= 0) --recheckTimer;
144   if (checkerW == 0 || recheckTimer == 0) setupChecker();
146   auto plr = level.player;
148   if (plr.imageFrame > 6 && (plr.spriteLName == 'sDuckToHangL' || plr.spriteLName == 'sDamselDtHL' || plr.spriteLName == 'sTunnelDtHL')) {
149     /*
150     writeln("*** DTH! xVel=", plr.xVel, "; yVel=", plr.yVel, "; ifrm=", trunc(plr.imageFrame));
151     writeln("me:(", checkerX, ",", checkerY, ")-(", checkerX+15, ",", checkerY+15, "); plr:(", plr.x0, ",", plr.y0, ")-(", plr.x1, ",", plr.y1, ")");
152     if (plr.isRectCollision(checkerX, checkerY, checkerW, checkerH)) {
153       writeln("   HIT!!!!!!!!!!");
154     }
155     */
156     // -2, due to ducking hitbox
157     if (plr.isRectCollision(checkerX, checkerY+checkerDuckOfs, checkerW, checkerH)) {
158       //writeln("!!!!!!");
159       fireProjectile();
160       return;
161     }
162   }
164   if (plr.xVel != 0 || plr.yVel != 0) {
165     if (plr.isRectCollision(checkerX, checkerY, checkerW, checkerH)) {
166       //writeln("xVel=", plr.xVel, "; yVel=", plr.yVel);
167       fireProjectile();
168       return;
169     }
170   }
172   /*
173   if (xspider) {
174     auto o = EnemySpiderHang(xspider);
175     //writeln("o: ", GetClassName(o.Class), "; xVel=", o.xVel, "; yVel=", o.yVel, "; status=", o.status);
176     writeln("o:(", o.x0, ",", o.y0, ")-(", o.x1, ",", o.y1, "); checker:(", checkerX, ",", checkerY, ")-(", checkerX+checkerW-1, ",", checkerY+checkerH-1, ")");
177   }
178   */
180   MapEntity hitDetected = level.forEachObjectInRect(checkerX, checkerY, checkerW, checkerH, delegate bool (MapObject o) {
181     // also:
182     //   boulder
183     //   laser
184     //   moveable solid
185     if (o.spectral || !o.visible) return false;
186     if (global.config.bomsDontSetArrowTraps && (o isa ItemBomb || o isa ItemRopeThrow)) {
187       // armed == true for rope means "it is launched by a player"
188       if (!o.armed) return false;
189     }
190     if (o isa MapEnemy || o isa MapItem) {
191       //if (/*!xspider &&*/ o isa EnemySpiderHang) xspider = o;
192       //writeln("o: ", GetClassName(o.Class), "; xVel=", o.xVel, "; yVel=", o.yVel, "; status=", o.status);
193       if (o.xVel != 0 || o.yVel != 0) {
194         return o.isRectCollision(checkerX, checkerY, checkerW, checkerH);
195       }
196     }
197     return false;
198   }, precise:false);
200   if (!hitDetected) {
201     // moveable block or rope should trigger it too
202     hitDetected = level.checkTilesInRect(checkerX, checkerY, checkerW, checkerH, delegate bool (MapTile o) {
203       if (o.visible && !o.spectral) {
204         if ((o.solid && (o.xVel != 0 || o.yVel != 0)) || o isa MapTileRope) {
205           return o.isRectCollision(checkerX, checkerY, checkerW, checkerH);
206         }
207       }
208       return false;
209     }, precise:false);
210   }
212   if (hitDetected) {
213     writeln("arrow trap detected ", GetClassName(hitDetected.Class), "(", hitDetected.objType, ":", hitDetected.objName, ")");
214     fireProjectile();
215   }
219 defaultproperties {
220   //hasThinker = true;
221   hideOre = true;
222   toSpecialGrid = true; // it need to think, so...
226 // ////////////////////////////////////////////////////////////////////////// //
227 class MapTileArrowTrapLeft['oArrowTrapLeft'] : MapTileArrowTrap;
230 override void setupTile () {
231   ::setupTile();
232   objType = 'oArrowTrap';
233   spriteName = (global.cityOfGold == 1 ? 'sArrowTrapGoldLeft' : 'sArrowTrapLeft');
234   dir = Dir.Left;
238 // ////////////////////////////////////////////////////////////////////////// //
239 class MapTileArrowTrapRight['oArrowTrapRight'] : MapTileArrowTrap;
242 override void setupTile () {
243   objType = 'oArrowTrap';
244   spriteName = (global.cityOfGold == 1 ? 'sArrowTrapGoldRight' : 'sArrowTrapRight');
245   dir = Dir.Right;
249 // ////////////////////////////////////////////////////////////////////////// //
250 class MapTileArrowTrapLeftLit['oArrowTrapLeftLit'] : MapTileArrowTrapLeft;
252 defaultproperties {
253   lightRadius = 32;
257 // ////////////////////////////////////////////////////////////////////////// //
258 class MapTileArrowTrapRightLit['oArrowTrapRightLit'] : MapTileArrowTrapRight;
260 defaultproperties {
261   lightRadius = 32;
265 // ////////////////////////////////////////////////////////////////////////// //
266 class MapTileArrowRepeaterLeft['oArrowRepeaterL'] : MapTileArrowTrapLeft;
269 override void setupTile () {
270   ::setupTile();
273 defaultproperties {
274   desc = "Arrow Repeater";
275   desc2 = "This trap is armed with a large supply of arrows that it shoots in a slow steam.";
276   repeater = true;
280 // ////////////////////////////////////////////////////////////////////////// //
281 class MapTileArrowRepeaterRight['oArrowRepeaterR'] : MapTileArrowTrapRight;
284 override void setupTile () {
285   ::setupTile();
289 defaultproperties {
290   desc = "Arrow Repeater";
291   desc2 = "This trap is armed with a large supply of arrows that it shoots in a slow steam.";
292   repeater = true;