diff --git a/assets/main.toy b/assets/main.toy index bb96415..a4ad7e5 100644 --- a/assets/main.toy +++ b/assets/main.toy @@ -1,16 +1,11 @@ -var screenWidth = 1280; -var screenHeight = 720; -var screenCaption = "Hello raylib from Toy!"; - +//"global" variables var frameCounter: Int = 0; var posCounter: Int = 0; -//this runs before the game starts fn onReady() { loadMonsterSprite("parvati", "assets/parvati.png", 32, 32); } -//this runs each frame fn onStep() { frameCounter++; @@ -20,7 +15,10 @@ fn onStep() { } } -//this runs as the game is closing down -fn onFinished() { +fn onClose() { // -} \ No newline at end of file +} + +//example API for the game +initScreen(1280, 720, "Hello raylib from Toy!"); +initLoop(onReady, onStep, onClose); \ No newline at end of file diff --git a/source/main.c b/source/main.c index 83724a1..d80e2d2 100644 --- a/source/main.c +++ b/source/main.c @@ -66,6 +66,101 @@ unsigned char* makeCodeFromSource(const char* source) { return code; } +//static pointers +static Toy_Function* onReady = NULL; +static Toy_Function* onStep = NULL; +static Toy_Function* onClose = NULL; + +//game API definitions +void initScreen(Toy_VM* vm) { + Toy_Value caption = Toy_popStack(&vm->stack); + Toy_Value height = Toy_popStack(&vm->stack); + Toy_Value width = Toy_popStack(&vm->stack); + + if (!TOY_VALUE_IS_STRING(caption) || TOY_VALUE_AS_STRING(caption)->info.type != TOY_STRING_LEAF || !TOY_VALUE_IS_INTEGER(height) || !TOY_VALUE_IS_INTEGER(width)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Bad types found in 'initScreen', exiting" TOY_CC_RESET "\n"); + exit(-1); + } + + //setup raylib + InitWindow(TOY_VALUE_AS_INTEGER(width), TOY_VALUE_AS_INTEGER(height), TOY_VALUE_AS_STRING(caption)->leaf.data); + SetTargetFPS(60); + + Toy_freeValue(width); + Toy_freeValue(height); + Toy_freeValue(caption); +} + +void initLoop(Toy_VM* vm) { + Toy_Value valueOnClose = Toy_popStack(&vm->stack); + Toy_Value valueOnStep = Toy_popStack(&vm->stack); + Toy_Value valueOnReady = Toy_popStack(&vm->stack); + + if (!TOY_VALUE_IS_FUNCTION(valueOnClose) && !TOY_VALUE_IS_NULL(valueOnClose)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Bad types found in 'initLoop', exiting" TOY_CC_RESET "\n"); + exit(-1); + } + + if (!TOY_VALUE_IS_FUNCTION(valueOnStep) && !TOY_VALUE_IS_NULL(valueOnStep)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Bad types found in 'initLoop', exiting" TOY_CC_RESET "\n"); + exit(-1); + } + + if (!TOY_VALUE_IS_FUNCTION(valueOnReady) && !TOY_VALUE_IS_NULL(valueOnReady)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Bad types found in 'initLoop', exiting" TOY_CC_RESET "\n"); + exit(-1); + } + + if (TOY_VALUE_IS_FUNCTION(valueOnReady)) { + if (TOY_VALUE_AS_FUNCTION(valueOnReady)->type != TOY_FUNCTION_CUSTOM) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Bad function found in 'initLoop', exiting (only allows custom functions or null)" TOY_CC_RESET "\n"); + exit(-1); + } + onReady = TOY_VALUE_AS_FUNCTION(valueOnReady); + } + if (TOY_VALUE_IS_FUNCTION(valueOnStep)) { + if (TOY_VALUE_AS_FUNCTION(valueOnStep)->type != TOY_FUNCTION_CUSTOM) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Bad function found in 'initLoop', exiting (only allows custom functions or null)" TOY_CC_RESET "\n"); + exit(-1); + } + onStep = TOY_VALUE_AS_FUNCTION(valueOnStep); + } + if (TOY_VALUE_IS_FUNCTION(valueOnClose)) { + if (TOY_VALUE_AS_FUNCTION(valueOnClose)->type != TOY_FUNCTION_CUSTOM) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Bad function found in 'initLoop', exiting (only allows custom functions or null)" TOY_CC_RESET "\n"); + exit(-1); + } + onClose = TOY_VALUE_AS_FUNCTION(valueOnClose); + } +} + +//game API tools +typedef struct CallbackPairs { + const char* name; + Toy_nativeCallback callback; +} CallbackPairs; + +static CallbackPairs callbackPairs[] = { + {"initScreen", initScreen}, + {"initLoop", initLoop}, + {NULL, NULL}, +}; + +void initGameAPI(Toy_VM* vm) { + if (vm == NULL || vm->scope == NULL || vm->memoryBucket == NULL) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Can't initialize game API, exiting\n" TOY_CC_RESET); + exit(-1); + } + + //declare each function in the global scope + for (int i = 0; callbackPairs[i].name; i++) { + Toy_String* key = Toy_toString(&(vm->memoryBucket), callbackPairs[i].name); + Toy_Function* fn = Toy_createFunctionFromCallback(&(vm->memoryBucket), callbackPairs[i].callback); + Toy_declareScope(vm->scope, key, TOY_VALUE_FUNCTION, TOY_VALUE_FROM_FUNCTION(fn), true); + Toy_freeString(key); + } +} + //player data typedef struct PlayerData { Texture2D texture; @@ -76,8 +171,10 @@ typedef struct PlayerData { PlayerData loadPlayerData(const char* fileName, Rectangle rect) { PlayerData player = {0}; - player.texture = LoadTexture(fileName); - player.rect = rect; + if (IsWindowReady()) { + player.texture = LoadTexture(fileName); + player.rect = rect; + } return player; } @@ -87,7 +184,7 @@ void unloadPlayerData(PlayerData player) { //main file int main() { - //example Toy controlling the window stuff + //load the entry point int size = 0; const char* source = (char*)readFile("assets/main.toy", &size); @@ -96,44 +193,31 @@ int main() { return -1; } - unsigned char* configCode = makeCodeFromSource(source); - unsigned char* invokeOnReady = makeCodeFromSource("onReady();"); - unsigned char* invokeOnStep = makeCodeFromSource("onStep();"); - unsigned char* invokeOnFinished = makeCodeFromSource("onFinished();"); + unsigned char* entryCode = makeCodeFromSource(source); - //build and run the VM + //build and run the VM with APIs Toy_VM vm; Toy_initVM(&vm); - Toy_bindVM(&vm, configCode, NULL); + Toy_bindVM(&vm, entryCode, NULL); + initGameAPI(&vm); + initMonsterAPI(&vm); Toy_runVM(&vm); - Toy_resetVM(&vm, true, false); //leave it in a valid, but unset state - - //extract the settings - Toy_Value* screenWidthPtr = Toy_accessScopeAsPointer(vm.scope, Toy_toString(&vm.memoryBucket, "screenWidth") ); - Toy_Value* screenHeightPtr = Toy_accessScopeAsPointer(vm.scope, Toy_toString(&vm.memoryBucket, "screenHeight") ); - Toy_Value* screenCaptionPtr = Toy_accessScopeAsPointer(vm.scope, Toy_toString(&vm.memoryBucket, "screenCaption") ); - - int screenWidth = screenWidthPtr != NULL && TOY_VALUE_IS_INTEGER(*screenWidthPtr) ? TOY_VALUE_AS_INTEGER(*screenWidthPtr) : 640; - int screenHeight = screenHeightPtr != NULL && TOY_VALUE_IS_INTEGER(*screenHeightPtr) ? TOY_VALUE_AS_INTEGER(*screenHeightPtr) : 480; - const char* screenCaption = screenCaptionPtr != NULL && TOY_VALUE_IS_STRING(*screenCaptionPtr) ? TOY_VALUE_AS_STRING(*screenCaptionPtr)->leaf.data : ""; - - //setup raylib - InitWindow(screenWidth, screenHeight, screenCaption); - SetTargetFPS(60); + Toy_resetVM(&vm, false, false); //leave in a valid, but unset state //load a sprite PlayerData player = loadPlayerData("assets/parvati.png", (Rectangle){0,0,32,32}); - //initialize the monster object pool and run the setup function - initMonsterObjectPool(&vm); - - //setup - Toy_bindVM(&vm, invokeOnReady, NULL); - Toy_runVM(&vm); - Toy_resetVM(&vm, true, false); + //setup and run the given loop functions + if (onReady != NULL) { + Toy_bindVM(&vm, onReady->bytecode.code, onReady->bytecode.parentScope); + Toy_runVM(&vm); + Toy_resetVM(&vm, false, false); + } //onStep is called each frame - Toy_bindVM(&vm, invokeOnStep, NULL); + if (onStep != NULL) { + Toy_bindVM(&vm, onStep->bytecode.code, onStep->bytecode.parentScope); + } while (!WindowShouldClose()) { //input @@ -143,7 +227,7 @@ int main() { if (IsKeyDown(KEY_RIGHT)) player.position.x += 5.0f; //run the onStep function - Toy_runVM(&vm); + Toy_runVM(&vm); //no check needed, empty VMs are skipped //drawing BeginDrawing(); @@ -159,23 +243,26 @@ int main() { } //clear onStep - Toy_resetVM(&vm, true, false); + if (onStep != NULL) { + Toy_resetVM(&vm, false, false); + } //cleanup - Toy_bindVM(&vm, invokeOnFinished, NULL); - Toy_runVM(&vm); - Toy_resetVM(&vm, true, false); + if (onClose != NULL) { + Toy_bindVM(&vm, onClose->bytecode.code, onClose->bytecode.parentScope); + Toy_runVM(&vm); + Toy_resetVM(&vm, false, false); + } - freeMonsterObjectPool(&vm); + freeMonsterAPI(&vm); unloadPlayerData(player); - CloseWindow(); + if (IsWindowReady()) { + CloseWindow(); + } Toy_freeVM(&vm); - free(invokeOnReady); - free(invokeOnStep); - free(invokeOnFinished); - free(configCode); + free(entryCode); return 0; } diff --git a/source/monster.c b/source/monster.c index c706c48..9e2d53e 100644 --- a/source/monster.c +++ b/source/monster.c @@ -31,6 +31,11 @@ static Toy_Array* monsterArray = NULL; static void loadMonsterSprite(Toy_VM* vm) { //key, file, width, height -> null + if (!IsWindowReady()) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Can't load monster sprites before the window has been initialized" TOY_CC_RESET "\n"); + return; + } + //check for initialization if (spriteTable == NULL || monsterArray == NULL) { fprintf(stderr, TOY_CC_ERROR "ERROR: Object pool for monster system hasn't been initialized" TOY_CC_RESET "\n"); @@ -114,7 +119,11 @@ static void spawnMonsterAt(Toy_VM* vm) { //get the sprite Toy_Value spriteValue = Toy_lookupTable(&spriteTable, key); if (TOY_VALUE_IS_NULL(spriteValue)) { - fprintf(stderr, TOY_CC_ERROR "ERROR: Can't spawn a monster with a non-existant sprite" TOY_CC_RESET "\n"); + Toy_String* string = Toy_stringifyValue(&(vm->memoryBucket), key); + char* cstr = Toy_getStringRaw(string); + fprintf(stderr, TOY_CC_ERROR "ERROR: Can't spawn a monster with a non-existant sprite '%s'" TOY_CC_RESET "\n", cstr); + free(cstr); + Toy_freeString(string); Toy_freeValue(key); Toy_freeValue(xpos); Toy_freeValue(ypos); @@ -160,7 +169,7 @@ typedef struct CallbackPairs { Toy_nativeCallback callback; } CallbackPairs; -CallbackPairs callbackPairs[] = { +static CallbackPairs callbackPairs[] = { {"loadMonsterSprite", loadMonsterSprite}, {"spawnMonsterAt", spawnMonsterAt}, @@ -168,7 +177,7 @@ CallbackPairs callbackPairs[] = { }; //exposed -void initMonsterObjectPool(Toy_VM* vm) { +void initMonsterAPI(Toy_VM* vm) { if (vm == NULL || vm->scope == NULL || vm->memoryBucket == NULL) { fprintf(stderr, TOY_CC_ERROR "ERROR: Can't initialize standard library, exiting\n" TOY_CC_RESET); exit(-1); @@ -189,7 +198,7 @@ void initMonsterObjectPool(Toy_VM* vm) { monsterArray = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY); } -void freeMonsterObjectPool(Toy_VM* vm) { +void freeMonsterAPI(Toy_VM* vm) { (void)vm; //free the GL textures diff --git a/source/monster.h b/source/monster.h index 9dadd58..d7bca39 100644 --- a/source/monster.h +++ b/source/monster.h @@ -3,7 +3,7 @@ #include "toy_vm.h" //object pool system -void initMonsterObjectPool(Toy_VM* vm); -void freeMonsterObjectPool(Toy_VM* vm); +void initMonsterAPI(Toy_VM* vm); +void freeMonsterAPI(Toy_VM* vm); void drawMonsters(Toy_VM* vm);