From a45b2e71af6050cf28709f9573a1ad39f560c1ce Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Fri, 1 May 2026 18:45:40 +1000 Subject: [PATCH] Monster spawning is controlled from the scripts --- Toy | 2 +- config.toy | 23 +++++- source/main.c | 35 ++++++++- source/monster.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++- source/monster.h | 18 ++--- 5 files changed, 256 insertions(+), 19 deletions(-) diff --git a/Toy b/Toy index eb33775..18a4b33 160000 --- a/Toy +++ b/Toy @@ -1 +1 @@ -Subproject commit eb33775314810e19eeac6ec176ef0b1746a7fead +Subproject commit 18a4b33c4e6a79c49ec1c2d94d56cf2c52c76e9a diff --git a/config.toy b/config.toy index e9ca6f0..bb96415 100644 --- a/config.toy +++ b/config.toy @@ -2,4 +2,25 @@ var screenWidth = 1280; var screenHeight = 720; var screenCaption = "Hello raylib from Toy!"; -//TODO: load monsters on a timer? \ No newline at end of file +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++; + + if (frameCounter % 100 == 0) { + spawnMonsterAt("parvati", posCounter*50, posCounter*50); + posCounter++; + } +} + +//this runs as the game is closing down +fn onFinished() { + // +} \ No newline at end of file diff --git a/source/main.c b/source/main.c index 8ca154b..6983616 100644 --- a/source/main.c +++ b/source/main.c @@ -7,6 +7,8 @@ #include "toy_compiler.h" #include "toy_vm.h" +#include "monster.h" + #include #include #include @@ -93,13 +95,17 @@ int main() { return -1; } - unsigned char* code = makeCodeFromSource(source); + unsigned char* configCode = makeCodeFromSource(source); + unsigned char* invokeOnReady = makeCodeFromSource("onReady();"); + unsigned char* invokeOnStep = makeCodeFromSource("onStep();"); + unsigned char* invokeOnFinished = makeCodeFromSource("onFinished();"); //build and run the VM Toy_VM vm; Toy_initVM(&vm); - Toy_bindVM(&vm, code, NULL); + Toy_bindVM(&vm, configCode, NULL); 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") ); @@ -117,6 +123,14 @@ int main() { //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); + + //run the onStep function (or die if it's undefined) + Toy_bindVM(&vm, invokeOnReady, NULL); + Toy_runVM(&vm); + Toy_resetVM(&vm, true, false); + while (!WindowShouldClose()) { //input if (IsKeyDown(KEY_UP)) player.position.y -= 5.0f; @@ -124,6 +138,11 @@ int main() { if (IsKeyDown(KEY_LEFT)) player.position.x -= 5.0f; if (IsKeyDown(KEY_RIGHT)) player.position.x += 5.0f; + //run the onStep function (or die if it's undefined) + Toy_bindVM(&vm, invokeOnStep, NULL); + Toy_runVM(&vm); + Toy_resetVM(&vm, true, false); + //drawing BeginDrawing(); ClearBackground(RAYWHITE); @@ -131,16 +150,26 @@ int main() { //draw the player DrawTextureRec(player.texture, player.rect, player.position, WHITE); + drawMonsterPool(&vm); + DrawFPS(0,0); EndDrawing(); } + Toy_bindVM(&vm, invokeOnFinished, NULL); + Toy_runVM(&vm); + Toy_resetVM(&vm, true, false); + + freeMonsterObjectPool(&vm); unloadPlayerData(player); CloseWindow(); Toy_freeVM(&vm); - free(code); + free(invokeOnReady); + free(invokeOnStep); + free(invokeOnFinished); + free(configCode); return 0; } diff --git a/source/monster.c b/source/monster.c index cd117bc..c5e1904 100644 --- a/source/monster.c +++ b/source/monster.c @@ -1,3 +1,198 @@ #include "monster.h" -//TODO: load monsters as an object pool \ No newline at end of file +#include "toy_console_colors.h" +#include "toy_table.h" +#include "toy_array.h" +#include "toy_string.h" + +#include "raylib.h" + +#include +#include + +//sprites loaded from disk +typedef struct MonsterSprite { + Texture2D texture; + Rectangle rect; +} MonsterSprite; + +//Monsters loaded from scripts +typedef struct MonsterData { + MonsterSprite* sprite; + Vector2 position; + int health; +} MonsterData; + +//static storage +static Toy_Table* spriteTable = NULL; +static Toy_Array* monsterArray = NULL; + +//callbacks +static void loadMonsterSprite(Toy_VM* vm) { + //key, file, width, height -> null + + //check parameter count + if (vm->stack->count < 4) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Not enough parameters found in 'loadMonsterSprite'" TOY_CC_RESET "\n"); + return; + } + + Toy_Value height = Toy_popStack(&vm->stack); + Toy_Value width = Toy_popStack(&vm->stack); + Toy_Value file = Toy_popStack(&vm->stack); + Toy_Value key = Toy_popStack(&vm->stack); + + //check types + if (!TOY_VALUE_IS_STRING(file) || !TOY_VALUE_IS_INTEGER(width) || !TOY_VALUE_IS_INTEGER(height)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Bad parameter types found in 'loadMonsterSprite'" TOY_CC_RESET "\n"); + return; + } + + //check for overwriting the key + if ( TOY_VALUE_IS_NULL(Toy_lookupTable(&spriteTable, key)) != true ) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Can't overwrite existing monster sprite key" TOY_CC_RESET "\n"); + Toy_freeValue(key); + Toy_freeValue(file); + Toy_freeValue(width); + Toy_freeValue(height); + return; + } + + //create the sprite stored in the bucket + MonsterSprite* sprite = (MonsterSprite*)Toy_partitionBucket(&(vm->memoryBucket), sizeof(MonsterSprite)); + sprite->rect = (Rectangle){ 0, 0, TOY_VALUE_AS_INTEGER(width), TOY_VALUE_AS_INTEGER(height) }; + + //load the texture from a file + Toy_String* str = TOY_VALUE_AS_STRING(file); + if (str->info.type == TOY_STRING_LEAF) { + sprite->texture = LoadTexture(str->leaf.data); + } + else { + char* cstr = Toy_getStringRaw(str); + sprite->texture = LoadTexture(cstr); + free(cstr); + } + + //insert into the table as an opaque + Toy_insertTable(&spriteTable, key, TOY_OPAQUE_FROM_POINTER(sprite)); +} + +static void spawnMonsterAt(Toy_VM* vm) { + //sprite, x, y -> void + + //check parameter count + if (vm->stack->count < 3) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Not enough parameters found in 'spawnMonsterAt'" TOY_CC_RESET "\n"); + return; + } + + Toy_Value ypos = Toy_popStack(&vm->stack); + Toy_Value xpos = Toy_popStack(&vm->stack); + Toy_Value key = Toy_popStack(&vm->stack); + + //check types + if (!TOY_VALUE_IS_INTEGER(xpos) || !TOY_VALUE_IS_INTEGER(ypos)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Bad parameter types found in 'spawnMonsterAt'" TOY_CC_RESET "\n"); + Toy_freeValue(key); + Toy_freeValue(xpos); + Toy_freeValue(ypos); + return; + } + + //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_freeValue(key); + Toy_freeValue(xpos); + Toy_freeValue(ypos); + return; + } + + //expand the array if needed + if (monsterArray->count == monsterArray->capacity) { + monsterArray = Toy_resizeArray(monsterArray, monsterArray->capacity * TOY_ARRAY_EXPANSION_RATE); + //set the new entries to null values + for (unsigned int i = monsterArray->count; i < monsterArray->capacity; i++) { + monsterArray->data[i] = TOY_VALUE_FROM_NULL(); + } + } + + //find an existing spot for the new monster, overwriting a dead one + MonsterData* newMonsterPtr = NULL; + for (unsigned int i = 0; i < monsterArray->count; i++) { + MonsterData* mData = (MonsterData*)TOY_VALUE_AS_OPAQUE(monsterArray->data[i]); + if (mData->health <= 0) { //if this monster is dead, steal the slot + newMonsterPtr = mData; + break; + } + } + + //if no dead monsters were found, make a new slot + if (newMonsterPtr == NULL) { + newMonsterPtr = (MonsterData*)Toy_partitionBucket(&(vm->memoryBucket), sizeof(MonsterData)); + monsterArray->data[monsterArray->count++] = TOY_OPAQUE_FROM_POINTER(newMonsterPtr); + } + + //finally, store the new monster's data + (*newMonsterPtr) = (MonsterData){ + .sprite = (MonsterSprite*)(TOY_VALUE_AS_OPAQUE(spriteValue)), + .position = { TOY_VALUE_AS_INTEGER(xpos), TOY_VALUE_AS_INTEGER(ypos) }, + .health = 10, + }; +} + +//callback utils +typedef struct CallbackPairs { + const char* name; + Toy_nativeCallback callback; +} CallbackPairs; + +CallbackPairs callbackPairs[] = { + {"loadMonsterSprite", loadMonsterSprite}, + {"spawnMonsterAt", spawnMonsterAt}, + + {NULL, NULL}, +}; + +//exposed +void initMonsterObjectPool(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); + } + + //declare each callback 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); + } + + //make the local storage (these use malloc(), don't they?) + spriteTable = Toy_allocateTable(TOY_TABLE_INITIAL_CAPACITY); + monsterArray = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY); +} + +void freeMonsterObjectPool(Toy_VM* vm) { + (void)vm; + + Toy_freeTable(spriteTable); + spriteTable = NULL; + + monsterArray = Toy_resizeArray(monsterArray, 0); +} + +void drawMonsterPool(Toy_VM* vm) { + (void)vm; + for (unsigned int i = 0; i < monsterArray->count; i++) { + MonsterData* monster = (MonsterData*)TOY_VALUE_AS_OPAQUE(monsterArray->data[i]); + + if (monster->health > 0) { + DrawTextureRec(monster->sprite->texture, monster->sprite->rect, monster->position, WHITE); + } + } +} \ No newline at end of file diff --git a/source/monster.h b/source/monster.h index 4651035..9be1af8 100644 --- a/source/monster.h +++ b/source/monster.h @@ -1,17 +1,9 @@ #pragma once -#include "raylib.h" +#include "toy_vm.h" -//sprites loaded from disk -typedef struct MonsterSprite { - Texture2D texture; - Rectangle rect; -} MonsterSprite; - -//Monsters loaded from scripts -typedef struct MonsterData { - MonsterSprite* sprite; - Vector2 position; - int health; -} MonsterData; +//object pool system +void initMonsterObjectPool(Toy_VM* vm); +void freeMonsterObjectPool(Toy_VM* vm); +void drawMonsterPool(Toy_VM* vm);