1 /**********************************************************************************
2 * Copyright (c) 2008, 2009 Derek Yu and Mossmouth, LLC
3 * Copyright (c) 2010, Moloch
4 * Copyright (c) 2018, Ketmar Dark
6 * This file is part of Spelunky.
8 * You can redistribute and/or modify Spelunky, including its source code, under
9 * the terms of the Spelunky User License.
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.
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/>
18 **********************************************************************************/
19 // ////////////////////////////////////////////////////////////////////////// //
20 class EnemyGiantSpiderHang['oGiantSpiderHang'] : MapEnemy;
23 bool createWeb = true;
33 setSprite('sGiantSpider');
35 setCollisionBounds(2, 16, 30, 32);
44 squirtTimer = global.randOther(100, 500);
49 override bool initialize () {
50 if (!::initialize()) return false;
51 writeln("*** GIANT SPIDER GENERATED");
52 setSprite('sGiantSpiderHang');
54 level.MakeMapObject(ix, iy+16, 'oWeb');
55 level.MakeMapObject(ix+16, iy+16, 'oWeb');
61 override void onAnimationLooped () {
62 if (spriteLName == 'sGiantSpiderFlip') {
63 setSprite('sGiantSpider');
65 } else if (spriteLName == 'sGiantSpiderSquirt') {
72 override bool onInsideBlock (MapTile block) {
73 // if we are inside a block, die
74 if (hp < 1) return false;
80 override bool onTouchedByPlayer (PlayerPawn plr) {
81 if (plr.dead || dead) return false;
83 // jumped on - oCaveman, oManTrap replaces this script with its own
84 if (abs(plr.ix-(ix+16)) > 16) {
86 } else if ((plr.status == JUMPING || plr.status == FALLING) && plr.iy < iy+16 && !plr.swimming) {
87 writeln("touched by player!");
88 plr.yVel = -6-0.2*plr.yVel;
89 if (global.hasSpikeShoes) {
90 hp -= trunci(3*(ffloor(plr.fallTimer/16.0)+1));
93 hp -= trunci(1*(ffloor(plr.fallTimer/16.0)+1));
97 plr.playSound('sndHit');
98 } else if (plr.invincible == 0) {
101 if (plr.iy < iy) plr.yVel = -6;
102 plr.xVel = (plr.ix < ix ? -6 : 6);
103 if (global.plife > 0) {
104 global.plife -= damage;
105 if (global.plife <= 0 /*and isRealLevel()*/) level.addDeath(objName);
107 plr.playSound('sndHurt');
110 return false; // don't skip thinker
114 // return `false` to do standard weapon processing
115 override bool onTouchedByPlayerWeapon (PlayerPawn plr, PlayerWeapon wpn) {
116 if (wpn !isa WeaponWhip) return false;
117 if (hp > 0 && !dead && !stunned && status < STUNNED) wpn.hitEnemy = true;
119 if (invincible <= 0) {
121 spillBlood(amount:1);
122 plr.playSound('sndHit');
129 void createPaste () {
132 /*auto obj =*/ level.MakeMapObject(ix+16, iy+24, 'oPaste');
139 spillBlood(amount:4);
140 if (countsAsKill) level.addKill(objName);
141 //!!!with (oBossBlock) dying = true;
142 foreach (; 0..global.randOther(1, 3)) {
143 MapObject gem = none;
144 switch (global.randOther(1, 3)) {
145 case 1: gem = level.MakeMapObject(ix+16, iy+24, 'oEmeraldBig'); break;
146 case 2: gem = level.MakeMapObject(ix+16, iy+24, 'oSapphireBig'); break;
147 case 3: gem = level.MakeMapObject(ix+16, iy+24, 'oRubyBig'); break;
150 gem.xVel = global.randOther(0, 3)-global.randOther(0, 3);
158 override bool onHitByItem (MapItem item) {
159 // item does no damage?
160 if (!item.damage) return false;
162 if (status == HANG) {
164 setSprite('sGiantSpiderFlip');
165 playSound('sndGiantSpider');
168 return ::onHitByItem(item);
172 override void thinkFrame () {
174 if (!isInstanceAlive) return;
176 if (status == HANG) {
179 if (level.isSolidAtPoint(x+16, y+8)) hp = 0;
180 if (hp < 1) { onDead(); return; }
182 if (hp < 10 || !level.isSolidAtPoint(x, y-16) ||
183 (level.player.iy > y && abs(level.player.ix-(x+16)) < 8 && distanceToEntityCenter(level.player) < 90) ||
184 level.isObjectInRect(x0, y0, width, height, delegate bool (MapObject o) { return (o isa ItemBomb); }, castClass:ItemBomb))
187 setSprite('sGiantSpiderFlip');
188 playSound('sndGiantSpider');
192 if (status != HANG) {
194 if (alarm0Left > 0) {
195 if (--alarm0Left == 0) {
196 if (spriteLName != 'sGiantSpiderSquirt') {
198 if (isCollisionBottom(1)) {
199 setSprite('sGiantSpider'); //k8:???
200 yVel = -global.randOther(2, 5);
201 xVel = (level.player.ix < ix+16 ? -2.5 : 2.5);
203 setSprite('sGiantSpiderJump');
210 if (level.isSolidAtPoint(ix+16, iy+24)) {
211 writeln("solid kill");
218 writeln("solid kill");
226 yVel = fmin(yVelLimit, yVel+myGrav);
233 if (isCollisionRight(1)) xVel = 1;
234 if (isCollisionLeft(1)) xVel = -1;
236 if (status != CRAWL && isCollisionTop(1) && isCollisionBottom(1)) {
238 xVel = (level.player.ix < x+16 ? -1 : 1);
241 if (squirtTimer > 0) squirtTimer -= 1;
243 if (status == IDLE) {
244 if (spriteLName != 'sGiantSpiderFlip') setSprite('sGiantSpider');
245 alarm0Left = global.randOther(5, 20);
246 status = (squirtTimer == 0 ? SQUIRT : RECOVER);
247 } else if (status == CRAWL) {
248 setSprite('sGiantSpiderCrawl');
249 if (!isCollisionTop(1) || !isCollisionBottom(1)) status = IDLE;
250 else if (isCollisionRight(1)) xVel = -1;
251 else if (isCollisionLeft(1)) xVel = 1;
252 } else if (status == SQUIRT) {
253 setSprite('sGiantSpiderSquirt');
254 if (imageFrame >= 5 && squirtTimer == 0) {
255 level.MakeMapObject(x+16, y+16, 'oWebBall');
256 squirtTimer = (global.config.bizarre ? global.randOther(100, 500) : global.randOther(100, 1000));
258 } else if (status == RECOVER) {
259 if (isCollisionBottom(1)) xVel = 0;
260 } else if (status == BOUNCE && distanceToEntityCenter(level.player) < 120) {
261 if (isCollisionBottom(1)) {
262 setSprite('sGiantSpider');
263 yVel = -global.randOther(3, 6);
264 xVel = (level.player.ix < x+16 ? -2.5 : 2.5);
265 playSound('sndSpiderJump');
266 if (global.randOther(1, 4) == 1) { status = IDLE; xVel = 0; yVel = 0; }
268 setSprite('sGiantSpiderJump');
270 } else if (status != DROWNED) {
275 if (isCollisionTop(1)) yVel = 1;
276 //if (isCollisionLeft(1) or isCollisionRight(1)) xVel = -xVel; // in the original
277 //if (isCollisionSolid()) y -= 2; // in the original
283 objName = 'Giant Spider';
285 desc = "Giant Spider";
286 desc2 = "An overgrown spider with the ability to launch a sticky web at its prey.";
288 setCollisionBounds(0, 0, 32, 16);
295 hp = 10; //k8: 1 for non-hanging giant spider; wtf?!
302 //squirtTimer = rand(100, 500);
316 doBasicPhysics = false;
317 leavesBody = true; // we'll do our own low hp processing
327 // ////////////////////////////////////////////////////////////////////////// //
328 class EnemyGiantSpider['oGiantSpider'] : EnemyGiantSpiderHang;
331 override bool initialize () {
332 if (!::initialize()) return false;