From eb8115aa7e1c9a34623023a58abe4de7e21fb096 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Wed, 13 May 2026 17:55:35 +1000 Subject: [PATCH] Each actor has an 'onStep' function, if set Replaced the global 'onStep' with 'onFrame', otherwise it works the same. --- Toy | 2 +- assets/main.toy | 50 ++++++++++----------- source/actor.c | 117 ++++++++++++++++++++---------------------------- source/actor.h | 10 +++-- source/main.c | 24 +++++----- 5 files changed, 92 insertions(+), 111 deletions(-) diff --git a/Toy b/Toy index 53d3606..6901b9a 160000 --- a/Toy +++ b/Toy @@ -1 +1 @@ -Subproject commit 53d3606c7e225496f3223621c789b3d91f998131 +Subproject commit 6901b9a6c9a2b3de99257b38d63b85100736281b diff --git a/assets/main.toy b/assets/main.toy index 4e963e8..ccc5b47 100644 --- a/assets/main.toy +++ b/assets/main.toy @@ -1,35 +1,35 @@ -//"global" variables -var frameCounter: Int = 0; -var posCounter: Int = 0; +var counter = 0; +var randi: Int = 69420; +fn rand() { + return randi = randi * 1664525 + 1013904223; //can be negative +} + +fn wander(actor: Opaque) { + actor.setX(actor.x + rand() % 5); + actor.setY(actor.y + rand() % 5); + + counter++; +} + +//when the game is ready, load the "zombie" sprite fn onReady() { loadSprite("zombie", "assets/parvati.png", 32, 32); + + //spawn some "zombies" looking for brains + spawnActorAt("zombie", wander, 250, 250); + spawnActorAt("zombie", wander, 250, 500); + spawnActorAt("zombie", wander, 500, 250); + spawnActorAt("zombie", wander, 500, 500); } -fn onStep() { - frameCounter++; - -// if (frameCounter % 10 == 0) { - spawnActorAt("zombie", posCounter*5, posCounter*5); - posCounter++; -// } - - if (posCounter > 150) { - posCounter = 0; - } -} - -fn onClose() { - // +fn onFrame() { + //debug + print counter; } //example API for the game -initScreen(1280, 720, "Hello raylib from Toy!"); -initLoop(onReady, onStep, onClose); +initScreen(1280, 720, "Oh no, Zombies!"); +initLoop(onReady, null, null); -//test -fn brains(actor: Opaque) { - actor.setX(actor.x + 1); -} -setActorStep(brains); diff --git a/source/actor.c b/source/actor.c index b5d6820..6f63502 100644 --- a/source/actor.c +++ b/source/actor.c @@ -12,7 +12,6 @@ //static storage static Toy_Table* spriteTable = NULL; static Toy_Array* actorArray = NULL; -static Toy_Function* actorStep = NULL; //callbacks static void api_loadSprite(Toy_VM* vm) { @@ -81,7 +80,7 @@ static void api_loadSprite(Toy_VM* vm) { } static void api_spawnActorAt(Toy_VM* vm) { - //sprite, x, y -> void + //sprite, onStep, x, y -> void //check for initialization if (spriteTable == NULL || actorArray == NULL) { @@ -90,19 +89,21 @@ static void api_spawnActorAt(Toy_VM* vm) { } //check parameter count - if (vm->stack->count < 3) { + if (vm->stack->count < 4) { fprintf(stderr, TOY_CC_ERROR "ERROR: Not enough parameters found in 'spawnActorAt'" TOY_CC_RESET "\n"); return; } Toy_Value ypos = Toy_popStack(&vm->stack); Toy_Value xpos = Toy_popStack(&vm->stack); + Toy_Value step = Toy_popStack(&vm->stack); Toy_Value key = Toy_popStack(&vm->stack); //check types - if (!TOY_VALUE_IS_INTEGER(xpos) || !TOY_VALUE_IS_INTEGER(ypos)) { + if (!TOY_VALUE_IS_INTEGER(xpos) || !TOY_VALUE_IS_INTEGER(ypos) || !(TOY_VALUE_IS_FUNCTION(step) || TOY_VALUE_IS_NULL(step))) { fprintf(stderr, TOY_CC_ERROR "ERROR: Bad parameter types found in 'spawnActorAt'" TOY_CC_RESET "\n"); Toy_freeValue(key); + Toy_freeValue(step); Toy_freeValue(xpos); Toy_freeValue(ypos); return; @@ -117,11 +118,17 @@ static void api_spawnActorAt(Toy_VM* vm) { free(cstr); Toy_freeString(string); Toy_freeValue(key); + Toy_freeValue(step); Toy_freeValue(xpos); Toy_freeValue(ypos); return; } + Toy_Function* onStep = NULL; + if (TOY_VALUE_IS_FUNCTION(step)) { + onStep = Toy_copyFunction(&vm->memoryBucket, TOY_VALUE_AS_FUNCTION(step)); + } + //expand the array if needed if (actorArray->count == actorArray->capacity) { actorArray = Toy_resizeArray(actorArray, actorArray->capacity * TOY_ARRAY_EXPANSION_RATE); @@ -135,7 +142,7 @@ static void api_spawnActorAt(Toy_VM* vm) { ActorData* newActorPtr = NULL; for (unsigned int i = 0; i < actorArray->count; i++) { ActorData* mData = (ActorData*)TOY_VALUE_AS_OPAQUE(actorArray->data[i]); - if (mData->health <= 0) { //if this actor is dead, steal the slot + if (!mData->enabled) { //if this actor is dead, steal the slot newActorPtr = mData; break; } @@ -150,35 +157,18 @@ static void api_spawnActorAt(Toy_VM* vm) { //finally, store the new actor's data (*newActorPtr) = (ActorData){ .sprite = (SpriteData*)(TOY_VALUE_AS_OPAQUE(spriteValue)), + .onStep = onStep, .position = { TOY_VALUE_AS_INTEGER(xpos), TOY_VALUE_AS_INTEGER(ypos) }, - .health = 10, + .enabled = true, }; Toy_freeValue(spriteValue); Toy_freeValue(key); + Toy_freeValue(step); Toy_freeValue(xpos); Toy_freeValue(ypos); } -static void api_setActorStep(Toy_VM* vm) { - Toy_Value value = Toy_popStack(&vm->stack); - - if (!TOY_VALUE_IS_FUNCTION(value) && !TOY_VALUE_IS_NULL(value)) { - fprintf(stderr, TOY_CC_ERROR "ERROR: Bad argument type found in 'setActorStep', exiting" TOY_CC_RESET "\n"); - Toy_freeValue(value); - exit(-1); - } - - if (TOY_VALUE_IS_FUNCTION(value)) { - if (TOY_VALUE_AS_FUNCTION(value)->type != TOY_FUNCTION_CUSTOM) { - fprintf(stderr, TOY_CC_ERROR "ERROR: Bad function found in 'setActorStep', exiting (only allows custom functions or null)" TOY_CC_RESET "\n"); - Toy_freeValue(value); - exit(-1); - } - actorStep = TOY_VALUE_AS_FUNCTION(value); //do not free, it'll be needed - } -} - //callback utils typedef struct CallbackPairs { const char* name; @@ -190,7 +180,6 @@ static CallbackPairs callbackPairs[] = { // {"unloadSprite", unloadSprite}, {"spawnActorAt", api_spawnActorAt}, // {"despawnActor", despawnActor}, - {"setActorStep", api_setActorStep}, {NULL, NULL}, }; @@ -231,64 +220,53 @@ void freeActorAPI(Toy_VM* vm) { spriteTable = NULL; actorArray = Toy_resizeArray(actorArray, 0); - - Toy_freeFunction(actorStep); - actorStep = NULL; } -void processActorStep(Toy_VM* vm) { +void processActors(Toy_VM* vm) { //check for initialization if (spriteTable == NULL || actorArray == NULL) { fprintf(stderr, TOY_CC_ERROR "ERROR: Object pool for actor system hasn't been initialized" TOY_CC_RESET "\n"); return; } - if (actorStep == NULL) { - return; //no-op - } - - //BUG: invoking a callback with a parameter is process-heavy - //bind a sub-vm Toy_VM subVM; Toy_inheritVM(vm, &subVM); - Toy_bindVM(&subVM, actorStep->bytecode.code, actorStep->bytecode.parentScope); //TODO: each actor needs its own step function? - - //paramAddr is relative to the data section, and is followed by the param type - unsigned int paramAddr = ((unsigned int*)(subVM.code + subVM.paramAddr))[0]; - Toy_ValueType paramType = (Toy_ValueType)(((unsigned int*)(subVM.code + subVM.paramAddr))[1]); - - //c-string of the param's name && as a name string - const char* cstr = ((char*)(subVM.code + subVM.dataAddr)) + paramAddr; - Toy_String* name = Toy_toStringLength(&subVM.memoryBucket, cstr, strlen(cstr)); //don't use 'create' - - int ticker = 0; //load each valid actor and process them one at a time for (unsigned int i = 0; i < actorArray->count; i++) { ActorData* actor = (ActorData*)TOY_VALUE_AS_OPAQUE(actorArray->data[i]); - if (actor->health > 0) { - ticker++; - subVM.scope = Toy_pushScope(&subVM.memoryBucket, subVM.scope); - Toy_declareScope(subVM.scope, name, paramType, Toy_copyValue(&subVM.memoryBucket, actorArray->data[i]), true); - Toy_runVM(&subVM); - - subVM.scope = Toy_popScope(subVM.scope); + if (!actor->enabled || actor->onStep == NULL) { + continue; } + + //prep the callback + Toy_bindVM(&subVM, actor->onStep->bytecode.code, actor->onStep->bytecode.parentScope); + + //paramAddr is relative to the data section, and is followed by the param type + unsigned int paramAddr = ((unsigned int*)(subVM.code + subVM.paramAddr))[0]; + Toy_ValueType paramType = (Toy_ValueType)(((unsigned int*)(subVM.code + subVM.paramAddr))[1]); + + //c-string of the param's name && as a name string + const char* cstr = ((char*)(subVM.code + subVM.dataAddr)) + paramAddr; + Toy_String* name = Toy_toStringLength(&subVM.memoryBucket, cstr, strlen(cstr)); //don't use 'create' + + + //inject this actor as a parameter + subVM.scope = Toy_pushScope(&subVM.memoryBucket, subVM.scope); + Toy_declareScope(subVM.scope, name, paramType, Toy_copyValue(&subVM.memoryBucket, actorArray->data[i]), true); + + //run + Toy_runVM(&subVM); + + //prep the next one + Toy_resetVM(&subVM, false, true); //preserving the stack prevents memory issues + subVM.scope = NULL; //BUGFIX: don't retain scope for different callbacks + Toy_freeString(name); } Toy_freeVM(&subVM); - - Toy_freeString(name); - - //DEBUG: "wipe" the actors if there's too many, so memory doesn't keep growing. - if (ticker >= 100) { - for (unsigned int i = 0; i < actorArray->count; i++) { - ActorData* actor = (ActorData*)TOY_VALUE_AS_OPAQUE(actorArray->data[i]); - actor->health = 0; - } - } } void drawActors(Toy_VM* vm) { @@ -303,7 +281,7 @@ void drawActors(Toy_VM* vm) { for (unsigned int i = 0; i < actorArray->count; i++) { ActorData* actor = (ActorData*)TOY_VALUE_AS_OPAQUE(actorArray->data[i]); - if (actor->health > 0) { + if (actor->enabled) { DrawTextureRec(actor->sprite->texture, actor->sprite->rect, actor->position, WHITE); } } @@ -342,8 +320,8 @@ void loadSprite(Toy_Bucket** bucketHandle, Toy_Value key, const char* fname, int Toy_insertTable(&spriteTable, Toy_copyValue(bucketHandle, key), TOY_OPAQUE_FROM_POINTER(sprite)); } -ActorData* spawnActorAt(Toy_Bucket** bucketHandle, Toy_Value key, int xpos, int ypos) { - //sprite, x, y -> void +ActorData* spawnActorAt(Toy_Bucket** bucketHandle, Toy_Value key, Toy_Function* onStep, int xpos, int ypos) { + //sprite, onStep, x, y -> void //check for initialization if (spriteTable == NULL || actorArray == NULL) { @@ -376,7 +354,7 @@ ActorData* spawnActorAt(Toy_Bucket** bucketHandle, Toy_Value key, int xpos, int ActorData* newActorPtr = NULL; for (unsigned int i = 0; i < actorArray->count; i++) { ActorData* mData = (ActorData*)TOY_VALUE_AS_OPAQUE(actorArray->data[i]); - if (mData->health <= 0) { //if this actor is dead, steal the slot + if (mData->enabled) { //if this actor is dead, steal the slot newActorPtr = mData; break; } @@ -391,8 +369,9 @@ ActorData* spawnActorAt(Toy_Bucket** bucketHandle, Toy_Value key, int xpos, int //finally, store the new actor's data (*newActorPtr) = (ActorData){ .sprite = (SpriteData*)(TOY_VALUE_AS_OPAQUE(spriteValue)), + .onStep = onStep, .position = { xpos, ypos }, - .health = 10, + .enabled = true, }; Toy_freeValue(spriteValue); diff --git a/source/actor.h b/source/actor.h index 4b3fc90..d7a7ba6 100644 --- a/source/actor.h +++ b/source/actor.h @@ -1,6 +1,7 @@ #pragma once #include "toy_vm.h" +#include "toy_function.h" #include "raylib.h" //sprites loaded from disk @@ -13,19 +14,20 @@ typedef struct SpriteData { //Actors loaded from scripts typedef struct ActorData { SpriteData* sprite; + Toy_Function* onStep; Vector2 position; - //TODO: animation - int health; + bool enabled; } ActorData; //object pool system void initActorAPI(Toy_VM* vm); void freeActorAPI(Toy_VM* vm); -void processActorStep(Toy_VM* vm); +void processActors(Toy_VM* vm); void drawActors(Toy_VM* vm); void loadSprite(Toy_Bucket** bucketHandle, Toy_Value key, const char* fname, int width, int height); -ActorData* spawnActorAt(Toy_Bucket** bucketHandle, Toy_Value key, int xpos, int ypos); +ActorData* spawnActorAt(Toy_Bucket** bucketHandle, Toy_Value key, Toy_Function* onStep, int xpos, int ypos); +//opaque hook Toy_Value handleActorAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute); \ No newline at end of file diff --git a/source/main.c b/source/main.c index 0404a1a..962c834 100644 --- a/source/main.c +++ b/source/main.c @@ -69,7 +69,7 @@ unsigned char* makeCodeFromSource(const char* source) { //static pointers static Toy_Function* onReady = NULL; -static Toy_Function* onStep = NULL; +static Toy_Function* onFrame = NULL; static Toy_Function* onClose = NULL; //game API definitions @@ -85,7 +85,7 @@ void initScreen(Toy_VM* vm) { //setup raylib InitWindow(TOY_VALUE_AS_INTEGER(width), TOY_VALUE_AS_INTEGER(height), TOY_VALUE_AS_STRING(caption)->leaf.data); - // SetTargetFPS(60); + SetTargetFPS(60); if (!IsWindowReady()) { fprintf(stderr, TOY_CC_ERROR "ERROR: raylib failed to init the window, exiting" TOY_CC_RESET "\n"); @@ -99,7 +99,7 @@ void initScreen(Toy_VM* vm) { void initLoop(Toy_VM* vm) { Toy_Value valueOnClose = Toy_popStack(&vm->stack); - Toy_Value valueOnStep = Toy_popStack(&vm->stack); + Toy_Value valueOnFrame = Toy_popStack(&vm->stack); Toy_Value valueOnReady = Toy_popStack(&vm->stack); if (!TOY_VALUE_IS_FUNCTION(valueOnClose) && !TOY_VALUE_IS_NULL(valueOnClose)) { @@ -107,7 +107,7 @@ void initLoop(Toy_VM* vm) { exit(-1); } - if (!TOY_VALUE_IS_FUNCTION(valueOnStep) && !TOY_VALUE_IS_NULL(valueOnStep)) { + if (!TOY_VALUE_IS_FUNCTION(valueOnFrame) && !TOY_VALUE_IS_NULL(valueOnFrame)) { fprintf(stderr, TOY_CC_ERROR "ERROR: Bad types found in 'initLoop', exiting" TOY_CC_RESET "\n"); exit(-1); } @@ -124,12 +124,12 @@ void initLoop(Toy_VM* vm) { } onReady = TOY_VALUE_AS_FUNCTION(valueOnReady); } - if (TOY_VALUE_IS_FUNCTION(valueOnStep)) { - if (TOY_VALUE_AS_FUNCTION(valueOnStep)->type != TOY_FUNCTION_CUSTOM) { + if (TOY_VALUE_IS_FUNCTION(valueOnFrame)) { + if (TOY_VALUE_AS_FUNCTION(valueOnFrame)->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); + onFrame = TOY_VALUE_AS_FUNCTION(valueOnFrame); } if (TOY_VALUE_IS_FUNCTION(valueOnClose)) { if (TOY_VALUE_AS_FUNCTION(valueOnClose)->type != TOY_FUNCTION_CUSTOM) { @@ -199,9 +199,9 @@ int main() { Toy_resetVM(&vm, false, false); } - //onStep is called each frame - if (onStep != NULL) { - Toy_bindVM(&vm, onStep->bytecode.code, onStep->bytecode.parentScope); + //onFrame is called each frame + if (onFrame != NULL) { + Toy_bindVM(&vm, onFrame->bytecode.code, onFrame->bytecode.parentScope); } while (!WindowShouldClose()) { @@ -212,7 +212,7 @@ int main() { // if (IsKeyDown(KEY_RIGHT)) player.position.x += 5.0f; //process the actors (if possible) - processActorStep(&vm); + processActors(&vm); //run the onStep function Toy_runVM(&vm); //no check needed, empty VMs are skipped @@ -226,7 +226,7 @@ int main() { } //clear onStep - if (onStep != NULL) { + if (onFrame != NULL) { Toy_resetVM(&vm, false, false); }