├── packaging ├── version.txt.in ├── app.ico ├── app.icns ├── candycrisis.png ├── screenshot.jpg ├── ReadMe.txt.in ├── candycrisis.desktop ├── prepare_appimage_appdir.sh ├── AppRun └── app.exe.rc ├── src ├── prefs.h ├── version.h ├── graymonitor.h ├── keyselect.h ├── gameticks.h ├── version.h.in ├── random.h ├── tweak.h ├── music.h ├── pause.h ├── victory.h ├── next.h ├── font.h ├── graphics.h ├── moving.h ├── score.h ├── soundfx.h ├── grays.h ├── tutorial.h ├── hiscore.h ├── opponent.h ├── MTypes.c ├── gameticks.c ├── control.h ├── MTypes.h ├── gworld.h ├── support │ ├── cmixer.h │ └── ibxm.h ├── zap.h ├── players.h ├── keyselect.c ├── level.h ├── soundfx.c ├── SDLU.h ├── music.c ├── blitter.h ├── random.c ├── graymonitor.c ├── prefs.c ├── tweak.c ├── font.c ├── score.c ├── moving.c ├── main.h ├── next.c ├── gworld.c ├── graphics.c ├── opponent.c ├── victory.c ├── grays.c ├── hiscore.c └── SDLU.c ├── .gitignore ├── CandyCrisisResources ├── mod_128.mod ├── mod_129.s3m ├── mod_130.s3m ├── mod_131.mod ├── mod_132.s3m ├── mod_133.s3m ├── mod_134.s3m ├── mod_135.s3m ├── mod_136.s3m ├── mod_137.s3m ├── mod_138.s3m ├── mod_139.mod ├── mod_140.mod ├── mod_141.mod ├── mod_142.s3m ├── snd_128.wav ├── snd_129.wav ├── snd_130.wav ├── snd_131.wav ├── snd_132.wav ├── snd_133.wav ├── snd_134.wav ├── snd_135.wav ├── snd_136.wav ├── snd_137.wav ├── snd_138.wav ├── snd_139.wav ├── snd_140.wav ├── snd_141.wav ├── snd_142.wav ├── snd_143.wav ├── snd_144.wav ├── PICT_1000.jpg ├── PICT_1001.jpg ├── PICT_1002.jpg ├── PICT_1004.jpg ├── PICT_1100.jpg ├── PICT_1101.jpg ├── PICT_1102.jpg ├── PICT_1104.jpg ├── PICT_1200.jpg ├── PICT_1201.jpg ├── PICT_1202.jpg ├── PICT_1204.jpg ├── PICT_1300.jpg ├── PICT_1301.jpg ├── PICT_1302.jpg ├── PICT_1304.jpg ├── PICT_1400.jpg ├── PICT_1401.jpg ├── PICT_1402.jpg ├── PICT_1404.jpg ├── PICT_1500.jpg ├── PICT_1501.jpg ├── PICT_1502.jpg ├── PICT_1504.jpg ├── PICT_1505.jpg ├── PICT_1600.jpg ├── PICT_1601.jpg ├── PICT_1602.jpg ├── PICT_1604.jpg ├── PICT_1700.jpg ├── PICT_1701.jpg ├── PICT_1702.jpg ├── PICT_1704.jpg ├── PICT_1800.jpg ├── PICT_1801.jpg ├── PICT_1802.jpg ├── PICT_1804.jpg ├── PICT_1805.jpg ├── PICT_1900.jpg ├── PICT_1901.jpg ├── PICT_1902.jpg ├── PICT_1904.jpg ├── PICT_200.png ├── PICT_2000.jpg ├── PICT_2001.jpg ├── PICT_2002.jpg ├── PICT_2004.jpg ├── PICT_201.png ├── PICT_202.png ├── PICT_203.png ├── PICT_204.png ├── PICT_205.png ├── PICT_206.jpg ├── PICT_2100.jpg ├── PICT_2101.jpg ├── PICT_2102.jpg ├── PICT_2104.jpg ├── PICT_2105.jpg ├── PICT_2300.jpg ├── PICT_2301.jpg ├── PICT_2302.jpg ├── PICT_250.png ├── PICT_251.png ├── PICT_252.png ├── PICT_253.png ├── PICT_254.png ├── PICT_255.png ├── PICT_256.png ├── PICT_257.png ├── PICT_258.png ├── PICT_259.png ├── PICT_260.png ├── PICT_300.png ├── PICT_301.png ├── PICT_302.jpg ├── PICT_303.jpg ├── PICT_304.jpg ├── PICT_305.jpg ├── PICT_306.jpg ├── PICT_307.jpg ├── PICT_308.jpg ├── PICT_309.jpg ├── PICT_310.jpg ├── PICT_500.jpg ├── PICT_5000.png ├── PICT_5001.png ├── PICT_5002.png ├── PICT_5003.png ├── PICT_5004.png ├── PICT_5005.png ├── PICT_5006.png ├── PICT_5007.png ├── PICT_5008.png ├── PICT_5009.png ├── PICT_501.jpg ├── PICT_5010.png ├── PICT_5011.png ├── PICT_5013.png ├── PICT_502.png ├── PICT_5100.png ├── PICT_5101.png ├── PICT_5102.png ├── PICT_5103.png ├── PICT_5104.png ├── PICT_5105.png ├── PICT_5106.png ├── PICT_5107.png ├── PICT_5108.png ├── PICT_5109.png ├── PICT_5110.png ├── PICT_5111.png └── PICT_5113.png ├── .editorconfig ├── .github └── workflows │ ├── metarelease.yml │ ├── release-windows.yml │ ├── release-appimage.yml │ └── release-mac.yml ├── CHANGELOG.md ├── README.md ├── BUILD.md └── CMakeLists.txt /packaging/version.txt.in: -------------------------------------------------------------------------------- 1 | @PROJECT_VERSION@ -------------------------------------------------------------------------------- /packaging/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/packaging/app.ico -------------------------------------------------------------------------------- /src/prefs.h: -------------------------------------------------------------------------------- 1 | // prefs.h 2 | 3 | void LoadPrefs( void ); 4 | void SavePrefs( void ); 5 | -------------------------------------------------------------------------------- /packaging/app.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/packaging/app.icns -------------------------------------------------------------------------------- /packaging/candycrisis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/packaging/candycrisis.png -------------------------------------------------------------------------------- /packaging/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/packaging/screenshot.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build* 2 | /cache 3 | /cmake-build* 4 | /dist 5 | /extern 6 | 7 | /SDL* 8 | 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /CandyCrisisResources/mod_128.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_128.mod -------------------------------------------------------------------------------- /CandyCrisisResources/mod_129.s3m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_129.s3m -------------------------------------------------------------------------------- /CandyCrisisResources/mod_130.s3m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_130.s3m -------------------------------------------------------------------------------- /CandyCrisisResources/mod_131.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_131.mod -------------------------------------------------------------------------------- /CandyCrisisResources/mod_132.s3m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_132.s3m -------------------------------------------------------------------------------- /CandyCrisisResources/mod_133.s3m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_133.s3m -------------------------------------------------------------------------------- /CandyCrisisResources/mod_134.s3m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_134.s3m -------------------------------------------------------------------------------- /CandyCrisisResources/mod_135.s3m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_135.s3m -------------------------------------------------------------------------------- /CandyCrisisResources/mod_136.s3m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_136.s3m -------------------------------------------------------------------------------- /CandyCrisisResources/mod_137.s3m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_137.s3m -------------------------------------------------------------------------------- /CandyCrisisResources/mod_138.s3m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_138.s3m -------------------------------------------------------------------------------- /CandyCrisisResources/mod_139.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_139.mod -------------------------------------------------------------------------------- /CandyCrisisResources/mod_140.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_140.mod -------------------------------------------------------------------------------- /CandyCrisisResources/mod_141.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_141.mod -------------------------------------------------------------------------------- /CandyCrisisResources/mod_142.s3m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/mod_142.s3m -------------------------------------------------------------------------------- /CandyCrisisResources/snd_128.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_128.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_129.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_129.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_130.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_130.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_131.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_131.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_132.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_132.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_133.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_133.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_134.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_134.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_135.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_135.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_136.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_136.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_137.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_137.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_138.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_138.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_139.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_139.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_140.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_140.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_141.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_141.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_142.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_142.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_143.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_143.wav -------------------------------------------------------------------------------- /CandyCrisisResources/snd_144.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/snd_144.wav -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{c,cpp,h,hpp,txt}] 4 | charset = utf-8 5 | indent_style = tab 6 | indent_size = 4 7 | -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1000.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1000.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1001.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1002.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1004.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1100.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1101.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1101.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1102.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1102.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1104.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1104.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1200.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1200.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1201.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1201.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1202.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1202.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1204.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1204.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1300.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1300.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1301.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1301.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1302.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1302.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1304.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1304.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1400.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1400.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1401.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1401.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1402.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1402.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1404.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1404.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1500.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1500.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1501.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1501.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1502.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1502.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1504.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1504.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1505.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1505.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1600.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1600.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1601.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1601.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1602.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1602.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1604.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1604.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1700.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1700.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1701.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1701.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1702.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1702.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1704.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1704.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1800.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1800.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1801.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1801.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1802.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1802.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1804.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1804.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1805.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1805.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1900.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1900.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1901.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1901.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1902.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1902.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_1904.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_1904.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_200.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_2000.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_2000.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_2001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_2001.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_2002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_2002.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_2004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_2004.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_201.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_201.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_202.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_202.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_203.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_203.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_204.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_204.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_205.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_205.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_206.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_206.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_2100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_2100.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_2101.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_2101.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_2102.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_2102.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_2104.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_2104.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_2105.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_2105.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_2300.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_2300.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_2301.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_2301.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_2302.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_2302.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_250.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_251.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_251.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_252.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_252.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_253.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_253.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_254.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_254.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_255.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_255.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_256.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_257.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_257.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_258.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_258.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_259.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_259.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_260.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_260.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_300.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_301.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_301.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_302.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_302.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_303.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_303.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_304.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_304.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_305.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_305.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_306.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_306.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_307.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_307.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_308.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_308.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_309.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_309.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_310.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_310.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_500.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_500.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5000.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5001.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5002.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5003.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5004.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5005.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5006.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5007.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5008.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5009.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_501.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_501.jpg -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5010.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5011.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5013.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_502.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_502.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5100.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5101.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5102.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5103.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5103.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5104.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5105.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5105.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5106.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5106.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5107.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5107.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5108.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5108.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5109.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5109.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5110.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5110.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5111.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5111.png -------------------------------------------------------------------------------- /CandyCrisisResources/PICT_5113.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorio/CandyCrisis/HEAD/CandyCrisisResources/PICT_5113.png -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PROJECT_VERSION "3.0.2" 4 | #define PROJECT_VERSION_MAJOR 3 5 | #define PROJECT_VERSION_MINOR 0 6 | #define PROJECT_VERSION_PATCH 2 7 | 8 | -------------------------------------------------------------------------------- /src/graymonitor.h: -------------------------------------------------------------------------------- 1 | // graymonitor.h 2 | 3 | void InitGrayMonitors( void ); 4 | void ShowGrayMonitor( short player ); 5 | 6 | extern MRect grayMonitorZRect, grayMonitorRect[2]; 7 | extern MBoolean grayMonitorVisible[2]; 8 | -------------------------------------------------------------------------------- /packaging/ReadMe.txt.in: -------------------------------------------------------------------------------- 1 | Candy Crisis source port @PROJECT_VERSION@ 2 | 3 | Homepage of the original game: 4 | https://candycrisis.sourceforge.net 5 | 6 | Homepage of this source port: 7 | https://github.com/jorio/candycrisis 8 | -------------------------------------------------------------------------------- /src/keyselect.h: -------------------------------------------------------------------------------- 1 | // keyselect.h 2 | 3 | 4 | #ifdef __cplusplus 5 | extern "C" 6 | #endif 7 | 8 | void CheckKeys(); 9 | 10 | extern SDL_Keycode playerKeys[2][4]; 11 | extern const SDL_Keycode defaultPlayerKeys[2][4]; 12 | -------------------------------------------------------------------------------- /src/gameticks.h: -------------------------------------------------------------------------------- 1 | // gameticks.h 2 | 3 | #include "MTypes.h" 4 | 5 | MTicks MTickCount(); 6 | void InitGameTickCount( void ); 7 | void FreezeGameTickCount( void ); 8 | void UnfreezeGameTickCount( void ); 9 | unsigned int GameTickCount( void ); 10 | -------------------------------------------------------------------------------- /src/version.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PROJECT_VERSION "@PROJECT_VERSION@" 4 | #define PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ 5 | #define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@ 6 | #define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@ 7 | 8 | -------------------------------------------------------------------------------- /src/random.h: -------------------------------------------------------------------------------- 1 | // random.h 2 | 3 | void InitRandom( int numPieces ); 4 | int GetPiece( int player ); 5 | int RandomBefore( int what ); 6 | int GetMagic( int player ); 7 | int GetGrenade( int player ); 8 | void AddExtraPiece( void ); 9 | 10 | extern int pieceMap[kBlobTypes]; 11 | -------------------------------------------------------------------------------- /packaging/candycrisis.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Version=1.0 4 | Name=Candy Crisis 5 | Comment=An exciting combination of pure action and puzzle gaming! 6 | Comment[fr]=Une combinaison palpitante d’action et de réflexion ! 7 | Icon=candycrisis 8 | Exec=candycrisis 9 | Terminal=false 10 | Categories=Game; 11 | -------------------------------------------------------------------------------- /.github/workflows/metarelease.yml: -------------------------------------------------------------------------------- 1 | name: Meta Release Big 3 2 | 3 | on: [workflow_dispatch] 4 | 5 | jobs: 6 | appimage: 7 | uses: ./.github/workflows/release-appimage.yml 8 | mac: 9 | uses: ./.github/workflows/release-mac.yml 10 | secrets: inherit 11 | windows: 12 | uses: ./.github/workflows/release-windows.yml 13 | -------------------------------------------------------------------------------- /src/tweak.h: -------------------------------------------------------------------------------- 1 | // tweak.h 2 | 3 | void TweakFirstBlob( int player, MRect *first ); 4 | void TweakSecondBlob( int player, MRect *second ); 5 | void StartTweak( int player, int direction, int rotate, int fall ); 6 | void UpdateTweak( int player, int suction ); 7 | void InitTweak( void ); 8 | 9 | #define d2r(x) ((x)*(kPi/180)) 10 | 11 | #define kTweakDelay 1 12 | -------------------------------------------------------------------------------- /src/music.h: -------------------------------------------------------------------------------- 1 | // music.h 2 | 3 | 4 | void EnableMusic( MBoolean on ); 5 | void PauseMusic( void ); 6 | void ResumeMusic( void ); 7 | void FastMusic( void ); 8 | void SlowMusic( void ); 9 | int GetCurrentMusic( void ); 10 | void ChooseMusic( short which ); 11 | void ShutdownMusic(); 12 | 13 | 14 | extern MBoolean musicOn; 15 | extern int musicSelection; 16 | 17 | -------------------------------------------------------------------------------- /src/pause.h: -------------------------------------------------------------------------------- 1 | // pause.h 2 | 3 | void HandleDialog( int type ); 4 | void SurfaceGetEdges( SDL_Surface* edgeSurface, const MRect *rect ); 5 | void SurfaceCurveEdges( SDL_Surface* edgeSurface, const MRect *rect ); 6 | 7 | enum 8 | { 9 | kPauseDialog = 0, 10 | kHiScoreDialog, 11 | kContinueDialog, 12 | kControlsDialog, 13 | kVideoDialog, 14 | kNumDialogs 15 | }; 16 | -------------------------------------------------------------------------------- /src/victory.h: -------------------------------------------------------------------------------- 1 | // victory.h 2 | 3 | void EndRound( int player ); 4 | void BeginVictory( int player ); 5 | void InitVictory( void ); 6 | void Lose( int player ); 7 | void Win( int player ); 8 | void DropLosers( int player, int skip ); 9 | void DrawTimerCount( int player ); 10 | void DrawTimerBonus( int player ); 11 | 12 | #define kNumLetters 11 13 | #define kLetterHorizSize 13 14 | #define kLetterVertSize 20 15 | -------------------------------------------------------------------------------- /src/next.h: -------------------------------------------------------------------------------- 1 | // next.h 2 | 3 | 4 | void InitNext( void ); 5 | void ShowNext( int player ); 6 | void RefreshNext( int player ); 7 | void UpdateNext( int player ); 8 | void PullNext( int player ); 9 | void ShowPull( int player ); 10 | 11 | extern SDL_Surface* nextSurface; 12 | extern SDL_Surface* nextDrawSurface; 13 | 14 | extern MBoolean nextWindowVisible[2]; 15 | extern MRect nextWindowZRect, nextWindowRect[2]; 16 | -------------------------------------------------------------------------------- /src/font.h: -------------------------------------------------------------------------------- 1 | // font.h 2 | 3 | 4 | #ifndef __FONT__ 5 | #define __FONT__ 6 | 7 | 8 | typedef struct 9 | { 10 | SDL_Surface *surface; 11 | int width[256]; 12 | int across[256]; 13 | } 14 | SkittlesFont, *SkittlesFontPtr; 15 | 16 | 17 | void InitFont( void ); 18 | SkittlesFontPtr GetFont( int pictID ); 19 | int GetTextWidth( SkittlesFontPtr font, const char *text ); 20 | 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Candy Crisis source port change log 2 | 3 | ## 3.0.1 (June 2023) 4 | 5 | - On non-Mac UNIXes, the game now looks for its assets in `(executable path)/../share/candycrisis` in addition to `(pwd)/CandyCrisisResources`. This is meant to help Linux distros package the game. 6 | - Most file/string operations were delegated to SDL instead of libc. 7 | - Fixed minor memory leak. 8 | 9 | ## 3.0.0 (February 2023) 10 | 11 | Initial release of my unofficial port of Candy Crisis. 12 | -------------------------------------------------------------------------------- /src/graphics.h: -------------------------------------------------------------------------------- 1 | // graphics.h 2 | 3 | 4 | void DrawSpriteBlobs( int player, int type ); 5 | void EraseSpriteBlobs( int player ); 6 | void CleanSpriteArea( int player, MRect *myRect ); 7 | void CalcBlobRect( int x, int y, MRect *myRect ); 8 | void DrawBackdrop( void ); 9 | void ShowTitle( void ); 10 | void InitBackdrop( void ); 11 | 12 | 13 | extern SDL_Surface* backdropSurface; 14 | 15 | 16 | enum 17 | { 18 | blobBlinkAnimation = 0, 19 | blobJiggleAnimation, 20 | blobCryAnimation 21 | }; 22 | -------------------------------------------------------------------------------- /src/moving.h: -------------------------------------------------------------------------------- 1 | // moving.h 2 | 3 | MBoolean CanMoveDirection( int player, int dirX, int dirY ); 4 | void CalcSecondBlobOffset( int player, int *x, int *y ); 5 | MBoolean CanGoLeft( int player ); 6 | void GoLeft( int player ); 7 | MBoolean CanGoRight( int player ); 8 | void GoRight( int player ); 9 | MBoolean CanFall( int player ); 10 | void DoFall( int player ); 11 | MBoolean CanRotate( int player ); 12 | void DoRotate( int player ); 13 | void DoDrop( int player ); 14 | void StopDrop( int player ); 15 | -------------------------------------------------------------------------------- /src/score.h: -------------------------------------------------------------------------------- 1 | // score.h 2 | 3 | void InitScore( void ); 4 | void UpdateScore( int player ); 5 | void ShowScore( int player ); 6 | void DrawCharacter( char which, const MRect *myRect ); 7 | 8 | #define kNumberHorizSize 16 9 | #define kNumberVertSize 32 10 | 11 | extern MRect scoreWindowZRect, scoreWindowRect[2]; 12 | extern MBoolean scoreWindowVisible[2]; 13 | extern int roundStartScore[2], score[2], displayedScore[2]; 14 | 15 | extern const char characterList[]; 16 | 17 | #define kCharacterScore '!' 18 | #define kCharacterStage '#' 19 | -------------------------------------------------------------------------------- /src/soundfx.h: -------------------------------------------------------------------------------- 1 | // soundfx.h 2 | 3 | 4 | void InitSound(); 5 | void ShutdownSound(); 6 | void PlayStereo( int player, short which ); 7 | void PlayStereoFrequency( int player, short which, short freq ); 8 | void PlayMono( int which ); 9 | void UpdateSound(); 10 | 11 | enum 12 | { 13 | kShift = 0, 14 | kRotate, 15 | kPlace, 16 | kSquishy, 17 | kBounce, 18 | kSplop, 19 | kWhistle, 20 | kPause, 21 | kLoss, 22 | kVictory, 23 | kMagic, 24 | kWhomp, 25 | kChime, 26 | kClick, 27 | kLevelUp, 28 | kContinueSnd, 29 | kBatsuSnd, 30 | kNumSounds 31 | }; 32 | 33 | extern MBoolean soundOn; 34 | -------------------------------------------------------------------------------- /src/grays.h: -------------------------------------------------------------------------------- 1 | // grays.h 2 | 3 | void InitGrays( void ); 4 | void CalculateGrays( int player, int BlobsToDrop ); 5 | void SetupGrays( int player ); 6 | void DropGrays( int player ); 7 | void BlinkGrays( int player ); 8 | void PlaceGrayRow( int player, int grayX ); 9 | void Bounce( int player ); 10 | void LockGrays( int player ); 11 | MBoolean BusyDroppingGrays( int player ); 12 | 13 | extern int grays[2][kGridAcross], grayAir[2][kGridAcross], graySpeed[2]; 14 | extern int unallocatedGrays[2], lockGrays[2], rowBounce[2][kGridAcross], splat[2][kGridAcross]; 15 | extern MTicks blinkTime[2]; 16 | extern int blinkStage[2]; 17 | 18 | #define kTimeBetweenBlinks 120 19 | -------------------------------------------------------------------------------- /src/tutorial.h: -------------------------------------------------------------------------------- 1 | // tutorial.h 2 | 3 | typedef enum 4 | { 5 | kMessage = 0, 6 | kIdleTicks, 7 | kRetrieve, 8 | kPosition, 9 | kSpin, 10 | kBlockUntilLand, 11 | kBlockUntilDrop, 12 | kPunish, 13 | kComplete, 14 | kBlockUntilComplete 15 | } 16 | AutoCommand; 17 | 18 | typedef struct 19 | { 20 | AutoCommand command; 21 | int d1, d2; 22 | const char *message; 23 | } 24 | AutoPattern, *AutoPatternPtr; 25 | 26 | extern AutoPatternPtr autoPattern; 27 | extern MTicks tutorialTime; 28 | 29 | void InitTutorial( void ); 30 | void StartBalloon( const char *message ); 31 | void StopBalloon( void ); 32 | void UpdateBalloon( void ); 33 | void EndTutorial( void ); 34 | -------------------------------------------------------------------------------- /src/hiscore.h: -------------------------------------------------------------------------------- 1 | // hiscore.h 2 | 3 | #define kNameLength 40 4 | 5 | typedef struct 6 | { 7 | signed char grid[kGridAcross][kGridDown]; 8 | signed char a, b, m, g, lv, x, r, player; 9 | int value; 10 | char name[kNameLength]; 11 | } Combo; 12 | 13 | typedef struct 14 | { 15 | char name[kNameLength]; 16 | int score; 17 | } HighScore; 18 | 19 | void ShowHiscore( void ); 20 | void ShowBestCombo( void ); 21 | void AddHiscore( int score ); 22 | void SubmitCombo( Combo *in ); 23 | void InitPotentialCombos( void ); 24 | 25 | extern Combo best, potentialCombo[2]; 26 | extern HighScore scores[10]; 27 | extern char highScoreName[256]; 28 | extern const char *highScoreText, *highScoreRank; 29 | -------------------------------------------------------------------------------- /src/opponent.h: -------------------------------------------------------------------------------- 1 | // opponent.h 2 | 3 | void InitOpponent( void ); 4 | void BeginOpponent( int which ); 5 | void UpdateOpponent( void ); 6 | void DrawFrozenOpponent( void ); 7 | void OpponentChatter( MBoolean on ); 8 | void OpponentPissed( void ); 9 | 10 | enum 11 | { 12 | kBored=0, 13 | kLookRight, 14 | kLookLeft, 15 | kBlink1, 16 | kBlink2 17 | }; 18 | 19 | #define kOppFrames (kBlink2+1) 20 | #define kGlowArraySize 30 21 | 22 | extern int opponentMood, opponentFrame; 23 | extern MTicks opponentTime, glowTime[kGlows], panicTime; 24 | extern int glowFrame[kGlows], panicFrame; 25 | extern int glowArray[kGlowArraySize], lightGlowArray[kGlowArraySize]; 26 | 27 | extern MRect opponentWindowZRect, opponentWindowRect; 28 | -------------------------------------------------------------------------------- /packaging/prepare_appimage_appdir.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | set -e 4 | 5 | EXE_PATH="$1" 6 | 7 | [[ ! -f "${EXE_PATH}" ]] && echo "must pass valid exe path" && exit 1 8 | 9 | APP_NAME="$(basename "${EXE_PATH}")" 10 | APP_NAME_LOWERCASE="$(echo "$APP_NAME" | awk '{print tolower($0)}')" 11 | APP_DIR="build/${APP_NAME}.AppDir" 12 | 13 | mkdir -p "${APP_DIR}/usr/bin" 14 | mkdir -p "${APP_DIR}/usr/share" 15 | 16 | # Copy executable and assets 17 | cp "${EXE_PATH}" "${APP_DIR}/usr/bin" 18 | cp -r CandyCrisisResources "${APP_DIR}/usr/share/candycrisis" 19 | 20 | # Copy XDG stuff 21 | cp "packaging/${APP_NAME_LOWERCASE}.desktop" "${APP_DIR}" 22 | cp "packaging/${APP_NAME_LOWERCASE}.png" "${APP_DIR}" 23 | 24 | # Copy AppImage kicker script 25 | cp "packaging/AppRun" "${APP_DIR}" 26 | chmod 755 "${APP_DIR}/AppRun" 27 | -------------------------------------------------------------------------------- /src/MTypes.c: -------------------------------------------------------------------------------- 1 | /// 2 | /// MTypes.c 3 | /// 4 | /// Generic replacements for very basic Mac types. 5 | /// 6 | /// John Stiles, 2002/10/14 7 | /// 8 | 9 | 10 | #include "MTypes.h" 11 | 12 | 13 | void UnionMRect( const MRect* a, const MRect* b, MRect* u ) 14 | { 15 | u->top = MinShort( a->top, b->top ); 16 | u->left = MinShort( a->left, b->left ); 17 | u->bottom = MaxShort( a->bottom, b->bottom ); 18 | u->right = MaxShort( a->right, b->right ); 19 | } 20 | 21 | 22 | void OffsetMRect( MRect* r, int x, int y ) 23 | { 24 | r->top += y; 25 | r->left += x; 26 | r->bottom += y; 27 | r->right += x; 28 | } 29 | 30 | 31 | unsigned char MPointInMRect( MPoint p, const MRect* r ) 32 | { 33 | return (p.h >= r->left) && 34 | (p.h < r->right) && 35 | (p.v >= r->top) && 36 | (p.v < r->bottom); 37 | } 38 | -------------------------------------------------------------------------------- /src/gameticks.c: -------------------------------------------------------------------------------- 1 | // gameticks.c 2 | 3 | #include 4 | #include "gameticks.h" 5 | 6 | MTicks baseTickCount, freezeTickCount; 7 | int freezeLevel; 8 | 9 | MTicks MTickCount() 10 | { 11 | return (unsigned int ) ((float)SDL_GetTicks() * 0.06f); 12 | } 13 | 14 | void InitGameTickCount( void ) 15 | { 16 | baseTickCount = freezeTickCount = 0; 17 | freezeLevel = 0; 18 | } 19 | 20 | void FreezeGameTickCount( void ) 21 | { 22 | if( freezeLevel == 0 ) 23 | { 24 | freezeTickCount = MTickCount( ); 25 | } 26 | freezeLevel--; 27 | } 28 | 29 | void UnfreezeGameTickCount( void ) 30 | { 31 | freezeLevel++; 32 | if( freezeLevel >= 0 ) 33 | { 34 | freezeLevel = 0; 35 | baseTickCount += MTickCount( ) - freezeTickCount; 36 | } 37 | } 38 | 39 | unsigned int GameTickCount( void ) 40 | { 41 | if( freezeLevel < 0 ) 42 | return freezeTickCount - baseTickCount; 43 | 44 | return MTickCount( ) - baseTickCount; 45 | } 46 | -------------------------------------------------------------------------------- /src/control.h: -------------------------------------------------------------------------------- 1 | // control.h 2 | 3 | void PlayerControl( int player ); 4 | void AIControl( int player ); 5 | void ChooseAIDestination( int player ); 6 | int TestAIDestination( int player, int testX, int testR ); 7 | int GetRowHeight( int player, int row ); 8 | int DetermineEmotion( int player ); 9 | int TestTemporaryGrid( void ); 10 | void QuickRemove( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color ); 11 | MBoolean CanReach( int player, int testX ); 12 | int ScoreTemporaryGrid( void ); 13 | int BestColor( int player, int BlobX, int BlobY ); 14 | void LogText( char string[] ); 15 | void LogGrid( signed char myGrid[kGridAcross][kGridDown] ); 16 | void AutoControl( int player ); 17 | 18 | extern int destinationX[2], destinationR[2]; 19 | extern signed char tempGrid[kGridAcross][kGridDown]; 20 | extern MTicks timeAI[2]; 21 | extern MBoolean moveQuick[2]; 22 | enum 23 | { 24 | kEmotionNeutral = 0, 25 | kEmotionSad, 26 | kEmotionHappy, 27 | kEmotionPanic 28 | }; 29 | -------------------------------------------------------------------------------- /src/MTypes.h: -------------------------------------------------------------------------------- 1 | ///// 2 | /// MTypes.h 3 | /// 4 | /// Generic replacements for very basic Mac types. 5 | /// 6 | /// John Stiles, 2002/10/14 7 | /// 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | typedef signed char MBoolean; 15 | 16 | typedef uint32_t MTicks; 17 | 18 | 19 | typedef struct MRGBColor 20 | { 21 | unsigned short red; 22 | unsigned short green; 23 | unsigned short blue; 24 | } MRGBColor; 25 | 26 | 27 | typedef struct MRect 28 | { 29 | short top; 30 | short left; 31 | short bottom; 32 | short right; 33 | } MRect; 34 | 35 | 36 | typedef struct MPoint 37 | { 38 | short v; 39 | short h; 40 | } MPoint; 41 | 42 | 43 | void UnionMRect( const MRect* a, const MRect* b, MRect* u ); 44 | void OffsetMRect( MRect* r, int x, int y ); 45 | unsigned char MPointInMRect( MPoint p, const MRect* r ); 46 | 47 | 48 | static inline short MinShort(short a, short b) { return a < b ? a : b; } 49 | static inline short MaxShort(short a, short b) { return a < b ? b : a; } 50 | static inline int MinInt(int a, int b) { return a < b ? a : b; } 51 | static inline int MaxInt(int a, int b) { return a < b ? b : a; } 52 | -------------------------------------------------------------------------------- /src/gworld.h: -------------------------------------------------------------------------------- 1 | // gworld.h 2 | 3 | 4 | #define kBlobHorizSize 24 5 | #define kBlobVertSize 24 6 | #define kBlobShadowDepth 6 7 | #define kBlobShadowError 2 8 | 9 | #define kBlastWidth 72 10 | #define kBlastHeight 72 11 | #define kBlastFrames 14 12 | 13 | void GetBlobGraphics(); 14 | void InitPlayerWorlds(); 15 | 16 | void DrawPICTInSurface( SDL_Surface* surface, int pictID ); 17 | SDL_Surface* LoadPICTAsSurface( int pictID, int depth ); 18 | MBoolean PICTExists( int pictID ); 19 | 20 | void SurfaceDrawBoard( int player, const MRect *myRect ); 21 | void SurfaceDrawShadow( const MRect *myRect, int blob, int state ); 22 | void SurfaceDrawAlpha( const MRect *myRect, int blob, int mask, int state ); 23 | void SurfaceDrawColor( const MRect *myRect, int blob, int state, int r, int g, int b, int w ); 24 | void SurfaceDrawBlob( int player, const MRect *myRect, int blob, int state, int charred ); 25 | void SurfaceDrawSprite( const MRect *myRect, int blob, int state ); 26 | 27 | extern SDL_Surface* blobSurface; 28 | extern SDL_Surface* maskSurface; 29 | extern SDL_Surface* charMaskSurface; 30 | extern SDL_Surface* boardSurface[2]; 31 | extern SDL_Surface* blastSurface; 32 | extern SDL_Surface* blastMaskSurface; 33 | extern SDL_Surface* playerSurface[2]; 34 | extern SDL_Surface* playerSpriteSurface[2]; 35 | -------------------------------------------------------------------------------- /src/support/cmixer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum 4 | { 5 | CM_STATE_STOPPED, 6 | CM_STATE_PLAYING, 7 | CM_STATE_PAUSED 8 | }; 9 | 10 | typedef struct CMVoice* CMVoicePtr; 11 | typedef const struct CMVoice* CMVoiceConstPtr; 12 | 13 | void CMVoice_Free(CMVoicePtr voice); 14 | void CMVoice_Rewind(CMVoicePtr voice); 15 | double CMVoice_GetLength(CMVoiceConstPtr voice); 16 | double CMVoice_GetPosition(CMVoiceConstPtr voice); 17 | int CMVoice_GetState(CMVoiceConstPtr voice); 18 | void CMVoice_SetGain(CMVoicePtr voice, double gain); 19 | void CMVoice_SetPan(CMVoicePtr voice, double pan); 20 | void CMVoice_SetPitch(CMVoicePtr voice, double pitch); 21 | void CMVoice_SetLoop(CMVoicePtr voice, int loop); 22 | void CMVoice_SetInterpolation(CMVoicePtr voice, int interpolation); 23 | void CMVoice_Play(CMVoicePtr voice); 24 | void CMVoice_Pause(CMVoicePtr voice); 25 | void CMVoice_TogglePause(CMVoicePtr voice); 26 | void CMVoice_Stop(CMVoicePtr voice); 27 | 28 | CMVoicePtr CMVoice_LoadWAV(const char* path); 29 | 30 | CMVoicePtr CMVoice_LoadMOD(const char* path); 31 | void CMVoice_SetMODPlaybackSpeed(CMVoicePtr voice, double speed); 32 | 33 | void cmixer_InitWithSDL(void); 34 | void cmixer_ShutdownWithSDL(void); 35 | double cmixer_GetMasterGain(void); 36 | void cmixer_SetMasterGain(double newGain); 37 | -------------------------------------------------------------------------------- /packaging/AppRun: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | SELF=$(readlink -f "$0") 3 | HERE=${SELF%/*} 4 | export PATH="${HERE}/usr/bin/:${HERE}/usr/sbin/:${HERE}/usr/games/:${HERE}/bin/:${HERE}/sbin/${PATH:+:$PATH}" 5 | export LD_LIBRARY_PATH="${HERE}/usr/lib/:${HERE}/usr/lib/i386-linux-gnu/:${HERE}/usr/lib/x86_64-linux-gnu/:${HERE}/usr/lib32/:${HERE}/usr/lib64/:${HERE}/lib/:${HERE}/lib/i386-linux-gnu/:${HERE}/lib/x86_64-linux-gnu/:${HERE}/lib32/:${HERE}/lib64/${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" 6 | export PYTHONPATH="${HERE}/usr/share/pyshared/${PYTHONPATH:+:$PYTHONPATH}" 7 | export XDG_DATA_DIRS="${HERE}/usr/share/${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}" 8 | export PERLLIB="${HERE}/usr/share/perl5/:${HERE}/usr/lib/perl5/${PERLLIB:+:$PERLLIB}" 9 | export GSETTINGS_SCHEMA_DIR="${HERE}/usr/share/glib-2.0/schemas/${GSETTINGS_SCHEMA_DIR:+:$GSETTINGS_SCHEMA_DIR}" 10 | export QT_PLUGIN_PATH="${HERE}/usr/lib/qt4/plugins/:${HERE}/usr/lib/i386-linux-gnu/qt4/plugins/:${HERE}/usr/lib/x86_64-linux-gnu/qt4/plugins/:${HERE}/usr/lib32/qt4/plugins/:${HERE}/usr/lib64/qt4/plugins/:${HERE}/usr/lib/qt5/plugins/:${HERE}/usr/lib/i386-linux-gnu/qt5/plugins/:${HERE}/usr/lib/x86_64-linux-gnu/qt5/plugins/:${HERE}/usr/lib32/qt5/plugins/:${HERE}/usr/lib64/qt5/plugins/${QT_PLUGIN_PATH:+:$QT_PLUGIN_PATH}" 11 | EXEC=$(grep -e '^Exec=.*' "${HERE}"/*.desktop | head -n 1 | cut -d "=" -f 2 | cut -d " " -f 1) 12 | cd $HERE 13 | exec "${EXEC}" "$@" 14 | -------------------------------------------------------------------------------- /src/zap.h: -------------------------------------------------------------------------------- 1 | // zap.h 2 | 3 | #include "font.h" 4 | 5 | void ZapScoreDisplay( int player, int amount, int multiplier, int x, int y, int c ); 6 | int SizeUp( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color ); 7 | int GetChainSize( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color ); 8 | void CleanSize( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color ); 9 | void CleanWithPolish( signed char myGrid[kGridAcross][kGridDown], signed char polish[kGridAcross][kGridDown], int x, int y, int color ); 10 | void RemoveBlobs( int player, int x, int y, int color, int generation ); 11 | void KillBlobs( int player ); 12 | void ZapBlobs( int player ); 13 | void CleanChunks( int player, int x, int y, int level, int style ); 14 | void DrawChunks( int player, int x, int y, int level, int style ); 15 | void CleanSplat( int player, int x, int y, int level ); 16 | void DrawSplat( int player, int x, int y, int level ); 17 | void GetZapStyle( int player, MRect *myRect, int *color, int *type, int which, 18 | int level, int style ); 19 | void InitZapStyle( void ); 20 | 21 | extern signed char death[2][kGridAcross][kGridDown]; 22 | extern int zapIteration[2]; 23 | 24 | extern int grenadeFrame[2]; 25 | extern MRect grenadeRect[2]; 26 | 27 | extern SkittlesFontPtr zapFont, zapOutline; 28 | 29 | #define kBlobClusterSize 4 30 | #define kZapFrames 20 31 | -------------------------------------------------------------------------------- /src/players.h: -------------------------------------------------------------------------------- 1 | // players.h 2 | 3 | void HandlePlayers( void ); 4 | void RetrieveBlobs( int player ); 5 | void Falling( int player ); 6 | void PlaceBlobs( int player ); 7 | void DropBlobs( int player ); 8 | void ResolveSuction( int player ); 9 | void BlinkBored( int player ); 10 | void InitPlayers( void ); 11 | void JiggleBlobs( int player ); 12 | void FastJiggleBlobs( int player ); 13 | void LockdownBlobs( int player ); 14 | void HandleMagic( int player ); 15 | void ChooseGlowingBlobs( int player ); 16 | void ConsiderGlow( int player, int color, int x, int y ); 17 | void GlowBlobs( int player ); 18 | void PlaceGrenade( int player ); 19 | void FadeCharred( int player ); 20 | void RedrawBoardContents( int player ); 21 | 22 | extern unsigned int boredTime[2], hintTime[2], fadeCharTime[2], animTime[2], shadowDepth[2], messageTime; 23 | extern MBoolean idling[2]; 24 | extern int emotions[2]; 25 | extern int glowColors[][3]; 26 | 27 | enum 28 | { 29 | kFalling = 0, 30 | kRetrieveBlobs, 31 | kPlaceBlobs, 32 | kJiggleBlobs, 33 | kFastJiggleBlobs, 34 | kLockdownBlobs, 35 | kDropBlobs, 36 | kZapBlobs, 37 | kKillBlobs, 38 | kDropGrays, 39 | kLosing, 40 | kWinning, 41 | kChooseDifficulty, 42 | kWaitingToStart, 43 | kIdlePlayer, 44 | kWaitForRetrieval 45 | }; 46 | 47 | #define kDropSpeed 1 48 | #define kFallSpeed 1 49 | #define kFlashSpeed 5 50 | 51 | #define kLeftPlayerWindowCenter 0.16 52 | #define kRightPlayerWindowCenter 0.84 53 | -------------------------------------------------------------------------------- /packaging/app.exe.rc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "version.h" 3 | 4 | #pragma code_page(1252) 5 | 6 | #define PROJECT_VERSION_4_WORDS PROJECT_VERSION_MAJOR,PROJECT_VERSION_MINOR,PROJECT_VERSION_PATCH,0 7 | 8 | // Icon with lowest ID value placed first to ensure application icon 9 | // remains consistent on all systems. 10 | // 11 | // IMPORTANT: The .ico should be UNCOMPRESSED, as a png-compressed .ico 12 | // may cause garbage AVs to flag the game as a false positive! 13 | IDI_ICON1 ICON DISCARDABLE "app.ico" 14 | 15 | VS_VERSION_INFO VERSIONINFO 16 | FILEVERSION PROJECT_VERSION_4_WORDS 17 | PRODUCTVERSION PROJECT_VERSION_4_WORDS 18 | FILEOS VOS_NT_WINDOWS32 19 | FILETYPE VFT_APP 20 | BEGIN 21 | BLOCK "StringFileInfo" 22 | BEGIN 23 | BLOCK "040904b0" // US English cp1252 (hex) 24 | BEGIN 25 | VALUE "CompanyName", "Candy Crisis" 26 | VALUE "FileDescription", "Candy Crisis" 27 | VALUE "FileVersion", PROJECT_VERSION 28 | VALUE "InternalName", "Candy Crisis" 29 | VALUE "LegalCopyright", "Candy Crisis" 30 | VALUE "OriginalFilename", "CandyCrisis.exe" 31 | VALUE "ProductName", "Candy Crisis" 32 | VALUE "ProductVersion", PROJECT_VERSION 33 | END 34 | END 35 | BLOCK "VarFileInfo" 36 | BEGIN 37 | VALUE "Translation", 0x409, 1252 // US English cp1252 38 | END 39 | END 40 | -------------------------------------------------------------------------------- /src/keyselect.c: -------------------------------------------------------------------------------- 1 | // keyselect.c 2 | 3 | #include "SDLU.h" 4 | 5 | #include 6 | 7 | #include "main.h" 8 | #include "players.h" 9 | #include "keyselect.h" 10 | 11 | 12 | SDL_Keycode playerKeys[2][4] = 13 | { 14 | { SDLK_A, SDLK_D, SDLK_X, SDLK_S }, 15 | { SDLK_LEFT, SDLK_RIGHT, SDLK_DOWN, SDLK_UP } 16 | }; 17 | 18 | const SDL_Keycode defaultPlayerKeys[2][4] = 19 | { 20 | { SDLK_A, SDLK_D, SDLK_X, SDLK_S }, 21 | { SDLK_LEFT, SDLK_RIGHT, SDLK_DOWN, SDLK_UP } 22 | }; 23 | 24 | 25 | void CheckKeys() 26 | { 27 | int player; 28 | int arraySize; 29 | const bool* pressedKeys; 30 | 31 | SDLU_PumpEvents(); 32 | pressedKeys = SDL_GetKeyboardState( &arraySize ); 33 | 34 | // Check for game keys 35 | for( player = 0; player < 2; player++ ) 36 | { 37 | if (pressedKeys[SDL_GetScancodeFromKey(playerKeys[player][0], NULL)]) 38 | hitKey[player].left++; 39 | else 40 | hitKey[player].left = 0; 41 | 42 | 43 | if (pressedKeys[SDL_GetScancodeFromKey(playerKeys[player][1], NULL)]) 44 | hitKey[player].right++; 45 | else 46 | hitKey[player].right = 0; 47 | 48 | 49 | if (pressedKeys[SDL_GetScancodeFromKey(playerKeys[player][2], NULL)]) 50 | hitKey[player].drop++; 51 | else 52 | hitKey[player].drop = 0; 53 | 54 | 55 | if (pressedKeys[SDL_GetScancodeFromKey(playerKeys[player][3], NULL)]) 56 | hitKey[player].rotate++; 57 | else 58 | hitKey[player].rotate = 0; 59 | } 60 | 61 | pauseKey = pressedKeys[SDL_SCANCODE_ESCAPE]; 62 | } 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Candy Crisis source port 2 | 3 | This is an unofficial source port of the old Mac game “[Candy Crisis](https://candycrisis.sourceforge.net)” to modern macOS, Linux, and Windows. This version has an optional widescreen mode. 4 | 5 | Get pre-built binaries here: https://github.com/jorio/CandyCrisis/releases 6 | 7 | ![Candy Crisis Screenshot](packaging/screenshot.jpg) 8 | 9 | ## How to build from source 10 | 11 | See [BUILD.md](BUILD.md) 12 | 13 | ## Credits/license 14 | 15 | - Original game: John Stiles 16 | - Original artwork: Kate Davis, Leanne Stiles, Arnauld de la Grandiere, Bob Frasure, Ryan Bliss 17 | - Music: Leanne Stiles, Lizardking, Armadon/Explizit, Leviathan/Nemesis, Jester/Pygmy, Siren, Sirrus, Scaven/FC, Spring, Timewalker, Jason/Silents, Chromatic Dragon, Ng Pei Sin 18 | 19 | According to the [original project's homepage on SourceForge](https://sourceforge.net/projects/candycrisis), the source code is licensed under the GNU GPL 2.0. Therefore, my additions and modifications to the game's source code are also covered by the GNU GPL 2.0. 20 | 21 | Artwork and music are copyrighted by their respective owners. 22 | 23 | Third-party libraries: 24 | - The audio mixer is a derivative work of [cmixer](https://github.com/rxi/cmixer), (C) 2017 rxi (MIT license) 25 | - [ibxm/micromod](https://github.com/martincameron/micromod), (C) 2019 Martin Cameron (BSD-3-Clause license) 26 | - [stb_image](https://github.com/nothings/stb), (C) 2017 Sean Barrett (MIT license) 27 | - [SDL](https://github.com/libsdl-org/SDL), (C) 1997-2023 Sam Lantinga (zlib license) 28 | -------------------------------------------------------------------------------- /src/level.h: -------------------------------------------------------------------------------- 1 | // level.h 2 | 3 | void InitGame( int player1, int player2 ); 4 | void ResetWidescreenLayout( ); 5 | MBoolean InitCharacter( int player, int level ); 6 | void PrepareStageGraphics( int type ); 7 | void BeginRound( MBoolean changeMusic ); 8 | void InitDifficulty( void ); 9 | void ChooseDifficulty( int player ); 10 | void SelectRandomLevel( void ); 11 | void IncrementLevel( void ); 12 | void TotalVictory( void ); 13 | void RegisteredVictory( void ); 14 | void SharewareVictory( void ); 15 | void InitStage( void ); 16 | void DrawStage( void ); 17 | void GameStartMenu( void ); 18 | void ShowGameOverScreen( void ); 19 | 20 | #define kGlows 2 21 | 22 | #define kLevels 12 23 | #define kTutorialLevel 14 24 | 25 | #define kEasyLevel 50 26 | #define kMediumLevel 70 27 | #define kHardLevel 90 28 | #define kUltraLevel 110 29 | 30 | 31 | typedef struct 32 | { 33 | unsigned char isHeavy; 34 | unsigned char colorR; 35 | unsigned char colorG; 36 | unsigned char colorB; 37 | short time; 38 | } Glow; 39 | 40 | typedef struct 41 | { 42 | short picture; 43 | short intellect; 44 | short zapStyle; 45 | short autoSetup[6]; 46 | short speedNormal; 47 | short speedRush; 48 | short music; 49 | short dropSpeed; 50 | Glow glow[kGlows]; 51 | short hints; 52 | } 53 | Character; 54 | 55 | enum 56 | { 57 | kPlayerControl = 0, 58 | kAIControl, 59 | kNobodyControl, 60 | kAutoControl 61 | }; 62 | 63 | extern Character character[2]; 64 | extern int level, players, credits, difficulty[2]; 65 | extern int difficultyTicks, backdropTicks, backdropFrame; 66 | 67 | 68 | -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | # How to build Candy Crisis from source 2 | 3 | ## macOS 4 | 5 | Prerequisites: Xcode, git, cmake (install via homebrew) 6 | 7 | Build recipe: 8 | 9 | ```bash 10 | git clone https://github.com/jorio/CandyCrisis 11 | cd CandyCrisis 12 | 13 | curl -LO https://github.com/libsdl-org/SDL/releases/download/release-3.2.0/SDL3-3.2.0.dmg 14 | hdiutil attach SDL3-*.dmg 15 | cp -a /Volumes/SDL3/SDL3.xcframework/macos-arm64_x86_64/SDL3.framework SDL2.framework 16 | hdiutil detach /Volumes/SDL2 17 | 18 | cmake -S . -B build -G Xcode 19 | cmake --build build --config Release 20 | ``` 21 | 22 | ## Windows 23 | 24 | Prerequisites: Visual Studio 2022, git, cmake 25 | 26 | Build recipe (to run in PowerShell): 27 | ```bash 28 | git clone https://github.com/jorio/CandyCrisis 29 | cd CandyCrisis 30 | 31 | Invoke-WebRequest -OutFile SDL3-VC.zip -Uri https://github.com/libsdl-org/SDL/releases/download/release-3.2.0/SDL3-devel-3.2.0-VC.zip 32 | Expand-Archive SDL3-VC.zip 33 | move SDL3-VC/SDL3-* SDL3 34 | 35 | cmake -S . -B build -G 'Visual Studio 17 2022' 36 | cmake --build build --config Release 37 | ``` 38 | 39 | ## Linux 40 | 41 | Prerequisites: a decent C compiler, git, cmake, and your platform's SDL3 development package 42 | 43 | ```bash 44 | git clone https://github.com/jorio/CandyCrisis 45 | cd CandyCrisis 46 | cmake -S . -B build -DCMAKE_BUILD_TYPE=Release 47 | cmake --build build 48 | ``` 49 | 50 | Or, if you want to build SDL3 from source and link it statically: 51 | 52 | ```bash 53 | git clone https://github.com/jorio/CandyCrisis 54 | cd CandyCrisis 55 | 56 | git clone --depth 1 --branch release-3.2.0 https://github.com/libsdl-org/SDL 57 | 58 | cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SDL_FROM_SOURCE=1 -DSDL_STATIC=1 59 | cmake --build build 60 | ``` 61 | -------------------------------------------------------------------------------- /src/soundfx.c: -------------------------------------------------------------------------------- 1 | // soundfx.c 2 | 3 | #include "support/cmixer.h" 4 | 5 | #include "main.h" 6 | #include "soundfx.h" 7 | #include "music.h" 8 | 9 | MBoolean soundOn = true; 10 | 11 | #define k_playerStereoSeparation (0.5f) 12 | #define k_soundEffectGain (0.7f) 13 | static CMVoicePtr s_soundBank[kNumSounds]; 14 | 15 | void InitSound() 16 | { 17 | cmixer_InitWithSDL(); 18 | 19 | for (int i = 0; i < kNumSounds; i++) 20 | { 21 | const char* path = QuickResourceName("snd", i+128, ".wav"); 22 | if (!FileExists(path)) 23 | { 24 | Error(path); 25 | } 26 | 27 | s_soundBank[i] = CMVoice_LoadWAV(path); 28 | CMVoice_SetInterpolation(s_soundBank[i], true); 29 | } 30 | } 31 | 32 | void ShutdownSound() 33 | { 34 | for (int i = 0; i < kNumSounds; i++) 35 | { 36 | CMVoice_Free(s_soundBank[i]); 37 | s_soundBank[i] = NULL; 38 | } 39 | 40 | cmixer_ShutdownWithSDL(); 41 | } 42 | 43 | void PlayMono( int which ) 44 | { 45 | PlayStereoFrequency(2, which, 0); 46 | } 47 | 48 | void PlayStereo( int player, short which ) 49 | { 50 | PlayStereoFrequency(player, which, 0); 51 | } 52 | 53 | void PlayStereoFrequency( int player, short which, short freq ) 54 | { 55 | if (soundOn) 56 | { 57 | CMVoicePtr effect = s_soundBank[which]; 58 | 59 | double pan; 60 | switch (player) 61 | { 62 | case 0: pan = -k_playerStereoSeparation; break; 63 | case 1: pan = +k_playerStereoSeparation; break; 64 | default: pan = 0.0; break; 65 | } 66 | 67 | //CMVoice_Stop(effect); 68 | CMVoice_Rewind(effect); 69 | CMVoice_SetGain(effect, k_soundEffectGain); 70 | CMVoice_SetPan(effect, pan); 71 | CMVoice_SetPitch(effect, 1.0 + freq/16.0); 72 | CMVoice_Play(effect); 73 | 74 | UpdateSound(); 75 | } 76 | } 77 | 78 | void UpdateSound() 79 | { 80 | } 81 | -------------------------------------------------------------------------------- /src/SDLU.h: -------------------------------------------------------------------------------- 1 | /// 2 | /// SDLU.h 3 | /// 4 | 5 | #pragma once 6 | 7 | #include 8 | #include "MTypes.h" 9 | 10 | #define BLUE_MASK 0x0000FF 11 | #define GREEN_MASK 0x00FF00 12 | #define RED_MASK 0xFF0000 13 | #define CHANNEL_MASK BLUE_MASK 14 | #define FULL_WEIGHT (1+CHANNEL_MASK) 15 | 16 | #define COLOR_T int 17 | 18 | #define BITS_PER_1CHANNEL 8 19 | #define BITS_PER_2CHANNELS 16 20 | #define BITS_PER_3CHANNELS 24 21 | 22 | #define BYTES_PER_PIXEL 4 23 | #define BYTES_PER_MASK_PIXEL 1 24 | #define MASK_DEPTH 8 25 | 26 | #define SYSTEM_CURSOR_OFF 0 27 | #define SYSTEM_CURSOR_ARROW 1 28 | #define SYSTEM_CURSOR_HAND 2 29 | 30 | void SDLU_Init(); 31 | void SDLU_CreateRendererTexture(); 32 | SDL_Rect* SDLU_MRectToSDLRect( const MRect* in, SDL_Rect* out ); 33 | MRect* SDLU_SDLRectToMRect( const SDL_Rect* in, MRect* out ); 34 | int SDLU_BlitSurface( SDL_Surface* src, SDL_Rect* srcrect, SDL_Surface* dst, SDL_Rect* dstrect ); 35 | int SDLU_BlitSurface1to1( SDL_Surface* src, SDL_Surface* dst ); 36 | void SDLU_GetPixel( SDL_Surface* surface, int x, int y, SDL_Color* pixel ); 37 | void SDLU_ChangeSurfaceDepth( SDL_Surface** surface, int depth ); 38 | SDL_Surface* SDLU_InitSurface( SDL_Rect* rect, int depth ); 39 | void SDLU_BlitFrontSurface( SDL_Surface* source, SDL_Rect* sourceSDLRect, SDL_Rect* destSDLRect ); 40 | void SDLU_SetBrightness( float b ); 41 | void SDLU_AcquireSurface( SDL_Surface* surface ); 42 | SDL_Surface* SDLU_GetCurrentSurface(); 43 | void SDLU_ReleaseSurface( SDL_Surface* surface ); 44 | void SDLU_GetMouse( MPoint* pt ); 45 | int SDLU_Button(); 46 | void SDLU_Yield(); 47 | void SDLU_PumpEvents(); 48 | void SDLU_StartWatchingTyping(); 49 | void SDLU_StopWatchingTyping(); 50 | MBoolean SDLU_CheckASCIITyping(char* ascii); 51 | MBoolean SDLU_CheckSDLTyping(SDL_Keycode* sdlKey); 52 | MBoolean SDLU_IsForeground(); 53 | void SDLU_Present(); 54 | void SDLU_SetSystemCursor(int which); 55 | -------------------------------------------------------------------------------- /.github/workflows/release-windows.yml: -------------------------------------------------------------------------------- 1 | name: Release Windows 2 | 3 | on: [workflow_dispatch, workflow_call] 4 | 5 | env: 6 | SDL_VERSION: "3.2.0" 7 | GAME_SHORTNAME: "CandyCrisis" 8 | GAME_LONGNAME: "Candy Crisis" 9 | 10 | jobs: 11 | release-windows: 12 | runs-on: windows-2022 13 | timeout-minutes: 20 14 | 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | with: {submodules: 'recursive'} 19 | 20 | - name: Get SDL 21 | run: | 22 | Invoke-WebRequest -OutFile SDL3-VC.zip -Uri https://github.com/libsdl-org/SDL/releases/download/release-${{env.SDL_VERSION}}/SDL3-devel-${{env.SDL_VERSION}}-VC.zip 23 | Expand-Archive SDL3-VC.zip 24 | move SDL3-VC/SDL3-* SDL3 25 | 26 | - name: Configure 27 | run: cmake -S . -B build -G 'Visual Studio 17 2022' 28 | 29 | - name: Prepare artifact names 30 | run: | 31 | $GAME_VERSION = Get-Content build/version.txt 32 | $FOLDER_NAME = "${{env.GAME_LONGNAME}} $GAME_VERSION" 33 | $ARTIFACT_NAME = "${{env.GAME_SHORTNAME}}-$GAME_VERSION-windows-x64.zip" 34 | echo "GAME_VERSION=$GAME_VERSION" >> $env:GITHUB_ENV 35 | echo "FOLDER_NAME=$FOLDER_NAME" >> $env:GITHUB_ENV 36 | echo "ARTIFACT_NAME=$ARTIFACT_NAME" >> $env:GITHUB_ENV 37 | 38 | - name: Build 39 | run: cmake --build build --config Release -- -m 40 | 41 | # Note: if the game contains C++, you should also copy msvcp140.dll and vcruntime140_1.dll 42 | - name: Copy extra stuff 43 | run: | 44 | cmake --install build --prefix build/install 45 | copy build/ReadMe.txt build/Release 46 | copy build/install/bin/vcruntime140.dll build/Release 47 | 48 | - name: Zip it up 49 | run: | 50 | move build/Release "build/${{env.FOLDER_NAME}}" 51 | Compress-Archive -Path "build/${{env.FOLDER_NAME}}" -DestinationPath build/${{env.ARTIFACT_NAME}} -CompressionLevel Optimal 52 | 53 | - name: Upload 54 | uses: actions/upload-artifact@v4 55 | with: 56 | name: ${{env.ARTIFACT_NAME}} 57 | path: build/${{env.ARTIFACT_NAME}} 58 | -------------------------------------------------------------------------------- /src/music.c: -------------------------------------------------------------------------------- 1 | // music.c 2 | 3 | #include "main.h" 4 | #include "music.h" 5 | #include "gworld.h" 6 | #include "gameticks.h" 7 | #include "soundfx.h" 8 | #include "graphics.h" 9 | #include "support/cmixer.h" 10 | 11 | #define k_noMusic (-1) 12 | #define k_songs 14 13 | 14 | MBoolean musicOn = true; 15 | int musicSelection = k_noMusic; 16 | 17 | static MBoolean s_musicFast = false; 18 | int s_musicPaused = 0; 19 | 20 | static struct CMVoice* s_musicChannel = NULL; 21 | 22 | void EnableMusic( MBoolean on ) 23 | { 24 | if (s_musicChannel) 25 | { 26 | CMVoice_SetGain(s_musicChannel, on ? 1 : 0); 27 | } 28 | } 29 | 30 | void FastMusic( void ) 31 | { 32 | if (s_musicChannel && !s_musicFast) 33 | { 34 | CMVoice_SetMODPlaybackSpeed(s_musicChannel, 1.3); 35 | s_musicFast = true; 36 | } 37 | } 38 | 39 | void SlowMusic( void ) 40 | { 41 | if (s_musicChannel && s_musicFast) 42 | { 43 | CMVoice_SetMODPlaybackSpeed(s_musicChannel, 1.0); 44 | s_musicFast = false; 45 | } 46 | } 47 | 48 | void PauseMusic( void ) 49 | { 50 | if (s_musicChannel) 51 | { 52 | CMVoice_Pause(s_musicChannel); 53 | s_musicPaused++; 54 | } 55 | } 56 | 57 | void ResumeMusic( void ) 58 | { 59 | if (s_musicChannel) 60 | { 61 | CMVoice_Play(s_musicChannel); 62 | s_musicPaused--; 63 | } 64 | } 65 | 66 | int GetCurrentMusic( void ) 67 | { 68 | return musicSelection; 69 | } 70 | 71 | void ChooseMusic( short which ) 72 | { 73 | // Kill existing song first, if any 74 | ShutdownMusic(); 75 | 76 | musicSelection = -1; 77 | 78 | if (which >= 0 && which <= k_songs) 79 | { 80 | const char* qrn = QuickResourceName("mod", which+128, ".mod"); 81 | if (!FileExists(qrn)) 82 | { 83 | qrn = QuickResourceName("mod", which+128, ".s3m"); 84 | } 85 | if (!FileExists(qrn)) 86 | { 87 | return; 88 | } 89 | 90 | s_musicChannel = CMVoice_LoadMOD(qrn); 91 | 92 | EnableMusic(musicOn); 93 | CMVoice_Play(s_musicChannel); 94 | 95 | musicSelection = which; 96 | s_musicPaused = 0; 97 | } 98 | } 99 | 100 | void ShutdownMusic() 101 | { 102 | if (s_musicChannel) 103 | { 104 | CMVoice_Free(s_musicChannel); 105 | s_musicChannel = NULL; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/blitter.h: -------------------------------------------------------------------------------- 1 | // blitter.h 2 | 3 | #include "font.h" 4 | 5 | 6 | #define _5TO8(x) (((x) << 3) | ((x) >> 2)) 7 | #define _15TO8_8_8(x) _5TO8((x >> 10) & 0x1F), \ 8 | _5TO8((x >> 5 ) & 0x1F), \ 9 | _5TO8(x & 0x1F) 10 | 11 | 12 | // SDL type 13 | void SurfaceBlitMask( SDL_Surface* object, SDL_Surface* mask, SDL_Surface* dest, 14 | const MRect* objectRect, const MRect* maskRect, const MRect* destRect ); 15 | 16 | void SurfaceBlitBlob( const MRect* blobRect, const MRect* destRect ); 17 | 18 | void SurfaceBlitColor( SDL_Surface* mask, SDL_Surface* dest, 19 | const MRect* maskRect, const MRect* destRect, 20 | int r, int g, int b, int weight ); 21 | 22 | void SurfaceBlitAlpha( SDL_Surface* back, SDL_Surface* source, SDL_Surface* alpha, SDL_Surface* dest, 23 | const MRect* backRect, const MRect* sourceRect, const MRect* alphaRect, const MRect* destRect ); 24 | 25 | void SurfaceBlitWeightedDualAlpha(SDL_Surface* back, SDL_Surface* source, SDL_Surface* mask, SDL_Surface* alpha, SDL_Surface* dest, 26 | const MRect* backRect, const MRect* srcRect, const MRect* maskRect, const MRect* alphaRect, const MRect* destRect, 27 | int inWeight ); 28 | 29 | void SurfaceBlitWeightedCharacter( SkittlesFontPtr font, unsigned char text, MPoint *dPoint, int r, int g, int b, int alpha ); 30 | 31 | void SurfaceBlitCharacter( SkittlesFontPtr font, unsigned char text, MPoint *dPoint, int r, int g, int b, int dropShadow ); 32 | 33 | void SurfaceBlitBlendOver(SDL_Surface* source, SDL_Surface* dest, 34 | const MRect* sourceRect, const MRect* destRect, 35 | int r1, int g1, int b1, 36 | int r2, int g2, int b2, 37 | int r3, int g3, int b3, 38 | int r4, int g4, int b4, 39 | int weight ); 40 | 41 | void SurfaceBlitColorOver(SDL_Surface* source, SDL_Surface* dest, 42 | const MRect* sourceRect, const MRect* destRect, 43 | int r, int g, int b, int weight ); 44 | 45 | 46 | 47 | void SetUpdateRect( int player, MRect *where ); 48 | void UpdatePlayerWindow( int player ); 49 | void InitBlitter( void ); 50 | 51 | extern MBoolean update[2][kGridAcross][kGridDown]; 52 | extern MBoolean refresh[2]; 53 | extern MPoint topLeft[2]; 54 | -------------------------------------------------------------------------------- /src/random.c: -------------------------------------------------------------------------------- 1 | // random.c 2 | 3 | #include "main.h" 4 | #include "random.h" 5 | 6 | unsigned int randomSeed[2], pieceCount[2], grenadeTimer[2]; 7 | int pieceMap[kBlobTypes], numPieces; 8 | 9 | 10 | static unsigned int internalRandomSeed = 1; 11 | 12 | static int internalRandom() // uses a generic rand() algorithm 13 | { 14 | internalRandomSeed = internalRandomSeed * 1103515245 + 12345; 15 | return ((internalRandomSeed >> 16) & 0x7FFF); 16 | } 17 | 18 | 19 | void InitRandom( int inNumPieces ) 20 | { 21 | int count, swap, swapWith; 22 | 23 | numPieces = inNumPieces; 24 | randomSeed[0] = randomSeed[1] = (unsigned int) SDL_GetTicks(); 25 | pieceCount[0] = pieceCount[1] = 0; 26 | grenadeTimer[0] = grenadeTimer[1] = 40; 27 | 28 | for( count=0; count= (6*4) ) 58 | { 59 | myRect.right += kBlobHorizSize; 60 | SurfaceDrawSprite( &myRect, 4, kSobBlob ); 61 | myRect.left = myRect.right; 62 | 63 | monitor -= (6*4); 64 | } 65 | 66 | CalcBlobRect( kNoSuction, kGray-1, &srcRect ); 67 | while( monitor >= 6 ) 68 | { 69 | myRect.right += kBlobHorizSize; 70 | SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink ); 71 | myRect.left = myRect.right; 72 | 73 | monitor -= 6; 74 | } 75 | 76 | if( monitor > 0 ) 77 | { 78 | myRect.right += kBlobHorizSize; 79 | SurfaceDrawAlpha( &myRect, kGray, kLight, smallGrayList[monitor] ); 80 | myRect.left = myRect.right; 81 | myRect.right += kBlobHorizSize; 82 | SurfaceDrawAlpha( &myRect, kGray, kLight, smallGrayList[monitor]+1 ); 83 | } 84 | 85 | SDLU_ReleaseSurface( smallGrayDrawSurface ); 86 | 87 | SDLU_BlitFrontSurface( smallGrayDrawSurface, 88 | SDLU_MRectToSDLRect( &grayMonitorZRect, &sourceSDLRect ), 89 | SDLU_MRectToSDLRect( &grayMonitorRect[player], &destSDLRect ) ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/support/ibxm.h: -------------------------------------------------------------------------------- 1 | 2 | /* ibxm/ac mod/xm/s3m replay (c)mumart@gmail.com */ 3 | 4 | extern const char *IBXM_VERSION; 5 | 6 | struct data { 7 | char *buffer; 8 | int length; 9 | }; 10 | 11 | struct sample { 12 | char name[32]; 13 | int loop_start, loop_length; 14 | short volume, panning, rel_note, fine_tune, *data; 15 | }; 16 | 17 | struct envelope { 18 | char enabled, sustain, looped, num_points; 19 | short sustain_tick, loop_start_tick, loop_end_tick; 20 | short points_tick[16], points_ampl[16]; 21 | }; 22 | 23 | struct instrument { 24 | int num_samples, vol_fadeout; 25 | char name[32], key_to_sample[97]; 26 | char vib_type, vib_sweep, vib_depth, vib_rate; 27 | struct envelope vol_env, pan_env; 28 | struct sample *samples; 29 | }; 30 | 31 | struct pattern { 32 | int num_channels, num_rows; 33 | char *data; 34 | }; 35 | 36 | struct module { 37 | char name[32]; 38 | int num_channels, num_instruments; 39 | int num_patterns, sequence_len, restart_pos; 40 | int default_gvol, default_speed, default_tempo, c2_rate, gain; 41 | int linear_periods, fast_vol_slides; 42 | unsigned char *default_panning, *sequence; 43 | struct pattern *patterns; 44 | struct instrument *instruments; 45 | }; 46 | 47 | /* Allocate and initialize a module from the specified data, returns NULL on error. 48 | Message must point to a 64-character buffer to receive error messages. */ 49 | struct module *module_load(struct data *data, char *message); 50 | /* Deallocate the specified module. */ 51 | void dispose_module(struct module *module); 52 | /* Allocate and initialize a replay with the specified module and sampling rate. */ 53 | struct replay *new_replay(struct module *module, int sample_rate, int interpolation); 54 | /* Deallocate the specified replay. */ 55 | void dispose_replay(struct replay *replay); 56 | /* Returns the song duration in samples at the current sampling rate. */ 57 | int replay_calculate_duration(struct replay *replay, int speed_multiplier_percent); 58 | /* Seek to approximately the specified sample position. 59 | The actual sample position reached is returned. */ 60 | //int replay_seek(struct replay *replay, int sample_pos); 61 | /* Set the pattern in the sequence to play. The tempo is reset to the default. */ 62 | void replay_set_sequence_pos(struct replay *replay, int pos); 63 | /* Generates audio and returns the number of stereo samples written into mix_buf. 64 | Individual channels may be excluded using the mute bitmask. */ 65 | int replay_get_audio(struct replay *replay, int *mix_buf, int mute, int speed_multiplier_percent); 66 | /* Returns the currently playing pattern in the sequence.*/ 67 | int replay_get_sequence_pos(struct replay *replay); 68 | /* Returns the currently playing row in the pattern. */ 69 | int replay_get_row(struct replay *replay); 70 | /* Returns the length of the output buffer required by replay_get_audio(). */ 71 | int calculate_mix_buf_len(int sample_rate, int speed_multiplier_percent); 72 | 73 | -------------------------------------------------------------------------------- /src/prefs.c: -------------------------------------------------------------------------------- 1 | // prefs.c 2 | 3 | #include 4 | 5 | #include "main.h" 6 | #include "prefs.h" 7 | #include "music.h" 8 | #include "soundfx.h" 9 | #include "hiscore.h" 10 | #include "keyselect.h" 11 | 12 | typedef struct Preference 13 | { 14 | const char* keyName; 15 | void* valuePtr; 16 | unsigned int valueLength; 17 | } Preference; 18 | 19 | Preference prefList[] = 20 | { 21 | { "MusicOn", &musicOn, sizeof(MBoolean ) }, 22 | { "SoundOn", &soundOn, sizeof(MBoolean ) }, 23 | { "KeyBindings", playerKeys, sizeof(playerKeys ) }, 24 | { "HighScores", scores, sizeof(scores ) }, 25 | { "BestCombo", &best, sizeof(best ) }, 26 | { "Fullscreen", &fullscreen, sizeof(fullscreen ) }, 27 | { "Widescreen", &widescreen, sizeof(widescreen ) }, 28 | { "CrispUpscaling", &crispUpscaling, sizeof(crispUpscaling ) }, 29 | }; 30 | 31 | static SDL_IOStream* GetPrefsStream(const char* openmode) 32 | { 33 | static char path[1024]; 34 | char* userDir = SDL_GetPrefPath(NULL, "CandyCrisis"); 35 | SDL_snprintf(path, sizeof(path), "%sCandyCrisisPrefs.bin", userDir); 36 | SDL_free(userDir); 37 | return SDL_IOFromFile(path, openmode); 38 | } 39 | 40 | void LoadPrefs() 41 | { 42 | SDL_IOStream* stream = GetPrefsStream("rb"); 43 | if (!stream) 44 | { 45 | return; 46 | } 47 | 48 | for (int i = 0; i < arrsize(prefList); i++) 49 | { 50 | Preference* pref = &prefList[i]; 51 | 52 | SDL_SeekIO(stream, 0, SDL_IO_SEEK_SET); 53 | 54 | while (1) 55 | { 56 | size_t numRead; 57 | Uint8 keyLength; 58 | char key[256]; 59 | unsigned int contentsLength; 60 | 61 | numRead = SDL_ReadIO(stream, &keyLength, sizeof(keyLength)); 62 | if (!numRead) 63 | break; 64 | SDL_ReadIO(stream, key, keyLength); 65 | key[keyLength] = '\0'; 66 | SDL_ReadIO(stream, &contentsLength, sizeof(contentsLength)); 67 | 68 | if (!SDL_strncmp(key, pref->keyName, SDL_strlen(pref->keyName))) 69 | { 70 | if (contentsLength != pref->valueLength) 71 | break; 72 | SDL_ReadIO(stream, pref->valuePtr, pref->valueLength); 73 | break; 74 | } 75 | else 76 | { 77 | SDL_SeekIO(stream, contentsLength, SDL_IO_SEEK_CUR); 78 | } 79 | } 80 | } 81 | 82 | SDL_CloseIO(stream); 83 | } 84 | 85 | void SavePrefs() 86 | { 87 | SDL_IOStream* stream = GetPrefsStream("wb"); 88 | if (!stream) 89 | { 90 | return; 91 | } 92 | 93 | for (int i = 0; i < arrsize(prefList); i++) 94 | { 95 | const Preference* pref = &prefList[i]; 96 | 97 | Uint8 keyLength = SDL_strlen(pref->keyName); 98 | 99 | SDL_WriteIO(stream, &keyLength, sizeof(keyLength)); 100 | SDL_WriteIO(stream, pref->keyName, SDL_strlen(pref->keyName)); 101 | SDL_WriteIO(stream, &pref->valueLength, sizeof(pref->valueLength)); 102 | SDL_WriteIO(stream, pref->valuePtr, pref->valueLength); 103 | } 104 | 105 | SDL_CloseIO(stream); 106 | } 107 | -------------------------------------------------------------------------------- /.github/workflows/release-appimage.yml: -------------------------------------------------------------------------------- 1 | name: Release AppImage 2 | 3 | on: [workflow_dispatch, workflow_call] 4 | 5 | env: 6 | SDL_VERSION: "3.2.0" 7 | APPIMAGETOOL_VERSION: "1.9.0" 8 | GAME_SHORTNAME: "candycrisis" 9 | GAME_LONGNAME: "Candy Crisis" 10 | 11 | jobs: 12 | build-linux-appimage: 13 | runs-on: ${{ matrix.platform.os }} 14 | timeout-minutes: 20 15 | 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | platform: # Use oldest available Ubuntu for maximum glibc compatibility 20 | - { name: x86_64, os: ubuntu-22.04 } 21 | - { name: aarch64, os: ubuntu-22.04-arm } 22 | 23 | steps: 24 | - name: Get CPU arch and count 25 | run: | 26 | NPROC=$(python3 -c 'import multiprocessing; print(multiprocessing.cpu_count())') 27 | ARCH=$(uname -m) 28 | echo "NPROC=$NPROC" >> $GITHUB_ENV 29 | echo "ARCH=$ARCH" >> $GITHUB_ENV 30 | echo CPU arch = $ARCH, CPU count = $NPROC 31 | 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | with: { submodules: 'recursive' } 35 | 36 | # cf. https://github.com/libsdl-org/SDL/blob/main/docs/README-linux.md 37 | # + desktop-file-utils for appimagetool 38 | - name: Get build dependencies for SDL from APT 39 | run: | 40 | sudo apt update 41 | sudo apt install -y libasound2-dev libpulse-dev \ 42 | libaudio-dev libjack-dev libsndio-dev libx11-dev libxext-dev \ 43 | libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev \ 44 | libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \ 45 | libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev fcitx-libs-dev \ 46 | desktop-file-utils 47 | 48 | - name: Get SDL source 49 | run: | 50 | git clone --depth 1 --branch release-${{ env.SDL_VERSION }} https://github.com/libsdl-org/SDL 51 | 52 | - name: Configure 53 | run: cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SDL_FROM_SOURCE=1 -DSTATIC_SDL=1 54 | 55 | - name: Prepare artifact names 56 | run: | 57 | GAME_VERSION=$(cat build/version.txt) 58 | ARTIFACT_NAME="${{env.GAME_SHORTNAME}}-$GAME_VERSION-linux-${{env.ARCH}}.AppImage" 59 | echo "GAME_VERSION=$GAME_VERSION" >> $GITHUB_ENV 60 | echo "ARTIFACT_NAME=$ARTIFACT_NAME" >> $GITHUB_ENV 61 | 62 | - name: Build 63 | run: cmake --build build -- -j ${{ env.NPROC }} 64 | 65 | - name: Prepare AppDir 66 | run: bash packaging/prepare_appimage_appdir.sh build/${{env.GAME_SHORTNAME}} 67 | 68 | - name: Make AppImage 69 | run: | 70 | wget https://github.com/AppImage/appimagetool/releases/download/${{env.APPIMAGETOOL_VERSION}}/appimagetool-${{env.ARCH}}.AppImage 71 | chmod +x appimagetool-${{env.ARCH}}.AppImage 72 | ./appimagetool-${{env.ARCH}}.AppImage --no-appstream build/${{env.GAME_SHORTNAME}}.AppDir build/${{env.ARTIFACT_NAME}} 73 | 74 | - name: Upload 75 | uses: actions/upload-artifact@v4 76 | with: 77 | name: ${{env.ARTIFACT_NAME}} 78 | path: build/${{env.ARTIFACT_NAME}} 79 | -------------------------------------------------------------------------------- /.github/workflows/release-mac.yml: -------------------------------------------------------------------------------- 1 | name: Release Mac 2 | 3 | on: [workflow_dispatch, workflow_call] 4 | 5 | env: 6 | SDL_VERSION: "3.2.0" 7 | GAME_SHORTNAME: "CandyCrisis" 8 | GAME_LONGNAME: "Candy Crisis" 9 | 10 | jobs: 11 | release-mac: 12 | runs-on: macos-latest 13 | timeout-minutes: 20 14 | 15 | steps: 16 | - name: Get CPU count 17 | run: | 18 | NPROC=$(python3 -c 'import multiprocessing; print(multiprocessing.cpu_count())') 19 | echo "NPROC=$NPROC" >> $GITHUB_ENV 20 | echo CPU count = $NPROC 21 | 22 | - name: Import codesigning certs 23 | uses: apple-actions/import-codesign-certs@v3 24 | with: 25 | p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }} 26 | p12-password: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }} 27 | 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | with: {submodules: 'recursive'} 31 | 32 | - name: Get SDL3.framework 33 | run: | 34 | curl -LO https://github.com/libsdl-org/SDL/releases/download/release-${{env.SDL_VERSION}}/SDL3-${{env.SDL_VERSION}}.dmg 35 | hdiutil attach SDL3-*.dmg 36 | cp -a /Volumes/SDL3/SDL3.xcframework/macos-arm64_x86_64/SDL3.framework SDL3.framework 37 | hdiutil detach /Volumes/SDL3 38 | 39 | - name: Configure 40 | run: cmake -S . -B build -G Xcode -DCODE_SIGN_IDENTITY=${{ secrets.APPLE_CODE_SIGN_IDENTITY }} 41 | 42 | - name: Prepare artifact names 43 | run: | 44 | GAME_VERSION=$(cat build/version.txt) 45 | FOLDER_NAME="${{env.GAME_LONGNAME}} $GAME_VERSION" 46 | ARTIFACT_NAME="${{env.GAME_SHORTNAME}}-$GAME_VERSION-mac.dmg" 47 | echo "GAME_VERSION=$GAME_VERSION" >> $GITHUB_ENV 48 | echo "FOLDER_NAME=$FOLDER_NAME" >> $GITHUB_ENV 49 | echo "ARTIFACT_NAME=$ARTIFACT_NAME" >> $GITHUB_ENV 50 | 51 | - name: Build 52 | run: cmake --build build --config RelWithDebInfo -- -j ${{ env.NPROC }} 53 | 54 | #- name: Force codesigning (not necessary since we tell Xcode to do it for us) 55 | # run: codesign --force --sign ${{ secrets.APPLE_DEVELOPMENT_TEAM }} --options runtime "build/RelWithDebInfo/${{env.GAME_LONGNAME}}.app" 56 | 57 | - name: Check codesigning 58 | run: codesign -vvv --deep --strict "build/RelWithDebInfo/${{env.GAME_LONGNAME}}.app" 59 | 60 | - name: Create dmg 61 | run: | 62 | cp build/ReadMe.txt build/RelWithDebInfo 63 | hdiutil create -fs HFS+ -srcfolder build/RelWithDebInfo -volname "${{env.FOLDER_NAME}}" build/${{env.ARTIFACT_NAME}} 64 | 65 | - name: Notarize 66 | run: | 67 | xcrun notarytool store-credentials GameNotarizationProfile --apple-id ${{ secrets.APPLE_NOTARIZATION_USERNAME }} --password ${{ secrets.APPLE_NOTARIZATION_PASSWORD }} --team-id ${{ secrets.APPLE_DEVELOPMENT_TEAM }} 68 | xcrun notarytool submit build/${{env.ARTIFACT_NAME}} --keychain-profile GameNotarizationProfile --wait 69 | 70 | - name: Staple 71 | run: xcrun stapler staple build/${{env.ARTIFACT_NAME}} 72 | 73 | - name: Upload 74 | uses: actions/upload-artifact@v4 75 | with: 76 | name: ${{env.ARTIFACT_NAME}} 77 | path: build/${{env.ARTIFACT_NAME}} 78 | -------------------------------------------------------------------------------- /src/tweak.c: -------------------------------------------------------------------------------- 1 | // tweak.c 2 | 3 | #include 4 | 5 | #include "main.h" 6 | #include "tweak.h" 7 | #include "gworld.h" 8 | #include "moving.h" 9 | #include "gameticks.h" 10 | #include "graphics.h" 11 | #include "players.h" 12 | 13 | MTicks xTweakTime[2], yTweakTime[2], rTweakTime[2]; 14 | int yTweak[2], xTweak[2], xDirection[2], rTweak[2], rDirection[2]; 15 | static unsigned int lastShadow[2]; 16 | int tweakOffsetX[4][11], tweakOffsetY[4][11]; 17 | 18 | void InitTweak( void ) 19 | { 20 | int rTweakValues[] = { 0, 5, 10, 30, 50, 70, 90, 110, 130, 150, 170 }; 21 | int count, rotate; 22 | double tweakRad; 23 | 24 | for( rotate = 0; rotate<2; rotate++ ) 25 | { 26 | for( count=0; count<=10; count++ ) 27 | { 28 | tweakRad = d2r( (90*rotate) - rTweakValues[count] ); 29 | tweakOffsetX[rotate][count] = (int) floor( 0.5 + cos( tweakRad ) * kBlobHorizSize ); 30 | tweakOffsetY[rotate][count] = (int) floor( 0.5 + sin( tweakRad ) * kBlobVertSize ); 31 | 32 | tweakOffsetX[rotate+2][count] = -tweakOffsetX[rotate][count]; 33 | tweakOffsetY[rotate+2][count] = -tweakOffsetY[rotate][count]; 34 | } 35 | } 36 | } 37 | 38 | void TweakFirstBlob( int player, MRect *first ) 39 | { 40 | int tweakValues[] = {0, -1, -2, -3, -6, -12}; 41 | 42 | if( xTweak[player] > 0 ) 43 | { 44 | OffsetMRect( first, xDirection[player] * tweakValues[xTweak[player]], 0 ); 45 | } 46 | 47 | if( yTweak[player] > 0 ) 48 | { 49 | OffsetMRect( first, 0, tweakValues[yTweak[player]] ); 50 | } 51 | } 52 | 53 | void TweakSecondBlob( int player, MRect *second ) 54 | { 55 | int x, y; 56 | 57 | CalcSecondBlobOffset( player, &x, &y ); 58 | OffsetMRect( second, 59 | tweakOffsetX[blobR[player]][rTweak[player]], 60 | tweakOffsetY[blobR[player]][rTweak[player]] ); 61 | } 62 | 63 | void StartTweak( int player, int direction, int rotate, int fall ) 64 | { 65 | if( fall != 0 ) 66 | { 67 | yTweak[player] = 3; 68 | yTweakTime[player] = GameTickCount() + kTweakDelay; 69 | } 70 | 71 | if( direction != 0 ) 72 | { 73 | xDirection[player] = direction; 74 | xTweak[player] = 5; 75 | xTweakTime[player] = GameTickCount() + kTweakDelay; 76 | } 77 | 78 | if( rotate != 0 ) 79 | { 80 | rTweak[player] = rotate * 5; 81 | rDirection[player] = rotate; 82 | rTweakTime[player] = GameTickCount() + kTweakDelay; 83 | } 84 | } 85 | 86 | void UpdateTweak( int player, int animation ) 87 | { 88 | MBoolean isXTweaked, isYTweaked, isRTweaked, isAnimTweaked = false; 89 | 90 | if( GameTickCount( ) >= animTime[player] ) 91 | { 92 | isAnimTweaked = true; 93 | animTime[player] += 2; 94 | anim[player]++; 95 | 96 | HandleMagic( player ); 97 | } 98 | 99 | isXTweaked = ( (GameTickCount() >= xTweakTime[player]) && (xTweak[player] > 0) ); 100 | isYTweaked = ( (GameTickCount() >= yTweakTime[player]) && (yTweak[player] > 0) ); 101 | isRTweaked = ( (GameTickCount() >= rTweakTime[player]) && (rTweak[player] > 0) ); 102 | 103 | if( isXTweaked || isRTweaked || isYTweaked || 104 | isAnimTweaked || (shadowDepth[player] != lastShadow[player]) ) 105 | { 106 | EraseSpriteBlobs( player ); 107 | 108 | if( isXTweaked ) 109 | { 110 | xTweak[player]--; 111 | xTweakTime[player] += kTweakDelay; 112 | } 113 | 114 | if( isYTweaked ) 115 | { 116 | yTweak[player]--; 117 | yTweakTime[player] += kTweakDelay; 118 | } 119 | 120 | if( isRTweaked ) 121 | { 122 | rTweak[player]--; 123 | rTweakTime[player] += kTweakDelay; 124 | } 125 | 126 | DrawSpriteBlobs( player, animation ); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/font.c: -------------------------------------------------------------------------------- 1 | // font.c 2 | 3 | #include "SDLU.h" 4 | 5 | #include "main.h" 6 | #include "font.h" 7 | #include "gworld.h" 8 | 9 | 10 | #define kNumFonts (picBatsuFont-picFont+1) 11 | 12 | static SkittlesFont s_font[kNumFonts]; 13 | 14 | 15 | static SkittlesFontPtr LoadFont( SkittlesFontPtr font, int pictID, unsigned char *letterMap ) 16 | { 17 | unsigned char* lastLine; 18 | unsigned char white; 19 | MBoolean success = false; 20 | int start, across, skip; 21 | SDL_Surface* temporarySurface; 22 | SDL_Rect sdlRect; 23 | 24 | temporarySurface = LoadPICTAsSurface( pictID, 8 ); 25 | 26 | if( temporarySurface ) 27 | { 28 | sdlRect.x = 0; 29 | sdlRect.y = 0; 30 | sdlRect.h = temporarySurface->h; 31 | sdlRect.w = temporarySurface->w; 32 | 33 | font->surface = SDLU_InitSurface( &sdlRect, 8 ); 34 | 35 | SDLU_BlitSurface( temporarySurface, &sdlRect, 36 | font->surface, &sdlRect ); 37 | 38 | SDL_DestroySurface( temporarySurface ); 39 | 40 | SDL_assert( font->surface->pixels ); 41 | 42 | white = SDL_MapSurfaceRGB( font->surface, 0xFF, 0xFF, 0xFF ); 43 | lastLine = (uint8_t*) font->surface->pixels + (font->surface->pitch * (font->surface->h - 1)); 44 | across = 0; 45 | 46 | // Measure empty space between character breaks 47 | while( lastLine[across] == white ) across++; 48 | skip = across; 49 | 50 | success = true; 51 | 52 | // Measure character starts and widths 53 | while( *letterMap ) 54 | { 55 | while( lastLine[across] != white ) across++; 56 | if( across > font->surface->pitch ) 57 | { 58 | success = false; 59 | break; 60 | } 61 | 62 | start = across; 63 | font->across[*letterMap] = across + (skip/2); 64 | 65 | while( lastLine[across] == white ) across++; 66 | font->width [*letterMap] = across - start - skip; 67 | 68 | letterMap++; 69 | } 70 | } 71 | 72 | if( success ) 73 | { 74 | return font; 75 | } 76 | else 77 | { 78 | Error( "LoadFont: files are missing or corrupt" ); 79 | return NULL; 80 | } 81 | } 82 | 83 | 84 | void InitFont( void ) 85 | { 86 | LoadFont( &s_font[0], picFont, (unsigned char*) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!:,.()*?0123456789'|-\x01\x02\x03\x04\x05 " ); 87 | LoadFont( &s_font[1], picHiScoreFont, (unsigned char*) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*().,/-=_+<>?|'\":; " ); 88 | LoadFont( &s_font[2], picContinueFont, (unsigned char*) "0123456789" ); 89 | LoadFont( &s_font[3], picBalloonFont, (unsigned char*) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-=_+;:,./<>? \x01\x02'\"" ); 90 | LoadFont( &s_font[4], picZapFont, (unsigned char*) "0123456789*PS" ); 91 | LoadFont( &s_font[5], picZapOutlineFont, (unsigned char*) "0123456789*" ); 92 | LoadFont( &s_font[6], picVictoryFont, (unsigned char*) "AB" ); 93 | LoadFont( &s_font[7], picBubbleFont, (unsigned char*) "*" ); 94 | LoadFont( &s_font[8], picTinyFont, (unsigned char*) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,.!`-=[];'/~_+{}:\"\\ " ); 95 | LoadFont( &s_font[9], picDashedLineFont, (unsigned char*) "." ); 96 | LoadFont( &s_font[10], picBatsuFont, (unsigned char*) "X" ); 97 | } 98 | 99 | 100 | SkittlesFontPtr GetFont( int pictID ) 101 | { 102 | int fontID = pictID - picFont; 103 | 104 | if( (fontID < 0) || (fontID >= kNumFonts) ) 105 | Error( "GetFont: fontID" ); 106 | 107 | return &s_font[fontID]; 108 | } 109 | 110 | 111 | int GetTextWidth( SkittlesFontPtr font, const char *text ) 112 | { 113 | int width = 0; 114 | while( *text ) 115 | { 116 | width += font->width[(uint8_t) *text++]; 117 | } 118 | 119 | return width; 120 | } 121 | 122 | -------------------------------------------------------------------------------- /src/score.c: -------------------------------------------------------------------------------- 1 | // score.c 2 | 3 | #include "SDLU.h" 4 | 5 | #include "main.h" 6 | #include "score.h" 7 | #include "gworld.h" 8 | #include "graphics.h" 9 | #include "blitter.h" 10 | #include "hiscore.h" 11 | #include "gameticks.h" 12 | #include "level.h" 13 | 14 | SDL_Surface* scoreSurface; 15 | SDL_Surface* numberSurface; 16 | SDL_Surface* numberMaskSurface; 17 | 18 | 19 | MRect scoreWindowZRect, scoreWindowRect[2]; 20 | MBoolean scoreWindowVisible[2] = {true, true}; 21 | int roundStartScore[2], score[2], displayedScore[2]; 22 | MTicks scoreTime[2]; 23 | const char characterList[] = 24 | { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 25 | 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 26 | 'U', 'V', 'W', 'X', 'Y', 'Z', '.', '0', '1', '2', 27 | '3', '4', '5', '6', '7', '8', '9', '!', '"', '#', 28 | '$' }; 29 | 30 | 31 | void InitScore( void ) 32 | { 33 | const double windowLoc[ ] = { 0.16, 0.84 }; 34 | SDL_Rect sdlRect; 35 | 36 | scoreWindowZRect.top = scoreWindowZRect.left = 0; 37 | scoreWindowZRect.bottom = 32; scoreWindowZRect.right = 144; 38 | 39 | scoreWindowRect[0] = scoreWindowRect[1] = scoreWindowZRect; 40 | CenterRectOnScreen( &scoreWindowRect[0], windowLoc[0], 0.89 ); 41 | CenterRectOnScreen( &scoreWindowRect[1], windowLoc[1], 0.89 ); 42 | 43 | scoreSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &scoreWindowZRect, &sdlRect ), 32 ); 44 | DrawPICTInSurface( scoreSurface, picBoard ); 45 | 46 | numberSurface = LoadPICTAsSurface( picNumber, 32 ); 47 | 48 | numberMaskSurface = LoadPICTAsSurface( picNumberMask, MASK_DEPTH ); 49 | 50 | displayedScore[0] = displayedScore[1] = 0; 51 | score[0] = score[1] = 0; 52 | scoreTime[0] = scoreTime[1] = 0; 53 | } 54 | 55 | void UpdateScore( int player ) 56 | { 57 | if( GameTickCount( ) >= scoreTime[player] ) 58 | { 59 | scoreTime[player] = GameTickCount() + 1; 60 | 61 | if( displayedScore[player] < score[player] ) 62 | { 63 | if( (score[player] - displayedScore[player]) > 5000 ) 64 | { 65 | displayedScore[player] += 1525; 66 | } 67 | else if( (score[player] - displayedScore[player]) > 1000 ) 68 | { 69 | displayedScore[player] += 175; 70 | } 71 | else 72 | { 73 | displayedScore[player] += 25; 74 | } 75 | 76 | if( displayedScore[player] > score[player] ) 77 | displayedScore[player] = score[player]; 78 | 79 | ShowScore( player ); 80 | } 81 | } 82 | } 83 | 84 | void ShowScore( int player ) 85 | { 86 | SDL_Rect sourceSDLRect, destSDLRect; 87 | MRect myRect; 88 | char myString[256]; 89 | int count; 90 | 91 | if( !scoreWindowVisible[player] ) return; 92 | 93 | if( control[player] != kNobodyControl ) 94 | { 95 | SDL_snprintf( myString, sizeof(myString), "%d", displayedScore[player] ); 96 | 97 | SDLU_AcquireSurface( scoreSurface ); 98 | 99 | SDL_GetSurfaceClipRect( scoreSurface, &sourceSDLRect ); 100 | SDLU_BlitSurface( boardSurface[player], &sourceSDLRect, 101 | scoreSurface, &sourceSDLRect ); 102 | 103 | myRect.top = 0; 104 | myRect.left = 2; 105 | myRect.bottom = kNumberVertSize; 106 | myRect.right = myRect.left + kNumberHorizSize; 107 | DrawCharacter( kCharacterScore, &myRect ); 108 | OffsetMRect( &myRect, kNumberHorizSize, 0 ); 109 | DrawCharacter( kCharacterScore+1, &myRect ); 110 | 111 | myRect = scoreWindowZRect; 112 | myRect.right -= 2; 113 | myRect.left = myRect.right - kNumberHorizSize; 114 | for( count = (int) SDL_strlen(myString) - 1; count >= 0; count-- ) 115 | { 116 | DrawCharacter( myString[count], &myRect ); 117 | OffsetMRect( &myRect, -kNumberHorizSize - 1, 0 ); 118 | } 119 | 120 | SDLU_ReleaseSurface( scoreSurface ); 121 | 122 | SDLU_BlitFrontSurface( scoreSurface, 123 | SDLU_MRectToSDLRect( &scoreWindowZRect, &sourceSDLRect ), 124 | SDLU_MRectToSDLRect( &scoreWindowRect[player], &destSDLRect ) ); 125 | } 126 | } 127 | 128 | void DrawCharacter( char which, const MRect *myRect ) 129 | { 130 | MRect srcRect; 131 | int count, result; 132 | 133 | result = -1; 134 | for( count = 0; count < arrsize(characterList); count++ ) 135 | { 136 | if( characterList[count] == which ) 137 | { 138 | result = count; 139 | break; 140 | } 141 | } 142 | 143 | if( result == -1 ) return; 144 | 145 | srcRect.top = 0; 146 | srcRect.left = result * kNumberHorizSize; 147 | srcRect.bottom = kNumberVertSize; 148 | srcRect.right = srcRect.left + kNumberHorizSize; 149 | 150 | SurfaceBlitMask( numberSurface, numberMaskSurface, SDLU_GetCurrentSurface(), 151 | &srcRect, &srcRect, myRect ); 152 | } 153 | -------------------------------------------------------------------------------- /src/moving.c: -------------------------------------------------------------------------------- 1 | // moving.c 2 | 3 | #include "main.h" 4 | #include "moving.h" 5 | #include "players.h" 6 | #include "graphics.h" 7 | #include "soundfx.h" 8 | #include "tweak.h" 9 | #include "gameticks.h" 10 | #include "level.h" 11 | 12 | void CalcSecondBlobOffset( int player, int *x, int *y ) 13 | { 14 | *x = *y = 0; 15 | 16 | switch( blobR[player] ) 17 | { 18 | case rightRotate: 19 | *x = 1; 20 | break; 21 | 22 | case downRotate: 23 | *y = 1; 24 | break; 25 | 26 | case leftRotate: 27 | *x = -1; 28 | break; 29 | 30 | case upRotate: 31 | *y = -1; 32 | break; 33 | } 34 | } 35 | 36 | MBoolean CanGoLeft( int player ) 37 | { 38 | return CanMoveDirection( player, -1, halfway[player]? 1: 0 ); 39 | } 40 | 41 | void GoLeft( int player ) 42 | { 43 | EraseSpriteBlobs( player ); 44 | blobX[player]--; 45 | StartTweak( player, -1, 0, 0 ); 46 | DrawSpriteBlobs( player, kNoSuction ); 47 | 48 | PlayStereo( player, kShift ); 49 | } 50 | 51 | MBoolean CanGoRight( int player ) 52 | { 53 | return CanMoveDirection( player, 1, halfway[player]? 1: 0 ); 54 | } 55 | 56 | void GoRight( int player ) 57 | { 58 | EraseSpriteBlobs( player ); 59 | blobX[player]++; 60 | StartTweak( player, 1, 0, 0 ); 61 | DrawSpriteBlobs( player, kNoSuction ); 62 | 63 | PlayStereo( player, kShift ); 64 | } 65 | 66 | MBoolean CanFall( int player ) 67 | { 68 | return CanMoveDirection( player, 0, 1 ); 69 | } 70 | 71 | MBoolean CanMoveDirection( int player, int dirX, int dirY ) 72 | { 73 | int currentX = blobX[player], currentY = blobY[player], x, y; 74 | 75 | currentX += dirX; 76 | currentY += dirY; 77 | 78 | if( currentX < 0 || currentX >= kGridAcross || currentY >= kGridDown ) 79 | return false; 80 | 81 | if( currentY >= 0 ) 82 | if( grid[player][currentX][currentY] != kEmpty ) 83 | return false; 84 | 85 | CalcSecondBlobOffset( player, &x, &y ); 86 | 87 | currentX += x; 88 | currentY += y; 89 | 90 | if( currentX < 0 || currentX >= kGridAcross || currentY >= kGridDown ) 91 | return false; 92 | 93 | if( currentY >= 0 ) 94 | if( grid[player][currentX][currentY] != kEmpty ) 95 | return false; 96 | 97 | return true; 98 | } 99 | 100 | void DoFall( int player ) 101 | { 102 | EraseSpriteBlobs( player ); 103 | 104 | if( halfway[player] ) 105 | blobY[player]++; 106 | halfway[player] = !halfway[player]; 107 | 108 | StartTweak( player, 0, 0, 1 ); 109 | 110 | DrawSpriteBlobs( player, kNoSuction ); 111 | } 112 | 113 | MBoolean CanRotate( int player ) 114 | { 115 | if( role[player] == kChooseDifficulty ) return false; 116 | 117 | if( grenade[player] ) return false; 118 | 119 | return true; 120 | 121 | } 122 | 123 | void DoRotate( int player ) 124 | { 125 | MBoolean possible; 126 | 127 | EraseSpriteBlobs( player ); 128 | 129 | blobR[player] = ( blobR[player] + 1 ) % 4; 130 | possible = CanMoveDirection( player, 0, halfway[player]? 1: 0 ); 131 | StartTweak( player, 0, 1, 0 ); // only rotates clockwise 132 | 133 | if( !possible ) 134 | { 135 | if( blobR[player] == downRotate ) 136 | { 137 | if( halfway[player] ) 138 | halfway[player] = false; 139 | else 140 | blobY[player]--; 141 | 142 | if( ++blobSpin[player] >= 4 ) 143 | { 144 | blobTime[player] = animTime[player] = GameTickCount( ); 145 | role[player] = kLockdownBlobs; 146 | anim[player] = 0; 147 | PlayStereoFrequency( player, kPlace, player ); 148 | } 149 | } 150 | 151 | if( blobR[player] == leftRotate ) 152 | { 153 | if( CanGoRight(player) ) 154 | GoRight( player ); 155 | else 156 | { 157 | blobR[player]++; 158 | StartTweak( player, 0, 2, 0 ); 159 | } 160 | } 161 | 162 | if( blobR[player] == rightRotate ) 163 | { 164 | if( CanGoLeft(player) ) 165 | GoLeft( player ); 166 | else 167 | { 168 | blobR[player]++; 169 | StartTweak( player, 0, 2, 0 ); 170 | 171 | if( !CanMoveDirection( player, 0, halfway[player]? 1: 0 ) ) 172 | { 173 | if( halfway[player] ) 174 | halfway[player] = false; 175 | else 176 | blobY[player]--; 177 | 178 | if( ++blobSpin[player] >= 4 ) 179 | { 180 | blobTime[player] = animTime[player] = GameTickCount( ); 181 | role[player] = kLockdownBlobs; 182 | anim[player] = 0; 183 | PlayStereoFrequency( player, kPlace, player ); 184 | } 185 | } 186 | } 187 | } 188 | } 189 | 190 | DrawSpriteBlobs( player, kNoSuction ); 191 | 192 | PlayStereo( player, kRotate ); 193 | } 194 | 195 | void DoDrop( int player ) 196 | { 197 | dropping[player] = true; 198 | 199 | if( role[player] != kJiggleBlobs && 200 | role[player] != kFastJiggleBlobs && 201 | role[player] != kLockdownBlobs ) 202 | blobTime[player] = GameTickCount( ); 203 | } 204 | 205 | void StopDrop( int player ) 206 | { 207 | dropping[player] = false; 208 | } 209 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | // main.h 2 | 3 | 4 | #include 5 | #include "MTypes.h" 6 | 7 | 8 | void Initialize( void ); 9 | void RetrieveResources( void ); 10 | void Error( const char* extra ); 11 | void CenterRectOnScreen( MRect *rect, double locationX, double locationY ); 12 | MBoolean AnyKeyIsPressed( void ); 13 | void RefreshAll( void ); 14 | void ReserveMonitor( void ); 15 | void ReleaseMonitor( void ); 16 | void SetFullscreen( MBoolean fullscreenMode ); 17 | void MaskRect( MRect *r ); 18 | void RefreshPlayerWindow( short player ); 19 | MBoolean IsRegistered( void ); 20 | int Warp( void ); 21 | void WaitForRelease( void ); 22 | void QuickFadeIn( MRGBColor* color ); 23 | void QuickFadeOut( MRGBColor* color ); 24 | MBoolean FileExists( const char* name ); 25 | void GoToBackground(); 26 | MBoolean ControlKeyIsPressed( void ); 27 | MBoolean OptionKeyIsPressed( void ); 28 | MBoolean DeleteKeyIsPressed( void ); 29 | MBoolean IsRegistered(); 30 | void InitRegistration(); 31 | void NoPaint(); 32 | void NeedRefresh(); 33 | const char* QuickResourceName( const char* prefix, int id, const char* extension ); 34 | void WaitForRegainFocus(); 35 | 36 | 37 | typedef struct KeyList 38 | { 39 | short left, right, drop, rotate; 40 | } KeyList; 41 | 42 | 43 | #define kGridAcross 6 44 | #define kGridDown 13 45 | 46 | enum 47 | { 48 | eNoState = 0, 49 | eStartMenu, 50 | eInGame, 51 | eInGameOver, 52 | eSharewareVictory, 53 | eRegisteredVictory, 54 | eFinished 55 | }; 56 | 57 | enum 58 | { 59 | picBoard = 1000, 60 | picBackdrop, 61 | picNext, 62 | picVictory, 63 | picSelectDifficulty, 64 | picBoardRight, 65 | picBlob = 200, 66 | picBlobMask, 67 | picCharMask, 68 | picNumber, 69 | picNumberMask, 70 | picBlast, 71 | picBlastMask, 72 | picFont = 250, 73 | picHiScoreFont, 74 | picContinueFont, 75 | picBalloonFont, 76 | picZapFont, 77 | picZapOutlineFont, 78 | picVictoryFont, 79 | picBubbleFont, 80 | picTinyFont, 81 | picDashedLineFont, 82 | picBatsuFont, 83 | picTitle = 300, 84 | picSharewareVictory, 85 | picGameStart, 86 | picGameOver, 87 | picVictory1, 88 | picVictory2, 89 | picVictory3, 90 | picVictory4, 91 | picVictory5, 92 | picVictory6, 93 | picLogo = 500, 94 | picLogoAlpha, 95 | picLogoMask 96 | }; 97 | 98 | enum 99 | { 100 | winPlayer = 128, 101 | winBackdrop, 102 | winNext, 103 | winScore, 104 | winOpponent, 105 | winTitle, 106 | winVictory, 107 | winLevel 108 | }; 109 | 110 | enum 111 | { 112 | rightRotate = 0, 113 | downRotate, 114 | leftRotate, 115 | upRotate 116 | }; 117 | 118 | enum 119 | { 120 | kEmpty = 0, 121 | kBlob, 122 | kBlob2, 123 | kBlob3, 124 | kBlob4, 125 | kBlob5, 126 | kBlob6, 127 | kBlob7, 128 | kBombTop, 129 | kBombBottom, 130 | kGray, 131 | kLight, 132 | kSun 133 | }; 134 | 135 | enum 136 | { 137 | kNoSuction = 0, 138 | kUp = 1, 139 | kRight = 2, 140 | kUpRight = 3, 141 | kDown = 4, 142 | kUpDown = 5, 143 | kRightDown = 6, 144 | kUpRightDown = 7, 145 | kLeft = 8, 146 | kLeftUp = 9, 147 | kLeftRight = 10, 148 | kLeftUpRight = 11, 149 | kLeftDown = 12, 150 | kLeftUpDown = 13, 151 | kLeftRightDown = 14, 152 | kLeftUpRightDown = 15, 153 | kDying = 16, 154 | kSquish = 17, 155 | kSquash = 18, 156 | kSquish1 = 19, 157 | kSquish2 = 20, 158 | kSquish3 = 21, 159 | kSquish4 = 22, 160 | kBlinkBlob = 23, 161 | kSobBlob = 24, 162 | kSob2Blob = 25, 163 | kFlashDarkBlob = 26, 164 | kFlashBrightBlob = 27, 165 | kJiggle1 = 28, 166 | kJiggle2 = 29, 167 | kJiggle3 = 30, 168 | kJiggle4 = 31, 169 | kJiggle5 = 32, 170 | kJiggle6 = 33, 171 | kJiggle7 = 34, 172 | kJiggle8 = 35, 173 | kInDoubt = 36, 174 | kInDeath = 37 175 | }; 176 | 177 | enum 178 | { 179 | kNoCharring = 0, 180 | kBombFuse1 = 0, 181 | kBombFuse2 = 1, 182 | kBombFuse3 = 2, 183 | kBlinkBomb1 = 3, 184 | kBlinkBomb2 = 4, 185 | kBlinkBomb3 = 5, 186 | kChar11, 187 | kChar31, 188 | kChar12, 189 | kChar32, 190 | kChar13, 191 | kChar33, 192 | kChar14, 193 | kChar24, 194 | kChar34 195 | }; 196 | 197 | enum 198 | { 199 | kDarkChar = 0xF0, 200 | kLightestChar = 0x00 201 | }; 202 | 203 | enum 204 | { 205 | kFlashAnimation = 0, 206 | kJiggleAnimation 207 | }; 208 | 209 | #define kBlobFrames (kFlashBrightBlob+1) 210 | 211 | enum 212 | { 213 | kGrayNoBlink = 0, 214 | kGrayBlink1, 215 | kGrayBlink2, 216 | kGrayBlink3, 217 | kSunGlow1, 218 | kSunGlow2, 219 | kSunGlow3, 220 | kSunGlow4, 221 | kSmallGray1, 222 | kSmallGray1b, 223 | kSmallGray2, 224 | kSmallGray2b, 225 | kSmallGray3, 226 | kSmallGray3b, 227 | kSmallGray4, 228 | kSmallGray4b, 229 | kSmallGray5, 230 | kSmallGray5b, 231 | kEasyGray = 25, 232 | kHardGray, 233 | kStageGray 234 | }; 235 | #define kGrayFrames (kGrayDying4+1) 236 | 237 | #define kFirstBlob kBlob 238 | #define kLastBlob kBlob7 239 | #define kBlobTypes (kLastBlob - kFirstBlob + 1) 240 | 241 | #define kPi 3.14159265358979323846264338327950288 242 | 243 | #define arrsize(x) ( (int)(sizeof((x)) / sizeof((x)[0])) ) 244 | 245 | extern SDL_Renderer* g_renderer; 246 | extern SDL_Window* g_window; 247 | extern SDL_Texture* g_windowTexture; 248 | extern SDL_Surface* g_frontSurface; 249 | extern SDL_Rect g_frontSurfaceClipRect; 250 | extern SDL_Rect g_widescreenCrop; 251 | 252 | extern signed char nextA[2], nextB[2], nextM[2], nextG[2], colorA[2], colorB[2], 253 | blobX[2], blobY[2], blobR[2], blobSpin[2], speed[2], role[2], halfway[2], 254 | control[2], dropping[2], magic[2], grenade[2], anim[2]; 255 | extern int chain[2]; 256 | extern MTicks blobTime[2], startTime, endTime; 257 | extern MBoolean finished, pauseKey, showStartMenu; 258 | extern MBoolean fullscreen, widescreen, crispUpscaling; 259 | extern signed char grid[2][kGridAcross][kGridDown], suction[2][kGridAcross][kGridDown], 260 | charred[2][kGridAcross][kGridDown], glow[2][kGridAcross][kGridDown]; 261 | extern MRect playerWindowZRect, playerWindowRect[2]; 262 | extern MBoolean playerWindowVisible[2]; 263 | extern KeyList hitKey[2]; 264 | extern int backgroundID; 265 | extern void (*DoFullRepaint)(); 266 | -------------------------------------------------------------------------------- /src/next.c: -------------------------------------------------------------------------------- 1 | // next.c 2 | 3 | #include "SDLU.h" 4 | 5 | #include "main.h" 6 | #include "next.h" 7 | #include "graphics.h" 8 | #include "gworld.h" 9 | #include "gameticks.h" 10 | #include "random.h" 11 | #include "blitter.h" 12 | #include "level.h" 13 | 14 | #define kJiggleFrames 8 15 | #define kPulling 10 16 | #define kPullingFrames 18 17 | 18 | SDL_Surface* nextSurface; 19 | SDL_Surface* nextDrawSurface; 20 | 21 | MRect nextWindowZRect, nextWindowRect[2]; 22 | MBoolean nextWindowVisible[2] = {true, true}; 23 | MTicks nextTime[2][2]; 24 | int nextStage[2][2], pullA[2], pullB[2]; 25 | 26 | void InitNext( void ) 27 | { 28 | const double windowLoc[] = {0.46, 0.54}; 29 | SDL_Rect sdlRect; 30 | 31 | nextWindowZRect.top = nextWindowZRect.left = 0; 32 | nextWindowZRect.bottom = 72; nextWindowZRect.right = 32; 33 | 34 | nextWindowRect[0] = nextWindowRect[1] = nextWindowZRect; 35 | CenterRectOnScreen( &nextWindowRect[0], windowLoc[0], 0.25 ); 36 | CenterRectOnScreen( &nextWindowRect[1], windowLoc[1], 0.25 ); 37 | 38 | nextSurface = LoadPICTAsSurface( picNext, 32 ); 39 | 40 | nextDrawSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &nextWindowZRect, &sdlRect ), 32 ); 41 | } 42 | 43 | void RefreshNext( int player ) 44 | { 45 | nextStage[player][0] = 0; 46 | nextStage[player][1] = 0; 47 | 48 | nextTime[player][0] = GameTickCount( ) + RandomBefore( 60 ); 49 | nextTime[player][1] = GameTickCount( ) + RandomBefore( 60 ); 50 | 51 | ShowNext( player ); 52 | } 53 | 54 | void PullNext( int player ) 55 | { 56 | pullA[player] = nextA[player]; 57 | pullB[player] = nextB[player]; 58 | nextStage[player][0] = kPulling; 59 | nextTime[player][0] = GameTickCount( ); 60 | } 61 | 62 | #define kNoDraw 999 63 | void ShowPull( int player ) 64 | { 65 | MRect srcRect; 66 | int yank[8] = { 20, 18, 15, 8, -6, -26, -46, -66 }; 67 | int slide[8] = { kNoDraw, 66, 48, 36, 29, 26, 24, 23 }; 68 | int drawA, drawB, offset, count; 69 | SDL_Rect sourceSDLRect, destSDLRect; 70 | 71 | if( !nextWindowVisible[player] ) return; 72 | 73 | SDLU_AcquireSurface( nextDrawSurface ); 74 | SDLU_BlitSurface1to1( nextSurface, nextDrawSurface ); 75 | 76 | for( count=0; count<2; count++ ) 77 | { 78 | offset = nextStage[player][0] - kPulling; 79 | 80 | switch( count ) 81 | { 82 | case 0: drawA = pullA[player]; drawB = pullB[player]; offset = yank[offset]; break; 83 | case 1: drawA = nextA[player]; drawB = nextB[player]; offset = slide[offset]; break; 84 | } 85 | 86 | if( offset != kNoDraw ) 87 | { 88 | MRect blobRect = { 0, 4, 0 + kBlobVertSize, 4 + kBlobHorizSize }; 89 | MRect shadowRect = { 4, 8, 4 + kBlobVertSize, 8 + kBlobHorizSize }; 90 | 91 | OffsetMRect( &blobRect, 0, offset ); 92 | OffsetMRect( &shadowRect, 0, offset ); 93 | 94 | SurfaceDrawShadow( &shadowRect, drawB, kNoSuction ); 95 | 96 | CalcBlobRect( kNoSuction, drawB-1, &srcRect ); 97 | SurfaceBlitBlob( &srcRect, &blobRect ); 98 | 99 | OffsetMRect( &blobRect, 0, kBlobVertSize ); 100 | OffsetMRect( &shadowRect, 0, kBlobVertSize ); 101 | 102 | SurfaceDrawShadow( &shadowRect, drawA, nextM[player]? kFlashDarkBlob: kNoSuction ); 103 | 104 | CalcBlobRect( nextM[player]? kFlashDarkBlob: kNoSuction, drawA-1, &srcRect ); 105 | SurfaceBlitBlob( &srcRect, &blobRect ); 106 | } 107 | } 108 | 109 | SDLU_ReleaseSurface( nextDrawSurface ); 110 | 111 | SDLU_BlitFrontSurface( nextDrawSurface, 112 | SDLU_MRectToSDLRect( &nextWindowZRect, &sourceSDLRect ), 113 | SDLU_MRectToSDLRect( &nextWindowRect[player], &destSDLRect ) ); 114 | } 115 | 116 | void UpdateNext( int player ) 117 | { 118 | MBoolean changed = false; 119 | int blob; 120 | 121 | if( nextStage[player][0] >= kPulling ) 122 | { 123 | if( GameTickCount() > nextTime[player][0] ) 124 | { 125 | if( ++nextStage[player][0] >= kPullingFrames ) 126 | { 127 | RefreshNext( player ); 128 | } 129 | else 130 | { 131 | ShowPull( player ); 132 | nextTime[player][0]++; 133 | } 134 | } 135 | } 136 | else 137 | { 138 | for( blob=0; blob<2; blob++ ) 139 | { 140 | if( GameTickCount() > nextTime[player][blob] ) 141 | { 142 | if( ++nextStage[player][blob] >= kJiggleFrames ) 143 | { 144 | nextStage[player][blob] = 0; 145 | nextTime[player][blob] += 40 + RandomBefore( 80 ); 146 | } 147 | else 148 | { 149 | nextTime[player][blob] += 2; 150 | } 151 | 152 | changed = true; 153 | } 154 | } 155 | 156 | if( changed ) ShowNext( player ); 157 | } 158 | } 159 | 160 | void ShowNext( int player ) 161 | { 162 | int jiggle[kJiggleFrames] = { kNoSuction, kSquish, kNoSuction, kSquash, 163 | kNoSuction, kSquish, kNoSuction, kSquash }; 164 | int nextFrame = kNoSuction; 165 | MRect blobRect = { 22, 4, 22 + kBlobVertSize, 4 + kBlobHorizSize }; 166 | MRect shadowRect = { 26, 8, 26 + kBlobVertSize, 8 + kBlobHorizSize }; 167 | MRect srcRect; 168 | SDL_Rect sourceSDLRect, destSDLRect; 169 | 170 | if( !nextWindowVisible[player] ) return; 171 | 172 | if( control[player] == kNobodyControl ) 173 | { 174 | } 175 | else 176 | { 177 | SDLU_AcquireSurface( nextDrawSurface ); 178 | SDLU_BlitSurface1to1( nextSurface, nextDrawSurface ); 179 | 180 | nextFrame = nextG[player]? kNoSuction: jiggle[nextStage[player][0]]; 181 | 182 | SurfaceDrawShadow( &shadowRect, nextB[player], nextFrame ); 183 | 184 | CalcBlobRect( nextFrame, nextB[player]-1, &srcRect ); 185 | SurfaceBlitBlob( &srcRect, &blobRect ); 186 | 187 | OffsetMRect( &blobRect, 0, kBlobVertSize ); 188 | OffsetMRect( &shadowRect, 0, kBlobVertSize ); 189 | 190 | nextFrame = nextG[player]? kNoSuction: 191 | (nextM[player]? kFlashDarkBlob: jiggle[nextStage[player][1]]); 192 | 193 | SurfaceDrawShadow( &shadowRect, nextA[player], nextFrame ); 194 | 195 | CalcBlobRect( nextFrame, nextA[player]-1, &srcRect ); 196 | SurfaceBlitBlob( &srcRect, &blobRect ); 197 | 198 | SDLU_ReleaseSurface( nextDrawSurface ); 199 | 200 | SDLU_BlitFrontSurface( nextDrawSurface, 201 | SDLU_MRectToSDLRect( &nextWindowZRect, &sourceSDLRect ), 202 | SDLU_MRectToSDLRect( &nextWindowRect[player], &destSDLRect ) ); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/gworld.c: -------------------------------------------------------------------------------- 1 | // gworld.c 2 | 3 | #include "SDLU.h" 4 | 5 | #include "main.h" 6 | #include "gworld.h" 7 | #include "blitter.h" 8 | #include "graphics.h" 9 | 10 | #define STBI_ONLY_JPEG 11 | #define STBI_ONLY_PNG 12 | #define STB_IMAGE_IMPLEMENTATION 13 | #include "support/stb_image.h" 14 | 15 | SDL_Surface* blobSurface; 16 | SDL_Surface* maskSurface; 17 | SDL_Surface* charMaskSurface; 18 | SDL_Surface* boardSurface[2]; 19 | SDL_Surface* blastSurface; 20 | SDL_Surface* blastMaskSurface; 21 | SDL_Surface* playerSurface[2]; 22 | SDL_Surface* playerSpriteSurface[2]; 23 | 24 | 25 | void GetBlobGraphics() 26 | { 27 | MRect myRect; 28 | 29 | // Get board 30 | 31 | myRect.top = myRect.left = 0; 32 | myRect.right = kBlobHorizSize * kGridAcross; 33 | myRect.bottom = kBlobVertSize * (kGridDown-1); 34 | 35 | boardSurface[0] = LoadPICTAsSurface( picBoard, 32 ); 36 | 37 | boardSurface[1] = LoadPICTAsSurface( picBoardRight, 32 ); 38 | if( boardSurface[1] == NULL ) 39 | boardSurface[1] = LoadPICTAsSurface( picBoard, 32 ); 40 | 41 | // Get blob worlds 42 | blobSurface = LoadPICTAsSurface( picBlob, 32 ); 43 | maskSurface = LoadPICTAsSurface( picBlobMask, MASK_DEPTH ); 44 | charMaskSurface = LoadPICTAsSurface( picCharMask, MASK_DEPTH ); 45 | 46 | // Get blast worlds 47 | 48 | blastSurface = LoadPICTAsSurface( picBlast, 32 ); 49 | blastMaskSurface = LoadPICTAsSurface( picBlastMask, 32 ); 50 | } 51 | 52 | 53 | void InitPlayerWorlds() 54 | { 55 | MRect myRect; 56 | SDL_Rect sdlRect; 57 | int count; 58 | 59 | myRect.top = myRect.left = 0; 60 | myRect.right = kGridAcross * kBlobHorizSize; 61 | myRect.bottom = kGridDown * kBlobVertSize; 62 | 63 | SDLU_MRectToSDLRect( &myRect, &sdlRect ); 64 | 65 | for( count=0; count<=1; count++ ) 66 | { 67 | playerSurface[count] = SDLU_InitSurface( &sdlRect, 32 ); 68 | playerSpriteSurface[count] = SDLU_InitSurface( &sdlRect, 32 ); 69 | } 70 | } 71 | 72 | 73 | void SurfaceDrawBoard( int player, const MRect *myRect ) 74 | { 75 | MRect srcRect, offsetRect; 76 | SDL_Rect srcSDLRect, offsetSDLRect; 77 | 78 | srcRect = *myRect; 79 | if( srcRect.bottom <= kBlobVertSize ) return; 80 | if( srcRect.top < kBlobVertSize ) srcRect.top = kBlobVertSize; 81 | 82 | offsetRect = srcRect; 83 | OffsetMRect( &offsetRect, 0, -kBlobVertSize ); 84 | 85 | SDLU_BlitSurface( boardSurface[player], SDLU_MRectToSDLRect( &offsetRect, &offsetSDLRect ), 86 | SDLU_GetCurrentSurface(), SDLU_MRectToSDLRect( &srcRect, &srcSDLRect ) ); 87 | } 88 | 89 | 90 | void SurfaceDrawBlob( int player, const MRect *myRect, int blob, int state, int charred ) 91 | { 92 | SurfaceDrawBoard( player, myRect ); 93 | SurfaceDrawSprite( myRect, blob, state ); 94 | 95 | if( charred & 0x0F ) 96 | { 97 | MRect blobRect, charRect, alphaRect; 98 | 99 | CalcBlobRect( (charred & 0x0F), kBombTop-1, &charRect ); 100 | CalcBlobRect( (charred & 0x0F), kBombBottom-1, &alphaRect ); 101 | CalcBlobRect( state, blob-1, &blobRect ); 102 | 103 | SurfaceBlitWeightedDualAlpha( SDLU_GetCurrentSurface(), blobSurface, charMaskSurface, blobSurface, SDLU_GetCurrentSurface(), 104 | myRect, &charRect, &blobRect, &alphaRect, myRect, 105 | (charred & 0xF0) ); 106 | } 107 | } 108 | 109 | void SurfaceDrawShadow( const MRect *myRect, int blob, int state ) 110 | { 111 | int x; 112 | const MPoint offset[4] = { {-2, 0}, {0, -2}, {2, 0}, {0, 2} }; 113 | 114 | if( blob > kEmpty ) 115 | { 116 | MRect blobRect, destRect; 117 | 118 | for( x=0; x<4; x++ ) 119 | { 120 | destRect = *myRect; 121 | OffsetMRect( &destRect, offset[x].h, offset[x].v ); 122 | 123 | CalcBlobRect( state, blob-1, &blobRect ); 124 | SurfaceBlitColor( maskSurface, SDLU_GetCurrentSurface(), 125 | &blobRect, &destRect, 126 | 0, 0, 0, _5TO8(3) ); 127 | } 128 | } 129 | } 130 | 131 | void SurfaceDrawColor( const MRect *myRect, int blob, int state, int r, int g, int b, int w ) 132 | { 133 | MRect blobRect; 134 | if( blob > kEmpty ) 135 | { 136 | CalcBlobRect( state, blob-1, &blobRect ); 137 | SurfaceBlitColor( charMaskSurface, SDLU_GetCurrentSurface(), 138 | &blobRect, myRect, 139 | r, g, b, w ); 140 | } 141 | } 142 | 143 | void SurfaceDrawAlpha( const MRect *myRect, int blob, int mask, int state ) 144 | { 145 | if( blob > kEmpty ) 146 | { 147 | MRect blobRect, alphaRect; 148 | 149 | CalcBlobRect( state, blob-1, &blobRect ); 150 | CalcBlobRect( state, mask-1, &alphaRect ); 151 | 152 | SurfaceBlitAlpha( SDLU_GetCurrentSurface(), blobSurface, blobSurface, SDLU_GetCurrentSurface(), 153 | myRect, &blobRect, &alphaRect, myRect ); 154 | } 155 | } 156 | 157 | void SurfaceDrawSprite( const MRect *myRect, int blob, int state ) 158 | { 159 | MRect blobRect; 160 | if( blob > kEmpty ) 161 | { 162 | CalcBlobRect( state, blob-1, &blobRect ); 163 | SurfaceBlitBlob( &blobRect, myRect ); 164 | } 165 | } 166 | 167 | 168 | MBoolean PICTExists( int pictID ) 169 | { 170 | if( FileExists( QuickResourceName( "PICT", pictID, ".jpg" ) ) ) 171 | return true; 172 | 173 | if( FileExists( QuickResourceName( "PICT", pictID, ".png" ) ) ) 174 | return true; 175 | 176 | return false; 177 | } 178 | 179 | 180 | SDL_Surface* LoadPICTAsSurface( int pictID, int depth ) 181 | { 182 | const char* filename; 183 | SDL_Surface* surface; 184 | SDL_Rect rect = {0}; 185 | uint8_t* pixels = NULL; 186 | 187 | filename = QuickResourceName( "PICT", pictID, ".jpg" ); 188 | if( !FileExists( filename ) ) 189 | { 190 | filename = QuickResourceName( "PICT", pictID, ".png" ); 191 | } 192 | if( !FileExists( filename ) ) 193 | { 194 | return NULL; 195 | } 196 | 197 | pixels = stbi_load(filename, &rect.w, &rect.h, NULL, 3); 198 | 199 | surface = SDLU_InitSurface(&rect, 32); 200 | SDL_LockSurface(surface); 201 | 202 | uint8_t* srcPixels = pixels; 203 | uint8_t* destPixels = (uint8_t*) surface->pixels; 204 | for (int i = 0; i < rect.w*rect.h; i++) 205 | { 206 | destPixels[0] = srcPixels[2]; 207 | destPixels[1] = srcPixels[1]; 208 | destPixels[2] = srcPixels[0]; 209 | destPixels[3] = 0xFF; 210 | destPixels += 4; 211 | srcPixels += 3; 212 | } 213 | 214 | SDL_UnlockSurface(surface); 215 | 216 | free(pixels); 217 | pixels = NULL; 218 | 219 | if( depth != 0 && depth != 32 ) 220 | { 221 | SDLU_ChangeSurfaceDepth( &surface, depth ); 222 | } 223 | 224 | return surface; 225 | } 226 | 227 | void DrawPICTInSurface( SDL_Surface* surface, int pictID ) 228 | { 229 | SDL_Surface* image; 230 | 231 | image = LoadPICTAsSurface( pictID, 0 ); 232 | if( image != NULL ) 233 | { 234 | SDLU_BlitSurface1to1( image, surface ); 235 | SDL_DestroySurface( image ); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/graphics.c: -------------------------------------------------------------------------------- 1 | // graphics.c 2 | 3 | #include "version.h" 4 | #include "SDLU.h" 5 | #include "main.h" 6 | #include "players.h" 7 | #include "graphics.h" 8 | #include "gworld.h" 9 | #include "moving.h" 10 | #include "tweak.h" 11 | #include "gameticks.h" 12 | #include "blitter.h" 13 | #include "victory.h" 14 | #include "grays.h" 15 | #include "level.h" 16 | #include "keyselect.h" 17 | #include "music.h" 18 | 19 | 20 | SDL_Surface* backdropSurface = NULL; 21 | 22 | 23 | void DrawSpriteBlobs( int player, int type ) 24 | { 25 | MRect firstRect, secondRect, thirdRect; 26 | 27 | #define repeat 0xFF 28 | #define forever 0xFE 29 | 30 | static const unsigned char blobAnimation[6][2][25] = 31 | { 32 | { { kNoSuction, kNoSuction, kNoSuction, kNoSuction, 33 | kNoSuction, kNoSuction, kNoSuction, kNoSuction, 34 | kNoSuction, kNoSuction, kNoSuction, kNoSuction, 35 | kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob, 36 | kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob, 37 | kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob, repeat }, 38 | { kNoSuction, kNoSuction, kNoSuction, kNoSuction, 39 | kNoSuction, kNoSuction, kNoSuction, kNoSuction, 40 | kNoSuction, kNoSuction, kNoSuction, kNoSuction, 41 | kNoSuction, kNoSuction, kNoSuction, kNoSuction, 42 | kNoSuction, kNoSuction, kNoSuction, kNoSuction, 43 | kNoSuction, kNoSuction, kNoSuction, kNoSuction, repeat } }, 44 | { { kNoSuction, kSquish, kNoSuction, kSquash, 45 | kNoSuction, kSquish, kNoSuction, kSquash, 46 | kNoSuction, forever }, 47 | { kNoSuction, kSquish, kNoSuction, kSquash, 48 | kNoSuction, kSquish, kNoSuction, kSquash, 49 | kNoSuction, forever } }, 50 | { { kSobBlob, kSobBlob, kSobBlob, kSobBlob, 51 | kSob2Blob, kSob2Blob, kSob2Blob, kSob2Blob, 52 | repeat }, 53 | { kSobBlob, kSobBlob, kSobBlob, kSobBlob, 54 | kSob2Blob, kSob2Blob, kSob2Blob, kSob2Blob, 55 | repeat } }, 56 | { { kBombFuse1, kBombFuse2, kBombFuse3, repeat }, 57 | { kBombFuse1, kBombFuse2, kBombFuse3, repeat } }, 58 | { { kBlinkBomb1, kBombFuse2, kBlinkBomb3, kBombFuse1, 59 | kBlinkBomb2, kBombFuse3, repeat }, 60 | { kBlinkBomb1, kBombFuse2, kBlinkBomb3, kBombFuse1, 61 | kBlinkBomb2, kBombFuse3, repeat } } 62 | }; 63 | 64 | if( grenade[player] ) type += 3; 65 | 66 | SDLU_AcquireSurface( playerSpriteSurface[player] ); 67 | 68 | if( blobAnimation[type][0][anim[player]] == forever ) anim[player]--; 69 | if( blobAnimation[type][0][anim[player]] == repeat ) anim[player] = 0; 70 | 71 | CalcBlobRect( blobX[player], blobY[player], &firstRect ); 72 | if( halfway[player] ) OffsetMRect( &firstRect, 0, kBlobVertSize / 2 ); 73 | 74 | TweakFirstBlob ( player, &firstRect ); 75 | secondRect = firstRect; 76 | TweakSecondBlob( player, &secondRect ); 77 | 78 | thirdRect = firstRect; 79 | thirdRect.top -= kBlobShadowError; 80 | thirdRect.left -= kBlobShadowError; 81 | thirdRect.right += kBlobShadowDepth + kBlobShadowError; 82 | thirdRect.bottom += kBlobShadowDepth + kBlobShadowError; 83 | CleanSpriteArea( player, &thirdRect ); 84 | 85 | thirdRect = secondRect; 86 | thirdRect.top -= kBlobShadowError; 87 | thirdRect.left -= kBlobShadowError; 88 | thirdRect.right += kBlobShadowDepth + kBlobShadowError; 89 | thirdRect.bottom += kBlobShadowDepth + kBlobShadowError; 90 | CleanSpriteArea( player, &thirdRect ); 91 | 92 | thirdRect = firstRect; 93 | OffsetMRect( &thirdRect, shadowDepth[player], shadowDepth[player] ); 94 | SurfaceDrawShadow( &thirdRect, colorA[player], blobAnimation[type][0][anim[player]] ); 95 | 96 | thirdRect = secondRect; 97 | OffsetMRect( &thirdRect, shadowDepth[player], shadowDepth[player] ); 98 | SurfaceDrawShadow( &thirdRect, colorB[player], blobAnimation[type][1][anim[player]] ); 99 | 100 | SurfaceDrawSprite( &firstRect, colorA[player], blobAnimation[type][0][anim[player]] ); 101 | 102 | SurfaceDrawSprite( &secondRect, colorB[player], blobAnimation[type][1][anim[player]] ); 103 | 104 | SDLU_ReleaseSurface( playerSpriteSurface[player] ); 105 | 106 | #undef repeat 107 | #undef forever 108 | } 109 | 110 | void CleanSpriteArea( int player, MRect *myRect ) 111 | { 112 | SDL_Rect sdlRect; 113 | 114 | SDLU_MRectToSDLRect( myRect, &sdlRect ); 115 | 116 | SDLU_BlitSurface( playerSurface[player], &sdlRect, 117 | playerSpriteSurface[player], &sdlRect ); 118 | 119 | SetUpdateRect( player, myRect ); 120 | } 121 | 122 | void EraseSpriteBlobs( int player ) 123 | { 124 | MRect myRect, secondRect; 125 | 126 | CalcBlobRect( blobX[player], blobY[player], &myRect ); 127 | if( halfway[player] ) OffsetMRect( &myRect, 0, kBlobVertSize / 2 ); 128 | 129 | TweakFirstBlob( player, &myRect ); 130 | secondRect = myRect; 131 | secondRect.top -= kBlobShadowError; 132 | secondRect.left -= kBlobShadowError; 133 | secondRect.right += kBlobShadowDepth + kBlobShadowError; 134 | secondRect.bottom += kBlobShadowDepth + kBlobShadowError; 135 | CleanSpriteArea( player, &secondRect ); 136 | 137 | TweakSecondBlob( player, &myRect ); 138 | myRect.top -= kBlobShadowError; 139 | myRect.left -= kBlobShadowError; 140 | myRect.right += kBlobShadowDepth + kBlobShadowError; 141 | myRect.bottom += kBlobShadowDepth + kBlobShadowError; 142 | CleanSpriteArea( player, &myRect ); 143 | } 144 | 145 | void CalcBlobRect( int x, int y, MRect *myRect ) 146 | { 147 | myRect->top = y * kBlobVertSize; 148 | myRect->left = x * kBlobHorizSize; 149 | myRect->bottom = myRect->top + kBlobVertSize; 150 | myRect->right = myRect->left + kBlobHorizSize; 151 | } 152 | 153 | void InitBackdrop( void ) 154 | { 155 | backdropSurface = LoadPICTAsSurface( picBackdrop, 32 ); 156 | } 157 | 158 | void DrawBackdrop( void ) 159 | { 160 | SDL_Rect backdropRect = { 0, 0, 640, 480 }; 161 | 162 | SDLU_BlitFrontSurface( backdropSurface, &backdropRect, &backdropRect ); 163 | } 164 | 165 | void ShowTitle( void ) 166 | { 167 | SDL_FillSurfaceRect( g_frontSurface, &g_frontSurfaceClipRect, SDL_MapSurfaceRGB( g_frontSurface, 0, 0, 0 ) ); 168 | SDLU_Present(); 169 | 170 | RetrieveResources( ); 171 | 172 | SDLU_AcquireSurface( g_frontSurface ); 173 | DrawPICTInSurface( g_frontSurface, picTitle ); 174 | 175 | SkittlesFontPtr font = GetFont(picTinyFont); 176 | MPoint dPoint; 177 | dPoint.v = (widescreen ? 420 : 480) - 16; 178 | dPoint.h = 4; 179 | for (const char* scan = "Source port v" PROJECT_VERSION; *scan; scan++) 180 | { 181 | SurfaceBlitCharacter(font, *scan, &dPoint, 50, 50, 50, 1); 182 | } 183 | SDLU_ReleaseSurface( g_frontSurface ); 184 | 185 | ChooseMusic( 13 ); 186 | 187 | QuickFadeIn( NULL ); 188 | 189 | MTicks time = MTickCount() + 150; 190 | while( time > MTickCount() && !SDLU_Button() ) 191 | { 192 | SDLU_Present(); 193 | SDLU_Yield(); 194 | } 195 | 196 | WaitForRelease(); 197 | 198 | QuickFadeOut( NULL ); 199 | 200 | SDL_FillSurfaceRect( g_frontSurface, &g_frontSurfaceClipRect, SDL_MapSurfaceRGB( g_frontSurface, 0, 0, 0 ) ); 201 | SDLU_Present(); 202 | } 203 | 204 | -------------------------------------------------------------------------------- /src/opponent.c: -------------------------------------------------------------------------------- 1 | // opponent.c 2 | 3 | #include "SDLU.h" 4 | 5 | #include 6 | 7 | #include "main.h" 8 | #include "level.h" 9 | #include "opponent.h" 10 | #include "gworld.h" 11 | #include "graphics.h" 12 | #include "random.h" 13 | #include "control.h" 14 | #include "players.h" 15 | #include "gameticks.h" 16 | #include "blitter.h" 17 | 18 | SDL_Surface* opponentSurface; 19 | SDL_Surface* opponentMaskSurface; 20 | SDL_Surface* opponentDrawSurface; 21 | 22 | MRect opponentWindowZRect, opponentWindowRect; 23 | int opponentMood, opponentFrame; 24 | MTicks opponentTime, glowTime[kGlows], panicTime; 25 | int glowFrame[kGlows], panicFrame; 26 | int heavyGlowArray[kGlowArraySize], glowArray[kGlowArraySize], lightGlowArray[kGlowArraySize]; 27 | 28 | void InitOpponent( void ) 29 | { 30 | MRect littleRect = {0, 0, 64, 64}, bigRect = {0, 0, 64, 64*(kOppFrames*3) }; 31 | SDL_Rect sdlRect; 32 | double index, value; 33 | 34 | opponentDrawSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &littleRect, &sdlRect ), 32 ); 35 | opponentSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &bigRect, &sdlRect ), 32 ); 36 | 37 | bigRect.bottom *= kGlows + 1; 38 | opponentMaskSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &bigRect, &sdlRect ), MASK_DEPTH ); 39 | 40 | opponentWindowZRect.top = opponentWindowZRect.left = 0; 41 | opponentWindowZRect.bottom = opponentWindowZRect.right = 64; 42 | opponentWindowRect = opponentWindowZRect; 43 | CenterRectOnScreen( &opponentWindowRect, 0.5, 0.5 ); 44 | 45 | opponentMood = 0; 46 | 47 | for( index=0; index opponentTime ) 117 | { 118 | switch( opponentMood ) 119 | { 120 | case 0: // Idle 121 | opponentTime += 60 + RandomBefore(180); 122 | opponentMood = RandomBefore(2) + 1; 123 | opponentFrame = (emotiMap[emotions[1]] * kOppFrames); 124 | break; 125 | 126 | case 1: // Shifty Eyes 127 | opponentTime += 40 + RandomBefore(60); 128 | opponentMood = 0; 129 | opponentFrame = (emotiMap[emotions[1]] * kOppFrames) + RandomBefore(2) + 1; 130 | break; 131 | 132 | case 2: // Blinks 133 | opponentTime += 3; 134 | opponentMood = 3; 135 | opponentFrame = (emotiMap[emotions[1]] * kOppFrames) + 3; 136 | break; 137 | 138 | case 3: // Blinks (more) 139 | opponentTime += 3; 140 | opponentMood = 4; 141 | opponentFrame = (emotiMap[emotions[1]] * kOppFrames) + 4; 142 | break; 143 | 144 | case 4: // Blinks (more) 145 | opponentTime += 3; 146 | opponentMood = 0; 147 | opponentFrame = (emotiMap[emotions[1]] * kOppFrames) + 3; 148 | break; 149 | 150 | case 5: // Chatter (only good for tutorial) 151 | opponentTime += 8; 152 | opponentMood = 6; 153 | opponentFrame = 5; 154 | break; 155 | 156 | case 6: // Chatter 2 (only good for tutorial) 157 | opponentTime += 8; 158 | opponentMood = 5; 159 | opponentFrame = 6; 160 | break; 161 | 162 | case 7: // Pissed (when hit with punishments) 163 | opponentTime += 60; 164 | opponentFrame = 7; 165 | opponentMood = 0; 166 | break; 167 | } 168 | 169 | draw = true; 170 | } 171 | 172 | if( GameTickCount( ) > panicTime ) 173 | { 174 | panicTime += 2; 175 | 176 | if( emotions[1] == kEmotionPanic ) 177 | { 178 | if( ++panicFrame >= kGlowArraySize ) panicFrame = 0; 179 | draw = true; 180 | } 181 | else 182 | { 183 | panicFrame = 0; 184 | } 185 | } 186 | 187 | for( count=0; count glowTime[count] ) 190 | { 191 | glowTime[count] += character[1].glow[count].time; 192 | 193 | if( character[1].glow[count].colorR || character[1].glow[count].colorG || character[1].glow[count].colorB ) 194 | { 195 | if( ++glowFrame[count] >= kGlowArraySize ) glowFrame[count] = 0; 196 | draw = true; 197 | } 198 | else 199 | { 200 | glowFrame[count] = 0; 201 | } 202 | } 203 | } 204 | 205 | if( draw ) 206 | { 207 | OffsetMRect( &myRect, 64*opponentFrame, 0 ); 208 | 209 | SDLU_AcquireSurface( opponentDrawSurface ); 210 | 211 | SDLU_BlitSurface( opponentSurface, SDLU_MRectToSDLRect( &myRect, &srcSDLRect ), 212 | opponentDrawSurface, SDLU_MRectToSDLRect( &dstRect, &dstSDLRect ) ); 213 | 214 | maskRect = myRect; 215 | for( count=0; count 26 | 27 | MTicks winTime, loseTime; 28 | int winStage, loseStage; 29 | float drop[kGridAcross], last[kGridAcross]; 30 | SkittlesFontPtr victoryFont; 31 | 32 | void InitVictory( void ) 33 | { 34 | victoryFont = GetFont( picVictoryFont ); 35 | } 36 | 37 | void EndRound( int player ) 38 | { 39 | int count; 40 | 41 | loseTime = GameTickCount( ); 42 | loseStage = 0; 43 | 44 | role[player] = kLosing; 45 | emotions[player] = kEmotionPanic; 46 | 47 | for( count=0; count loseTime ) 96 | { 97 | skip = 2; 98 | } 99 | 100 | loseTime += skip; 101 | loseStage += skip; 102 | 103 | if( loseStage < 120 ) 104 | { 105 | DropLosers( player, skip ); 106 | } 107 | else if( loseStage == 120 || loseStage == 121 ) 108 | { 109 | loseStage = 122; 110 | 111 | boardRect.top = boardRect.left = 0; 112 | boardRect.bottom = playerSurface[player]->h; 113 | boardRect.bottom = playerSurface[player]->w; 114 | 115 | SDLU_AcquireSurface( playerSurface[player] ); 116 | SurfaceDrawBoard( player, &boardRect ); 117 | SDLU_ReleaseSurface( playerSurface[player] ); 118 | 119 | CleanSpriteArea( player, &boardRect ); 120 | } 121 | else if( loseStage == 240 || loseStage == 241 ) 122 | { 123 | loseStage = 242; 124 | if( players == 1 && control[player] == kPlayerControl ) 125 | { 126 | if( --credits > 0 ) 127 | { 128 | HandleDialog( kContinueDialog ); 129 | } 130 | else 131 | { 132 | AddHiscore( score[player] ); 133 | ShowGameOverScreen( ); 134 | 135 | showStartMenu = true; 136 | } 137 | } 138 | else if( players == 2 ) 139 | { 140 | AddHiscore( score[player] ); 141 | } 142 | } 143 | } 144 | 145 | void DropLosers( int player, int skip ) 146 | { 147 | int x, y, suck; 148 | int beginDrop[] = { 28, 14, 0, 7, 21, 35 }; 149 | float thisDrop; 150 | MRect myRect; 151 | 152 | SDLU_AcquireSurface( playerSpriteSurface[player] ); 153 | 154 | for( x=0; x= beginDrop[x] ) 157 | { 158 | thisDrop = last[x] + ( (float)(skip) * ( 0.7f + last[x] / 12.0f ) ); 159 | 160 | CalcBlobRect( x, 0, &myRect ); 161 | myRect.top = (int) last[x]; 162 | myRect.bottom = kGridDown * kBlobVertSize; 163 | SurfaceDrawBoard( player, &myRect ); 164 | SetUpdateRect( player, &myRect ); 165 | 166 | if( thisDrop < (kGridDown*kBlobVertSize) ) 167 | { 168 | myRect.top = (int) thisDrop; 169 | myRect.bottom = myRect.top + kBlobVertSize; 170 | 171 | y=0; 172 | while( myRect.top < (kGridDown*kBlobVertSize) ) 173 | { 174 | if( grid[player][x][y] >= kFirstBlob && 175 | grid[player][x][y] <= kLastBlob ) 176 | { 177 | suck = suction[player][x][y] & (kUpDown); 178 | if( suck == kNoSuction ) suck = kDying; 179 | SurfaceDrawBlob( player, &myRect, grid[player][x][y], suck, charred[player][x][y] ); 180 | } 181 | else if( grid[player][x][y] == kGray ) 182 | { 183 | SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink ); 184 | } 185 | 186 | OffsetMRect( &myRect, 0, kBlobVertSize ); 187 | y++; 188 | } 189 | 190 | last[x] = thisDrop; 191 | } 192 | } 193 | } 194 | 195 | SDLU_ReleaseSurface( playerSpriteSurface[player] ); 196 | } 197 | 198 | void Win( int player ) 199 | { 200 | int x, y; 201 | 202 | if( GameTickCount() >= winTime ) 203 | { 204 | if( winStage < (kGridDown * kGridAcross) ) 205 | { 206 | y = (kGridDown-1) - (winStage / kGridAcross); 207 | x = (winStage % kGridAcross); 208 | if( y & 2 ) x = (kGridAcross-1) - x; 209 | 210 | if( grid[player][x][y] == kGray ) 211 | { 212 | suction[player][x][y] = kGrayBlink1; 213 | death[player][x][y] = 0; 214 | score[player] += 20; 215 | } 216 | else if( grid[player][x][y] >= kFirstBlob && grid[player][x][y] <= kLastBlob ) 217 | { 218 | suction[player][x][y] = kInDeath; 219 | death[player][x][y] = 0; 220 | score[player] += 100; 221 | } 222 | } 223 | else if( winStage == 140 && control[player] == kPlayerControl ) 224 | { 225 | DrawTimerCount( player ); 226 | } 227 | else if( winStage == 200 && control[player] == kPlayerControl ) 228 | { 229 | DrawTimerBonus( player ); 230 | } 231 | 232 | winTime++; 233 | winStage++; 234 | } 235 | 236 | if( winStage < 140 ) 237 | { 238 | KillBlobs( player ); 239 | } 240 | 241 | if( winStage >= 280 ) 242 | { 243 | if( control[player] == kPlayerControl ) 244 | { 245 | IncrementLevel( ); 246 | BeginRound( true ); 247 | } 248 | } 249 | } 250 | 251 | void DrawTimerCount( int player ) 252 | { 253 | MRect playerRect; 254 | 255 | SDLU_AcquireSurface( playerSurface[player] ); 256 | 257 | { 258 | MPoint dPoint = { (kBlobVertSize * 3), 15 }; 259 | 260 | SurfaceBlitCharacter( victoryFont, 'A', &dPoint, 255, 255, 0, 1 ); 261 | } 262 | 263 | { 264 | MPoint dPoint = { (kBlobVertSize * 4), kBlobHorizSize }; 265 | char seconds[20]; 266 | char *scan = seconds; 267 | 268 | SDL_snprintf( seconds, sizeof(seconds), "%d", (endTime - startTime) / 60 ); 269 | while( *scan ) 270 | { 271 | SurfaceBlitCharacter( zapFont, *scan++, &dPoint, 255, 255, 255, 1 ); 272 | dPoint.h--; 273 | } 274 | 275 | dPoint.h += 6; 276 | SurfaceBlitCharacter( zapFont, 'S', &dPoint, 255, 255, 255, 1 ); 277 | } 278 | 279 | playerRect.top = playerRect.left = 0; 280 | playerRect.bottom = playerSurface[player]->h; 281 | playerRect.right = playerSurface[player]->w; 282 | 283 | CleanSpriteArea( player, &playerRect ); 284 | PlayStereo( player, kSplop ); 285 | 286 | SDLU_ReleaseSurface( playerSurface[player] ); 287 | } 288 | 289 | void DrawTimerBonus( int player ) 290 | { 291 | MRect playerRect; 292 | int timer, bonus; 293 | 294 | SDLU_AcquireSurface( playerSurface[player] ); 295 | 296 | { 297 | MPoint dPoint = { (kBlobVertSize * 6), 15 }; 298 | 299 | SurfaceBlitCharacter( victoryFont, 'B', &dPoint, 255, 255, 0, 1 ); 300 | } 301 | 302 | timer = (endTime - startTime) / 60; 303 | if( timer <= 10 ) bonus = 30000; 304 | else if( timer <= 20 ) bonus = 10000; 305 | else if( timer <= 30 ) bonus = 5000; 306 | else if( timer <= 45 ) bonus = 4000; 307 | else if( timer <= 60 ) bonus = 3000; 308 | else if( timer <= 80 ) bonus = 2000; 309 | else if( timer <=100 ) bonus = 1000; 310 | else if( timer <=120 ) bonus = 500; 311 | else bonus = 0; 312 | 313 | if( players == 1 ) bonus *= level; 314 | 315 | score[player] += bonus; 316 | 317 | { 318 | MPoint dPoint = { (kBlobVertSize * 7), kBlobHorizSize }; 319 | char points[20]; 320 | char *scan = points; 321 | 322 | SDL_snprintf( points, sizeof(points), "%d", bonus ); 323 | while( *scan ) 324 | { 325 | SurfaceBlitCharacter( zapFont, *scan++, &dPoint, 255, 255, 255, 1 ); 326 | dPoint.h--; 327 | } 328 | 329 | dPoint.h += 6; 330 | SurfaceBlitCharacter( zapFont, 'P', &dPoint, 255, 255, 255, 1 ); 331 | } 332 | 333 | playerRect.top = playerRect.left = 0; 334 | playerRect.bottom = playerSurface[player]->h; 335 | playerRect.right = playerSurface[player]->w; 336 | 337 | CleanSpriteArea( player, &playerRect ); 338 | PlayStereo( player, kSplop ); 339 | 340 | SDLU_ReleaseSurface( playerSurface[player] ); 341 | } 342 | -------------------------------------------------------------------------------- /src/grays.c: -------------------------------------------------------------------------------- 1 | // grays.c 2 | 3 | #include "SDLU.h" 4 | 5 | #include "main.h" 6 | #include "grays.h" 7 | #include "players.h" 8 | #include "graphics.h" 9 | #include "gworld.h" 10 | #include "control.h" 11 | #include "soundfx.h" 12 | #include "score.h" 13 | #include "random.h" 14 | #include "graymonitor.h" 15 | #include "gameticks.h" 16 | #include "blitter.h" 17 | #include "zap.h" 18 | #include "level.h" 19 | #include "opponent.h" 20 | 21 | int grays[2][kGridAcross], grayAir[2][kGridAcross]; 22 | int unallocatedGrays[2], lockGrays[2], rowBounce[2][kGridAcross], splat[2][kGridAcross]; 23 | MTicks blinkTime[2]; 24 | int blinkStage[2]; 25 | const int blinkList[] = { kGrayNoBlink, kGrayBlink1, kGrayBlink2, 26 | kGrayBlink3, kGrayBlink2, kGrayBlink1 }; 27 | 28 | void InitGrays( void ) 29 | { 30 | int player, gridX; 31 | 32 | blinkTime[0] = GameTickCount( ); 33 | blinkTime[1] = GameTickCount( ) + 60; 34 | 35 | for( player=0; player<=1; player++ ) 36 | { 37 | blinkStage[player] = 0; 38 | unallocatedGrays[player] = 0; 39 | 40 | for( gridX=0; gridX GameTickCount( ) ) 54 | return; 55 | 56 | blinkTime[player] += 2; 57 | blinkStage[player]++; 58 | 59 | if( blinkStage[player] > 5 ) 60 | { 61 | blinkStage[player] = 0; 62 | blinkTime[player] = GameTickCount( ) + kTimeBetweenBlinks; 63 | } 64 | 65 | currentBlinkGraphic = blinkList[ blinkStage[player] ]; 66 | 67 | SDLU_AcquireSurface( playerSurface[player] ); 68 | 69 | for( x=0; x=0; y-- ) 74 | { 75 | if( (grid[player][x][y] == kGray) && 76 | (suction[player][x][y] == kGrayNoBlink) ) 77 | { 78 | CalcBlobRect( x, y, &myRect ); 79 | SurfaceDrawBoard( player, &myRect ); 80 | SurfaceDrawAlpha( &myRect, kGray, kLight, currentBlinkGraphic ); 81 | CleanSpriteArea( player, &myRect ); 82 | } 83 | } 84 | } 85 | } 86 | 87 | SDLU_ReleaseSurface( playerSurface[player] ); 88 | } 89 | 90 | void CalculateGrays( int player, int blobsToDrop ) 91 | { 92 | if( blobsToDrop < unallocatedGrays[1-player] ) 93 | { 94 | unallocatedGrays[1-player] -= blobsToDrop; 95 | lockGrays[1-player] -= blobsToDrop; 96 | if( lockGrays[1-player] < 0 ) lockGrays[1-player] = 0; 97 | 98 | blobsToDrop = 0; 99 | ShowGrayMonitor( 1-player ); 100 | } 101 | else 102 | { 103 | blobsToDrop -= unallocatedGrays[1-player]; 104 | unallocatedGrays[1-player] = 0; 105 | lockGrays[1-player] = 0; 106 | ShowGrayMonitor( 1-player ); 107 | 108 | unallocatedGrays[player] += blobsToDrop; 109 | ShowGrayMonitor( player ); 110 | } 111 | } 112 | 113 | void LockGrays( int player ) 114 | { 115 | lockGrays[player] = unallocatedGrays[player]; 116 | } 117 | 118 | void SetupGrays( int player ) 119 | { 120 | int grayX, change; 121 | MBoolean onlyOnce[kGridAcross]; 122 | int rowFree[kGridAcross]; 123 | 124 | if( role[player] == kDropGrays ) return; // next time around 125 | 126 | for( grayX=0; grayX < kGridAcross; grayX++ ) 127 | { 128 | grayAir[player][grayX] = -RandomBefore(kBlobVertSize*3/2); 129 | rowFree[grayX] = -1; 130 | 131 | while( grid[player][grayX][rowFree[grayX]+1] == kEmpty && 132 | rowFree[grayX] < (kGridDown-1) ) 133 | rowFree[grayX]++; 134 | } 135 | 136 | while( lockGrays[player] >= kGridAcross ) 137 | { 138 | change = 0; 139 | 140 | for( grayX=0; grayX < kGridAcross; grayX++ ) 141 | { 142 | if( rowFree[grayX] >= 0 ) 143 | { 144 | grays[player][grayX]++; 145 | //grayAir[player][grayX] -= kBlobVertSize; 146 | rowFree[grayX]--; 147 | change++; 148 | } 149 | } 150 | 151 | lockGrays[player] -= change; 152 | unallocatedGrays[player] -= change; 153 | 154 | if( change == 0 ) break; 155 | } 156 | 157 | if( lockGrays[player] > 0 ) 158 | { 159 | for( grayX = 0; grayX < kGridAcross; grayX++ ) 160 | { 161 | onlyOnce[grayX] = rowFree[grayX] > 0; 162 | } 163 | 164 | while( (onlyOnce[0] || onlyOnce[1] || onlyOnce[2] || 165 | onlyOnce[3] || onlyOnce[4] || onlyOnce[5]) && 166 | (lockGrays[player] > 0) ) 167 | { 168 | grayX = RandomBefore(kGridAcross); 169 | 170 | if( onlyOnce[grayX] ) 171 | { 172 | grays[player][grayX]++; 173 | //grayAir[player][grayX] -= kBlobVertSize; 174 | lockGrays[player]--; 175 | unallocatedGrays[player]--; 176 | onlyOnce[grayX] = false; 177 | } 178 | } 179 | } 180 | } 181 | 182 | void DropGrays( int player ) 183 | { 184 | MRect myRect; 185 | int count, grayX; 186 | 187 | if( blobTime[player] > GameTickCount( ) ) 188 | return; 189 | 190 | blobTime[player]++; 191 | 192 | for( grayX = 0; grayX < kGridAcross; grayX++ ) 193 | { 194 | if( grays[player][grayX] > 0 ) 195 | { 196 | myRect.bottom = grayAir[player][grayX]; 197 | myRect.left = kBlobHorizSize * grayX; 198 | myRect.top = myRect.bottom - (kBlobVertSize * grays[player][grayX]); 199 | myRect.right = myRect.left + kBlobHorizSize; 200 | CleanSpriteArea( player, &myRect ); 201 | 202 | grayAir[player][grayX] += 4; 203 | if( grayAir[player][grayX] > 0 ) grayAir[player][grayX] += grayAir[player][grayX] / 12; 204 | 205 | if( ( grayAir[player][grayX] / kBlobVertSize ) >= GetRowHeight( player, grayX ) ) 206 | { 207 | PlaceGrayRow( player, grayX ); 208 | ShowGrayMonitor( player ); 209 | } 210 | else 211 | { 212 | myRect.top = grayAir[player][grayX]; 213 | myRect.bottom = myRect.top + kBlobVertSize; 214 | 215 | SDLU_AcquireSurface( playerSpriteSurface[player] ); 216 | 217 | for( count=0; count= 0 || 244 | grays[player][grayX] > 0 ) 245 | { 246 | return true; 247 | } 248 | } 249 | 250 | return false; 251 | } 252 | 253 | void PlaceGrayRow( int player, int grayX ) 254 | { 255 | int grayY; 256 | MRect myRect; 257 | 258 | if( player == 1 ) OpponentPissed( ); 259 | 260 | rowBounce[player][grayX] = 0; 261 | splat[player][grayX] = GetRowHeight( player, grayX )+1; 262 | 263 | SDLU_AcquireSurface( playerSurface[player] ); 264 | 265 | while( grays[player][grayX] > 0 ) 266 | { 267 | grayY = GetRowHeight( player, grayX ); 268 | 269 | grid[player][grayX][grayY] = kGray; 270 | suction[player][grayX][grayY] = kGrayNoBlink; 271 | 272 | CalcBlobRect( grayX, grayY, &myRect ); 273 | SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink ); 274 | CleanSpriteArea( player, &myRect ); 275 | 276 | grays[player][grayX]--; 277 | } 278 | 279 | SDLU_ReleaseSurface( playerSurface[player] ); 280 | 281 | PlayStereoFrequency( player, kBounce, player ); 282 | } 283 | 284 | void Bounce( int player ) 285 | { 286 | int x, y, bounce, suck, blob, rows, currentBlinkGraphic; 287 | MRect blobRect; 288 | double blobTop, compress; 289 | const double compressList[kZapFrames+1] = { 290 | -kBlobVertSize + 0.83, 291 | -kBlobVertSize + 1.58, 292 | -kBlobVertSize + 2.22, 293 | -kBlobVertSize + 2.76, 294 | -kBlobVertSize + 3.20, 295 | -kBlobVertSize + 3.55, 296 | -kBlobVertSize + 3.80, 297 | -kBlobVertSize + 3.95, 298 | -kBlobVertSize + 4.00, 299 | -kBlobVertSize + 3.95, 300 | -kBlobVertSize + 3.80, 301 | -kBlobVertSize + 3.55, 302 | -kBlobVertSize + 3.20, 303 | -kBlobVertSize + 2.76, 304 | -kBlobVertSize + 2.22, 305 | -kBlobVertSize + 1.58, 306 | -kBlobVertSize + 0.83, 307 | -kBlobVertSize, 308 | -kBlobVertSize, 309 | -kBlobVertSize, 310 | -kBlobVertSize 311 | }; 312 | 313 | currentBlinkGraphic = blinkList[ blinkStage[player] ]; 314 | 315 | SDLU_AcquireSurface( playerSpriteSurface[player] ); 316 | 317 | for( x=0; x= 0 ) CleanSplat( player, x, splat[player][x], rowBounce[player][x] ); 320 | } 321 | 322 | for( x=0; x= 0 ) 326 | { 327 | compress = compressList[bounce]; 328 | rows = GetRowHeight( player, x ); 329 | 330 | CalcBlobRect( x, rows, &blobRect ); 331 | blobRect.bottom = kBlobVertSize * kGridDown; 332 | SurfaceDrawBoard( player, &blobRect ); 333 | SetUpdateRect( player, &blobRect ); 334 | 335 | blobRect.top = kBlobVertSize * (kGridDown-1); 336 | blobTop = kBlobVertSize * (kGridDown-1); 337 | 338 | for( y=kGridDown-1; y>=rows; y-- ) 339 | { 340 | suck = suction[player][x][y]; 341 | blob = grid[player][x][y]; 342 | if( suck == kNoSuction && blob >= kFirstBlob && 343 | blob <= kLastBlob && compress > (-kBlobVertSize + 3) ) 344 | { 345 | suck = kSquish; 346 | } 347 | 348 | if( blob == kGray ) SurfaceDrawAlpha( &blobRect, kGray, kLight, currentBlinkGraphic ); 349 | else SurfaceDrawBlob( player, &blobRect, blob, suck, charred[player][x][y] ); 350 | 351 | blobTop += compress; 352 | blobRect.top = (short) blobTop; 353 | blobRect.bottom = blobRect.top + kBlobVertSize; 354 | } 355 | } 356 | } 357 | 358 | for( x=0; x= 0 ) DrawSplat( player, x, splat[player][x], rowBounce[player][x] ); 361 | } 362 | 363 | SDLU_ReleaseSurface( playerSpriteSurface[player] ); 364 | 365 | for( x=0; x= 0 && rowBounce[player][x] < (kZapFrames) ) 368 | rowBounce[player][x]++; 369 | else 370 | rowBounce[player][x] = -1; 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | 3 | if(APPLE OR WIN32) 4 | set(GAME_TARGET "CandyCrisis") 5 | else() 6 | set(GAME_TARGET "candycrisis") 7 | endif() 8 | set(GAME_FRIENDLY_NAME "Candy Crisis") 9 | set(GAME_BUNDLE_ID "io.jor.candycrisis") 10 | set(GAME_DATA_DIR "CandyCrisisResources") 11 | set(GAME_MAC_COPYRIGHT "https://github.com/jorio/CandyCrisis") 12 | 13 | # Apply macOS deployment target and architectures to all subprojects 14 | set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum macOS deployment version") # Keep in sync with SDL3's Info.plist 15 | set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "Target macOS architectures") 16 | 17 | set(CMAKE_C_STANDARD 17) 18 | 19 | project(${GAME_TARGET} LANGUAGES C 20 | VERSION 3.0.2) 21 | 22 | # Create an option to switch between a system sdl library and a vendored sdl library 23 | option(BUILD_SDL_FROM_SOURCE "Build SDL from source" OFF) 24 | option(SDL_STATIC "Static link SDL" OFF) 25 | option(SANITIZE "Build with asan/ubsan" OFF) 26 | set(CODE_SIGN_IDENTITY "" CACHE STRING "macOS code signing identity. If omitted, the app won't be signed.") 27 | 28 | if(NOT WIN32 AND NOT APPLE) 29 | if(SANITIZE) 30 | message("Sanitizers enabled") 31 | else() 32 | message("Sanitizers disabled (pass -DSANITIZE=1 to enable)") 33 | endif() 34 | endif() 35 | 36 | # Set Visual Studio startup project 37 | set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${GAME_TARGET}) 38 | 39 | set(GAME_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) 40 | 41 | #------------------------------------------------------------------------------ 42 | # FIND SDL 43 | #------------------------------------------------------------------------------ 44 | 45 | # If SDL3_DIR wasn't specified, discover if the user put some prebuilt SDL3 package in the project's root directory 46 | if(NOT DEFINED SDL3_DIR) 47 | if(APPLE) 48 | set(_sdl3_maybe "${CMAKE_SOURCE_DIR}/SDL3.framework/Resources/CMake") 49 | elseif(WIN32) 50 | set(_sdl3_maybe "${CMAKE_SOURCE_DIR}/SDL3/cmake") 51 | endif() 52 | 53 | if(DEFINED _sdl3_maybe AND EXISTS "${_sdl3_maybe}") 54 | set(SDL3_DIR "${_sdl3_maybe}") 55 | else() 56 | message("Couldn't find pre-built SDL3 package in: ${_sdl3_maybe}") 57 | endif() 58 | unset(_sdl3_maybe) 59 | endif() 60 | 61 | if(NOT BUILD_SDL_FROM_SOURCE) 62 | find_package(SDL3 CONFIG REQUIRED) 63 | message("Found pre-built SDL3") 64 | else() 65 | if(NOT DEFINED SDL3_DIR) 66 | set(SDL3_DIR "${CMAKE_SOURCE_DIR}/SDL") 67 | endif() 68 | message("Building SDL3 from source: " ${SDL3_DIR}) 69 | add_subdirectory("${SDL3_DIR}" EXCLUDE_FROM_ALL) 70 | endif() 71 | 72 | #------------------------------------------------------------------------------ 73 | # GENERATED FILES 74 | #------------------------------------------------------------------------------ 75 | 76 | # Write header file containing version info 77 | configure_file(${GAME_SOURCE_DIR}/version.h.in ${GAME_SOURCE_DIR}/version.h) 78 | 79 | # Readme file containing version info 80 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/packaging/ReadMe.txt.in ${CMAKE_CURRENT_BINARY_DIR}/ReadMe.txt) 81 | 82 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/packaging/version.txt.in ${CMAKE_CURRENT_BINARY_DIR}/version.txt) 83 | 84 | #------------------------------------------------------------------------------ 85 | # EXECUTABLE TARGET 86 | #------------------------------------------------------------------------------ 87 | 88 | file(GLOB_RECURSE GAME_SOURCES CONFIGURE_DEPENDS ${GAME_SOURCE_DIR}/*.c ${GAME_SOURCE_DIR}/*.h) 89 | 90 | if(WIN32) 91 | list(APPEND GAME_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/packaging/app.exe.rc) 92 | elseif(APPLE) 93 | list(APPEND GAME_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/packaging/app.icns) 94 | set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/packaging/app.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") 95 | endif() 96 | 97 | # Executable 98 | add_executable(${GAME_TARGET} ${GAME_SOURCES}) 99 | 100 | target_include_directories(${GAME_TARGET} PRIVATE ${GAME_SOURCE_DIR}) 101 | 102 | # Create macOS app bundle with friendly name 103 | if(APPLE) 104 | set_target_properties(${GAME_TARGET} PROPERTIES OUTPUT_NAME "${GAME_FRIENDLY_NAME}") 105 | endif() 106 | 107 | set_target_properties(${GAME_TARGET} PROPERTIES 108 | #-------------------------------------------------------------------------- 109 | # MSVC/WIN32 110 | #-------------------------------------------------------------------------- 111 | 112 | WIN32_EXECUTABLE TRUE # GUI application instead of console application 113 | VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" 114 | VS_DPI_AWARE "PerMonitor" 115 | 116 | #-------------------------------------------------------------------------- 117 | # APPLE 118 | #-------------------------------------------------------------------------- 119 | 120 | # Build it as an .app bundle 121 | MACOSX_BUNDLE TRUE 122 | 123 | # Set up Info.plist values 124 | MACOSX_BUNDLE_ICON_FILE "app.icns" # CFBundleIconFile 125 | MACOSX_BUNDLE_EXECUTABLE_NAME "${GAME_TARGET}" # CFBundleExecutable - executable name inside the bundle 126 | MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}" # CFBundleShortVersionString 127 | MACOSX_BUNDLE_COPYRIGHT "${GAME_MAC_COPYRIGHT}" # NSHumanReadableCopyright (supersedes CFBundleGetInfoString (MACOSX_BUNDLE_INFO_STRING)) 128 | MACOSX_BUNDLE_BUNDLE_NAME "${GAME_FRIENDLY_NAME}" # CFBundleName - user-visible short name for the bundle 129 | MACOSX_BUNDLE_GUI_IDENTIFIER "${GAME_BUNDLE_ID}" # CFBundleIdentifier - unique bundle ID in reverse-DNS format 130 | 131 | # Set framework search path to (App bundle)/Contents/Frameworks so the game can use its embedded SDL3.framework 132 | XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks" 133 | 134 | # If CODE_SIGN_IDENTITY is NOT empty: tell Xcode to codesign the app properly 135 | # Otherwise, if it's empty: explicitly turn off code signing, otherwise downloaded app will be quarantined forever 136 | XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${CODE_SIGN_IDENTITY}" 137 | XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "" # don't bother signing debug build 138 | 139 | # Bundle ID required for code signing - must match CFBundleIdentifier otherwise xcode will complain 140 | XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER ${GAME_BUNDLE_ID} 141 | 142 | # Don't bother with universal builds when we're working on the debug version 143 | XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] "YES" 144 | 145 | XCODE_EMBED_FRAMEWORKS "SDL3.framework" 146 | XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY "YES" # frameworks must be signed by the same developer as the binary 147 | XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY "YES" # not strictly necessary, but that's cleaner 148 | XCODE_ATTRIBUTE_COPY_PHASE_STRIP[variant=Debug] "NO" # avoid "skipping copy phase strip" warning while working on Debug config 149 | 150 | # The following is to pass notarization requirements 151 | XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME "YES" 152 | XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS "NO" 153 | XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--options=runtime --timestamp" 154 | ) 155 | 156 | #------------------------------------------------------------------------------ 157 | # COMPILER/LINKER OPTIONS 158 | #------------------------------------------------------------------------------ 159 | 160 | add_compile_definitions( 161 | "$<$:_DEBUG>" 162 | ) 163 | 164 | if(NOT MSVC) 165 | target_compile_options(${GAME_TARGET} PRIVATE 166 | -Wall 167 | -Wextra 168 | #-Wshadow # Note: Candy Crisis goes crazy with shadowing 169 | -Werror=return-type 170 | -Wstrict-aliasing=2 171 | -Wno-multichar 172 | ) 173 | 174 | # Sanitizers in debug mode (Linux only) 175 | # When using a debugger, you should export LSAN_OPTIONS=detect_leaks=0 176 | if(SANITIZE) 177 | list(INSERT GAME_LIBRARIES 0 asan ubsan) 178 | target_compile_options(${GAME_TARGET} PRIVATE 179 | -fsanitize=alignment 180 | -fsanitize=address 181 | -fsanitize=leak 182 | -fsanitize=undefined 183 | -fno-omit-frame-pointer 184 | ) 185 | endif() 186 | else() 187 | # On Win32, we need NOGDI and NOUSER to be able to define some Mac functions 188 | # whose names are otherwise taken by Windows APIs. 189 | target_compile_definitions(${GAME_TARGET} PRIVATE 190 | WIN32_LEAN_AND_MEAN 191 | _CRT_SECURE_NO_WARNINGS # quit whining about snprintf_s 192 | ) 193 | 194 | target_compile_options(${GAME_TARGET} PRIVATE 195 | /W4 196 | /wd5105 # see https://developercommunity.visualstudio.com/t/1249671 197 | /we4013 # treat warning as error: undefined function (assuming extern returning int) 198 | /MP # multiprocessor build 199 | /Zi # output info to PDB 200 | ) 201 | 202 | # Let executable be debugged with PDB, even in Release builds 203 | target_link_options(${GAME_TARGET} PRIVATE /DEBUG) 204 | 205 | # Enable console for debug builds 206 | set_target_properties(${GAME_TARGET} PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") 207 | set_target_properties(${GAME_TARGET} PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE") 208 | endif() 209 | 210 | #------------------------------------------------------------------------------ 211 | # LINK LIBRARIES 212 | #------------------------------------------------------------------------------ 213 | 214 | # Explicitly link math lib on Linux 215 | if(NOT APPLE AND NOT WIN32) 216 | target_link_libraries(${GAME_TARGET} PRIVATE m) 217 | endif() 218 | 219 | if(NOT SDL_STATIC) 220 | target_link_libraries(${GAME_TARGET} PRIVATE SDL3::SDL3) 221 | else() 222 | target_link_libraries(${GAME_TARGET} PRIVATE SDL3::SDL3-static) 223 | endif() 224 | 225 | #------------------------------------------------------------------------------ 226 | # COPY ASSETS 227 | #------------------------------------------------------------------------------ 228 | 229 | if(APPLE) 230 | set(GAME_DATA_TARGET_LOCATION "$/../Resources") 231 | else() 232 | set(GAME_DATA_TARGET_LOCATION "$/${GAME_DATA_DIR}") 233 | endif() 234 | 235 | add_custom_command(TARGET ${GAME_TARGET} POST_BUILD 236 | COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/${GAME_DATA_DIR}" "${GAME_DATA_TARGET_LOCATION}") 237 | 238 | # Copy SDL3 DLL for convenience (WARNING: TARGET_RUNTIME_DLLS requires CMake 3.21, so this copy command was separated from the command above.) 239 | if(WIN32) 240 | add_custom_command(TARGET ${GAME_TARGET} POST_BUILD 241 | COMMAND ${CMAKE_COMMAND} -E copy $ $) 242 | endif() 243 | 244 | #------------------------------------------------------------------------------ 245 | # INSTALL 246 | #------------------------------------------------------------------------------ 247 | 248 | # Install Windows-specific libraries (cmake --install): copy Visual Studio redistributable DLLs to install location 249 | if(WIN32) 250 | include(InstallRequiredSystemLibraries) 251 | elseif(APPLE) 252 | # no-op 253 | else() 254 | install(TARGETS ${GAME_TARGET} DESTINATION bin) 255 | install(FILES ${CMAKE_SOURCE_DIR}/packaging/${GAME_TARGET}.desktop DESTINATION share/applications/) 256 | install(FILES ${CMAKE_SOURCE_DIR}/packaging/${GAME_TARGET}.png DESTINATION share/icons/hicolor/128x128/apps/) 257 | install(DIRECTORY ${CMAKE_SOURCE_DIR}/${GAME_DATA_DIR}/ DESTINATION share/${GAME_TARGET}/) 258 | endif() 259 | -------------------------------------------------------------------------------- /src/hiscore.c: -------------------------------------------------------------------------------- 1 | // hiscore.c 2 | 3 | #include "SDLU.h" 4 | 5 | #include "main.h" 6 | #include "gworld.h" 7 | #include "graphics.h" 8 | #include "score.h" 9 | #include "hiscore.h" 10 | #include "keyselect.h" 11 | #include "font.h" 12 | #include "blitter.h" 13 | #include "random.h" 14 | #include "pause.h" 15 | #include "level.h" 16 | #include "tutorial.h" 17 | #include "graymonitor.h" 18 | #include "players.h" 19 | #include "gameticks.h" 20 | #include "music.h" 21 | #include "soundfx.h" 22 | 23 | Combo defaultBest = 24 | { 25 | /*bestGrid[kGridAcross][kGridDown] = */ 26 | { { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty }, 27 | { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty }, 28 | { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, 1, 1, 1, 2, 2 }, 29 | { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, 1, 1 }, 30 | { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty }, 31 | { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty } }, 32 | 33 | /*bestA = */ 2, 34 | /*bestB = */ 2, 35 | /*bestM = */ false, 36 | /*bestG = */ false, 37 | /*bestLv = */ kTutorialLevel, 38 | /*bestX = */ 1, 39 | /*bestR = */ upRotate, 40 | /*bestPlayer = */ 0, 41 | /*bestValue = */ (40*1) + (50*9), 42 | /*bestName = */ "Tutorial" 43 | }; 44 | 45 | Combo best = 46 | { 47 | /*bestGrid[kGridAcross][kGridDown] = */ 48 | { { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty }, 49 | { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty }, 50 | { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, 1, 1, 1, 2, 2 }, 51 | { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, 1, 1 }, 52 | { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty }, 53 | { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty } }, 54 | 55 | /*bestA = */ 2, 56 | /*bestB = */ 2, 57 | /*bestM = */ false, 58 | /*bestG = */ false, 59 | /*bestLv = */ kTutorialLevel, 60 | /*bestX = */ 1, 61 | /*bestR = */ upRotate, 62 | /*bestPlayer = */ 0, 63 | /*bestValue = */ (40*1) + (50*9), 64 | /*bestName = */ "Tutorial" 65 | }; 66 | 67 | Combo evenBetter = {0}; 68 | Combo potentialCombo[2]; 69 | 70 | AutoPattern hiScorePattern[] = 71 | { 72 | { kIdleTicks, 60, 0, NULL }, 73 | { kBlockUntilDrop, 0, 0, NULL }, 74 | { kBlockUntilComplete, 0, 0, NULL }, 75 | { kIdleTicks, 60, 0, NULL }, 76 | { kComplete, 0, 0, NULL } 77 | }; 78 | 79 | HighScore scores[10] = 80 | { 81 | {"Leviathan", 40000}, 82 | {"Dr. Crisis", 36000}, 83 | {"Angel", 32000}, 84 | {"Spike", 28000}, 85 | {"Fox", 24000}, 86 | {"Raguel", 20000}, 87 | {"Kumo", 16000}, 88 | {"Patty", 12000}, 89 | {"Yuurei", 8000}, 90 | {"Glurp", 4000} 91 | }; 92 | 93 | HighScore defaultScores[10] = 94 | { 95 | {"Leviathan", 40000}, 96 | {"Dr. Crisis", 36000}, 97 | {"Angel", 32000}, 98 | {"Spike", 28000}, 99 | {"Fox", 24000}, 100 | {"Raguel", 20000}, 101 | {"Kumo", 16000}, 102 | {"Patty", 12000}, 103 | {"Yuurei", 8000}, 104 | {"Glurp", 4000} 105 | }; 106 | 107 | char highScoreName[256]; 108 | const char *highScoreText; 109 | const char *highScoreRank; 110 | 111 | static void FadeScreen( SDL_Surface* hiScoreSurface, SDL_Surface* fadeSurface, int start, int end ) 112 | { 113 | MTicks timer; 114 | int skip, frame, fade, color, direction, fadeStart, fadeEnd; 115 | SDL_Rect destSDLRect; 116 | SDL_Rect fullSDLRect = { 0, 0, 640, 480 }; 117 | int black; 118 | 119 | black = SDL_MapSurfaceRGB( fadeSurface, 0, 0, 0 ); 120 | 121 | if( start < end ) 122 | { 123 | direction = 1; 124 | fadeStart = 0; 125 | fadeEnd = 32; 126 | } 127 | else 128 | { 129 | direction = -1; 130 | fadeStart = 32; 131 | fadeEnd = 0; 132 | } 133 | 134 | skip = 1; 135 | timer = MTickCount( ) + 1; 136 | while( timer > MTickCount() ) { } 137 | 138 | for( frame = start; (direction>0)? (frame <= end): (frame >= end); frame += direction ) 139 | { 140 | MRect drawRect = {0, 0, 15, 640}; 141 | timer += skip; 142 | 143 | for( fade = fadeStart; fade != fadeEnd; fade += direction ) 144 | { 145 | color = frame + fade; 146 | if( color < 0 ) color = 0; 147 | if( color > 31 ) color = 31; 148 | 149 | SDLU_MRectToSDLRect( &drawRect, &destSDLRect ); 150 | 151 | switch( color ) 152 | { 153 | case 0: 154 | SDLU_BlitSurface( hiScoreSurface, &destSDLRect, 155 | fadeSurface, &destSDLRect ); 156 | break; 157 | 158 | case 31: 159 | SDL_FillSurfaceRect( fadeSurface, &destSDLRect, black ); 160 | break; 161 | 162 | default: 163 | SurfaceBlitColorOver( hiScoreSurface, fadeSurface, 164 | &drawRect, &drawRect, 165 | 0, 0, 0, _5TO8(color) ); 166 | break; 167 | } 168 | 169 | OffsetMRect( &drawRect, 0, 15 ); 170 | } 171 | 172 | SDLU_BlitFrontSurface( fadeSurface, &fullSDLRect, &fullSDLRect ); 173 | SDLU_Present(); 174 | 175 | if( timer <= MTickCount( ) ) 176 | { 177 | skip = 2; 178 | } 179 | else 180 | { 181 | skip = 1; 182 | while( timer > MTickCount( ) ) 183 | { 184 | SDLU_Yield(); 185 | } 186 | } 187 | } 188 | } 189 | 190 | void ShowHiscore( void ) 191 | { 192 | short count, length; 193 | char myString[256]; 194 | int stringLength; 195 | SDL_Surface* hiScoreSurface; 196 | SDL_Surface* fadeSurface; 197 | SDL_Rect fullSDLRect = { 0, 0, 640, 480 }; 198 | SkittlesFontPtr font; 199 | SDL_Color anyColor; 200 | MPoint dPoint; 201 | const char* highScores = "HIGH SCORES"; 202 | int r, g, b; 203 | 204 | if (DeleteKeyIsPressed()) 205 | { 206 | // If the user holds delete while opening the high scores, 207 | // clear the high score table. 208 | 209 | SDL_memcpy( &scores, &defaultScores, sizeof( scores ) ); 210 | SDL_memcpy( &best, &defaultBest, sizeof( best ) ); 211 | } 212 | 213 | hiScoreSurface = LoadPICTAsSurface( picBackdrop + (100 * RandomBefore(kLevels)), 32 ); 214 | fadeSurface = SDLU_InitSurface( &fullSDLRect, 32 ); 215 | 216 | 217 | SDLU_AcquireSurface( hiScoreSurface ); 218 | 219 | SDLU_GetPixel( hiScoreSurface, RandomBefore( fullSDLRect.w ), RandomBefore( fullSDLRect.h ), &anyColor ); 220 | 221 | anyColor.r = MinInt( 255, anyColor.r + 112 ); 222 | anyColor.g = MinInt( 255, anyColor.g + 112 ); 223 | anyColor.b = MinInt( 255, anyColor.b + 112 ); 224 | 225 | dPoint.v = widescreen? 100: 20; 226 | dPoint.h = 225; 227 | font = GetFont( picHiScoreFont ); 228 | for( count=0; highScores[count]; count++ ) 229 | { 230 | SurfaceBlitCharacter( font, highScores[count], &dPoint, 255, 255, 255, 1 ); 231 | } 232 | 233 | font = GetFont(widescreen ? picFont : picHiScoreFont); 234 | for( count=0; count<=9; count++ ) 235 | { 236 | r = ((255 * (10-count)) + (anyColor.r * count)) / 10; 237 | g = ((255 * (10-count)) + (anyColor.g * count)) / 10; 238 | b = ((255 * (10-count)) + (anyColor.b * count)) / 10; 239 | 240 | if (widescreen) 241 | dPoint.v = 150 + count * 24; 242 | else 243 | dPoint.v = 75 + count * 38; 244 | dPoint.h = 85; 245 | 246 | if( count<9 ) 247 | { 248 | SurfaceBlitCharacter( font, count + '1', &dPoint, r, g, b, 1 ); 249 | } 250 | else 251 | { 252 | SurfaceBlitCharacter( font, '1', &dPoint, r, g, b, 1 ); 253 | SurfaceBlitCharacter( font, '0', &dPoint, r, g, b, 1 ); 254 | } 255 | 256 | SurfaceBlitCharacter( font, '.', &dPoint, r, g, b, 1 ); 257 | SurfaceBlitCharacter( font, ' ', &dPoint, r, g, b, 1 ); 258 | 259 | length = 0; 260 | while( scores[count].name[length] && dPoint.h < 430 ) 261 | { 262 | SurfaceBlitCharacter( font, scores[count].name[length++], &dPoint, r, g, b, 1 ); 263 | } 264 | 265 | while( dPoint.h < 450 ) 266 | { 267 | SurfaceBlitCharacter( font, '.', &dPoint, r, g, b, 1 ); 268 | } 269 | 270 | dPoint.h = 470; 271 | 272 | stringLength = SDL_snprintf( myString, sizeof(myString), "%d", scores[count].score ); 273 | for( length=0; length < stringLength; length++ ) 274 | { 275 | SurfaceBlitCharacter( font, myString[length], &dPoint, r, g, b, 1 ); 276 | } 277 | } 278 | 279 | SDLU_ReleaseSurface( hiScoreSurface ); 280 | 281 | SDL_FillSurfaceRect( g_frontSurface, &g_frontSurfaceClipRect, SDL_MapSurfaceRGB(g_frontSurface, 0, 0, 0) ); 282 | SDLU_Present(); 283 | 284 | SDLU_SetBrightness( 1.0f ); 285 | 286 | FadeScreen( hiScoreSurface, fadeSurface, 31, -32 ); 287 | do 288 | { 289 | } 290 | while( !AnyKeyIsPressed( ) && !SDLU_Button() ); 291 | FadeScreen( hiScoreSurface, fadeSurface, -31, 32 ); 292 | 293 | SDLU_SetBrightness( 0.0f ); 294 | 295 | SDL_DestroySurface( hiScoreSurface ); 296 | SDL_DestroySurface( fadeSurface ); 297 | } 298 | 299 | void InitPotentialCombos( void ) 300 | { 301 | memset( &potentialCombo[0], 0, sizeof(Combo) ); 302 | memset( &potentialCombo[1], 0, sizeof(Combo) ); 303 | potentialCombo[0].player = 0; 304 | potentialCombo[1].player = 1; 305 | } 306 | 307 | void SubmitCombo( Combo *in ) 308 | { 309 | if( in->value > best.value && in->value > evenBetter.value ) 310 | { 311 | PlayMono( kContinueSnd ); 312 | SDL_memcpy( &evenBetter, in, sizeof( Combo ) ); 313 | } 314 | } 315 | 316 | // Please note: This function may well be the biggest kludge in Candy Crisis. 317 | // It relies on tons of insider knowledge. But it works. 318 | void ShowBestCombo( void ) 319 | { 320 | SkittlesFontPtr font; 321 | const char *bestCombo = "BEST COMBO"; 322 | const char *scan; 323 | char bestInfo[256]; 324 | MPoint dPoint; 325 | int levelCap; 326 | 327 | StopBalloon( ); 328 | InitGame( kAutoControl, kNobodyControl ); 329 | scoreWindowVisible[0] = false; 330 | grayMonitorVisible[0] = false; 331 | 332 | level = best.lv; 333 | levelCap = kLevels; 334 | if( (level < 1 || level > levelCap) && level != kTutorialLevel ) 335 | { 336 | SDL_memcpy( &best, &defaultBest, sizeof(best) ); 337 | showStartMenu = true; 338 | return; 339 | } 340 | 341 | BeginRound( false ); 342 | character[0].dropSpeed = 12; 343 | 344 | SDLU_AcquireSurface( backdropSurface ); 345 | 346 | font = GetFont(picHiScoreFont); 347 | dPoint.v = widescreen? 70: 40; 348 | dPoint.h = 225; 349 | for( scan = bestCombo; *scan; scan++ ) 350 | { 351 | SurfaceBlitCharacter( font, *scan, &dPoint, 255, 255, 255, 1 ); 352 | } 353 | 354 | SDL_snprintf( bestInfo, sizeof(bestInfo), "%s (%d points)", best.name, best.value ); 355 | 356 | font = GetFont(widescreen ? picFont : picHiScoreFont); 357 | dPoint.v = widescreen? 388: 410; 358 | dPoint.h = 320 - (GetTextWidth( font, bestInfo ) / 2); 359 | 360 | for( scan = bestInfo; *scan; scan++ ) 361 | { 362 | SurfaceBlitCharacter( font, *scan, &dPoint, 255, 255, 255, 1 ); 363 | } 364 | 365 | SDLU_ReleaseSurface( backdropSurface ); 366 | 367 | SDL_memcpy( grid[0], best.grid, kGridAcross * kGridDown ); 368 | ResolveSuction( 0 ); 369 | RedrawBoardContents( 0 ); 370 | RefreshAll( ); 371 | 372 | nextA[0] = best.a; 373 | nextB[0] = best.b; 374 | nextG[0] = best.g; 375 | nextM[0] = best.m; 376 | RetrieveBlobs( 0 ); 377 | 378 | EraseSpriteBlobs( 0 ); 379 | blobX[0] = best.x; 380 | blobR[0] = best.r; 381 | DrawSpriteBlobs( 0, kNoSuction ); 382 | 383 | QuickFadeIn( NULL ); 384 | blobTime[0] = animTime[0] = GameTickCount( ); 385 | 386 | autoPattern = hiScorePattern; 387 | tutorialTime = 0; 388 | } 389 | 390 | void AddHiscore( int score ) 391 | { 392 | short count, item; 393 | char rank[50]; 394 | char text[256]; 395 | const char *playerName = "You"; 396 | const char *twoPlayerNames[] = { "Player 1", "Player 2" }; 397 | int highScoreLevel = -1; 398 | 399 | 400 | // Check for high score 401 | // (only do high scores if it's player vs CPU) 402 | 403 | if( players == 1 && 404 | control[0] == kPlayerControl && 405 | control[1] == kAIControl ) 406 | { 407 | for( count=0; count<=9; count++ ) 408 | { 409 | if( score >= scores[count].score ) 410 | { 411 | SDL_snprintf( rank, sizeof(rank), "%d points", score ); 412 | highScoreLevel = count; 413 | break; 414 | } 415 | } 416 | } 417 | 418 | // Determine player's name for best combo 419 | 420 | if( players == 2 ) playerName = twoPlayerNames[evenBetter.player]; 421 | 422 | 423 | // See if both best combo AND high score 424 | 425 | if( evenBetter.value > best.value && highScoreLevel != -1 ) 426 | { 427 | 428 | SDL_snprintf( text, sizeof(text), "You got a high score and the best combo!" ); 429 | 430 | highScoreText = text; 431 | highScoreRank = rank; 432 | 433 | HandleDialog( kHiScoreDialog ); 434 | 435 | if( !finished ) 436 | { 437 | highScoreName[kNameLength] = '\0'; 438 | 439 | SDL_memcpy( &best, &evenBetter, sizeof(Combo) ); 440 | SDL_strlcpy( best.name, highScoreName, sizeof(best.name) ); 441 | 442 | for( item=8; item>=highScoreLevel; item-- ) 443 | { 444 | memmove( &scores[item+1], &scores[item], sizeof( HighScore ) ); 445 | } 446 | 447 | scores[highScoreLevel].score = score; 448 | SDL_strlcpy( scores[highScoreLevel].name, highScoreName, sizeof(scores[highScoreLevel].name) ); 449 | } 450 | } 451 | 452 | // See if best combo has been won 453 | 454 | else if( evenBetter.value > best.value ) 455 | { 456 | SDL_snprintf( text, sizeof(text), "Congratulations! %s got best combo!", playerName ); 457 | 458 | highScoreText = text; 459 | highScoreRank = ""; 460 | 461 | HandleDialog( kHiScoreDialog ); 462 | 463 | if( !finished ) 464 | { 465 | highScoreName[kNameLength] = '\0'; 466 | 467 | SDL_memcpy( &best, &evenBetter, sizeof(Combo) ); 468 | SDL_strlcpy( best.name, highScoreName, sizeof(best.name) ); 469 | } 470 | } 471 | 472 | // See if high score has been won 473 | 474 | else if( highScoreLevel != -1 ) 475 | { 476 | highScoreText = "Congratulations! You got a high score!"; 477 | highScoreRank = rank; 478 | 479 | HandleDialog( kHiScoreDialog ); 480 | 481 | if( !finished ) 482 | { 483 | highScoreName[kNameLength] = '\0'; 484 | 485 | for( item=8; item>=highScoreLevel; item-- ) 486 | { 487 | SDL_memmove( &scores[item+1], &scores[item], sizeof( HighScore ) ); 488 | } 489 | 490 | scores[highScoreLevel].score = score; 491 | SDL_strlcpy( scores[highScoreLevel].name, highScoreName, sizeof(scores[highScoreLevel].name) ); 492 | } 493 | } 494 | } 495 | -------------------------------------------------------------------------------- /src/SDLU.c: -------------------------------------------------------------------------------- 1 | /// 2 | /// SDLU.c 3 | /// 4 | /// SDL utilities. 5 | /// 6 | /// John Stiles, 2002/10/12 7 | /// 8 | 9 | #include "SDLU.h" 10 | #include "gameticks.h" 11 | #include "music.h" 12 | #include "main.h" // for Error 13 | 14 | // for acquiresurface 15 | #define k_acquireMax 10 16 | static int s_acquireHead = -1; 17 | static SDL_Surface* s_acquireList[k_acquireMax]; 18 | 19 | // for initsurface 20 | static SDL_Palette* s_grayscalePalette; 21 | 22 | // for button and getmouse 23 | static int s_mouseButton; 24 | static MPoint s_mousePosition; 25 | 26 | // system mouse cursors 27 | static SDL_Cursor* s_standardCursor = NULL; 28 | static SDL_Cursor* s_handCursor = NULL; 29 | 30 | // for event loop 31 | static MBoolean s_isForeground = true; 32 | 33 | // for fade out / fade in 34 | static float s_fadeGamma = 1; 35 | 36 | // for checktyping 37 | typedef struct BufferedKey 38 | { 39 | bool isASCII; 40 | 41 | union 42 | { 43 | char ascii; 44 | SDL_Keycode keycode; 45 | } value; 46 | } BufferedKey; 47 | 48 | #define k_maxBufferedKeys 256 49 | 50 | static MBoolean s_interestedInTyping = false; 51 | static BufferedKey s_keyBuffer[k_maxBufferedKeys]; 52 | static int s_keyBufferSize = 0; 53 | 54 | bool SDLUi_EventFilter(void* junk, SDL_Event *event) 55 | { 56 | (void) junk; 57 | 58 | switch (event->type) 59 | { 60 | case SDL_EVENT_TEXT_INPUT: 61 | { 62 | // Put text input into a buffer. 63 | if (s_interestedInTyping) 64 | { 65 | for (char* asciiPtr = event->text.text; *asciiPtr; ++asciiPtr) 66 | { 67 | BufferedKey key; 68 | key.isASCII = true; 69 | key.value.ascii = *asciiPtr; 70 | s_keyBuffer[s_keyBufferSize] = key; 71 | s_keyBufferSize = MinInt(k_maxBufferedKeys, s_keyBufferSize + 1); 72 | } 73 | } 74 | break; 75 | } 76 | 77 | case SDL_EVENT_KEY_DOWN: 78 | { 79 | // Put keydowns in a buffer 80 | if (s_interestedInTyping) 81 | { 82 | BufferedKey key; 83 | key.isASCII = false; 84 | key.value.keycode = event->key.key; 85 | s_keyBuffer[s_keyBufferSize] = key; 86 | s_keyBufferSize = MinInt(k_maxBufferedKeys, s_keyBufferSize + 1); 87 | } 88 | break; 89 | } 90 | 91 | // Get mouse state 92 | case SDL_EVENT_MOUSE_BUTTON_DOWN: 93 | { 94 | if( event->button.button == SDL_BUTTON_LEFT ) 95 | s_mouseButton = true; 96 | 97 | SDL_ConvertEventToRenderCoordinates(g_renderer, event); 98 | s_mousePosition.v = event->button.y; 99 | s_mousePosition.h = event->button.x; 100 | break; 101 | } 102 | 103 | case SDL_EVENT_MOUSE_BUTTON_UP: 104 | { 105 | if( event->button.button == SDL_BUTTON_LEFT ) 106 | s_mouseButton = false; 107 | 108 | SDL_ConvertEventToRenderCoordinates(g_renderer, event); 109 | s_mousePosition.v = event->button.y; 110 | s_mousePosition.h = event->button.x; 111 | break; 112 | } 113 | 114 | case SDL_EVENT_MOUSE_MOTION: 115 | { 116 | SDL_ConvertEventToRenderCoordinates(g_renderer, event); 117 | s_mousePosition.v = event->motion.y; 118 | s_mousePosition.h = event->motion.x; 119 | s_mouseButton = event->motion.state & SDL_BUTTON_LMASK; 120 | break; 121 | } 122 | 123 | case SDL_EVENT_QUIT: 124 | { 125 | finished = true; 126 | break; 127 | } 128 | 129 | case SDL_EVENT_WINDOW_FOCUS_LOST: 130 | if (s_isForeground) 131 | { 132 | FreezeGameTickCount(); 133 | //EnableMusic(false); 134 | s_isForeground = false; 135 | } 136 | break; 137 | 138 | case SDL_EVENT_WINDOW_FOCUS_GAINED: 139 | if (!s_isForeground) 140 | { 141 | UnfreezeGameTickCount(); 142 | //EnableMusic(musicOn); 143 | s_isForeground = true; 144 | 145 | DoFullRepaint(); 146 | } 147 | break; 148 | 149 | case SDL_EVENT_WINDOW_RESIZED: 150 | { 151 | SDLU_CreateRendererTexture(); 152 | break; 153 | } 154 | } 155 | 156 | return true; 157 | } 158 | 159 | 160 | void SDLU_CreateRendererTexture() 161 | { 162 | if (!g_renderer) 163 | return; 164 | 165 | int logicalWidth = 640; 166 | int logicalHeight = widescreen ? 360 : 480; 167 | 168 | SDL_RendererLogicalPresentation logicalPresentation = SDL_LOGICAL_PRESENTATION_LETTERBOX; 169 | SDL_ScaleMode scaleMode = SDL_SCALEMODE_LINEAR; 170 | 171 | if (crispUpscaling) 172 | { 173 | int currentWidth = 0; 174 | int currentHeight = 0; 175 | SDL_GetWindowSizeInPixels(g_window, ¤tWidth, ¤tHeight); 176 | 177 | if (currentWidth >= logicalWidth && currentHeight >= logicalHeight) 178 | { 179 | logicalPresentation = SDL_LOGICAL_PRESENTATION_INTEGER_SCALE; 180 | scaleMode = SDL_SCALEMODE_NEAREST; 181 | } 182 | } 183 | 184 | if (g_windowTexture) 185 | SDL_DestroyTexture(g_windowTexture); 186 | 187 | g_windowTexture = SDL_CreateTexture(g_renderer, 188 | SDL_PIXELFORMAT_XRGB8888, 189 | SDL_TEXTUREACCESS_STREAMING, 190 | 640, 480); 191 | 192 | SDL_SetTextureScaleMode(g_windowTexture, scaleMode); 193 | SDL_SetRenderLogicalPresentation(g_renderer, logicalWidth, logicalHeight, logicalPresentation); 194 | } 195 | 196 | 197 | void SDLU_Init() 198 | { 199 | SDL_SetEventFilter(SDLUi_EventFilter, NULL); 200 | 201 | // Initialize eight bit grayscale ramp palette. 202 | SDL_Color grayscaleColors[256]; 203 | for (int index=0; index<256; index++) 204 | { 205 | grayscaleColors[index].r = 206 | grayscaleColors[index].g = 207 | grayscaleColors[index].b = 255 - index; 208 | grayscaleColors[index].a = 255; 209 | } 210 | 211 | s_grayscalePalette = SDL_CreatePalette(256); 212 | bool ok = SDL_SetPaletteColors(s_grayscalePalette, grayscaleColors, 0, arrsize(grayscaleColors)); 213 | SDL_assert(ok); 214 | 215 | s_standardCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); 216 | s_handCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER); 217 | } 218 | 219 | 220 | SDL_Rect* SDLU_MRectToSDLRect( const MRect* in, SDL_Rect* out ) 221 | { 222 | int t = in->top, l = in->left, b = in->bottom, r = in->right; 223 | 224 | out->x = l; 225 | out->y = t; 226 | out->w = r - l; 227 | out->h = b - t; 228 | 229 | return out; 230 | } 231 | 232 | 233 | MRect* SDLU_SDLRectToMRect( const SDL_Rect* in, MRect* out ) 234 | { 235 | int x = in->x, y = in->y, w = in->w, h = in->h; 236 | 237 | out->top = y; 238 | out->left = x; 239 | out->bottom = y + h; 240 | out->right = x + w; 241 | 242 | return out; 243 | } 244 | 245 | 246 | int SDLU_BlitSurface( SDL_Surface* src, SDL_Rect* srcrect, 247 | SDL_Surface* dst, SDL_Rect* dstrect ) 248 | { 249 | return SDL_BlitSurface( src, srcrect, dst, dstrect ); 250 | } 251 | 252 | 253 | int SDLU_BlitSurface1to1( SDL_Surface* src, SDL_Surface* dst ) 254 | { 255 | SDL_Rect srcrect, dstrect; 256 | SDL_GetSurfaceClipRect( src, &srcrect ); 257 | SDL_GetSurfaceClipRect( dst, &dstrect ); 258 | return SDL_BlitSurface( src, &srcrect, 259 | dst, &dstrect ); 260 | } 261 | 262 | 263 | void SDLU_GetPixel( SDL_Surface* surface, int x, int y, SDL_Color* pixel ) 264 | { 265 | unsigned int px; 266 | unsigned char* ptr; 267 | 268 | switch( SDL_BYTESPERPIXEL(surface->format) ) 269 | { 270 | case 1: 271 | ptr = (unsigned char*)surface->pixels + (y * surface->pitch) + (x); 272 | px = *(unsigned char*) ptr; 273 | break; 274 | 275 | case 2: 276 | ptr = (unsigned char*)surface->pixels + (y * surface->pitch) + (x * 2); 277 | px = *(unsigned short*) ptr; 278 | break; 279 | 280 | case 4: 281 | ptr = (unsigned char*)surface->pixels + (y * surface->pitch) + (x * 4); 282 | px = *(unsigned int *) ptr; 283 | break; 284 | 285 | default: 286 | Error("SDLU_GetPixel: unrecognized surface format"); 287 | return; 288 | } 289 | 290 | SDL_GetRGB( px, SDL_GetPixelFormatDetails(surface->format), SDL_GetSurfacePalette(surface), &pixel->r, &pixel->g, &pixel->b ); 291 | } 292 | 293 | 294 | void SDLU_ChangeSurfaceDepth( SDL_Surface** surface, int depth ) 295 | { 296 | SDL_Surface* oldSurface = *surface; 297 | SDL_Surface* newSurface; 298 | SDL_Rect clipRect; 299 | 300 | SDL_GetSurfaceClipRect( oldSurface, &clipRect ); 301 | 302 | newSurface = SDLU_InitSurface( &clipRect, depth ); 303 | 304 | SDLU_BlitSurface1to1( oldSurface, newSurface ); 305 | 306 | SDL_DestroySurface( oldSurface ); 307 | 308 | *surface = newSurface; 309 | } 310 | 311 | 312 | SDL_Surface* SDLU_InitSurface( SDL_Rect* rect, int depth ) 313 | { 314 | SDL_Surface* surface = NULL; 315 | bool ok = true; 316 | 317 | switch( depth ) 318 | { 319 | case 32: 320 | surface = SDL_CreateSurface(rect->w, rect->h, SDL_PIXELFORMAT_XRGB8888); 321 | break; 322 | 323 | case 8: 324 | surface = SDL_CreateSurface(rect->w, rect->h, SDL_PIXELFORMAT_INDEX8); 325 | SDL_assert(surface); 326 | ok = SDL_SetSurfacePalette(surface, s_grayscalePalette); 327 | SDL_assert(ok); 328 | break; 329 | 330 | default: 331 | Error("SDLU_InitSurface: invalid depth"); 332 | return NULL; 333 | } 334 | 335 | if( surface == NULL ) 336 | { 337 | Error( "SDLU_InitSurface: SDL_CreateRGBSurface" ); 338 | return NULL; 339 | } 340 | 341 | // SDL_FillRect only works on 8-bit or higher surfaces. 342 | if( depth >= 8 ) 343 | { 344 | ok = SDL_FillSurfaceRect( surface, rect, SDL_MapSurfaceRGB( surface, 0xFF, 0xFF, 0xFF ) ); 345 | SDL_assert(ok); 346 | } 347 | 348 | return surface; 349 | } 350 | 351 | 352 | void SDLU_BlitFrontSurface( SDL_Surface* source, SDL_Rect* sourceSDLRect, SDL_Rect* destSDLRect ) 353 | { 354 | SDLU_BlitSurface( source, sourceSDLRect, 355 | g_frontSurface, destSDLRect ); 356 | } 357 | 358 | 359 | void SDLU_SetBrightness( float b ) 360 | { 361 | s_fadeGamma = b; 362 | } 363 | 364 | void SDLU_Yield() 365 | { 366 | SDL_Delay( 2 ); 367 | SDL_PumpEvents(); 368 | } 369 | 370 | void SDLU_PumpEvents() 371 | { 372 | static unsigned int lastPump = 0; 373 | unsigned int time = MTickCount(); 374 | 375 | if( lastPump != time ) 376 | { 377 | SDL_Event evt; 378 | while( SDL_PollEvent( &evt ) ) { } 379 | lastPump = time; 380 | } 381 | } 382 | 383 | 384 | MBoolean SDLU_IsForeground() 385 | { 386 | return s_isForeground; 387 | } 388 | 389 | 390 | void SDLU_StartWatchingTyping() 391 | { 392 | s_interestedInTyping = true; 393 | s_keyBufferSize = 0; // clear keybuffer 394 | SDL_StartTextInput(g_window); 395 | } 396 | 397 | 398 | void SDLU_StopWatchingTyping() 399 | { 400 | s_interestedInTyping = false; 401 | SDL_StopTextInput(g_window); 402 | } 403 | 404 | 405 | MBoolean SDLU_CheckASCIITyping(char* ascii) 406 | { 407 | if (s_keyBufferSize > 0 && s_keyBuffer[0].isASCII) 408 | { 409 | *ascii = s_keyBuffer[0].value.ascii; 410 | 411 | s_keyBufferSize--; 412 | SDL_memcpy(&s_keyBuffer[0], &s_keyBuffer[1], (s_keyBufferSize) * sizeof(BufferedKey)); 413 | 414 | return true; 415 | } 416 | 417 | *ascii = '\0'; 418 | return false; 419 | } 420 | 421 | 422 | MBoolean SDLU_CheckSDLTyping(SDL_Keycode* sdlKey) 423 | { 424 | if (s_keyBufferSize > 0 && !s_keyBuffer[0].isASCII) 425 | { 426 | *sdlKey = s_keyBuffer[0].value.keycode; 427 | 428 | s_keyBufferSize--; 429 | SDL_memcpy(&s_keyBuffer[0], &s_keyBuffer[1], (s_keyBufferSize) * sizeof(BufferedKey)); 430 | 431 | return true; 432 | } 433 | 434 | *sdlKey = SDLK_UNKNOWN; 435 | return false; 436 | } 437 | 438 | 439 | void SDLU_GetMouse( MPoint* pt ) 440 | { 441 | SDLU_PumpEvents(); 442 | *pt = s_mousePosition; 443 | if (widescreen) 444 | { 445 | pt->h += g_widescreenCrop.x; 446 | pt->v += g_widescreenCrop.y; 447 | } 448 | } 449 | 450 | 451 | int SDLU_Button() 452 | { 453 | SDLU_PumpEvents(); 454 | return s_mouseButton; 455 | } 456 | 457 | 458 | void SDLU_AcquireSurface( SDL_Surface* surface ) 459 | { 460 | if (s_acquireHead >= arrsize(s_acquireList) - 1) 461 | Error("SDLU_AcquireSurface: overflow"); 462 | 463 | s_acquireList[++s_acquireHead] = surface; 464 | } 465 | 466 | 467 | SDL_Surface* SDLU_GetCurrentSurface() 468 | { 469 | return s_acquireList[s_acquireHead]; 470 | } 471 | 472 | 473 | void SDLU_ReleaseSurface( SDL_Surface* surface ) 474 | { 475 | if (s_acquireHead < 0) 476 | Error( "SDLU_ReleaseSurface: underflow" ); 477 | 478 | if( s_acquireList[s_acquireHead] != surface ) 479 | Error( "SDLU_ReleaseSurface: out of order" ); 480 | 481 | s_acquireHead--; 482 | } 483 | 484 | 485 | void SDLU_Present() 486 | { 487 | SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, 255); 488 | 489 | SDL_UpdateTexture(g_windowTexture, NULL, g_frontSurface->pixels, g_frontSurface->pitch); 490 | SDL_RenderClear(g_renderer); 491 | 492 | SDL_FRect wsc = {.x=g_widescreenCrop.x, .y=g_widescreenCrop.y, .w=g_widescreenCrop.w, .h=g_widescreenCrop.h}; 493 | SDL_RenderTexture(g_renderer, g_windowTexture, widescreen ? &wsc : NULL, NULL); 494 | 495 | if (s_fadeGamma < 1.0) 496 | { 497 | SDL_SetRenderDrawBlendMode(g_renderer, SDL_BLENDMODE_BLEND); 498 | SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, (Uint8)((1.0f - s_fadeGamma) * 255.0f)); 499 | SDL_RenderFillRect(g_renderer, NULL); 500 | } 501 | 502 | SDL_RenderPresent(g_renderer); 503 | 504 | #if 0 505 | static int s_fpsAccumulator = 0; 506 | static int s_fpsSampleStart = 0; 507 | const int k_fpsSampleInterval = 500; 508 | 509 | s_fpsAccumulator++; 510 | int now = SDL_GetTicks(); 511 | int elapsed = now - s_fpsSampleStart; 512 | if (elapsed > k_fpsSampleInterval) 513 | { 514 | float fps = s_fpsAccumulator / (elapsed / 1000.0f); 515 | #if _DEBUG 516 | printf("FPS: %.1f\n", fps); 517 | #endif 518 | s_fpsAccumulator = 0; 519 | s_fpsSampleStart = now; 520 | } 521 | #endif 522 | } 523 | 524 | 525 | void SDLU_SetSystemCursor(int which) 526 | { 527 | #if USE_CURSOR_SPRITE 528 | SDL_HideCursor(); 529 | #else 530 | switch (which) 531 | { 532 | case SYSTEM_CURSOR_OFF: 533 | SDL_HideCursor(); 534 | SDL_SetCursor(s_standardCursor); 535 | break; 536 | 537 | case SYSTEM_CURSOR_ARROW: 538 | SDL_SetCursor(s_standardCursor); 539 | SDL_ShowCursor(); 540 | break; 541 | 542 | case SYSTEM_CURSOR_HAND: 543 | SDL_SetCursor(s_handCursor); 544 | SDL_ShowCursor(); 545 | break; 546 | } 547 | #endif 548 | } 549 | --------------------------------------------------------------------------------