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 class TitleTileBase : MapTile;
23 override int width () { return tW; }
24 override int height () { return tH; }
26 override void setupTile () {
29 override bool initialize () {
30 if (!::initialize()) return false;
31 auto spr = getSprite();
32 if (spr && spr.frames.length) {
33 tW = spr.frames[0].tex.width;
34 tH = spr.frames[0].tex.height;
35 //writeln("tile '", objType, "': ", tW, "x", tH);
41 // ////////////////////////////////////////////////////////////////////////// //
42 class TitleTileBG['oTitleBG'] : TitleTileBase;
46 invincible = true; //???
48 spriteName = 'sTitleBG';
54 // ////////////////////////////////////////////////////////////////////////// //
55 class TitleTileLogo['oTitleLogo'] : TitleTileBase;
61 final void removeTilesAtPix (int px, int py) {
63 auto obj = level.checkTileAtPoint(px, py, delegate bool (MapTile t) { return true; });
70 void removeLadderAndBricks () {
71 ladderCreated = false;
72 bricksCreated = false;
73 foreach (int tn; 5..12) removeTilesAtPix(352+8, tn*16+8);
74 removeTilesAtPix(320+8, 96+8);
75 removeTilesAtPix(336+8, 96+8);
79 void createLadder () {
80 if (ladderCreated) return;
81 removeLadderAndBricks();
82 foreach (int tn; 5..12) {
83 if (global.config.gameMode != GameConfig::GameMode.Vanilla && !global.config.bizarrePlusTitle) {
84 level.MakeMapTileAtPix(352, tn*16, (tn == 5 ? 'oVineTop' : 'oVine'));
87 level.MakeMapTileAtPix(352, tn*16, (tn == 6 ? 'oLadderOrange' : tn == 7 ? 'oLadderTop' : 'oLadderOrange'));
95 void createBricks () {
96 if (bricksCreated) return;
97 removeLadderAndBricks();
98 level.MakeMapTileAtPix(320, 96, 'oBrick');
99 level.MakeMapTileAtPix(336, 96, 'oBrick');
100 bricksCreated = true;
107 override void thinkFrame () {
108 if (global.config.scumUnlocked || level.stats.isAnyTunnelActive) {
109 //level.setRoomBounds(0, 0, 624, 232-16);
110 //level.setRoomBounds(624-320+16, 0, 624, 232-16);
113 //level.setRoomBounds(624-320+16, 0, 624, 232-16);
116 if (global.config.scumBallAndChain) level.player.spawnBallAndChain(); else level.player.removeBallAndChain();
119 if (level.player.ix <= 624-320+16 && level.player.dir == Dir.Left) {
120 level.setRoomBounds(0, 0, 624, 232-16);
121 level.cameraSlideToPoint(0, 0, 16, 0);
123 } else if (level.player.ix >= 320 && level.player.dir == Dir.Right) {
124 level.setRoomBounds(0, 0, 624, 232-16);
125 level.cameraSlideToPoint(624-320+16, 0, 16, 0);
130 if (level.cameraSlideToSpeed.x != 0) {
131 if (level.cameraSlideToCurr.x == level.cameraSlideToDest.x) level.cameraSlideToSpeed.x = 0;
133 if (level.cameraSlideToSpeed.x == 0) {
134 if (slidingDir < 0) {
135 level.setRoomBounds(0, 0, 320-16, 232-16);
137 level.setRoomBounds(624-320+16, 0, 624, 232-16);
146 override void drawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
147 if (invisible || !visible) return;
148 ::drawWithOfs(xpos, ypos, scale, currFrameDelta);
151 int x = ix*scale-xpos;
152 int y = iy*scale-ypos;
153 int tCol = 0x93_53_11; // light dirt
154 if (global.config.gameMode != GameConfig::GameMode.Vanilla) {
155 tCol = (global.config.bizarrePlusTitle ? 0x25_59_8e : 0x78_61_21);
158 auto spf = getSpriteFrame();
159 GLVideo.color = tCol;
160 spf.blitAt(x, y, scale);
163 auto oclr = GLVideo.color;
164 GLVideo.color = 0x8f_81_5b;
165 level.sprStore.loadFont('sFontSmall');
166 level.sprStore.renderText((ix+110)*scale-xpos, (iy+26)*scale-ypos, "K8", scale);
167 //writeln("(", ix, ",", iy, ") : (526, 106)");
168 string bdate = level.getBuildDateString();
170 level.sprStore.loadFont('sFontTiny');
171 GLVideo.color = 0x8f_81_5b;
172 level.sprStore.renderText((ix+110-32)*scale-xpos, (iy+26+8)*scale-ypos, bdate, scale);
175 if (level.loserGPU) {
176 GLVideo.color = 0xff_00_00;
177 level.sprStore.loadFont('sFontSmall');
178 level.sprStore.renderText((ix-32)*scale-xpos, (iy+32)*scale-ypos, "YOUR GPU SUX!", scale);
179 level.sprStore.renderText((ix-32)*scale-xpos, (iy+32+8)*scale-ypos, "DARKNESS WILL LOOK UGLY!", scale);
182 GLVideo.color = oclr;
187 objType = 'oTitleLogo';
188 invincible = true; //???
190 spriteName = 'sTitle';
191 toSpecialGrid = true;
195 // ////////////////////////////////////////////////////////////////////////// //
196 class TitleTileTitleGlow['oTitleGlow'] : TitleTileBase;
203 override void setupTile () {
204 timerGlow = global.randOther(3, 8)*30;
208 override void onAnimationLooped () {
210 timerGlow = global.randOther(3, 8)*30;
214 override void thinkFrame () {
216 if (--timerBurn == 0) {
219 if (global.randOther(1, 2) == 1) playSound('sndIgnite');
220 level.MakeMapObject(ix+4+global.randOther(1, 8), iy+global.randOther(1, 15), 'oBurn');
221 level.MakeMapObject(ix+global.randOther(1, 8), iy+10+global.randOther(1, 6), 'oBurn');
222 level.MakeMapObject(ix+8+global.randOther(1, 8), iy+10+global.randOther(1, 6), 'oBurn');
223 timerBurn = global.randOther(10, 20);
232 if (--timerGlow == 0) {
233 imageSpeed = global.randOther(2, 4)/10.0;
237 // open test chamber door
238 auto hit = level.player.holdItem;
239 if (hit isa ItemFlare && hit.isRectHitSimple(x0, y0, width, height)) {
240 level.player.holdItem = none;
241 level.MakeMapObject(ix+4+global.randOther(1, 8), iy+4+global.randOther(1, 8), 'oBurn');
242 level.MakeMapObject(ix+2+global.randOther(1, 4), iy+10+global.randOther(1, 4), 'oBurn');
243 level.MakeMapObject(ix+10+global.randOther(1, 4), iy+10+global.randOther(1, 4), 'oBurn');
244 foreach (; 0..5) level.MakeMapObject(ix+8, iy+8, 'oFlame');
245 level.MakeMapTileAtPix(ix, iy, 'oXDebug');
246 playSound('sndChestOpen');
247 global.testChamberRevealed = true;
249 timerBurn = global.randOther(10, 15);
250 hit.instanceRemove();
256 objType = 'oTitleGlow';
257 invincible = true; //???
259 spriteName = 'sTitleGlow';
260 toSpecialGrid = true;
266 // ////////////////////////////////////////////////////////////////////////// //
267 class TitleTileTextSigns : TitleTileBase;
270 invincible = true; //???
272 toSpecialGrid = true;
277 // ////////////////////////////////////////////////////////////////////////// //
278 class TitleTileQuitSign['oQuitSign'] : TitleTileTextSigns;
281 objType = 'oQuitSign';
282 spriteName = 'sQuitSign';
286 // ////////////////////////////////////////////////////////////////////////// //
287 class TitleTileTutorialSign['oTutorialSign'] : TitleTileTextSigns;
290 objType = 'oTutorialSign';
291 spriteName = 'sTutorialSign';
295 // ////////////////////////////////////////////////////////////////////////// //
296 class TitleTileDoorArcs : TitleTileBase;
299 invincible = true; //???
301 toSpecialGrid = true;
307 // ////////////////////////////////////////////////////////////////////////// //
308 class TitleTileLevel5Sign['oLevel5Sign'] : TitleTileDoorArcs;
311 objType = 'oLevel5Sign';
312 spriteName = 'sLevel5Sign';
316 class TitleTileLevel9Sign['oLevel9Sign'] : TitleTileDoorArcs;
319 objType = 'oLevel9Sign';
320 spriteName = 'sLevel9Sign';
324 class TitleTileLevel13Sign['oLevel13Sign'] : TitleTileDoorArcs;
327 objType = 'oLevel13Sign';
328 spriteName = 'sLevel13Sign';
332 // ////////////////////////////////////////////////////////////////////////// //
333 class TitleTileStartSignBig['oStartSignBig'] : TitleTileDoorArcs;
336 objType = 'oStartSignBig';
337 invincible = true; //???
339 spriteName = 'sStartSignBig';
340 toSpecialGrid = true;
344 // ////////////////////////////////////////////////////////////////////////// //
345 class TitleTileScoresSign['oScoresSign'] : TitleTileDoorArcs;
348 objType = 'oScoresSign';
349 spriteName = 'sScoresSign';
353 // ////////////////////////////////////////////////////////////////////////// //
354 class TitleTileModeSign['oModeSign'] : TitleTileDoorArcs;
357 objType = 'oModeSign';
358 spriteName = 'sModeSign';
362 // ////////////////////////////////////////////////////////////////////////// //
363 class TitleTileCopy['oCopy'] : TitleTileBase;
366 override void drawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
367 if (invisible || !visible) return;
369 int x = ix*scale-xpos;
370 int y = iy*scale-ypos;
371 int tCol = 0x93_53_11; // light dirt
372 if (global.config.gameMode != GameConfig::GameMode.Vanilla) {
373 tCol = (global.config.bizarrePlusTitle ? 0x25_59_8e : 0x78_61_21);
376 auto spf = getSpriteFrame();
377 auto oclr = GLVideo.color;
378 GLVideo.color = tCol;
379 spf.blitAt(x, y, scale);
380 GLVideo.color = oclr;
386 invincible = true; //???
388 spriteName = 'sCopy';
390 toSpecialGrid = true;
395 // ////////////////////////////////////////////////////////////////////////// //
396 class TitleTileExitBase : TitleTileBase;
398 override void setupTile () {
402 spriteName = 'sExit';
403 invincible = true; //???
405 toSpecialGrid = true;
411 // ////////////////////////////////////////////////////////////////////////// //
412 class TitleTileXTitle['oXTitle'] : TitleTileExitBase;
414 override string getExitMessage () {
415 return "RETURN TO TITLE SCREEN.";
420 objType = 'oXTitile';
424 // ////////////////////////////////////////////////////////////////////////// //
425 class TitleTileXTutorial['oXTutorial'] : TitleTileExitBase;
427 override string getExitMessage () {
428 return "BASIC TUTORIAL.";
431 override void setupTile () {
436 objType = 'oXTutorial';
438 //spriteName = 'sEntrance'; // alas
442 // ////////////////////////////////////////////////////////////////////////// //
443 class TitleTileXScores['oXScores'] : TitleTileExitBase;
446 override string getExitMessage () {
447 return "CHALLENGE CHAMBERS.";
452 objType = 'oXScores';
454 //spriteName = 'sEntrance'; // alas
458 // ////////////////////////////////////////////////////////////////////////// //
459 class TitleTileXMode['oXMode'] : TitleTileExitBase;
465 spriteName = 'sEntrance'; // alas
469 // ////////////////////////////////////////////////////////////////////////// //
470 class TitleTileXDebug['oXDebug'] : TitleTileExitBase;
476 spriteName = 'sEntrance'; // alas
480 // ////////////////////////////////////////////////////////////////////////// //
481 class TitleTileShortcutDoor : TitleTileExitBase;
486 bool isAllowed () { return true; }
488 override bool isExitActive () { return (visible && isAllowed()); }
491 override void thinkFrame () {
492 if (global.config.scumUnlocked || isAllowed()) {
508 // ////////////////////////////////////////////////////////////////////////// //
509 class TitleTileXStart['oXStart'] : TitleTileShortcutDoor;
512 override string getExitMessage () {
513 return "START NEW GAME.";
517 override void snapToExit (MapEntity e) {
518 if (!e || !e.isInstanceAlive) return;
519 if (e.ix < ix+14) e.fltx = ix+14;
520 else if (e.ix+14 > ix+width) e.fltx = ix+width-14;
527 //<origin x="16" y="32"/>
530 spriteName = 'sBigExit';
532 ignoreFrameOffsetX = true;
533 ignoreFrameOffsetY = true;
537 // ////////////////////////////////////////////////////////////////////////// //
538 class TitleTileXShortcut5['oXShortcut5'] : TitleTileShortcutDoor;
541 override bool isAllowed () { return (global.config.scumUnlocked || level.stats.isTunnelActive(1)); }
544 override string getExitMessage () {
545 return "START NEW GAME FROM JUNGLE.";
550 objType = 'oXShortcut5';
555 // ////////////////////////////////////////////////////////////////////////// //
556 class TitleTileXShortcut9['oXShortcut9'] : TitleTileShortcutDoor;
559 override bool isAllowed () { return (global.config.scumUnlocked || level.stats.isTunnelActive(2)); }
562 override string getExitMessage () {
563 return "START NEW GAME FROM ICE CAVES.";
568 objType = 'oXShortcut9';
573 // ////////////////////////////////////////////////////////////////////////// //
574 class TitleTileXShortcut13['oXShortcut13'] : TitleTileShortcutDoor;
577 override bool isAllowed () { return (global.config.scumUnlocked || level.stats.isTunnelActive(3)); }
580 override string getExitMessage () {
581 return "START NEW FROM TEMPLE.";
586 objType = 'oXShortcut13';