Blame src/world.c

3f9996
a20939
#include <time.h></time.h>
a20939
f63775
#include <sdl.h></sdl.h>
3f9996
3f9996
#include "private.h"
3e7c5f
#include "drawing.h"
3f9996
#include "world.h"
3f9996
3f9996
a20939
static int sdlInitialized = 0;
87fe10
static SDL_Window *window;
87fe10
static SDL_GLContext context;
1015c5
3f9996
static int started;
3f9996
static int stopped;
8bc1f1
static int firstFrame = TRUE;
3f9996
static unsigned long long frameCount;
a20939
static unsigned long long elapsedTimeUs;
a20939
static unsigned long long elapsedTimeSinceLastFrameUs;
a20939
static unsigned int prevFrameTimeMs;
3f9996
534343
HeliDialog dialog;
87fe10
HeliGlStencilOpSeparatePtr heliGlStencilOpSeparatePtr = NULL;
534343
3f9996
static Callback initCallback;
3f9996
static Callback drawCallback;
1015c5
static Callback deinitCallback;
3f9996
534343
static const int minWidth = 200;
534343
static const int minHeight = 200;
534343
ba9f06
static int width = 512;
ba9f06
static int height = 512;
8eb855
static int resizable;
8eb855
static char title[1000];
8eb855
static int titleSize = (int)(sizeof(title)/sizeof(*title));
dba3fc
static double minFPS = HELI_DEFAULT_FPS;
dba3fc
static double maxFPS = HELI_DEFAULT_FPS;
dba3fc
static double frameTime = 1.0/HELI_DEFAULT_FPS;
3f9996
1015c5
static HeliArray keyEvents[6];
1015c5
static int keyEventsCount = (int)(sizeof(keyEvents)/sizeof(*keyEvents));
3f9996
3f9996
static int mouseMovedInFrame;
3f9996
static double _mouseX;
3f9996
static double _mouseY;
3f9996
3f9996
3f9996
1015c5
int keyEventGetCount(KeyEvent mode)
1015c5
	{ return (int)mode >= 0 && (int)mode <= keyEventsCount ? keyEvents[mode].count : 0; }
1015c5
const char *keyEventGet(KeyEvent mode, int i)
1015c5
	{ return (int)mode >= 0 && (int)mode <= keyEventsCount ? heliArrayGetValue(&keyEvents[mode], i) : NULL; }
1015c5
1015c5
static int keyEventCheck(KeyEvent mode, const char *code) {
1015c5
	int count = keyEventGetCount(mode);
1015c5
	for(int i = 0; i < count; ++i)
1015c5
		if (strcmp(keyEventGet(mode, i), code) == 0)
1015c5
			return TRUE;
1015c5
	return FALSE;
1015c5
}
1015c5
1015c5
static void keyEventAdd(KeyEvent mode, const char *code) {
1015c5
	if ((int)mode >= 0 && mode <= (int)keyEventsCount && !keyEventCheck(mode, code))
1015c5
		heliArrayInsert(&keyEvents[mode], -1, heliStringCopy(code), &free);
1015c5
}
1015c5
1015c5
static void keyEventRemove(KeyEvent mode, const char *code) {
1015c5
	if ((int)mode >= 0 && mode <= (int)keyEventsCount)
1015c5
		for(int i = keyEvents[mode].count-1; i >= 0; --i)
1015c5
			if (strcmp(keyEvents[mode].items[i].value, code) == 0)
1015c5
				heliArrayRemove(&keyEvents[mode], i);
1015c5
}
3f9996
1015c5
	
8535a3
int keyDown(const char *code)
1015c5
	{ return keyEventCheck(KEYEVENT_KEY_DOWN, code); }
07b70f
int keyWentDown(const char *code)
1015c5
	{ return keyEventCheck(KEYEVENT_KEY_WENTDOWN, code); }
07b70f
int keyWentUp(const char *code)
1015c5
	{ return keyEventCheck(KEYEVENT_KEY_WENTUP, code); }
3f9996
3f9996
int mouseDidMove()
3f9996
	{ return mouseMovedInFrame; }
8535a3
int mouseDown(const char *code)
1015c5
	{ return keyEventCheck(KEYEVENT_MOUSE_DOWN, code); }
8535a3
int mouseWentDown(const char *code)
1015c5
	{ return keyEventCheck(KEYEVENT_MOUSE_WENTDOWN, code); }
8535a3
int mouseWentUp(const char *code)
1015c5
	{ return keyEventCheck(KEYEVENT_MOUSE_WENTUP, code); }
3f9996
double mouseX()
3f9996
	{ return _mouseX; }
3f9996
double mouseY()
3f9996
	{ return _mouseY; }
3f9996
8bc1f1
double transformedMouseX() {
8bc1f1
	double x = mouseX(), y = mouseY();
8bc1f1
	heliGLBackTransform(&x, &y);
8bc1f1
	return x;
8bc1f1
}
8bc1f1
8bc1f1
double transformedMouseY() {
8bc1f1
	double x = mouseX(), y = mouseY();
8bc1f1
	heliGLBackTransform(&x, &y);
8bc1f1
	return y;
8bc1f1
}
8bc1f1
981405
static void resize(int w, int h) {
534343
	w = w > minWidth ? w : minWidth;
534343
	h = h > minHeight ? h : minHeight;
59dae5
	if (width != w || height != h) {
a20939
		width = w;
a20939
		height = h;
a20939
		if (started && !stopped && window)
a20939
			SDL_SetWindowSize(window, width, height);
981405
	}
981405
}
8bc1f1
3f9996
int worldGetWidth()
3f9996
	{ return width; }
981405
void worldSetWidth(int w)
981405
	{ resize(w, height); }
3f9996
07b70f
int worldGetHeight()
3f9996
	{ return height; }
981405
void worldSetHeight(int h)
981405
	{ resize(width, h); }
3f9996
8eb855
int worldGetResizable()
8eb855
	{ return resizable; }
8eb855
void worldSetResizable(int r) {
8eb855
	if (resizable == r) return;
8eb855
	resizable = r ? TRUE : FALSE;
8eb855
	if (started && !stopped && window)
a20939
		SDL_SetWindowResizable(window, resizable);
8eb855
}
8eb855
8eb855
const char* worldGetTitle()
8eb855
	{ return title; }
8eb855
void worldSetTitle(const char *t) {
8eb855
	int changed = FALSE;
8eb855
	for(int i = 0; i < titleSize-1; ++i) {
8eb855
		if (title[i] != t[i]) changed = TRUE;
8eb855
		title[i] = t[i];
8eb855
		if (!t[i]) break;
8eb855
	}
8eb855
	if (changed && started && !stopped && window)
a20939
		SDL_SetWindowTitle(window, title);
8eb855
}
8eb855
8bc1f1
double worldGetMinFrameRate()
8bc1f1
	{ return minFPS; }
8bc1f1
double worldGetMaxFrameRate()
8bc1f1
	{ return minFPS; }
8bc1f1
void worldSetFrameRateEx(double minFrameRate, double maxFrameRate) {
dba3fc
	if (!(minFrameRate > HELI_MIN_FPS)) minFrameRate = HELI_MIN_FPS;
dba3fc
	if (!(minFrameRate < HELI_MAX_FPS)) minFrameRate = HELI_MAX_FPS;
dba3fc
	if (!(maxFrameRate > HELI_MIN_FPS)) maxFrameRate = HELI_MIN_FPS;
dba3fc
	if (!(maxFrameRate < HELI_MAX_FPS)) maxFrameRate = HELI_MAX_FPS;
8bc1f1
	if (minFrameRate > maxFrameRate) minFrameRate = maxFrameRate;
8bc1f1
	minFPS = minFrameRate;
8bc1f1
	maxFPS = maxFrameRate;
3f9996
}
8bc1f1
void worldSetFrameRate(double frameRate)
8bc1f1
	{ worldSetFrameRateEx(frameRate, frameRate); }
8bc1f1
8bc1f1
double worldGetFrameTime()
8bc1f1
	{ return frameTime; }
8bc1f1
8bc1f1
double worldGetMinFrameRate();
8bc1f1
void worldSetMinFrameRate(double minFrameRate);
8bc1f1
8bc1f1
double worldGetMaxFrameRate();
8bc1f1
void worldSetMaxFrameRate(double maxFrameRate);
8bc1f1
8bc1f1
void worldSetFrameRate(double frameRate);
8bc1f1
8bc1f1
double worldGetFrameTime();
3f9996
981405
3f9996
int worldGetFrameCount()
3f9996
	{ return (int)frameCount; }
3f9996
double worldGetSeconds()
a20939
	{ return started ? elapsedTimeUs*1e-6 : 0.0; }
3f9996
3f9996
void worldSetInit(Callback init)
3f9996
	{ initCallback = init; }
3f9996
void worldSetDraw(Callback draw)
3f9996
	{ drawCallback = draw; }
1015c5
void worldSetDeinit(Callback deinit)
1015c5
	{ deinitCallback = deinit; }
3f9996
void worldStop()
3f9996
	{ if (started) stopped = TRUE; }
3f9996
3f9996
3954ba
void messageBox(const char *message) {
3954ba
	SDL_ShowSimpleMessageBox(
3954ba
		SDL_MESSAGEBOX_INFORMATION,
3954ba
		title,
3954ba
		message,
3954ba
		window );
534343
	prevFrameTimeMs = SDL_GetTicks();
534343
}
534343
f8dca4
int questionBox(const char *question, const char *answer0, const char *answer1) {
f8dca4
	SDL_MessageBoxButtonData buttons[2] = {};
f8dca4
	
f8dca4
	buttons[0].buttonid = 1;
f8dca4
	buttons[0].flags = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
f8dca4
	buttons[0].text = answer1;
f8dca4
f8dca4
	buttons[1].buttonid = 0;
f8dca4
	buttons[1].flags = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
f8dca4
	buttons[1].text = answer0;
f8dca4
	
f8dca4
	SDL_MessageBoxData data = {};
f8dca4
	data.flags = SDL_MESSAGEBOX_INFORMATION;
f8dca4
	data.window = window;
f8dca4
	data.title = title;
f8dca4
	data.message = question;
f8dca4
	data.buttons = buttons;
f8dca4
	data.numbuttons = 2;
f8dca4
	
f8dca4
	int buttonid = 0;;
f8dca4
	SDL_ShowMessageBox(&data, &buttonid);
f8dca4
	prevFrameTimeMs = SDL_GetTicks();
f8dca4
	
f8dca4
	return buttonid;
f8dca4
}
f8dca4
f8dca4
int questionBox3(const char* question, const char* answer0, const char* answer1, const char* answer2) {
f8dca4
	SDL_MessageBoxButtonData buttons[3] = {};
f8dca4
	
f8dca4
	buttons[0].buttonid = 2;
f8dca4
	buttons[0].flags = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
f8dca4
	buttons[0].text = answer2;
f8dca4
	
f8dca4
	buttons[1].buttonid = 1;
f8dca4
	buttons[1].text = answer1;
f8dca4
f8dca4
	buttons[2].buttonid = 0;
f8dca4
	buttons[2].flags = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
f8dca4
	buttons[2].text = answer0;
f8dca4
	
f8dca4
	SDL_MessageBoxData data = {};
f8dca4
	data.flags = SDL_MESSAGEBOX_INFORMATION;
f8dca4
	data.window = window;
f8dca4
	data.title = title;
f8dca4
	data.message = question;
f8dca4
	data.buttons = buttons;
f8dca4
	data.numbuttons = 3;
f8dca4
	
f8dca4
	int buttonid = 0;;
f8dca4
	SDL_ShowMessageBox(&data, &buttonid);
f8dca4
	prevFrameTimeMs = SDL_GetTicks();
f8dca4
	
f8dca4
	return buttonid;
f8dca4
}
f8dca4
dba3fc
int askText(const char *question, char *answer, int maxAnswerSize)
dba3fc
	{ return askTextEx(question, answer, maxAnswerSize, FALSE, FALSE); }
534343
534343
534343
static void resetEvents() {
534343
	dialog.newText[0] = 0;
534343
	heliArrayClear(&keyEvents[KEYEVENT_KEY_WENTDOWN]);
534343
	heliArrayClear(&keyEvents[KEYEVENT_KEY_WENTUP]);
534343
	heliArrayClear(&keyEvents[KEYEVENT_MOUSE_WENTDOWN]);
534343
	heliArrayClear(&keyEvents[KEYEVENT_MOUSE_WENTUP]);
3954ba
}
3954ba
3954ba
a20939
static void draw() {
a20939
	unsigned int currentFrameTimeMs = SDL_GetTicks();
8bc1f1
	unsigned long long deltaUs = firstFrame ? 0 : (currentFrameTimeMs - prevFrameTimeMs)*1000ull;
a20939
	prevFrameTimeMs = currentFrameTimeMs;
d1f083
	
8bc1f1
	double actualMinFPS = minFPS, actualMaxFPS = maxFPS;
8bc1f1
	if (dialog.shown) { actualMinFPS = 1, actualMaxFPS = 100; }
8bc1f1
	
8bc1f1
	unsigned long long minTimeStepUs = (unsigned long long)round(1e6/actualMaxFPS);
8bc1f1
	unsigned long long maxTimeStepUs = (unsigned long long)round(1e6/actualMinFPS);
8bc1f1
	elapsedTimeSinceLastFrameUs += deltaUs;
8bc1f1
	if (elapsedTimeSinceLastFrameUs > 2000000)
8bc1f1
		elapsedTimeSinceLastFrameUs = 2000000;
8bc1f1
	if (firstFrame || elapsedTimeSinceLastFrameUs >= minTimeStepUs) {
8bc1f1
		unsigned long long encountedTimeUs = elapsedTimeSinceLastFrameUs;
8bc1f1
		if (encountedTimeUs > maxTimeStepUs) encountedTimeUs = maxTimeStepUs;
8bc1f1
		double dt = encountedTimeUs*1e-6;
8bc1f1
		
8bc1f1
		if (!firstFrame) elapsedTimeSinceLastFrameUs -= encountedTimeUs;
8bc1f1
		
8bc1f1
		if (!dialog.shown) {
8bc1f1
			elapsedTimeUs += encountedTimeUs;
8bc1f1
			++frameCount;
8bc1f1
			frameTime = firstFrame ? 1/maxFPS : dt;
dba3fc
			heliAnimationUpdate(dt);
8bc1f1
			heliSpriteUpdate(dt);
8bc1f1
		}
09c823
		heliSoundUpdate();
650f35
		
8bc1f1
		firstFrame = FALSE;
8bc1f1
		
a20939
		glViewport(0, 0, width, height);
a20939
		glMatrixMode(GL_PROJECTION);
a20939
		glLoadIdentity();
a20939
		glOrtho(0, width, height, 0, -1.0, 1.0);
a20939
		glMatrixMode(GL_MODELVIEW);
a20939
		
a20939
		heliDrawingPrepareFrame();
8bc1f1
		if (dialog.shown) {
8bc1f1
			heliDialogDraw(&dialog);
8bc1f1
		} else {
8bc1f1
		if (drawCallback) 
8bc1f1
			drawCallback();
8bc1f1
		}
650f35
		
534343
		resetEvents();
a20939
		SDL_GL_SwapWindow(window);
8bc1f1
	}
8bc1f1
	
8bc1f1
	unsigned long long addUs = elapsedTimeSinceLastFrameUs + (SDL_GetTicks() - prevFrameTimeMs)*1000ull;
8bc1f1
	if (addUs < minTimeStepUs) {
8bc1f1
		unsigned long long waitUs = minTimeStepUs - addUs;
8bc1f1
		if (waitUs > 2000)
8bc1f1
			SDL_Delay( (unsigned int)((waitUs + 500)/1000ull) );
650f35
	}
a20939
}
a20939
a20939
a20939
static void deinit() {
87fe10
	heliGlStencilOpSeparatePtr = NULL;
87fe10
	
a20939
	if (context) SDL_GL_DeleteContext(context);
a20939
	context = NULL;
650f35
	
a20939
	if (window) SDL_DestroyWindow(window);
a20939
	window = NULL;
650f35
	
a20939
	if (sdlInitialized) SDL_Quit();
a20939
	sdlInitialized = FALSE;
a20939
}
a20939
a20939
a20939
static int init() {
a20939
	if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
3954ba
		fprintf(stderr, "helianthus: SDL_Init failed\n");
a20939
		deinit();
a20939
		return FALSE;
a20939
	}
a20939
	sdlInitialized = TRUE;
a20939
	
a20939
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
a20939
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
b9c036
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
ba9f06
	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
a20939
	SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
a20939
	SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8);
a20939
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
a20939
	SDL_GL_SetSwapInterval(1);
a20939
	
a20939
	unsigned int flags = SDL_WINDOW_OPENGL;
a20939
	if (resizable) flags |= SDL_WINDOW_RESIZABLE;
a20939
	window = SDL_CreateWindow(
a20939
		title,
a20939
		SDL_WINDOWPOS_CENTERED,
a20939
		SDL_WINDOWPOS_CENTERED,
a20939
		width,
a20939
		height,
a20939
		flags );
a20939
	
a20939
	if (!window) {
3954ba
		fprintf(stderr, "helianthus: cannot create window: %s\n", SDL_GetError());
a20939
		SDL_ClearError();
a20939
		deinit();
a20939
		return FALSE;
a20939
	}
a20939
	
534343
	SDL_SetWindowMinimumSize(window, minWidth, minHeight);
534343
	
a20939
	context = SDL_GL_CreateContext(window);
a20939
	if (!context) {
3954ba
		fprintf(stderr, "helianthus: cannot create OpenGL context: %s\n", SDL_GetError());
a20939
		SDL_ClearError();
a20939
		deinit();
a20939
		return FALSE;
e034e4
	}
650f35
	
87fe10
	heliGlStencilOpSeparatePtr = SDL_GL_GetProcAddress("glStencilOpSeparate");
87fe10
	
3f9996
	return TRUE;
3f9996
}
3f9996
a20939
a20939
static void handleEvent(SDL_Event *e) {
a20939
	if (e->type == SDL_QUIT) {
a20939
		stopped = TRUE;
a20939
	} else
a20939
	if (e->type == SDL_WINDOWEVENT) {
a20939
		if (e->window.event == SDL_WINDOWEVENT_CLOSE) {
a20939
			stopped = TRUE;
a20939
		} else
a20939
		if (e->window.event == SDL_WINDOWEVENT_RESIZED) {
a20939
			width = e->window.data1;
a20939
			height = e->window.data2;
a20939
		} else
a20939
		if (e->window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
1015c5
			int count = keyEventGetCount(KEYEVENT_KEY_DOWN);
1015c5
			for(int i = 0; i < count; ++i)
1015c5
				keyEventAdd(KEYEVENT_KEY_WENTUP, keyEventGet(KEYEVENT_KEY_DOWN, i));
1015c5
			heliArrayClear(&keyEvents[KEYEVENT_KEY_DOWN]);
a20939
			
a20939
			count = keyEventGetCount(KEYEVENT_MOUSE_DOWN);
a20939
			for(int i = 0; i < count; ++i)
a20939
				keyEventAdd(KEYEVENT_MOUSE_WENTUP, keyEventGet(KEYEVENT_MOUSE_DOWN, i));
a20939
			heliArrayClear(&keyEvents[KEYEVENT_MOUSE_DOWN]);
1015c5
		}
a20939
	} else
a20939
	if (e->type == SDL_KEYDOWN || e->type == SDL_KEYUP) {
a20939
		const char *keynameOrig = SDL_GetKeyName(e->key.keysym.sym);
a20939
		if (keynameOrig && *keynameOrig) {
a20939
			char *keyname = heliStringCopy(keynameOrig);
a20939
			heliLowercase(keyname);
a20939
			if (e->type == SDL_KEYDOWN) {
a20939
				keyEventAdd(KEYEVENT_KEY_DOWN, keyname);
a20939
				keyEventAdd(KEYEVENT_KEY_WENTDOWN, keyname);
a20939
			} else {
a20939
				keyEventRemove(KEYEVENT_KEY_DOWN, keyname);
a20939
				keyEventAdd(KEYEVENT_KEY_WENTUP, keyname);
a20939
			}
a20939
			free(keyname);
a20939
		}
a20939
	} else
a20939
	if (e->type == SDL_MOUSEBUTTONDOWN || e->type == SDL_MOUSEBUTTONUP) {
a20939
		char *button = NULL;
a20939
		switch(e->button.button) {
a20939
			case SDL_BUTTON_LEFT:   button = "left";   break;
a20939
			case SDL_BUTTON_MIDDLE: button = "middle"; break;
a20939
			case SDL_BUTTON_RIGHT:  button = "right";  break;
a20939
			default: break;
a20939
		}
a20939
		if (button) {
a20939
			if (e->type == SDL_MOUSEBUTTONDOWN) {
a20939
				keyEventAdd(KEYEVENT_MOUSE_DOWN, button);
a20939
				keyEventAdd(KEYEVENT_MOUSE_WENTDOWN, button);
a20939
			} else {
a20939
				keyEventRemove(KEYEVENT_MOUSE_DOWN, button);
a20939
				keyEventAdd(KEYEVENT_MOUSE_WENTUP, button);
a20939
			}
a20939
		}
a20939
		_mouseX = e->button.x;
a20939
		_mouseY = e->button.y;
a20939
	} else
a20939
	if (e->type == SDL_MOUSEMOTION) {
a20939
		_mouseX = e->motion.x;
a20939
		_mouseY = e->motion.y;
534343
	} else
534343
	if (e->type == SDL_TEXTINPUT) {
534343
		if (dialog.shown) {
534343
			int len = strlen(dialog.newText);
534343
			int newlen = strlen(e->text.text);
534343
			int dl = len + newlen + 1 - sizeof(dialog.newText);
534343
			if (dl > 0) newlen -= dl;
534343
			if (newlen > 0) {
534343
				memcpy(dialog.newText + len, e->text.text, newlen);
534343
				dialog.newText[len + newlen] = 0;
534343
			}
534343
		}
534343
	}
534343
}
534343
534343
dba3fc
int askTextEx(const char *question, char *answer, int maxAnswerSize, int multiline, int password) {
534343
	if (maxAnswerSize < 0 || !answer) maxAnswerSize = 0;
534343
	
534343
	memset(&dialog, 0, sizeof(dialog));
534343
	dialog.shown = TRUE;
534343
	dialog.question = question ? question : "";
534343
	dialog.answer = calloc(1, maxAnswerSize + 1);
97fe4e
	dialog.passwordText = calloc(1, maxAnswerSize*4 + 1);
534343
	dialog.maxAnswerSize = maxAnswerSize - 1;
534343
	if (maxAnswerSize > 0) memcpy(dialog.answer, answer, maxAnswerSize);
97fe4e
	dialog.multiline = multiline != 0 && password == 0;
534343
	dialog.password = password != 0;
534343
	dialog.pos = dialog.selPos = strlen(dialog.answer);
534343
	dialog.success = FALSE;
534343
	
534343
	SDL_StartTextInput();
534343
	while(dialog.shown && !stopped) {
534343
		SDL_Event event;
534343
		while (SDL_PollEvent(&event))
534343
			handleEvent(&event);
534343
		draw();
3f9996
	}
534343
	SDL_StopTextInput();
534343
	
dba3fc
	int success = dialog.success;
534343
	if (dialog.success && maxAnswerSize > 0) strcpy(answer, dialog.answer);
534343
	free(dialog.answer);
97fe4e
	free(dialog.passwordText);
534343
	memset(&dialog, 0, sizeof(dialog));
534343
	
534343
	resetEvents();
534343
	heliArrayClear(&keyEvents[KEYEVENT_KEY_DOWN]);
534343
	heliArrayClear(&keyEvents[KEYEVENT_MOUSE_DOWN]);
a20939
	
534343
	prevFrameTimeMs = SDL_GetTicks();
534343
	heliDrawingPrepareFrame();
dba3fc
	
dba3fc
	return success;
3f9996
}
3f9996
a20939
a20939
static void run() {
a20939
	while(!stopped) {
a20939
		SDL_Event event;
a20939
		while (SDL_PollEvent(&event))
a20939
			handleEvent(&event);
a20939
		draw();
a20939
	}
3f9996
}
3f9996
a20939
8eb855
void worldRun() {
3f9996
	if (started) return;
3f9996
	started = TRUE;
3f9996
	stopped = FALSE;
a20939
	
8bc1f1
	firstFrame = TRUE;
3f9996
	frameCount = 0;
a20939
	elapsedTimeUs = 0;
a20939
	elapsedTimeSinceLastFrameUs = 0;
3f9996
	srand(time(NULL));
1015c5
	
a20939
	if (init()) {
a20939
		heliInitialized = TRUE;
a20939
		
3e7c5f
		resetState();
da4619
		heliDoTests();
da4619
		
a20939
		if (initCallback) initCallback();
a20939
		run();
a20939
		if (deinitCallback) deinitCallback();
a20939
		
a20939
		heliArrayClear(&heliObjectsSet);
a20939
		heliSpriteFinish();
a20939
		heliDrawingFinish();
a20939
		heliFontFinish();
a20939
		heliAnimationFinish();
a20939
		heliSoundFinish();
a20939
		heliArrayDestroy(&heliObjectsSet);
a20939
		
a20939
		heliInitialized = FALSE;
a20939
		
a20939
		deinit();
a20939
		
a20939
		for(int i = 0; i < keyEventsCount; ++i)
a20939
			heliArrayDestroy(&keyEvents[i]);
a20939
	}
3f9996
	
3f9996
	started = FALSE;
3f9996
}