diff --git a/Toy b/Toy index b0387ed..bbb1e38 160000 --- a/Toy +++ b/Toy @@ -1 +1 @@ -Subproject commit b0387edeb0f9227067e1a1dc276b926c68992d17 +Subproject commit bbb1e3864987de13f70bf212dc4915f1107bca20 diff --git a/assets/main.toy b/assets/main.toy index a2e666d..47f536d 100644 --- a/assets/main.toy +++ b/assets/main.toy @@ -1,4 +1,4 @@ - +//debug testing var counter = 0; var randi: Int = 69420; fn rand() { @@ -12,6 +12,45 @@ fn wander(actor: Opaque) { counter++; } +//TODO: general purpose math functions +//TODO: optimize away single-line blocks in the AST + +//player controlled character +var playerMaxMotion: Int = 5; +var playerMotionX: Int = 0; +var playerMotionY: Int = 0; + +fn playerOnFrame(player: Opaque) { + //accept input + if (Keyboard.UP) { + playerMotionY -= 5; + } + if (Keyboard.DOWN) { + playerMotionY += 5; + } + if (Keyboard.LEFT) { + playerMotionX -= 5; + } + if (Keyboard.RIGHT) { + playerMotionX += 5; + } + +// print playerMotionX; +// print playerMotionY; +// print playerMaxMotion; +// print -playerMaxMotion; + + //cap the speed + if (playerMotionX > playerMaxMotion) playerMotionX = playerMaxMotion; + if (playerMotionX < -playerMaxMotion) playerMotionX = -playerMaxMotion; + if (playerMotionY > playerMaxMotion) playerMotionY = playerMaxMotion; + if (playerMotionY < -playerMaxMotion) playerMotionY = -playerMaxMotion; + + //move the player + player.setX(player.x + playerMotionX); + player.setY(player.y + playerMotionY); +} + //when the game is ready, load the "zombie" sprite fn onReady() { loadSprite("zombie", "assets/parvati.png", 32, 32); @@ -21,14 +60,17 @@ fn onReady() { spawnActorAt("zombie", wander, 250, 500); spawnActorAt("zombie", wander, 500, 250); spawnActorAt("zombie", wander, 500, 500); + + //spawn the player + loadSprite("player", "assets/parvati.png", 32, 32); + spawnActorAt("player", playerOnFrame, 300, 300); } fn onFrame() { //debug - print counter; +// print Keyboard.A; } //example API for the game initScreen(1280, 720, "Oh no, Zombies!"); -initLoop(onReady, null, null); - +initLoop(onReady, onFrame, null); diff --git a/makefile b/makefile index 678b2cb..7d096a7 100644 --- a/makefile +++ b/makefile @@ -4,6 +4,8 @@ #LIBS+=-lm #LDFLAGS+= +#TODO: add debug & release build options, at least for Toy + #directories export VAMP_SOURCEDIR=source export VAMP_OUTDIR=out diff --git a/source/actor.c b/source/actor.c index 6b2ffdc..3393a5a 100644 --- a/source/actor.c +++ b/source/actor.c @@ -13,7 +13,7 @@ static Toy_Table* spriteTable = NULL; static Toy_Array* actorArray = NULL; -//callbacks +//API bindings static void api_loadSprite(Toy_VM* vm, Toy_FunctionNative* self) { //key, file, width, height -> null (void)self; @@ -165,6 +165,7 @@ static void api_spawnActorAt(Toy_VM* vm, Toy_FunctionNative* self) { //finally, store the new actor's data (*newActorPtr) = (ActorData){ + .type = OPAQUE_ACTOR, .sprite = (SpriteData*)(TOY_VALUE_AS_OPAQUE(spriteValue)), .onStep = onStep, .position = { TOY_VALUE_AS_INTEGER(xpos), TOY_VALUE_AS_INTEGER(ypos) }, @@ -309,100 +310,7 @@ void drawActors(Toy_VM* vm) { } } -//accessors & mutators -void loadSprite(Toy_Bucket** bucketHandle, Toy_Value key, const char* fname, int width, int height) { - //key, file, width, height -> null - - if (!IsWindowReady()) { - fprintf(stderr, TOY_CC_ERROR "ERROR: Can't load actor sprites before the window has been initialized" TOY_CC_RESET "\n"); - return; - } - - //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; - } - - //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 actor sprite key" TOY_CC_RESET "\n"); - Toy_freeValue(key); - return; - } - - //create the sprite stored in the bucket - SpriteData* sprite = (SpriteData*)Toy_partitionBucket(bucketHandle, sizeof(SpriteData)); - sprite->rect = (Rectangle){ 0, 0, width, height }; - - //load the texture from a file - sprite->texture = LoadTexture(fname); - - //insert into the table as an opaque - Toy_insertTable(&spriteTable, Toy_copyValue(bucketHandle, key), TOY_OPAQUE_FROM_POINTER(sprite)); -} - -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) { - fprintf(stderr, TOY_CC_ERROR "ERROR: Object pool for actor system hasn't been initialized" TOY_CC_RESET "\n"); - return NULL; - } - - //get the sprite - Toy_Value spriteValue = Toy_lookupTable(&spriteTable, key); - if (TOY_VALUE_IS_NULL(spriteValue)) { - Toy_String* string = Toy_stringifyValue(bucketHandle, key); - char* cstr = Toy_getStringRaw(string); - fprintf(stderr, TOY_CC_ERROR "ERROR: Can't spawn a actor with a non-existant sprite '%s'" TOY_CC_RESET "\n", cstr); - free(cstr); - Toy_freeString(string); - Toy_freeValue(key); - return NULL; - } - - //expand the array if needed - if (actorArray->count == actorArray->capacity) { - actorArray = Toy_resizeArray(actorArray, actorArray->capacity * TOY_ARRAY_EXPANSION_RATE); - //set the new entries to null values - for (unsigned int i = actorArray->count; i < actorArray->capacity; i++) { - actorArray->data[i] = TOY_VALUE_FROM_NULL(); - } - } - - //find an existing spot for the new actor, overwriting a dead one if able - ActorData* newActorPtr = NULL; - for (unsigned int i = 0; i < actorArray->count; i++) { - ActorData* mData = (ActorData*)TOY_VALUE_AS_OPAQUE(actorArray->data[i]); - if (mData->enabled) { //if this actor is dead, steal the slot - newActorPtr = mData; - break; - } - } - - //if no dead actors were found, make a new slot - if (newActorPtr == NULL) { - newActorPtr = (ActorData*)Toy_partitionBucket(bucketHandle, sizeof(ActorData)); - actorArray->data[actorArray->count++] = TOY_OPAQUE_FROM_POINTER(newActorPtr); - } - - //finally, store the new actor's data - (*newActorPtr) = (ActorData){ - .sprite = (SpriteData*)(TOY_VALUE_AS_OPAQUE(spriteValue)), - .onStep = onStep, - .position = { xpos, ypos }, - .enabled = true, - }; - - Toy_freeValue(spriteValue); - Toy_freeValue(key); - - return newActorPtr; -} - -//opaque handler +//opaque bindings static void attr_actorSetX(Toy_VM* vm, Toy_FunctionNative* self) { (void)self; @@ -440,12 +348,6 @@ Toy_Value handleActorAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attrib return TOY_VALUE_FROM_NULL(); } - //check for correct types - if (!TOY_VALUE_IS_OPAQUE(compound) || !TOY_VALUE_IS_STRING(attribute) || TOY_VALUE_AS_STRING(attribute)->info.type != TOY_STRING_LEAF) { - fprintf(stderr, TOY_CC_ERROR "ERROR: Bad parameters found in 'handleActorAttributes'" TOY_CC_RESET "\n"); - return TOY_VALUE_FROM_NULL(); //do not free the params here - } - ActorData* actor = (ActorData*)TOY_VALUE_AS_OPAQUE(compound); if (TOY_VALUE_AS_STRING(attribute)->info.length == 1 && strncmp(TOY_VALUE_AS_STRING(attribute)->leaf.data, "x", 1) == 0) { @@ -465,7 +367,7 @@ Toy_Value handleActorAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attrib else { char buffer[256]; - snprintf(buffer, 256, "Unknown attribute '%s' of type ActorData (an Opaque)", TOY_VALUE_AS_STRING(attribute)->leaf.data); + snprintf(buffer, 256, "Unknown attribute '%s' of 'ActorData'", TOY_VALUE_AS_STRING(attribute)->leaf.data); Toy_error(buffer); return TOY_VALUE_FROM_NULL(); } diff --git a/source/actor.h b/source/actor.h index d7a7ba6..4c02186 100644 --- a/source/actor.h +++ b/source/actor.h @@ -1,5 +1,6 @@ #pragma once +#include "opaque_type.h" #include "toy_vm.h" #include "toy_function.h" #include "raylib.h" @@ -13,21 +14,19 @@ typedef struct SpriteData { //Actors loaded from scripts typedef struct ActorData { + OpaqueType type; SpriteData* sprite; Toy_Function* onStep; Vector2 position; bool enabled; } ActorData; -//object pool system +//opaque API void initActorAPI(Toy_VM* vm); void freeActorAPI(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, Toy_Function* onStep, int xpos, int ypos); - -//opaque hook +//opaque binding Toy_Value handleActorAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute); \ No newline at end of file diff --git a/source/keyboard.c b/source/keyboard.c new file mode 100644 index 0000000..b1fd3e7 --- /dev/null +++ b/source/keyboard.c @@ -0,0 +1,140 @@ +#include "keyboard.h" + +#include "string.h" + +KeyboardMap keyboardMap[] = { + {KEY_NULL, ""}, + {KEY_APOSTROPHE, "APOSTROPHE"}, + {KEY_COMMA, "COMMA"}, + {KEY_MINUS, "MINUS"}, + {KEY_PERIOD, "PERIOD"}, + {KEY_SLASH, "SLASH"}, + {KEY_ZERO, "ZERO"}, + {KEY_ONE, "ONE"}, + {KEY_TWO, "TWO"}, + {KEY_THREE, "THREE"}, + {KEY_FOUR, "FOUR"}, + {KEY_FIVE, "FIVE"}, + {KEY_SIX, "SIX"}, + {KEY_SEVEN, "SEVEN"}, + {KEY_EIGHT, "EIGHT"}, + {KEY_NINE, "NINE"}, + {KEY_SEMICOLON, "SEMICOLON"}, + {KEY_EQUAL, "EQUAL"}, + {KEY_A, "A"}, + {KEY_B, "B"}, + {KEY_C, "C"}, + {KEY_D, "D"}, + {KEY_E, "E"}, + {KEY_F, "F"}, + {KEY_G, "G"}, + {KEY_H, "H"}, + {KEY_I, "I"}, + {KEY_J, "J"}, + {KEY_K, "K"}, + {KEY_L, "L"}, + {KEY_M, "M"}, + {KEY_N, "N"}, + {KEY_O, "O"}, + {KEY_P, "P"}, + {KEY_Q, "Q"}, + {KEY_R, "R"}, + {KEY_S, "S"}, + {KEY_T, "T"}, + {KEY_U, "U"}, + {KEY_V, "V"}, + {KEY_W, "W"}, + {KEY_X, "X"}, + {KEY_Y, "Y"}, + {KEY_Z, "Z"}, + {KEY_LEFT_BRACKET, "LEFT_BRACKET"}, + {KEY_BACKSLASH, "BACKSLASH"}, + {KEY_RIGHT_BRACKET, "RIGHT_BRACKET"}, + {KEY_GRAVE, "GRAVE"}, + {KEY_SPACE, "SPACE"}, + {KEY_ESCAPE, "ESCAPE"}, + {KEY_ENTER, "ENTER"}, + {KEY_TAB, "TAB"}, + {KEY_BACKSPACE, "BACKSPACE"}, + {KEY_INSERT, "INSERT"}, + {KEY_DELETE, "DELETE"}, + {KEY_RIGHT, "RIGHT"}, + {KEY_LEFT, "LEFT"}, + {KEY_DOWN, "DOWN"}, + {KEY_UP, "UP"}, + {KEY_PAGE_UP, "PAGE_UP"}, + {KEY_PAGE_DOWN, "PAGE_DOWN"}, + {KEY_HOME, "HOME"}, + {KEY_END, "END"}, + {KEY_CAPS_LOCK, "CAPS_LOCK"}, + {KEY_SCROLL_LOCK, "SCROLL_LOCK"}, + {KEY_NUM_LOCK, "NUM_LOCK"}, + {KEY_PRINT_SCREEN, "PRINT_SCREEN"}, + {KEY_PAUSE, "PAUSE"}, + {KEY_F1, "F1"}, + {KEY_F2, "F2"}, + {KEY_F3, "F3"}, + {KEY_F4, "F4"}, + {KEY_F5, "F5"}, + {KEY_F6, "F6"}, + {KEY_F7, "F7"}, + {KEY_F8, "F8"}, + {KEY_F9, "F9"}, + {KEY_F10, "F10"}, + {KEY_F11, "F11"}, + {KEY_F12, "F12"}, + {KEY_LEFT_SHIFT, "LEFT_SHIFT"}, + {KEY_LEFT_CONTROL, "LEFT_CONTROL"}, + {KEY_LEFT_ALT, "LEFT_ALT"}, + {KEY_LEFT_SUPER, "LEFT_SUPER"}, + {KEY_RIGHT_SHIFT, "RIGHT_SHIFT"}, + {KEY_RIGHT_CONTROL, "RIGHT_CONTROL"}, + {KEY_RIGHT_ALT, "RIGHT_ALT"}, + {KEY_RIGHT_SUPER, "RIGHT_SUPER"}, + {KEY_KB_MENU, "KB_MENU"}, + {KEY_KP_0, "KP_0"}, + {KEY_KP_1, "KP_1"}, + {KEY_KP_2, "KP_2"}, + {KEY_KP_3, "KP_3"}, + {KEY_KP_4, "KP_4"}, + {KEY_KP_5, "KP_5"}, + {KEY_KP_6, "KP_6"}, + {KEY_KP_7, "KP_7"}, + {KEY_KP_8, "KP_8"}, + {KEY_KP_9, "KP_9"}, + {KEY_KP_DECIMAL, "KP_DECIMAL"}, + {KEY_KP_DIVIDE, "KP_DIVIDE"}, + {KEY_KP_MULTIPLY, "KP_MULTIPLY"}, + {KEY_KP_SUBTRACT, "KP_SUBTRACT"}, + {KEY_KP_ADD, "KP_ADD"}, + {KEY_KP_ENTER, "KP_ENTER"}, + {KEY_KP_EQUAL, "KP_EQUAL"}, + {KEY_BACK, "BACK"}, + {KEY_MENU, "MENU"}, + {KEY_VOLUME_UP, "VOLUME_UP"}, + {KEY_VOLUME_DOWN, "VOLUME_DOWN"}, + {0, NULL}, +}; + +KeyboardData keyboardData = { //NOTE: it is just a dummy struct right now so the API looks nice + .type = OPAQUE_KEYBOARD, +}; + +Toy_Value handleKeyboardAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) { + (void)vm; + (void)compound; + + Toy_String* string = TOY_VALUE_AS_STRING(attribute); + const char* cstr = string->leaf.data; + + //find the mapped value, if available + for (KeyboardMap* ptr = keyboardMap; ptr->cstr != NULL; ptr++) { + if (strlen(ptr->cstr) == strlen(cstr) && strncmp(cstr, ptr->cstr, strlen(ptr->cstr)) == 0) { + bool result = IsKeyPressed(ptr->raykey); + return TOY_VALUE_FROM_BOOLEAN(result); + } + } + + //unknown key + return TOY_VALUE_FROM_NULL(); +} \ No newline at end of file diff --git a/source/keyboard.h b/source/keyboard.h new file mode 100644 index 0000000..036b668 --- /dev/null +++ b/source/keyboard.h @@ -0,0 +1,22 @@ +#pragma once + +#include "opaque_type.h" +#include "toy_vm.h" +#include "raylib.h" + +//wraps raylib's 'KeyboardKey' enum to a c-string +typedef struct KeyboardMap { + int raykey; + char* cstr; +} KeyboardMap; + +extern KeyboardMap keyboardMap[]; + +//keyboard opaque +typedef struct KeyboardData { + OpaqueType type; +} KeyboardData; + +extern KeyboardData keyboardData; + +Toy_Value handleKeyboardAttributes(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 6e30543..b6a4acd 100644 --- a/source/main.c +++ b/source/main.c @@ -8,12 +8,15 @@ #include "toy_vm.h" #include "toy_attributes.h" +#include "keyboard.h" #include "actor.h" #include #include #include +// #include "bytecode_inspector.h" + //utils unsigned char* readFile(char* path, int* size) { //open the file @@ -43,7 +46,7 @@ unsigned char* readFile(char* path, int* size) { return NULL; } - buffer[(*size)++] = '\0'; + buffer[(*size)] = '\0'; //clean up and return fclose(file); @@ -144,7 +147,31 @@ void api_initLoop(Toy_VM* vm, Toy_FunctionNative* self) { } } -//game API tools +//opaque dispatch +Toy_Value dispatchOpaqueAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) { + //check for correct types + if (!TOY_VALUE_IS_OPAQUE(compound) || !TOY_VALUE_IS_STRING(attribute) || TOY_VALUE_AS_STRING(attribute)->info.type != TOY_STRING_LEAF) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Bad parameters found in 'handleOpaqueAttributes'" TOY_CC_RESET "\n"); + return TOY_VALUE_FROM_NULL(); //do not free the params here + } + + //assume the first byte is the type + OpaqueType* type = (OpaqueType*)TOY_VALUE_AS_OPAQUE(compound); + + switch(*type) { + case OPAQUE_KEYBOARD: + return handleKeyboardAttributes(vm, compound, attribute); + + case OPAQUE_ACTOR: + return handleActorAttributes(vm, compound, attribute); + + default: + fprintf(stderr, TOY_CC_ERROR "ERROR: Bad opaque type found in 'handleOpaqueAttributes'" TOY_CC_RESET "\n"); + return TOY_VALUE_FROM_NULL(); //do not free the params here + } +} + +//API tools typedef struct CallbackPairs { const char* name; Toy_nativeCallback callback; @@ -169,6 +196,11 @@ void initGameAPI(Toy_VM* vm) { Toy_declareScope(vm->scope, key, TOY_VALUE_FUNCTION, TOY_VALUE_FROM_FUNCTION(fn), true); Toy_freeString(key); } + + //declare keyboard opaque + Toy_String* keyboardString = Toy_toString(&vm->memoryBucket, "Keyboard"); + Toy_declareScope(vm->scope, keyboardString, TOY_VALUE_OPAQUE, TOY_OPAQUE_FROM_POINTER(&keyboardData), true); + Toy_freeString(keyboardString); } //main file @@ -191,7 +223,9 @@ int main() { initGameAPI(&vm); initActorAPI(&vm); - Toy_setOpaqueAttributeHandler(handleActorAttributes); + Toy_setOpaqueAttributeHandler(dispatchOpaqueAttributes); + + // inspect_bytecode(entryCode); Toy_runVM(&vm); Toy_resetVM(&vm, false, false); //leave in a valid, but unset state @@ -209,12 +243,6 @@ int main() { } while (!WindowShouldClose()) { - //TODO: player input - // if (IsKeyDown(KEY_UP)) player.position.y -= 5.0f; - // if (IsKeyDown(KEY_DOWN)) player.position.y += 5.0f; - // if (IsKeyDown(KEY_LEFT)) player.position.x -= 5.0f; - // if (IsKeyDown(KEY_RIGHT)) player.position.x += 5.0f; - //process the actors (if possible) processActors(&vm); diff --git a/source/opaque_type.h b/source/opaque_type.h new file mode 100644 index 0000000..08a777d --- /dev/null +++ b/source/opaque_type.h @@ -0,0 +1,11 @@ +#pragma once + +//always first member of any opaques +typedef enum OpaqueType { + OPAQUE_KEYBOARD, + OPAQUE_ACTOR, +} OpaqueType; + +typedef struct DummyOpaque { + OpaqueType type; +} DummyOpaque; \ No newline at end of file