-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.cpp
708 lines (604 loc) · 18.7 KB
/
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
// main.c
//
// CANDY CRISIS 32-BIT - OS X and Windows
//
// FEATURES:
// • Runs natively on SDL 2.0, for both Intel OS X and Windows. Targeting
// popular Windows installs in 2015 (i.e. Win7/8/10).
// • Now runs in 32-bit color.
//
// ETC:
// • Codebase should once again be almost entirely platform-neutral.
//
//
// CANDY CRISIS SDL 2.0
//
// FEATURES:
// • Runs natively on SDL 2.0 and Intel OS X. Targeting modern OS X,
// circa 2015 (e.g. OS X 10.10).
//
// ETC:
// • Removed registration.
// • Resources moved into app bundle.
// • Migrated to SDL 2 and SDL_image 2 (for graphics--also removes
// subdependencies on libjpeg/libpng)
//
//
// CANDY CRISIS SDL
//
// FEATURES:
// • Runs natively on any platform with support for SDL. Developed
// primarily around the lame Mac Carbon SDL. Can compile and run
// with gcc/mingw32 on Windows and gcc on Linux.
//
// ETC:
// • Game functionality unchanged.
// • Removed all Mac OS-specific concepts from the game, i.e.:
// - GWorlds replaced with SDL_Surfaces.
// - Rects and Points replaced with MRects and MPoints. (same contents)
// • All resources moved into external files in a folder called
// "CandyCrisisResources." Graphics are all JPG and PNG, opened
// with SDL_image. Sounds are WAV. Music is still MOD type.
// • Using the following Open Source projects:
// SDL, SDL_image (for graphics)
// libjpeg (used by SDL_image)
// libpng (used by SDL_image)
// zlib (used by libpng)
// • Using fmod for sound; replaces MikMod => with SDL_sound optionnal path
// • Wrote a utility library, SDLU, to pick up slack in the SDL
// implementation and to help SDL mesh with a Mac-centric universe.
// • New registration code algorithm based on a fast string hash.
//
//
// CANDY CRISIS X
//
// FEATURES:
// • Runs natively on Mac OS X. Developed around Mac OS X
// Public Beta 1H39 and 2E14.
// Updated 3/25/2001 for Mac OS X 4K78--OS X 10.0.
// Updated 8/25/2001 for Mac OS X 10.0.4.
//
// ETC:
// • Game functionality unchanged.
// • Zerius Sound System scrapped, replaced with LibMikMod. I'm
// very unhappy with performance relative to ZSS, but on G3s and up,
// performance should not be a major concern. If only I could get
// the source to ZSS so it could be Carbonized!
// • Everything runs in one window now, instead of having
// one window per interface element. This was necessary
// because OS X wanted to put drop shadows around everything
// and it looked pretty weird. This was also a personal pet
// peeve that I never had the motivation to fix until now.
// • Controls dialog is super Aqua savvy, using Theme Text
// and Theme Buttons.
// • A couple of kludges added, to work around OS X bugs. Ugh.
// • Found bug which was causing blitter to draw larger dirty rects
// than necessary (top/left of dirty rect was always 0/0). Not sure
// if it ever shipped like that or if this is something I changed
// post-Candy Crisis 1.0.
// • Changed cursor management since OS X cursors don't know how to
// hide and show themselves properly.
//
// UNRESOLVED:
// • Stopped getting Out of Memory reports. I wonder if any of the
// Candy Crisis cleanups affected this...?
// • OS X displays a line of garbage when you try to put up a totally
// blank cursor. I'm not going to spend too long analyzing this; it's
// not my bug.
//
// CANDY CRISIS 1.0 UPDATE
//
// FEATURES:
// • Rebranded "Candy Crisis" at the request of Mars Candy Co.
// Many, many graphical changes as a result. (New logo thanks
// to Bob Frasure.)
// • "Controls" button in main menu per many user requests.
// • Slightly improved error reporting.
// • Option-key at startup to turn on "allow background tasks"
// or "don't change resolutions." (Don't change resolutions
// requires DrawSprocket 1.7.)
//
// NONCRITICAL:
// • Fixed bug in 2P mode where game would say "Player 1 got
// best combo!" when Player 2 really got it, and vice versa.
// • Changed Magic Skittle ratio to 1/19 instead of 1/17, after
// watching Brett get tons of Magic Skittles at work. Hmm.
// • Replaced RandomBefore with less hacked-up code, because
// Magic Skittles STILL seemed to be coming up more often than
// expected. That seemed to take care of it.
// • Fixed rare bug where, after losing, the game would sometimes
// get stuck until you explicitly chose "end game." (Would manifest
// more often on a slow computer and/or when Background Tasks were
// activated.)
//
// ETC:
// • Antipiracy measures.
// • Game fonts all loaded at startup time instead of dynamically,
// in an attempt to reduce the number of GWorlds which are created,
// then torn down, during the game (which could have been potentially
// fragmenting the heap, though I doubt this was a real problem).
//
// UNRESOLVED:
// • Still a handful of people who get Out of Memory when they
// try to pause a game. Damn. Hopefully now I'll at least know
// where they're dying (though I highly suspect it's InitGWorld,
// which without a stack crawl is pretty much useless info...)
// One guy says this is fixed by deleting prefs. Huh??
//
//
// 2.0.2 UPDATE
//
// FEATURES:
// • When you continue, your score is now rolled back to what
// it was when you first started the round. This prevents people
// from racking up high scores by continuing over and over again
// on the highest board.
// • You can now clear the high score tables to their default
// values by holding delete while clicking "high scores" on the
// main screen.
//
// ETC:
// • Added small picture to the controls dialog so people know
// which color is Player 1, and which is Player 2.
// • Holding option while warping causes a CPU/CPU match to
// occur.
// • Changed in-game registration URL to:
// http://emulation.net/s2.com/register.html
//
// CRITICAL:
// • Fixed minor memory corruption when a bomb hits floor or
// gray Skittle. Could potentially have corrupted 3 tiles of
// opponent's board.
// • Fixed bug where potential combo data would not be cleared
// when choosing "End Game" and then starting a new game, which
// led to really weird corruptions of potential combo data.
// • Fixed bug where holding down button after end-credits rolled
// would cause the pause dialog to pop up on a zero-gamma screen
// (whoops).
//
// NONCRITICAL:
// • Fixed bug where dropping bomb would not display associated
// points.
// • Fixed bug where dropping bomb on floor/gray Skittle would
// reward the player for "killing" empty squares, making it
// score 100x(9-number of grays in 3x3 area) as opposed to
// the correct 100x(number of blobs in 3x3 area).
// • Occasionally, when Best Combo got corrupted, it would show
// the ending credits instead of displaying the Best Combo. Now
// there is code to ensure that the level # of the Best Combo
// structure is in bounds. (If it isn't, the best combo is
// assumed to be corrupt, and it is deleted. Not the most
// optimal solution, but what can I do?)
// • If you started the tutorial, ended it, then viewed the best
// combo, you'd see a speech balloon appear for one frame.
// • Fixed bug where bringing up InputSprocket dialog would
// unload ics8's used to draw key caps (damn InputSprocket bugs).
// Does this only affect ISp < 1.7?
// • Fixed bug where bringing up InputSprocket dialog would not
// update game windows behind it after it got closed.
//
// UNRESOLVED:
// • Slow loading time issue seems to only be affecting an
// incredible minority of people. It's being caused by QuickTime
// decompressing JPEGs. I think it's not a Skittles 2 issue.
// • One guy says if he quits the game, reopens it, starts a game,
// then pauses it, he gets an Out of Memory condition. He's running
// 8.6-D clean. Hmm.
//
//
// 2.0.1 UPDATE
//
// FEATURES:
// • Best Combo
// • New bg for level 8
// • New sfx for continue sound (requested by Nathan Lamont)
//
// ETC:
// • High score dialog enhanced to support Best Combo stuff
// • Tutorial suggests pressing esc to set up keys now
// • More aggressive AI for intellect>18 (does not percieve
// 1-level zap as advantageous)
//
// CRITICAL:
// • Fix for out-of-bounds array read (->crash) inside
// ZapScoreDisplay.
// • Fixed bug where falling Skittles (in DropBlobs) would
// occasionally have their bottom half lopped off. Tough to
// see while in motion but totally obvious in screenshots.
// • Fixed InputSprocket icons in ISpConfigure dialog
// • Workaround for System 7 bug, where setting the
// cursor while gamma is faded causes solid black cursor.
//
// NONCRITICAL:
// • Fixed bug where char fading on blobs that were
// in motion would cause crap to appear for one frame.
// Only apparent on slow Macs or in screenshots. Otherwise
// appeared as flicker and easily dismissed.
// • Cmd-tab inside High Scores or Game Over screen
// no longer leaves a white box (empty window) open
// and will not switch out with 0 gamma
// • Fixed DrawSprocket bug on Macs that can't do 640x480
// (i.e. PowerBook G3 is stuck at 1024x768). The window
// would be drawn in lower-right hand corner instead of
// centered.
// • Fixed bug in multipliers for getting multiple
// colors at once. (Should have been *3/*6/*12/*24, was
// actually *3/*9/*21/*45! Ouch!)
// • Hitting cmd-Q at high score dialog would allow
// empty high score name to be added to high score table.
//
// UNRESOLVED:
// • A few users report very slow loading times between
// levels and during loading sequence. Problem tends to
// be alleviated by turning on VM. Can't reproduce here.
//
//
// 2.0.0 INITIAL RELEASE
//
#include "stdafx.h"
#if _WIN32
#include <windows.h>
#include <io.h> // for _chdir
#endif
#if __APPLE__
#include <Cocoa/Cocoa.h>
#endif
#include "SDLU.h"
#include "main.h"
#include <string.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include "hiscore.h"
#include "control.h"
#include "players.h"
#include "gworld.h"
#include "graphics.h"
#include "grays.h"
#include "soundfx.h"
#include "next.h"
#include "random.h"
#include "victory.h"
#include "score.h"
#include "graymonitor.h"
#include "music.h"
#include "gameticks.h"
#include "level.h"
#include "opponent.h"
#include "keyselect.h"
#include "blitter.h"
#include "prefs.h"
#include "tweak.h"
#include "zap.h"
#include "pause.h"
#include "tutorial.h"
SDL_Renderer* g_renderer;
SDL_Window* g_window;
SDL_Texture* g_windowTexture;
SDL_Surface* g_frontSurface;
signed char nextA[2], nextB[2], nextM[2], nextG[2], colorA[2], colorB[2],
blobX[2], blobY[2], blobR[2], blobSpin[2], speed[2], role[2], halfway[2],
control[2], dropping[2], magic[2], grenade[2], anim[2];
int chain[2];
int blobTime[2], startTime, endTime;
MBoolean finished = false, pauseKey = false, showStartMenu = true;
signed char grid[2][kGridAcross][kGridDown], suction[2][kGridAcross][kGridDown], charred[2][kGridAcross][kGridDown], glow[2][kGridAcross][kGridDown];
MRect playerWindowZRect, playerWindowRect[2];
MBoolean playerWindowVisible[2] = { true, true };
KeyList hitKey[2];
int backgroundID = -1;
MPoint blobWindow[8][2];
void (*DoFullRepaint)() = NoPaint;
MBoolean needsRefresh = false;
static char candyCrisisResources[512];
#if _WIN32
int WinMain(
_In_ HINSTANCE,
_In_opt_ HINSTANCE,
_In_ LPSTR,
_In_ int)
#else
int main(int, char *[])
#endif
{
Initialize( );
LoadPrefs( );
ReserveMonitor( );
ShowTitle( );
ChooseMusic( 13 );
while (!finished)
{
if (showStartMenu)
{
GameStartMenu();
showStartMenu = false;
}
if (finished)
break;
DoFullRepaint = NeedRefresh;
CheckKeys( );
HandlePlayers( );
UpdateOpponent( );
UpdateBalloon( );
UpdateSound( );
DoFullRepaint = NoPaint;
if (needsRefresh)
{
RefreshAll();
needsRefresh = false;
}
SDLU_Present();
if (!showStartMenu && pauseKey)
{
FreezeGameTickCount( );
PauseMusic( );
MaskRect( &playerWindowRect[0] );
MaskRect( &playerWindowRect[1] );
WaitForRelease( );
HandleDialog( kPauseDialog );
WaitForRelease( );
RefreshPlayerWindow( 0 );
RefreshPlayerWindow( 1 );
ResumeMusic( );
UnfreezeGameTickCount( );
}
}
SavePrefs( );
ReleaseMonitor( );
return 0;
}
void NoPaint( void )
{
}
void MaskRect( MRect *r )
{
SDL_Rect sdlRect;
SDLU_MRectToSDLRect( r, &sdlRect );
SDLU_BlitFrontSurface( backdropSurface, &sdlRect, &sdlRect );
}
void RefreshPlayerWindow( short player )
{
MRect fullUpdate = {0, 0, kGridDown * kBlobVertSize, kGridAcross * kBlobHorizSize };
if( control[player] == kNobodyControl )
{
MaskRect( &playerWindowRect[player] );
}
else
{
SetUpdateRect( player, &fullUpdate );
UpdatePlayerWindow( player );
}
}
void NeedRefresh()
{
needsRefresh = true;
}
void RefreshAll( void )
{
DrawBackdrop( );
ShowGrayMonitor( 0 );
ShowGrayMonitor( 1 );
RefreshNext( 0 );
RefreshNext( 1 );
RefreshPlayerWindow( 0 );
RefreshPlayerWindow( 1 );
DrawFrozenOpponent( );
DrawStage( );
ShowScore( 0 );
ShowScore( 1 );
}
void Error( const char* extra )
{
#if _WIN32
char error[1024];
sprintf_s(error, "Sorry, a critical error has occurred. Please report the following error message:\n %s", extra );
MessageBox(NULL, error, "Candy Crisis", MB_OK | MB_ICONERROR);
#else
fprintf(stderr, "Sorry, a critical error has occurred. Please report the following error message:\n %s", extra );
#endif
abort();
}
void WaitForRelease( void )
{
do
{
SDLU_Yield();
}
while( AnyKeyIsPressed( ) || SDLU_Button() );
}
MBoolean AnyKeyIsPressed( void )
{
int arraySize;
const Uint8* pressedKeys;
SDLU_PumpEvents();
pressedKeys = SDL_GetKeyboardState( &arraySize );
for (int index = 0; index < arraySize; index++)
{
if (pressedKeys[index] &&
index != SDL_SCANCODE_CAPSLOCK &&
index != SDL_SCANCODE_NUMLOCKCLEAR &&
index != SDL_SCANCODE_SCROLLLOCK)
{
return true;
}
}
return false;
}
MBoolean ControlKeyIsPressed( void )
{
int arraySize;
const Uint8* pressedKeys;
SDLU_PumpEvents();
pressedKeys = SDL_GetKeyboardState( &arraySize );
return pressedKeys[SDL_SCANCODE_LCTRL] || pressedKeys[SDL_SCANCODE_RCTRL];
}
MBoolean OptionKeyIsPressed( void )
{
int arraySize;
const Uint8* pressedKeys;
SDLU_PumpEvents();
pressedKeys = SDL_GetKeyboardState( &arraySize );
return pressedKeys[SDL_SCANCODE_LALT] || pressedKeys[SDL_SCANCODE_RALT];
}
MBoolean DeleteKeyIsPressed( void )
{
int arraySize;
const Uint8* pressedKeys;
SDLU_PumpEvents();
pressedKeys = SDL_GetKeyboardState( &arraySize );
return pressedKeys[SDL_SCANCODE_BACKSPACE];
}
void RetrieveResources( void )
{
InitSound( );
InitBackdrop( );
GetBlobGraphics( );
InitNext( );
InitScore( );
InitGrayMonitors( );
InitOpponent( );
InitStage( ); // must run after backdrop window is open
InitGameTickCount( );
InitPlayers( ); // must run after backdrop window is open
InitFont( );
InitZapStyle( );// must run after fonts are inited
InitBlitter( ); // must run after player windows are open
InitPlayerWorlds( );
InitVictory( ); // must run after fonts are inited
InitTweak( );
}
void CenterRectOnScreen( MRect *rect, double locationX, double locationY )
{
MPoint dest = {0,0};
dest.h = (short)(locationX * (640 - (rect->right - rect->left)));
dest.h &= ~3;
dest.v = (short)(locationY * (480 - (rect->bottom - rect->top)));
OffsetMRect( rect, -rect->left, -rect->top );
OffsetMRect( rect, dest.h, dest.v );
}
void ReserveMonitor( void )
{
SDL_ShowCursor( SDL_DISABLE );
// SDL_CreateWindowAndRenderer(640, 480, 0, &g_window, &g_renderer);
SDL_CreateWindowAndRenderer(0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP, &g_window, &g_renderer);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
SDL_RenderSetLogicalSize(g_renderer, 640, 480);
SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, 255);
SDL_RenderClear(g_renderer);
SDL_RenderPresent(g_renderer);
g_frontSurface = SDL_CreateRGBSurface(0, 640, 480, 32,
RED_MASK, GREEN_MASK, BLUE_MASK, 0);
g_windowTexture = SDL_CreateTexture(g_renderer,
SDL_PIXELFORMAT_RGB888,
SDL_TEXTUREACCESS_STREAMING,
640, 480);
}
void ReleaseMonitor( void )
{
}
int Warp( void )
{
return 8;
}
const char* QuickResourceName( const char* prefix, int id, const char* extension )
{
static char name[1024];
if (id)
{
sprintf( name, "%s%s_%d%s", candyCrisisResources, prefix, id, extension );
}
else
{
sprintf( name, "%s%s%s", candyCrisisResources, prefix, extension );
}
return name;
}
void Initialize(void)
{
#if _WIN32
HMODULE module;
char name[MAX_PATH + 1], *lastBackslash;
module = GetModuleHandle(NULL);
GetModuleFileName(module, name, MAX_PATH);
lastBackslash = strrchr(name, '\\');
if (lastBackslash != NULL)
{
*lastBackslash = '\0';
strcpy(candyCrisisResources, name);
strcat(candyCrisisResources, "\\CandyCrisisResources\\");
}
#endif
#if __APPLE__
sprintf(candyCrisisResources, "%s/", [[[NSBundle mainBundle] resourcePath] UTF8String]);
#endif
#ifdef linux
strcpy(candyCrisisResources, "CandyCrisisResources/");
#endif
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
{
Error("SDL_Init failed");
}
int desiredFormats = (IMG_INIT_JPG | IMG_INIT_PNG);
int haveFormats = IMG_Init(desiredFormats);
if ((haveFormats & desiredFormats) != desiredFormats)
{
Error("IMG_Init failed");
}
atexit(IMG_Quit);
atexit(SDL_Quit);
SDLU_Init();
}
void QuickFadeIn( MRGBColor *color )
{
/* (void) color; // is unused
int c;
float percent;
for( percent=0.0f; percent<1.0f; percent += 0.04f )
{
c = MTickCount( );
SDLU_SetBrightness( percent );
while( c == MTickCount( ) )
{
SDLU_Yield();
}
}
SDLU_SetBrightness( percent );*/
SDL_Delay(200);
}
void QuickFadeOut( MRGBColor *color )
{/*
(void) color; // is unused
int c;
float percent;
for( percent=1.0f; percent>0.0f; percent -= 0.04f )
{
c = MTickCount( );
SDLU_SetBrightness( percent );
while( c == MTickCount( ) )
{
SDLU_Yield();
}
}
SDLU_SetBrightness( percent );*/
SDL_Delay(200);
}
MBoolean FileExists( const char* name )
{
FILE* f = fopen( name, "rb" );
if( f == NULL )
{
return false;
}
fclose( f );
return true;
}
void WaitForRegainFocus()
{
do
{
SDLU_PumpEvents();
SDL_Delay(50);
}
while( !SDLU_IsForeground() );
DoFullRepaint();
}