removed forgotten code from the previous commit
[k8vacspelynky.git] / mapent / items / dice.vc
blob5c100971ca771862538d2686d267b83e86adeecc
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 ItemDice['oDice'] : MapItem;
21 //int value;
22 enum RollState {
23   None, // ready to roll
24   Rolling, // rolled
25   Finished, // landed in shop
26   Failed, // landed outside of the shop
28 RollState rollState;
30 bool pickedOutsideOfAShop;
33 override bool initialize () {
34   if (!::initialize()) return false;
35   setSprite('sDice1');
36   value = global.randOther(1, 6);
37   return true;
41 // 0: failed roll
42 final int getRollNumber () {
43   if (rollState != RollState.Finished) return 0;
44   return value;
48 final void resetRollState () {
49   rollState = RollState.None;
53 bool isReadyToThrowForBet () {
54   if (!forSale) return false;
55   return ((rollState == RollState.Failed || rollState == RollState.None) && level.player.bet);
59 bool isInsideCrapsShop () {
60   if (!level.isInShop(ix/16, iy/16)) return false;
61   auto sst = level.lg.roomShopType(ix/16, iy/16);
62   return (sst == 'Craps');
66 // various side effects
67 // called only if object was succesfully put into player hands
68 override void onPickedUp (PlayerPawn plr) {
69   pickedOutsideOfAShop = !isInsideCrapsShop();
70   if (rollState != RollState.Finished) rollState = RollState.None;
74 override void drawSignsWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
75   if (!forSale) return;
76   if ((rollState == RollState.Failed || rollState == RollState.None) && level.player.bet) {
77     int xi, yi;
78     getInterpCoords(currFrameDelta, scale, out xi, out yi);
79     auto spr = level.sprStore['sRedArrowDown'];
80     if (spr && spr.frames.length) {
81       auto spf = spr.frames[0];
82       spf.tex.blitAt(xi-xpos-spf.xofs*scale, yi-ypos-(12+spf.yofs)*scale, scale);
83     }
84   }
88 override void onCheckItemStolen (PlayerPawn plr) {
89   if (!heldBy || pickedOutsideOfAShop) return;
90   if (forSale && !level.player.bet && rollState != RollState.Failed) {
91     bool inShop = level.isInShop(ix/16, iy/16);
92     if (!inShop) {
93       level.scrShopkeeperAnger(GameLevel::SCAnger.ItemStolen); // don't steal it!
94     }
95   }
99 override void thinkFrame () {
100   if (forSale && /*!forVending && cost > 0 &&*/ !level.hasAliveShopkeepers(skipAngry:true)) {
101     forSale = false;
102     cost = 0;
103   }
105   lightRadius = max(origLightRadius, (forSale && level.isInShop(ix/16, iy/16) ? 64 : 0));
107   if (heldBy) {
108     /*
109          if (oCharacter.facing == LEFT) x = oCharacter.x - 4;
110     else if (oCharacter.facing == RIGHT) x = oCharacter.x + 4;
112     if (heavy) {
113       if (oCharacter.state == DUCKING and abs(oCharacter.xVel) < 2) y = oCharacter.y; else y = oCharacter.y-2;
114     } else {
115       if (oCharacter.state == DUCKING and abs(oCharacter.xVel) < 2) y = oCharacter.y+4; else y = oCharacter.y+2;
116     }
117     depth = 1;
119     if (oCharacter.holdItem == 0) held = false;
120     */
121     // stealing makes shopkeeper angry
122     //writeln("!!! fs=", forSale, "; bet=", level.player.bet, "; st=", rollState);
123   } else {
124     moveRel(xVel, yVel);
126     bool colLeft = !!isCollisionLeft(1);
127     bool colRight = !!isCollisionRight(1);
128     bool colBot = !!isCollisionBottom(1);
129     bool colTop = !!isCollisionTop(1);
131     if (!colBot && yVel < 6) yVel += myGrav;
133          if (fabs(xVel) < 0.1) xVel = 0;
134     else if (colLeft || colRight) xVel = -xVel*0.5;
136     if (colBot) {
137       // bounce
138       if (yVel > 1) { ++bounceCount; yVel = -yVel*bounceFactor; } else yVel = 0;
139       // friction
140            if (fabs(xVel) < 0.1) xVel = 0;
141       else if (fabs(xVel) != 0) xVel *= frictionFactor;
142       if (fabs(yVel) < 1) {
143         flty -= 1;
144         if (!isCollisionBottom(1)) flty += 1;
145         yVel = 0;
146       }
147     }
149     if (colLeft) {
150       if (!colRight) fltx += 1;
151       //yVel = 0;
152     } else if (colRight) {
153       fltx -= 1;
154       //yVel = 0;
155     }
157     if (isCollisionTop(1)) {
158       if (yVel < 0) yVel = -yVel*0.8; else flty += 1;
159     }
161     //!depth = (global.hasSpectacles ? 0 : 101); //???
163     if (isCollisionInRect(ix-3, iy-3, 7, 7, &level.cbCollisionLava)) {
164       myGrav = 0;
165       xVel = 0;
166       yVel = 0;
167       shiftY(0.05);
168     } else {
169       myGrav = 0.6;
170     }
172     if (isCollisionAtPoint(ix, iy-5, &level.cbCollisionLava)) {
173       instanceRemove();
174       return;
175     }
176   }
178   if (!isInstanceAlive || spectral) return;
180   if (fabs(xVel) > 3 || fabs(yVel) > 3) {
181     /*
182     auto plr = level.player;
183     if (plr.isRectCollision(ix+enemyColX, iy+enemyColY, enemyColW, enemyColH)) {
184       doPlayerColAction(plr);
185     }
186     */
187     spectral = true;
188     level.forEachObjectInRect(ix-2, iy-2, 5, 5, &doObjectColAction);
189     spectral = false;
190   }
192   // roll states
193   if (fabs(yVel) > 2 || fabs(xVel) > 2) {
194     setSprite('sDiceRoll');
195     value = global.randOther(1, 6);
196     switch (rollState) {
197       case RollState.Finished:
198         // NO CHEATING!
199         if (level.player.bet > 0) level.scrShopkeeperAnger(GameLevel::SCAnger.CrapsCheated);
200         break;
201       default:
202         rollState = RollState.Rolling;
203         break;
204     }
205   } else if (yVel == 0 && fabs(xVel <= 2) && isCollisionBottom(1)) {
206     // landed
207     switch (rollState) {
208       case RollState.Rolling:
209         rollState = (isInsideCrapsShop() ? RollState.Finished : RollState.Failed);
210         if (rollState == RollState.Finished) level.player.onDieRolled(self);
211         break;
212       case RollState.Finished:
213       case RollState.Failed:
214         break;
215       case RollState.None:
216       default: rollState = RollState.None; break;
217     }
218   }
220   switch (value) {
221     case 1: setSprite('sDice1'); break;
222     case 2: setSprite('sDice2'); break;
223     case 3: setSprite('sDice3'); break;
224     case 4: setSprite('sDice4'); break;
225     case 5: setSprite('sDice5'); break;
226     default: setSprite('sDice6'); break;
227   }
229   canPickUp = (rollState != RollState.Rolling);
233 defaultproperties {
234   objType = 'Dice';
235   desc = "Die";
236   desc2 = "A six-sided die. The storeowner talks to it every night before he goes to sleep.";
237   setCollisionBounds(-6, 0, 6, 8);
238   heavy = true;
239   //rolled = false;
240   //rolling = false;
241   canBeNudged = true;
242   canPickUp = true;
243   holdYOfs = -4;
244   rollState = RollState.None;
245   bloodless = true; // just in case, lol
246   canHitEnemies = true;