1 /**********************************************************************************
2 * Copyright (c) 2008, 2009 Derek Yu and Mossmouth, LLC
3 * Copyright (c) 2018, Ketmar Dark
5 * This file is part of Spelunky.
7 * You can redistribute and/or modify Spelunky, including its source code, under
8 * the terms of the Spelunky User License.
10 * Spelunky is distributed in the hope that it will be entertaining and useful,
11 * but WITHOUT WARRANTY. Please see the Spelunky User License for more details.
13 * The Spelunky User License should be available in "Game Information", which
14 * can be found in the Resource Explorer, or as an external file called COPYING.
15 * If not, please obtain a new copy of Spelunky from <http://spelunkyworld.com/>
17 **********************************************************************************/
18 // recoded by Ketmar // Invisible Vector
19 class PlayerPowerup : Object abstract;
23 int renderPriority; // from lower to higher
24 bool lostOnDeath = true;
28 // called when the player get it for the first time
35 // called when the player lost it (for some reason)
36 bool onDeactivate (optional bool forced) {
42 void onPlayerDied () {
43 onDeactivate(forced:true);
55 void preDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
59 void postDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
63 void lastDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
67 // ////////////////////////////////////////////////////////////////////////// //
68 class PPCape : PlayerPowerup;
77 // called when the player get it for the first time
78 override bool onActivate () {
79 owner.global.hasCape = true;
87 // called when the player lost it (for some reason)
88 override bool onDeactivate (optional bool forced) {
89 owner.global.hasCape = false;
97 override void onPostThink () {
98 visible = (owner.status != MapObject::DUCKTOHANG);
101 if (instance_exists(oTransition) && !instance_exists(oPDummy))
108 else if (instance_exists(oPDummy))
110 if (oPDummy.sprite_index == sPExit || oPDummy.sprite_index == sDamselExit || oPDummy.sprite_index == sTunnelExit)
114 sprite_index = sCapeBack;
121 if (oPDummy.sprite_index == sRunLeft || oPDummy.sprite_index == sDamselRunL || oPDummy.sprite_index == sTunnelRunL) sprite_index = sCapeRight;
122 else sprite_index = sCapeDR;
128 if (!owner.whipping && (owner.status == MapObject::CLIMBING || owner.isExitingSprite())) {
131 sprName = 'sCapeBack';
135 else if (owner.dir == MapObject::Dir.Right) {
137 dy = (owner.status == MapObject::DUCKING || owner.stunned ? 2 : -2);
138 if (open) sprName = 'sCapeUR';
139 else if (fabs(owner.xVel) > 0 && owner.status != MapObject::DUCKING) sprName = 'sCapeRight';
140 else sprName = 'sCapeDR';
142 beforePlayer = false;
146 dy = (owner.status == MapObject::DUCKING || owner.stunned ? 2 : -2);
147 if (open) sprName = 'sCapeUL';
148 else if (fabs(owner.xVel) > 0 && owner.status != MapObject::DUCKING) sprName = 'sCapeLeft';
149 else sprName = 'sCapeDL';
151 beforePlayer = false;
154 //writeln("CAPE: '", sprName, "'; before=", beforePlayer);
158 final void doDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
159 if (!sprName) return;
161 auto spr = owner.level.sprStore[sprName];
162 if (!spr || spr.frames.length < 1) return;
163 auto spf = spr.frames[0];
164 if (!spf || spf.width < 1 || spf.height < 1) return;
167 owner.getInterpCoords(currFrameDelta, scale, out xi, out yi);
172 xi -= spf.xofs*scale;
173 yi -= spf.yofs*scale;
175 spf.tex.blitAt(xi-xpos, yi-ypos, scale);
179 override void preDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
180 if (!visible || beforePlayer) return;
181 doDrawWithOfs(xpos, ypos, scale, currFrameDelta);
185 override void lastDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
186 if (!visible || !beforePlayer) return;
187 doDrawWithOfs(xpos, ypos, scale, currFrameDelta);
193 renderPriority = 100;
194 sprName = 'sCapeRight';
198 // ////////////////////////////////////////////////////////////////////////// //
199 class PPParachute : PlayerPowerup;
206 // called when the player get it for the first time
207 override bool onActivate () {
215 // called when the player lost it (for some reason)
216 override bool onDeactivate (optional bool forced) {
222 override void onPostThink () {
224 auto spr = owner.level.sprStore['sParaOpen'];
226 if (imageFrame >= spr.frames.length) {
236 final void doDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
237 auto spr = owner.level.sprStore[opening ? 'sParaOpen' : 'sParachute'];
238 if (!spr || spr.frames.length < 1) return;
239 auto spf = spr.frames[imageFrame];
240 if (!spf || spf.width < 1 || spf.height < 1) return;
243 owner.getInterpCoords(currFrameDelta, scale, out xi, out yi);
248 xi -= spf.xofs*scale;
249 yi -= spf.yofs*scale;
251 spf.tex.blitAt(xi-xpos, yi-ypos, scale);
255 override void preDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
256 doDrawWithOfs(xpos, ypos, scale, currFrameDelta);
262 renderPriority = 100;