some fixes for new vccrun
[k8vacspelynky.git] / packages / Game / GameStats.vc
blob29228e90f33a70e91598d2742091b0b2ea6c3fe2
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 // stats: kills, highscores, etc.
20 class GameStats : Object;
22 transient GameGlobal global;
24 // highscore flags
25 transient bool newMoneyRecord = false;
26 transient bool newKillsRecord = false;
27 transient bool newSavesRecord = false;
28 transient bool newTimeRecord = false;
30 int frameAccum; // yes, save this
31 int playingTime; // in seconds
33 // Tunnel Man Prices
34 const int tunnel1Price = 100000;
35 const int tunnel2Price = 200000;
36 const int tunnel3Price = 300000;
39 int tunnel1Left = tunnel1Price+1;
40 int tunnel2Left = tunnel2Price+1;
42 bool tunnel1Active = false;
43 bool tunnel2Active = false;
44 bool tunnel3Active = false;
46 int[3] tunnelPaymentLeft;
47 int[3] walksLeftForTunnelMan;
50 bool isTunnelActive (int idx) {
51   return (idx >= 1 && idx <= 3 ? tunnelPaymentLeft[idx-1] <= 0 : false);
55 bool isAnyTunnelActive () {
56   return (tunnelPaymentLeft[0] <= 0 || tunnelPaymentLeft[1] <= 0 || tunnelPaymentLeft[2] <= 0);
60 bool needTunnelMan (int transidx) {
61   if (transidx < 1 || transidx > 3) return false;
62   --transidx;
63   if (walksLeftForTunnelMan[transidx] > 0) {
64     --walksLeftForTunnelMan[transidx];
65     return false;
66   }
67   return (tunnelPaymentLeft[transidx] > 0);
71 int leftForNextTunnel () {
72   foreach (int val; tunnelPaymentLeft) if (val > 0) return val;
73   return 0;
77 // returns `true` if new tunnel is built
78 bool payForTunnel (int donation) {
79   if (donation < 1) return false;
80   bool res = false;
81   foreach (ref int val; tunnelPaymentLeft) {
82     if (val > 0) {
83       int spend = min(val, donation);
84       val -= spend;
85       donation -= spend;
86       res = res || (val == 0);
87     }
88   }
89   return res;
93 void activateTunnel (int idx) {
94   if (idx >= 1 && idx <= 3) {
95     --idx;
96     tunnelPaymentLeft[idx] = 0;
97   }
101 // ////////////////////////////////////////////////////////////////////////// //
102 // indexed by object name
103 struct TotalItem {
104   name objName;
105   int count;
109 static final void addTotalCollapsed (ref array!TotalItem totals, name aname, optional int amount) {
110   if (!aname) return;
111   if (!specified_amount) amount = 1;
112   if (amount < 1) return;
113   foreach (int idx, ref auto tt; totals) {
114     if (tt.objName == aname) {
115       ++tt.count;
116       return;
117     }
118   }
119   // new object
120   totals.length += 1;
121   totals[$-1].objName = aname;
122   totals[$-1].count = 1;
126 // ////////////////////////////////////////////////////////////////////////// //
127 // all-time totals, collapsed
128 array!TotalItem totalDeaths; // death from xxx
129 array!TotalItem totalKills; // player kills
130 array!TotalItem totalCollected; // collected items
131 array!int levelDeaths; // deaths on the corresponding level (1-based)
132 int totalDamselsSaved;
133 int totalIdolsStolen;
134 int totalIdolsConverted; // successfully carried out of level
135 int totalCrystalIdolsStolen;
136 int totalCrystalIdolsConverted; // successfully carried out of level
137 int totalGhostSummoned;
138 int totalScarabs;
139 int totalSacrifices;
140 int totalSelfSacrifices;
141 int totalDestroyedKaliAltars;
142 int totalTelefragKills;
143 int totalDiceGamesWon;
144 int totalDiceGamesLost;
145 int totalDiceGamesWonPrize;
146 // maximum money
147 int maxMoney = 0;
148 int gamesWon = 0;
149 int maxLevelComplete = 0;
151 int starsKills = 0; // shopkeepers killed
153 // Minigames
154 int mini1 = 0;
155 int mini2 = 0;
156 int mini3 = 0;
157 int scoresStart = 0;
159 int introViewed = 0;
162 // current game, uncollapsed
163 struct LevelStatInfo {
164   name aname;
165   // for transition screen
166   bool render;
167   int x, y;
170 transient array!LevelStatInfo kills;
171 transient array!LevelStatInfo collected;
172 transient int damselsSaved;
173 transient int idolsStolen;
174 transient int idolsConverted; // successfully carried out of level
175 transient int crystalIdolsStolen;
176 transient int crystalIdolsConverted; // successfully carried out of level
177 transient int ghostSummoned;
178 transient int money;
179 transient int scarabs;
181 private transient bool moneyCheat;
184 // ////////////////////////////////////////////////////////////////////////// //
185 final int countKills () {
186   return kills.length;
190 final int getDeathCountOnLevel (int level) {
191   if (level < 1 || level >= levelDeaths.length) return 0;
192   return levelDeaths[level];
195 final void addDeath (name aname) {
196   if (!aname) return;
197   addTotalCollapsed(totalDeaths, aname, 1);
198   if (global.currLevel > 0 && global.currLevel < 666) {
199     if (global.currLevel >= levelDeaths.length) levelDeaths.length = global.currLevel+1;
200     ++levelDeaths[global.currLevel];
201   }
204 final void addKill (name aname, optional bool telefrag) {
205   if (!aname) return;
206   if (telefrag) ++totalTelefragKills;
207   if (telefrag) {
208     addTotalCollapsed(totalKills, name(string(aname)~" Telefrag"), 1);
209   } else {
210     addTotalCollapsed(totalKills, aname, 1);
211   }
212   kills.length += 1;
213   kills[$-1].aname = aname;
214   kills[$-1].render = false;
217 final void addCollect (name aname, optional int amount) {
218   if (!aname) return;
219   if (!specified_amount) amount = 1;
220   if (amount < 1) return;
221   addTotalCollapsed(totalCollected, aname, amount);
222   collected.length += 1;
223   collected[$-1].aname = aname;
224   collected[$-1].render = false;
227 final void addDamselSaved () { ++totalDamselsSaved; ++damselsSaved; }
228 final void addIdolStolen () { ++totalIdolsStolen; ++idolsStolen; }
229 final void addIdolConverted () { ++totalIdolsConverted; ++idolsConverted; }
230 final void addCrystalIdolStolen () { ++totalCrystalIdolsStolen; ++crystalIdolsStolen; }
231 final void addCrystalIdolConverted () { ++totalCrystalIdolsConverted; ++crystalIdolsConverted; }
232 final void addGhostSummoned () { ++totalGhostSummoned; ++ghostSummoned; }
234 final void gameOver () {
235   if (!moneyCheat && money > 0 && money > maxMoney) {
236     newMoneyRecord = true;
237     maxMoney = money;
238   }
241 final void setMoneyCheat () { moneyCheat = true; }
243 final void setMoney (int newmoney) {
244   newmoney = max(0, newmoney);
245   money = newmoney;
248 final void addMoney (int amount) {
249   setMoney(money+amount);
252 final void takeMoney (int amount) {
253   setMoney(money-amount);
256 final void oneMoreFramePlayed () {
257   if (++frameAccum >= 30) {
258     playingTime += frameAccum/30;
259     frameAccum %= 30;
260   }
264 // ////////////////////////////////////////////////////////////////////////// //
265 final void clearLevelTotals () {
266   kills.clear();
267   collected.clear();
271 // ////////////////////////////////////////////////////////////////////////// //
272 final void clearGameTotals () {
273   clearLevelTotals();
274   damselsSaved = 0;
275   idolsStolen = 0;
276   idolsConverted = 0;
277   crystalIdolsStolen = 0;
278   crystalIdolsConverted = 0;
279   ghostSummoned = 0;
280   money = 0;
281   scarabs = 0;
282   newMoneyRecord = false;
283   moneyCheat = false;
287 // ////////////////////////////////////////////////////////////////////////// //
288 void resetTunnelPrices () {
289   tunnelPaymentLeft[0] = tunnel1Price;
290   tunnelPaymentLeft[1] = tunnel2Price;
291   tunnelPaymentLeft[2] = tunnel3Price;
292   walksLeftForTunnelMan[0] = 0;
293   walksLeftForTunnelMan[1] = 0;
294   walksLeftForTunnelMan[2] = 0;
299 // ////////////////////////////////////////////////////////////////////////// //
300 defaultproperties {
301   tunnelPaymentLeft[0] = tunnel1Price;
302   tunnelPaymentLeft[1] = tunnel2Price;
303   tunnelPaymentLeft[2] = tunnel3Price;
304   walksLeftForTunnelMan[0] = 3;
305   walksLeftForTunnelMan[1] = 2;
306   walksLeftForTunnelMan[2] = 1;