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;
26 bool prePreDucking; // for cape
29 // called when the player get it for the first time
36 // called when the player lost it (for some reason)
37 bool onDeactivate (optional bool forced) {
43 void onPlayerDied () {
44 onDeactivate(forced:true);
56 void preDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
60 void postDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
64 void lastDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
68 // ////////////////////////////////////////////////////////////////////////// //
69 class PPCape : PlayerPowerup;
78 // called when the player get it for the first time
79 override bool onActivate () {
80 owner.global.hasCape = true;
88 // called when the player lost it (for some reason)
89 override bool onDeactivate (optional bool forced) {
90 owner.global.hasCape = false;
98 override void onPostThink () {
99 visible = (owner.status != MapObject::DUCKTOHANG);
102 if (instance_exists(oTransition) && !instance_exists(oPDummy))
109 else if (instance_exists(oPDummy))
111 if (oPDummy.sprite_index == sPExit || oPDummy.sprite_index == sDamselExit || oPDummy.sprite_index == sTunnelExit)
115 sprite_index = sCapeBack;
122 if (oPDummy.sprite_index == sRunLeft || oPDummy.sprite_index == sDamselRunL || oPDummy.sprite_index == sTunnelRunL) sprite_index = sCapeRight;
123 else sprite_index = sCapeDR;
129 if (!owner.whipping && (owner.status == MapObject::CLIMBING || owner.isExitingSprite())) {
132 sprName = 'sCapeBack';
136 else if (owner.dir == MapObject::Dir.Right) {
138 dy = (owner.status == MapObject::DUCKING || owner.stunned ? 2 : -2);
139 if (open) sprName = 'sCapeUR';
140 else if (fabs(owner.xVel) > 0 && owner.status != MapObject::DUCKING) sprName = 'sCapeRight';
141 else sprName = 'sCapeDR';
143 beforePlayer = false;
147 dy = (owner.status == MapObject::DUCKING || owner.stunned ? 2 : -2);
148 if (open) sprName = 'sCapeUL';
149 else if (fabs(owner.xVel) > 0 && owner.status != MapObject::DUCKING) sprName = 'sCapeLeft';
150 else sprName = 'sCapeDL';
152 beforePlayer = false;
155 //writeln("CAPE: '", sprName, "'; before=", beforePlayer);
159 final void doDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
160 if (!sprName) return;
162 auto spr = owner.level.sprStore[sprName];
163 if (!spr || spr.frames.length < 1) return;
164 auto spf = spr.frames[0];
165 if (!spf || spf.width < 1 || spf.height < 1) return;
168 owner.getInterpCoords(currFrameDelta, scale, out xi, out yi);
173 xi -= spf.xofs*scale;
174 yi -= spf.yofs*scale;
176 spf.tex.blitAt(xi-xpos, yi-ypos, scale);
180 override void preDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
181 if (!visible || beforePlayer) return;
182 doDrawWithOfs(xpos, ypos, scale, currFrameDelta);
186 override void lastDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
187 if (!visible || !beforePlayer) return;
188 doDrawWithOfs(xpos, ypos, scale, currFrameDelta);
194 renderPriority = 1010; // behind the bricks
195 sprName = 'sCapeRight';
196 prePreDucking = true;
200 // ////////////////////////////////////////////////////////////////////////// //
201 class PPParachute : PlayerPowerup;
208 // called when the player get it for the first time
209 override bool onActivate () {
217 // called when the player lost it (for some reason)
218 override bool onDeactivate (optional bool forced) {
224 override void onPostThink () {
226 auto spr = owner.level.sprStore['sParaOpen'];
228 if (imageFrame >= spr.frames.length) {
238 final void doDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
239 auto spr = owner.level.sprStore[opening ? 'sParaOpen' : 'sParachute'];
240 if (!spr || spr.frames.length < 1) return;
241 auto spf = spr.frames[imageFrame];
242 if (!spf || spf.width < 1 || spf.height < 1) return;
245 owner.getInterpCoords(currFrameDelta, scale, out xi, out yi);
250 xi -= spf.xofs*scale;
251 yi -= spf.yofs*scale;
253 spf.tex.blitAt(xi-xpos, yi-ypos, scale);
257 override void preDrawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
258 doDrawWithOfs(xpos, ypos, scale, currFrameDelta);
264 renderPriority = 1010; // behind the bricks