SDL2 support + emscripten
This commit is contained in:
parent
1e5a105036
commit
b842059480
|
@ -9,4 +9,5 @@ build/
|
|||
*.elf
|
||||
*.smdh
|
||||
win-build/
|
||||
em-build/
|
||||
ccleste-win.zip
|
||||
|
|
20
Makefile
20
Makefile
|
@ -1,5 +1,21 @@
|
|||
CFLAGS=-Wall -g -O2
|
||||
LDFLAGS=`sdl-config --cflags --libs` -lSDL_mixer
|
||||
|
||||
# set to 1 to use SDL1.2
|
||||
SDL_VER?=2
|
||||
|
||||
ifeq ($(SDL_VER),2)
|
||||
SDL_CONFIG=sdl2-config
|
||||
SDL_LD=-lSDL2 -lSDL2_mixer
|
||||
else
|
||||
ifeq ($(SDL_VER),1)
|
||||
SDL_CONFIG=sdl-config
|
||||
SDL_LD=-lSDL -lSDL_mixer
|
||||
else
|
||||
SDL_CONFIG=$(error "invalid SDL version '$(SDL_VER)'. possible values are '1' and '2'")
|
||||
endif
|
||||
endif
|
||||
|
||||
CFLAGS=-Wall -g -O2 `$(SDL_CONFIG) --cflags`
|
||||
LDFLAGS=$(SDL_LD)
|
||||
CELESTE_CC=$(CC)
|
||||
|
||||
ifneq ($(USE_FIXEDP),)
|
||||
|
|
|
@ -86,7 +86,7 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
|||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CFILES := sdl12main.c celeste.c
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica)))
|
||||
|
|
|
@ -6,6 +6,8 @@ This is a C source port of the [original celeste (Celeste classic)](https://www.
|
|||
|
||||
Go to [the releases tab](https://github.com/lemon-sherbet/ccleste/releases) for the latest pre-built binaries.
|
||||
|
||||
An experimental web port is also available [here](https://lemon-sherbet.github.io/ccleste.html).
|
||||
|
||||
celeste.c + celeste.h is where the game code is, translated from the pico 8 lua code by hand.
|
||||
These files don't depend on anything other than the c standard library and don't perform any allocations (it uses its own internal global state).
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/sh
|
||||
CFLAGS="-Wall -O2"
|
||||
EMFLAGS="-s ALLOW_MEMORY_GROWTH=1 -s USE_SDL=2 -s USE_SDL_MIXER=2 -s USE_OGG=1"
|
||||
OUTDIR="em-build/"
|
||||
|
||||
mkdir -p $OUTDIR
|
||||
emcc $CFLAGS $EMFLAGS sdl12main.c -c -o $OUTDIR/sdl12main.wasm
|
||||
emcc $CFLAGS $EMFLAGS celeste.c -c -o $OUTDIR/celeste.wasm
|
||||
emcc $EMFLAGS --shell-file emscripten-shell.html --preload-file data/ $OUTDIR/sdl12main.wasm $OUTDIR/celeste.wasm -o $OUTDIR/ccleste.html
|
|
@ -0,0 +1,162 @@
|
|||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>ccleste (experimental emscripten build)</title>
|
||||
<style>
|
||||
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
|
||||
textarea.emscripten { font-family: monospace; width: 80%; }
|
||||
div.emscripten { text-align: center; }
|
||||
div.emscripten_border { border: 1px solid black; }
|
||||
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
|
||||
canvas.emscripten { border: 0px none; background-color: black; }
|
||||
|
||||
.spinner {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
margin: 0px auto;
|
||||
-webkit-animation: rotation .8s linear infinite;
|
||||
-moz-animation: rotation .8s linear infinite;
|
||||
-o-animation: rotation .8s linear infinite;
|
||||
animation: rotation 0.8s linear infinite;
|
||||
border-left: 10px solid rgb(0,150,240);
|
||||
border-right: 10px solid rgb(0,150,240);
|
||||
border-bottom: 10px solid rgb(0,150,240);
|
||||
border-top: 10px solid rgb(100,0,200);
|
||||
border-radius: 100%;
|
||||
background-color: rgb(200,100,250);
|
||||
}
|
||||
@-webkit-keyframes rotation {
|
||||
from {-webkit-transform: rotate(0deg);}
|
||||
to {-webkit-transform: rotate(360deg);}
|
||||
}
|
||||
@-moz-keyframes rotation {
|
||||
from {-moz-transform: rotate(0deg);}
|
||||
to {-moz-transform: rotate(360deg);}
|
||||
}
|
||||
@-o-keyframes rotation {
|
||||
from {-o-transform: rotate(0deg);}
|
||||
to {-o-transform: rotate(360deg);}
|
||||
}
|
||||
@keyframes rotation {
|
||||
from {transform: rotate(0deg);}
|
||||
to {transform: rotate(360deg);}
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<hr/>
|
||||
<div class="emscripten">
|
||||
<p>Experimental <a href="https://emscripten.org/">emscripten</a> build of <a href="https://github.com/lemon-sherbet/ccleste">ccleste</a>. <br>
|
||||
<ul>
|
||||
<li>Jump with Z/C</li>
|
||||
<li>Dash with X/V</li>
|
||||
<li>Move with arrow keys</li>
|
||||
<li>Escape to pause</li>
|
||||
<li>Shift+S to save state</li>
|
||||
<li>Shift+D to load state</li>
|
||||
<li>Hold F9 to reset</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
<figure style="overflow:visible;" id="spinner"><div class="spinner"></div><center style="margin-top:0.5em"><strong>emscripten</strong></center></figure>
|
||||
<div class="emscripten" id="status">Downloading...</div>
|
||||
<div class="emscripten">
|
||||
<progress value="0" max="100" id="progress" hidden=1></progress>
|
||||
</div>
|
||||
<div class="emscripten_border">
|
||||
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="emscripten">
|
||||
<input type="checkbox" id="resize">Resize canvas
|
||||
<input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer
|
||||
|
||||
<!--input type="button" value="Fullscreen" onclick="Module.requestFullscreen(document.getElementById('pointerLock').checked,
|
||||
document.getElementById('resize').checked)" !-->
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
<textarea class="emscripten" id="output" rows="8"></textarea>
|
||||
<hr>
|
||||
<script type='text/javascript'>
|
||||
var statusElement = document.getElementById('status');
|
||||
var progressElement = document.getElementById('progress');
|
||||
var spinnerElement = document.getElementById('spinner');
|
||||
|
||||
var Module = {
|
||||
preRun: [],
|
||||
postRun: [],
|
||||
print: (function() {
|
||||
var element = document.getElementById('output');
|
||||
if (element) element.value = ''; // clear browser cache
|
||||
return function(text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
// These replacements are necessary if you render to raw HTML
|
||||
//text = text.replace(/&/g, "&");
|
||||
//text = text.replace(/</g, "<");
|
||||
//text = text.replace(/>/g, ">");
|
||||
//text = text.replace('\n', '<br>', 'g');
|
||||
console.log(text);
|
||||
if (element) {
|
||||
element.value += text + "\n";
|
||||
element.scrollTop = element.scrollHeight; // focus on bottom
|
||||
}
|
||||
};
|
||||
})(),
|
||||
printErr: function(text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.error(text);
|
||||
},
|
||||
canvas: (function() {
|
||||
var canvas = document.getElementById('canvas');
|
||||
|
||||
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
|
||||
// application robust, you may want to override this behavior before shipping!
|
||||
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
|
||||
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
|
||||
|
||||
return canvas;
|
||||
})(),
|
||||
setStatus: function(text) {
|
||||
if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
|
||||
if (text === Module.setStatus.last.text) return;
|
||||
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
|
||||
var now = Date.now();
|
||||
if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
|
||||
Module.setStatus.last.time = now;
|
||||
Module.setStatus.last.text = text;
|
||||
if (m) {
|
||||
text = m[1];
|
||||
progressElement.value = parseInt(m[2])*100;
|
||||
progressElement.max = parseInt(m[4])*100;
|
||||
progressElement.hidden = false;
|
||||
spinnerElement.hidden = false;
|
||||
} else {
|
||||
progressElement.value = null;
|
||||
progressElement.max = null;
|
||||
progressElement.hidden = true;
|
||||
if (!text) spinnerElement.hidden = true;
|
||||
}
|
||||
statusElement.innerHTML = text;
|
||||
},
|
||||
totalDependencies: 0,
|
||||
monitorRunDependencies: function(left) {
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
|
||||
}
|
||||
};
|
||||
Module.setStatus('Downloading...');
|
||||
window.onerror = function() {
|
||||
Module.setStatus('Exception thrown, see JavaScript console');
|
||||
spinnerElement.style.display = 'none';
|
||||
Module.setStatus = function(text) {
|
||||
if (text) Module.printErr('[post-exception status] ' + text);
|
||||
};
|
||||
};
|
||||
</script>
|
||||
{{{ SCRIPT }}}
|
||||
</body>
|
||||
</html>
|
316
sdl12main.c
316
sdl12main.c
|
@ -1,5 +1,8 @@
|
|||
#include <SDL.h>
|
||||
#include <SDL_mixer.h>
|
||||
#if SDL_MAJOR_VERSION >= 2
|
||||
#include "sdl20compat.inc.c"
|
||||
#endif
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
@ -160,9 +163,9 @@ static void LoadData(void) {
|
|||
loadbmpscale("font.bmp", &font);
|
||||
LOGDONE();
|
||||
|
||||
static signed char sndids[] = {0,1,2,3,4,5,6,7,8,9,13,14,15,16,23,35,37,38,40,50,51,54,55, -1};
|
||||
for (signed char* iid = sndids; *iid != -1; iid++) {
|
||||
int id = *iid;
|
||||
static const char sndids[] = {0,1,2,3,4,5,6,7,8,9,13,14,15,16,23,35,37,38,40,50,51,54,55};
|
||||
for (int iid = 0; iid < sizeof sndids; iid++) {
|
||||
int id = sndids[iid];
|
||||
char fname[20];
|
||||
sprintf(fname, "snd%i.wav", id);
|
||||
char path[4096];
|
||||
|
@ -174,9 +177,9 @@ static void LoadData(void) {
|
|||
}
|
||||
LOGDONE();
|
||||
}
|
||||
static signed char musids[] = {0,10,20,30,40, -1};
|
||||
for (signed char* iid = musids; *iid != -1; iid++) {
|
||||
int id = *iid;
|
||||
static const char musids[] = {0,10,20,30,40};
|
||||
for (int iid = 0; iid < sizeof musids; iid++) {
|
||||
int id = musids[iid];
|
||||
char fname[20];
|
||||
sprintf(fname, "mus%i.ogg", id);
|
||||
LOGLOAD(fname);
|
||||
|
@ -229,6 +232,13 @@ static void OSDdraw(void) {
|
|||
|
||||
static Mix_Music* current_music = NULL;
|
||||
static _Bool enable_screenshake = 1;
|
||||
static _Bool paused = 0;
|
||||
static _Bool running = 1;
|
||||
static void* initial_game_state = NULL;
|
||||
static void* game_state = NULL;
|
||||
static Mix_Music* game_state_music = NULL;
|
||||
static void mainLoop(void);
|
||||
static FILE* TAS = NULL;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
SDL_CHECK(SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO) == 0);
|
||||
|
@ -260,7 +270,6 @@ int main(int argc, char** argv) {
|
|||
ResetPalette();
|
||||
SDL_ShowCursor(0);
|
||||
|
||||
FILE* TAS = NULL;
|
||||
if (argc > 1) {
|
||||
TAS = fopen(argv[1], "r");
|
||||
if (!TAS) {
|
||||
|
@ -310,7 +319,7 @@ int main(int argc, char** argv) {
|
|||
Celeste_P8_set_call_func(pico8emu);
|
||||
|
||||
//for reset
|
||||
void* initial_game_state = SDL_malloc(Celeste_P8_get_state_size());
|
||||
initial_game_state = SDL_malloc(Celeste_P8_get_state_size());
|
||||
if (initial_game_state) Celeste_P8_save_state(initial_game_state);
|
||||
|
||||
if (TAS) {
|
||||
|
@ -324,150 +333,15 @@ int main(int argc, char** argv) {
|
|||
|
||||
printf("ready\n");
|
||||
|
||||
void* game_state = NULL;
|
||||
Mix_Music* game_state_music = NULL;
|
||||
|
||||
_Bool running = 1;
|
||||
_Bool paused = 0;
|
||||
while (running) {
|
||||
Uint8* kbstate = SDL_GetKeyState(NULL);
|
||||
|
||||
static int reset_input_timer = 0;
|
||||
//hold F9 (select+start+y) to reset
|
||||
if (initial_game_state != NULL
|
||||
#ifdef _3DS
|
||||
&& kbstate[SDLK_LSHIFT] && kbstate[SDLK_ESCAPE] && kbstate[SDLK_F11]
|
||||
#ifndef EMSCRIPTEN
|
||||
while (running) mainLoop();
|
||||
#else
|
||||
&& kbstate[SDLK_F9]
|
||||
#include <emscripten.h>
|
||||
//FIXME: this assumes that the display refreshes at 60Hz
|
||||
emscripten_set_main_loop(mainLoop, 0, 0);
|
||||
emscripten_set_main_loop_timing(EM_TIMING_RAF, 2);
|
||||
return 0;
|
||||
#endif
|
||||
) {
|
||||
reset_input_timer++;
|
||||
if (reset_input_timer >= 30) {
|
||||
reset_input_timer=0;
|
||||
//reset
|
||||
OSDset("reset");
|
||||
paused = 0;
|
||||
Celeste_P8_load_state(initial_game_state);
|
||||
Mix_HaltChannel(-1);
|
||||
Mix_HaltMusic();
|
||||
Celeste_P8_init();
|
||||
}
|
||||
} else reset_input_timer = 0;
|
||||
SDL_Event ev;
|
||||
while (SDL_PollEvent(&ev)) switch (ev.type) {
|
||||
case SDL_QUIT: running = 0; break;
|
||||
case SDL_KEYDOWN: {
|
||||
/*if (ev.key.keysym.sym == SDLK_ESCAPE) {
|
||||
running = 0;
|
||||
break;
|
||||
} else*/ if (ev.key.keysym.sym == SDLK_ESCAPE) { //do pause
|
||||
if (paused) Mix_Resume(-1), Mix_ResumeMusic(); else Mix_Pause(-1), Mix_PauseMusic();
|
||||
paused = !paused;
|
||||
break;
|
||||
} else if (ev.key.keysym.sym == SDLK_F11 && !(kbstate[SDLK_LSHIFT] || kbstate[SDLK_ESCAPE])) {
|
||||
if (SDL_WM_ToggleFullScreen(screen)) { //this doesn't work on windows..
|
||||
OSDset("toggle fullscreen");
|
||||
}
|
||||
break;
|
||||
} else if (0 && ev.key.keysym.sym == SDLK_5) {
|
||||
Celeste_P8__DEBUG();
|
||||
break;
|
||||
} else if (ev.key.keysym.sym == SDLK_s && kbstate[SDLK_LSHIFT]) { //save state
|
||||
game_state = game_state ? game_state : SDL_malloc(Celeste_P8_get_state_size());
|
||||
if (game_state) {
|
||||
OSDset("save state");
|
||||
Celeste_P8_save_state(game_state);
|
||||
game_state_music = current_music;
|
||||
}
|
||||
break;
|
||||
} else if (ev.key.keysym.sym == SDLK_d && kbstate[SDLK_LSHIFT]) { //load state
|
||||
if (game_state) {
|
||||
OSDset("load state");
|
||||
if (paused) paused = 0, Mix_Resume(-1), Mix_ResumeMusic();
|
||||
Celeste_P8_load_state(game_state);
|
||||
if (current_music != game_state_music) {
|
||||
Mix_HaltMusic();
|
||||
current_music = game_state_music;
|
||||
if (game_state_music) Mix_PlayMusic(game_state_music, -1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else if ( //toggle screenshake (e / L+R)
|
||||
#ifdef _3DS
|
||||
(ev.key.keysym.sym == SDLK_d && kbstate[SDLK_s]) || (ev.key.keysym.sym == SDLK_s && kbstate[SDLK_d])
|
||||
#else
|
||||
ev.key.keysym.sym == SDLK_e
|
||||
#endif
|
||||
) {
|
||||
enable_screenshake = !enable_screenshake;
|
||||
OSDset("screenshake: %s", enable_screenshake ? "on" : "off");
|
||||
}
|
||||
//else: fallthrough
|
||||
}
|
||||
case SDL_KEYUP: {
|
||||
int down = ev.type == SDL_KEYDOWN;
|
||||
int b = -1;
|
||||
switch (ev.key.keysym.sym) {
|
||||
case SDLK_LEFT: b = 0; break;
|
||||
case SDLK_RIGHT: b = 1; break;
|
||||
case SDLK_UP: b = 2; break;
|
||||
case SDLK_DOWN: b = 3; break;
|
||||
case SDLK_z: case SDLK_c: case SDLK_n: case SDLK_a:
|
||||
b = 4; break;
|
||||
case SDLK_x: case SDLK_v: case SDLK_m: case SDLK_b:
|
||||
b = 5; break;
|
||||
default: break;
|
||||
}
|
||||
if (!TAS && b >= 0) {
|
||||
if (down) buttons_state |= (1<<b);
|
||||
else buttons_state &= ~(1<<b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TAS && !paused) {
|
||||
static int t = 0;
|
||||
t++;
|
||||
if (t==1) buttons_state = 1<<4;
|
||||
else if (t > 80) {
|
||||
fscanf(TAS, "%d,", &buttons_state);
|
||||
} else buttons_state = 0;
|
||||
}
|
||||
|
||||
if (paused) {
|
||||
const int x0 = PICO8_W/2-3*4, y0 = 8;
|
||||
|
||||
p8_rectfill(x0-1,y0-1, 6*4+x0+1,6+y0+1, 6);
|
||||
p8_rectfill(x0,y0, 6*4+x0,6+y0, 0);
|
||||
p8_print("paused", x0+1, y0+1, 7);
|
||||
} else {
|
||||
Celeste_P8_update();
|
||||
Celeste_P8_draw();
|
||||
}
|
||||
OSDdraw();
|
||||
|
||||
/*for (int i = 0 ; i < 16;i++) {
|
||||
SDL_Rect rc = {i*8*scale, 0, 8*scale, 4*scale};
|
||||
SDL_FillRect(screen, &rc, i);
|
||||
}*/
|
||||
|
||||
SDL_Flip(screen);
|
||||
|
||||
#ifdef _3DS //using SDL_DOUBLEBUF for videomode makes it so SDL_Flip waits for Vsync; so we dont have to delay manually
|
||||
SDL_Delay(1);
|
||||
#else
|
||||
static int t = 0;
|
||||
static unsigned frame_start = 0;
|
||||
unsigned frame_end = SDL_GetTicks();
|
||||
unsigned frame_time = frame_end-frame_start;
|
||||
static const unsigned target_millis = 1000/30;
|
||||
if (frame_time < target_millis) {
|
||||
SDL_Delay((target_millis - frame_time) + (t & 1));
|
||||
}
|
||||
t++;
|
||||
frame_start = SDL_GetTicks();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (game_state) SDL_free(game_state);
|
||||
if (initial_game_state) SDL_free(initial_game_state);
|
||||
|
@ -487,6 +361,148 @@ int main(int argc, char** argv) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mainLoop(void) {
|
||||
const Uint8* kbstate = SDL_GetKeyState(NULL);
|
||||
|
||||
static int reset_input_timer = 0;
|
||||
//hold F9 (select+start+y) to reset
|
||||
if (initial_game_state != NULL
|
||||
#ifdef _3DS
|
||||
&& kbstate[SDLK_LSHIFT] && kbstate[SDLK_ESCAPE] && kbstate[SDLK_F11]
|
||||
#else
|
||||
&& kbstate[SDLK_F9]
|
||||
#endif
|
||||
) {
|
||||
reset_input_timer++;
|
||||
if (reset_input_timer >= 30) {
|
||||
reset_input_timer=0;
|
||||
//reset
|
||||
OSDset("reset");
|
||||
paused = 0;
|
||||
Celeste_P8_load_state(initial_game_state);
|
||||
Mix_HaltChannel(-1);
|
||||
Mix_HaltMusic();
|
||||
Celeste_P8_init();
|
||||
}
|
||||
} else reset_input_timer = 0;
|
||||
SDL_Event ev;
|
||||
while (SDL_PollEvent(&ev)) switch (ev.type) {
|
||||
case SDL_QUIT: running = 0; break;
|
||||
case SDL_KEYDOWN: {
|
||||
/*if (ev.key.keysym.sym == SDLK_ESCAPE) {
|
||||
running = 0;
|
||||
break;
|
||||
} else*/ if (ev.key.keysym.sym == SDLK_ESCAPE) { //do pause
|
||||
if (paused) Mix_Resume(-1), Mix_ResumeMusic(); else Mix_Pause(-1), Mix_PauseMusic();
|
||||
paused = !paused;
|
||||
break;
|
||||
} else if (ev.key.keysym.sym == SDLK_F11 && !(kbstate[SDLK_LSHIFT] || kbstate[SDLK_ESCAPE])) {
|
||||
if (SDL_WM_ToggleFullScreen(screen)) { //this doesn't work on windows..
|
||||
OSDset("toggle fullscreen");
|
||||
}
|
||||
screen = SDL_GetVideoSurface();
|
||||
break;
|
||||
} else if (0 && ev.key.keysym.sym == SDLK_5) {
|
||||
Celeste_P8__DEBUG();
|
||||
break;
|
||||
} else if (ev.key.keysym.sym == SDLK_s && kbstate[SDLK_LSHIFT]) { //save state
|
||||
game_state = game_state ? game_state : SDL_malloc(Celeste_P8_get_state_size());
|
||||
if (game_state) {
|
||||
OSDset("save state");
|
||||
Celeste_P8_save_state(game_state);
|
||||
game_state_music = current_music;
|
||||
}
|
||||
break;
|
||||
} else if (ev.key.keysym.sym == SDLK_d && kbstate[SDLK_LSHIFT]) { //load state
|
||||
if (game_state) {
|
||||
OSDset("load state");
|
||||
if (paused) paused = 0, Mix_Resume(-1), Mix_ResumeMusic();
|
||||
Celeste_P8_load_state(game_state);
|
||||
if (current_music != game_state_music) {
|
||||
Mix_HaltMusic();
|
||||
current_music = game_state_music;
|
||||
if (game_state_music) Mix_PlayMusic(game_state_music, -1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else if ( //toggle screenshake (e / L+R)
|
||||
#ifdef _3DS
|
||||
(ev.key.keysym.sym == SDLK_d && kbstate[SDLK_s]) || (ev.key.keysym.sym == SDLK_s && kbstate[SDLK_d])
|
||||
#else
|
||||
ev.key.keysym.sym == SDLK_e
|
||||
#endif
|
||||
) {
|
||||
enable_screenshake = !enable_screenshake;
|
||||
OSDset("screenshake: %s", enable_screenshake ? "on" : "off");
|
||||
}
|
||||
//else: fallthrough
|
||||
}
|
||||
case SDL_KEYUP: {
|
||||
int down = ev.type == SDL_KEYDOWN;
|
||||
int b = -1;
|
||||
switch (ev.key.keysym.sym) {
|
||||
case SDLK_LEFT: b = 0; break;
|
||||
case SDLK_RIGHT: b = 1; break;
|
||||
case SDLK_UP: b = 2; break;
|
||||
case SDLK_DOWN: b = 3; break;
|
||||
case SDLK_z: case SDLK_c: case SDLK_n: case SDLK_a:
|
||||
b = 4; break;
|
||||
case SDLK_x: case SDLK_v: case SDLK_m: case SDLK_b:
|
||||
b = 5; break;
|
||||
default: break;
|
||||
}
|
||||
if (!TAS && b >= 0) {
|
||||
if (down) buttons_state |= (1<<b);
|
||||
else buttons_state &= ~(1<<b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TAS && !paused) {
|
||||
static int t = 0;
|
||||
t++;
|
||||
if (t==1) buttons_state = 1<<4;
|
||||
else if (t > 80) {
|
||||
fscanf(TAS, "%d,", &buttons_state);
|
||||
} else buttons_state = 0;
|
||||
}
|
||||
|
||||
if (paused) {
|
||||
const int x0 = PICO8_W/2-3*4, y0 = 8;
|
||||
|
||||
p8_rectfill(x0-1,y0-1, 6*4+x0+1,6+y0+1, 6);
|
||||
p8_rectfill(x0,y0, 6*4+x0,6+y0, 0);
|
||||
p8_print("paused", x0+1, y0+1, 7);
|
||||
} else {
|
||||
Celeste_P8_update();
|
||||
Celeste_P8_draw();
|
||||
}
|
||||
OSDdraw();
|
||||
|
||||
/*for (int i = 0 ; i < 16;i++) {
|
||||
SDL_Rect rc = {i*8*scale, 0, 8*scale, 4*scale};
|
||||
SDL_FillRect(screen, &rc, i);
|
||||
}*/
|
||||
|
||||
SDL_Flip(screen);
|
||||
|
||||
#if defined(_3DS) /*using SDL_DOUBLEBUF for videomode makes it so SDL_Flip waits for Vsync; so we dont have to delay manually*/ \
|
||||
|| defined(EMSCRIPTEN) //emscripten_set_main_loop already sets the fps
|
||||
SDL_Delay(1);
|
||||
#else
|
||||
static int t = 0;
|
||||
static unsigned frame_start = 0;
|
||||
unsigned frame_end = SDL_GetTicks();
|
||||
unsigned frame_time = frame_end-frame_start;
|
||||
static const unsigned target_millis = 1000/30;
|
||||
if (frame_time < target_millis) {
|
||||
SDL_Delay((target_millis - frame_time) + (t & 1));
|
||||
}
|
||||
t++;
|
||||
frame_start = SDL_GetTicks();
|
||||
#endif
|
||||
}
|
||||
|
||||
static int gettileflag(int, int);
|
||||
static void p8_line(int,int,int,int,unsigned char);
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
#include<assert.h>
|
||||
|
||||
//dummy values
|
||||
enum {
|
||||
SDL_PHYSPAL = 1,
|
||||
SDL_LOGPAL = 2,
|
||||
SDL_SRCCOLORKEY = 4,
|
||||
SDL_HWPALETTE = 8,
|
||||
};
|
||||
static SDL_Surface* sdl2_screen = NULL;
|
||||
static SDL_Window* sdl2_window = NULL;
|
||||
static SDL_Surface *SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) {
|
||||
if (!sdl2_window) {
|
||||
sdl2_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 0);
|
||||
if (!sdl2_window) return NULL;
|
||||
}
|
||||
sdl2_screen = SDL_GetWindowSurface(sdl2_window);
|
||||
assert(sdl2_screen && sdl2_screen->format->BitsPerPixel == bpp);
|
||||
return sdl2_screen;
|
||||
}
|
||||
static void SDL_SetPalette(SDL_Surface* surf, int flag, SDL_Color* pal, int begin, int count) {
|
||||
(void)surf;
|
||||
(void)flag;
|
||||
(void)pal;
|
||||
(void)begin;
|
||||
(void)count;
|
||||
}
|
||||
static void SDL_WM_SetCaption(const char* title, const char* icon) {
|
||||
assert(sdl2_window != NULL);
|
||||
SDL_SetWindowTitle(sdl2_window, title);
|
||||
(void)icon;
|
||||
}
|
||||
static int SDL_WM_ToggleFullScreen(SDL_Surface* screen) {
|
||||
assert(screen == sdl2_screen);
|
||||
assert(sdl2_window != NULL);
|
||||
static int fullscreen = 0;
|
||||
fullscreen = !fullscreen;
|
||||
return SDL_SetWindowFullscreen(sdl2_window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0) == 0;
|
||||
}
|
||||
static SDL_Surface* SDL_GetVideoSurface(void) {
|
||||
return sdl2_screen = SDL_GetWindowSurface(sdl2_window);
|
||||
}
|
||||
static void SDL_Flip_(SDL_Surface* screen) {
|
||||
assert(screen == sdl2_screen);
|
||||
assert(sdl2_window != NULL);
|
||||
SDL_UpdateWindowSurface(sdl2_window);
|
||||
}
|
||||
//hack because for some reason SDL_GetVideoSurface right after SDL_SetWindowFullscreen isn't enough to get the screen back(?) (i think its because of a resize event)
|
||||
#define SDL_Flip(ref_screen) SDL_Flip_((ref_screen = SDL_GetVideoSurface()))
|
||||
|
||||
#define SDL_GetKeyState SDL_GetKeyboardState
|
||||
//the above function now returns array indexed by scancodes, so we need to use those constants
|
||||
#define SDLK_F9 SDL_SCANCODE_F9
|
||||
#define SDLK_ESCAPE SDL_SCANCODE_ESCAPE
|
||||
#define SDLK_F11 SDL_SCANCODE_F11
|
||||
#define SDLK_LSHIFT SDL_SCANCODE_LSHIFT
|
||||
#define SDLK_LEFT SDL_SCANCODE_LEFT
|
||||
#define SDLK_RIGHT SDL_SCANCODE_RIGHT
|
||||
#define SDLK_UP SDL_SCANCODE_UP
|
||||
#define SDLK_DOWN SDL_SCANCODE_DOWN
|
||||
#define SDLK_5 SDL_SCANCODE_5
|
||||
#define SDLK_s SDL_SCANCODE_S
|
||||
#define SDLK_d SDL_SCANCODE_D
|
||||
#define SDLK_c SDL_SCANCODE_C
|
||||
#define SDLK_x SDL_SCANCODE_X
|
||||
#define SDLK_n SDL_SCANCODE_N
|
||||
#define SDLK_a SDL_SCANCODE_A
|
||||
#define SDLK_b SDL_SCANCODE_B
|
||||
#define SDLK_z SDL_SCANCODE_Z
|
||||
#define SDLK_e SDL_SCANCODE_E
|
||||
#define SDLK_v SDL_SCANCODE_V
|
||||
|
||||
#define sym scancode // SDL_Keysym.sym -> SDL_Keysym.scancode
|
Loading…
Reference in New Issue