Monster spawning is controlled from the scripts

This commit is contained in:
2026-05-01 18:45:40 +10:00
parent 502dab670e
commit a45b2e71af
5 changed files with 256 additions and 19 deletions
+1 -1
Submodule Toy updated: eb33775314...18a4b33c4e
+22 -1
View File
@@ -2,4 +2,25 @@ var screenWidth = 1280;
var screenHeight = 720; var screenHeight = 720;
var screenCaption = "Hello raylib from Toy!"; var screenCaption = "Hello raylib from Toy!";
//TODO: load monsters on a timer? 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() {
//
}
+32 -3
View File
@@ -7,6 +7,8 @@
#include "toy_compiler.h" #include "toy_compiler.h"
#include "toy_vm.h" #include "toy_vm.h"
#include "monster.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -93,13 +95,17 @@ int main() {
return -1; 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 //build and run the VM
Toy_VM vm; Toy_VM vm;
Toy_initVM(&vm); Toy_initVM(&vm);
Toy_bindVM(&vm, code, NULL); Toy_bindVM(&vm, configCode, NULL);
Toy_runVM(&vm); Toy_runVM(&vm);
Toy_resetVM(&vm, true, false); //leave it in a valid, but unset state
//extract the settings //extract the settings
Toy_Value* screenWidthPtr = Toy_accessScopeAsPointer(vm.scope, Toy_toString(&vm.memoryBucket, "screenWidth") ); Toy_Value* screenWidthPtr = Toy_accessScopeAsPointer(vm.scope, Toy_toString(&vm.memoryBucket, "screenWidth") );
@@ -117,6 +123,14 @@ int main() {
//load a sprite //load a sprite
PlayerData player = loadPlayerData("assets/parvati.png", (Rectangle){0,0,32,32}); 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()) { while (!WindowShouldClose()) {
//input //input
if (IsKeyDown(KEY_UP)) player.position.y -= 5.0f; 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_LEFT)) player.position.x -= 5.0f;
if (IsKeyDown(KEY_RIGHT)) 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 //drawing
BeginDrawing(); BeginDrawing();
ClearBackground(RAYWHITE); ClearBackground(RAYWHITE);
@@ -131,16 +150,26 @@ int main() {
//draw the player //draw the player
DrawTextureRec(player.texture, player.rect, player.position, WHITE); DrawTextureRec(player.texture, player.rect, player.position, WHITE);
drawMonsterPool(&vm);
DrawFPS(0,0); DrawFPS(0,0);
EndDrawing(); EndDrawing();
} }
Toy_bindVM(&vm, invokeOnFinished, NULL);
Toy_runVM(&vm);
Toy_resetVM(&vm, true, false);
freeMonsterObjectPool(&vm);
unloadPlayerData(player); unloadPlayerData(player);
CloseWindow(); CloseWindow();
Toy_freeVM(&vm); Toy_freeVM(&vm);
free(code); free(invokeOnReady);
free(invokeOnStep);
free(invokeOnFinished);
free(configCode);
return 0; return 0;
} }
+196 -1
View File
@@ -1,3 +1,198 @@
#include "monster.h" #include "monster.h"
//TODO: load monsters as an object pool #include "toy_console_colors.h"
#include "toy_table.h"
#include "toy_array.h"
#include "toy_string.h"
#include "raylib.h"
#include <stdio.h>
#include <stdlib.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;
//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);
}
}
}
+5 -13
View File
@@ -1,17 +1,9 @@
#pragma once #pragma once
#include "raylib.h" #include "toy_vm.h"
//sprites loaded from disk //object pool system
typedef struct MonsterSprite { void initMonsterObjectPool(Toy_VM* vm);
Texture2D texture; void freeMonsterObjectPool(Toy_VM* vm);
Rectangle rect;
} MonsterSprite;
//Monsters loaded from scripts
typedef struct MonsterData {
MonsterSprite* sprite;
Vector2 position;
int health;
} MonsterData;
void drawMonsterPool(Toy_VM* vm);