Sfoglia il codice sorgente

Dirty base realisation

Evgeniy Parfenyuk 1 mese fa
parent
commit
64a964d313
8 ha cambiato i file con 487 aggiunte e 6 eliminazioni
  1. BIN
      bg.jpg
  2. BIN
      ch.png
  3. 209 0
      src/chunks.c
  4. 48 0
      src/chunks.h
  5. 40 6
      src/main.c
  6. 56 0
      src/test.vnrs
  7. 33 0
      src/text_utils.h
  8. 101 0
      src/vnrs_parser.c

BIN
bg.jpg


BIN
ch.png


+ 209 - 0
src/chunks.c

@@ -0,0 +1,209 @@
+/*
+chunk.c -- working with chunks of Vinora Screenplay.
+
+Copyright (c) 2025 Evgeniy "Parthen" Parfenyuk <parthen [at] riseup.net>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "chunks.h"
+#include "text_utils.h"
+
+
+/* ===============================================================
+ * check_<chunk>_chunk functions are returning <chunk> if true and
+ * CHUNK_DIALOG if false. (And CHUNK_EMPTY for invalid lines).
+ * ===============================================================
+*/
+
+static chunk_t check_character_chunk(const char *s)
+{    
+    if (!s || s[0] == '\0') return CHUNK_EMPTY;
+    
+    if (s[0] == '@' && s[strlen(s) - 2] == ' '
+                    && s[strlen(s) - 3] == ' ')
+        return CHUNK_CHARACTER;
+
+    return CHUNK_DIALOG;
+    
+}
+
+static chunk_t check_header_chunk(const char *s)
+{
+    if (!s || s[0] == '\0') return CHUNK_EMPTY;
+
+    int hash_count = 0;
+    for (size_t i = 0; i < strlen(s); i++) {
+        if (s[i] == '#') {
+            hash_count++;
+            continue;
+        }
+
+        if (s[i] == ' ' && hash_count > 0 && hash_count <= 6)
+            return CHUNK_HEADER;
+        else
+            return CHUNK_DIALOG;
+    }
+    return CHUNK_DIALOG;
+}
+
+static chunk_t check_choice_chunk(const char *s)
+{
+    if (!s || s[0] == '\0') return CHUNK_EMPTY;
+
+    if (s[0] != '+' && s[0] != '*' && s[0] != '-')
+        return CHUNK_DIALOG;
+    
+    size_t pos = 1;
+    while (is_whitespace(s[pos])) pos++;
+    if (s[pos] != '[') return CHUNK_DIALOG;
+    pos++;
+    
+    while (s[pos] != ']' && s[pos] != '\0') pos++;
+    if (s[pos] != ']') return CHUNK_DIALOG;
+    pos++;
+
+    while (is_whitespace(s[pos])) pos++;
+    if (s[pos] != '(') return CHUNK_DIALOG;
+    pos++;
+    int paren_level = 1;
+    while (s[pos] != '\0') {
+        if (s[pos] == '(') paren_level++;
+        else if (s[pos] == ')') { paren_level--; if (paren_level == 0) break;}
+        pos++;
+    }
+    if (paren_level != 0) return CHUNK_DIALOG;  // unclosed (
+
+    pos++;
+
+    return CHUNK_CHOICE;
+}
+
+static chunk_t check_image_chunk(const char *s)
+{
+    if (!s || s[0] == '\0') return CHUNK_EMPTY;
+
+    if (!s || s[0] != '!' || s[1] == '\0')
+        return CHUNK_DIALOG;
+    
+    size_t pos = 1;
+    while (s[pos] != ']' && s[pos] != '\0' && s[pos+1] != '\0') pos++;
+    if (s[pos] != ']' || s[pos+1] != '(') return CHUNK_DIALOG;
+    
+    pos += 2;
+    while (s[pos] != ')' && s[pos] != '\0') pos++;
+    if (s[pos] != ')') return CHUNK_DIALOG;
+
+    return CHUNK_IMAGE;
+}
+
+static chunk_t check_media_chunk(const char *s)
+{
+    if (!s || s[0] == '\0') return CHUNK_EMPTY;
+
+    if (!s || s[0] == '\0' || s[1] == '\0')
+        return CHUNK_DIALOG;
+    
+    size_t pos = 0;
+    while (s[pos] != ']' && s[pos] != '\0' && s[pos+1] != '\0') pos++;
+    if (s[pos] != ']' || s[pos+1] != '(') return CHUNK_DIALOG;
+    
+    pos += 2;
+    while (s[pos] != ')' && s[pos] != '\0') pos++;
+    if (s[pos] != ')') return CHUNK_DIALOG;
+
+    return CHUNK_MEDIA;
+}
+
+static chunk_t check_comment_chunk(const char *s)
+{
+    if (!s || s[0] == '\0') return CHUNK_EMPTY;
+
+    if (!s || s[0] != '>' || s[1] == '\0')
+        return CHUNK_DIALOG;
+
+    return CHUNK_COMMENT;
+}
+
+static chunk_t check_directive_chunk(const char *s)
+{
+    if (!s || s[0] == '\0') return CHUNK_EMPTY;
+
+    if (s[0] == '{' && s[strlen(s) - 2] == '}' &&
+        s[1] == '{' && s[strlen(s) - 3] == '}')
+        return CHUNK_DIRECTIVE;
+
+    return CHUNK_DIALOG;
+}
+
+static chunk_t check_literal_chunk(const char *s)
+{
+    if (!s || s[0] == '\0') return CHUNK_EMPTY;
+
+    if (s[0] == '`' &&
+        s[1] == '`' &&
+        s[2] == '`')
+        return CHUNK_LITERAL;
+
+    return CHUNK_DIALOG;
+}
+
+/* ===============================================================
+ * End of check_<chunk>_chunk functions
+ * ===============================================================
+*/
+
+// First line of chunk is enough to identify type
+chunk_t get_chunk_type(const char *s)
+{
+    if (!s || s[0] == '\0') return CHUNK_EMPTY;
+
+    switch (s[0]) {
+        case '@': return check_character_chunk(s); break;
+        case '#': return check_header_chunk(s);    break;
+
+        case '+':
+        case '-':
+        case '*': return check_choice_chunk(s);    break;
+
+        case '!': return check_image_chunk(s);     break;
+        case '[': return check_media_chunk(s);     break;
+        case '>': return check_comment_chunk(s);   break;
+        case '{': return check_directive_chunk(s); break;
+        case '`': return check_literal_chunk(s);   break;
+        default:  return CHUNK_DIALOG;    break;
+    }
+}
+
+char *chunk_type_to_name(const chunk_t t)
+{
+    switch (t) {
+        case CHUNK_DIALOG:         return "DIALOG";
+        case CHUNK_CHARACTER:      return "CHARACTER";
+        case CHUNK_HEADER:         return "HEADER";
+        case CHUNK_CHOICE:         return "CHOICE";
+        case CHUNK_IMAGE:          return "IMAGE";
+        case CHUNK_MEDIA:          return "MEDIA";
+        case CHUNK_COMMENT:        return "COMMENT";
+        case CHUNK_DIRECTIVE:      return "DIRECTIVE";
+        case CHUNK_LITERAL:        return "LITERAL";
+        case CHUNK_EMPTY:          return "EMPTY";
+        default:                   return "UNKNOWN";
+    }
+}
+
+

+ 48 - 0
src/chunks.h

@@ -0,0 +1,48 @@
+/*
+chunk.h -- working with chunks of Vinora Screenplay.
+
+Copyright (c) 2025 Evgeniy "Parthen" Parfenyuk <parthen [at] riseup.net>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef CHUNKS_H
+#define CHUNKS_H
+
+#define MAX_LINE_LENGTH  320
+#define WARN_LINE_LENGTH 80
+
+typedef enum {
+    CHUNK_DIALOG,
+    CHUNK_CHARACTER,
+    CHUNK_HEADER,
+    CHUNK_CHOICE,
+    CHUNK_IMAGE,
+    CHUNK_MEDIA,
+    CHUNK_COMMENT,
+    CHUNK_DIRECTIVE,
+    CHUNK_LITERAL,
+    CHUNK_EMPTY
+} chunk_t;
+
+typedef struct {
+    char data[MAX_LINE_LENGTH];
+    size_t len;
+} string_t;
+
+// First line of chunk is enough to identify type
+chunk_t get_chunk_type(const char *s);
+char *chunk_type_to_name(const chunk_t t);
+
+#endif

+ 40 - 6
src/main.c

@@ -20,23 +20,57 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 #include <raylib.h>
 #include <string.h>
 
+typedef struct {
+    Texture2D img;
+    Vector2 pos; // In percents
+} Layer;
+
+typedef struct {
+    Layer layers[10];
+    int layers_count;
+
+    char *name;
+    Color name_color;
+    char *text;
+    Color text_color;
+} GameState; 
+
+
 int main(void)
 {
-    const int screenWidth = 800;
-    const int screenHeight = 600;
+    const int screenWidth = 1920;
+    const int screenHeight = 1080;
 
+    SetConfigFlags(FLAG_FULLSCREEN_MODE);
     InitWindow(screenWidth, screenHeight,
                "Vinora Engine");
 
-    SetTargetFPS(60);   
     
-    char *text = "Hello, World!";
+    GameState cur_state = {0};
+
+    cur_state.name = "Alice";
+    cur_state.name_color = RAYWHITE;
+
+    cur_state.layers[0].img = LoadTexture("bg.jpg");
+    cur_state.layers[0].pos = (Vector2){0, 0};
+    cur_state.layers[1].img = LoadTexture("ch.png");
+    cur_state.layers[1].pos = (Vector2){0.5, 0};
+    cur_state.layers_count += 2;
+
     while (!WindowShouldClose())   
     {
         BeginDrawing();
             ClearBackground(RAYWHITE);
-            DrawText(text, screenWidth/2  - (strlen(text)*40/2)/2,
-                           screenHeight/2 - 40, 40, GRAY);
+           
+            for (int i = 0; i < cur_state.layers_count; i++) {
+                DrawTexture(cur_state.layers[i].img, 
+                            cur_state.layers[i].pos.x, 
+                            cur_state.layers[i].pos.y,
+                            RAYWHITE);
+            }
+            DrawText(cur_state.name, screenWidth/2  - (strlen(cur_state.name)*40/2)/2,
+                           screenHeight/5*4 - 40, 40, cur_state.name_color);
+            DrawFPS(5, 5);
 
         EndDrawing();
     }

+ 56 - 0
src/test.vnrs

@@ -0,0 +1,56 @@
+# Chapter 1: The Beginning
+## Small header
+###### SMALLEST HEADER
+
+##Invalid header
+####### Invalid header
+
+
+{{NVL}}
+
+![](bg/park/evening.jpg)
+
+[](music/calm_piano.mp3)
+
+ > This is a comment and will be ignored in normal mode
+
+ Evening falls over the quiet park. The moon hangs low in the sky.
+
+@NARRATOR  
+
+The wind rustles through the leaves as two figures sit on an old wooden bench.
+
+{{ADV}}
+
+![](sprites/alice/normal.png){left}
+![](sprites/bob/normal.png){right}
+
+@ALICE  
+Hi Bob! It's such a beautiful night, isn't it?
+
+@BOB  
+Yeah, Alice. The moon looks amazing tonight.
+
+@ALICE  
+*smiles softly* You're always so quiet when we're alone like this...
+
+@BOB  
+I just... like listening to you.
+
++   []()
+- [(test)
+* [(TODO) I will go with Chloe](chloe_route.vnrs
+
+
+```
+Literal text!
+```
+
+
+
++ [I will go with Bob](bob_route.vnrs)
+- [(TODO) I will go with Chloe](chloe_route.vnrs)
+* [Go alone](secret_route.vnrs){social<0}
+
+
++ [Link, which not be executed](unwritten.vnrs)

+ 33 - 0
src/text_utils.h

@@ -0,0 +1,33 @@
+/*
+text_utils.h -- useful functions for working with text.
+
+Copyright (c) 2025 Evgeniy "Parthen" Parfenyuk <parthen [at] riseup.net>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef TEXT_UTILS_H
+#define TEXT_UTILS_H
+
+#include <stdbool.h>
+
+inline bool is_whitespace(int ch)
+{
+    return (unsigned char)ch <= 0x20 &&
+           (ch == ' '  || ch == '\t' || ch == '\n' ||
+            ch == '\r' || ch == '\v' || ch == '\f');
+}
+
+
+#endif

+ 101 - 0
src/vnrs_parser.c

@@ -0,0 +1,101 @@
+/*
+vnrs_parser.c -- parse Vinora Screenplay files.
+
+Copyright (c) 2025 Evgeniy "Parthen" Parfenyuk <parthen [at] riseup.net>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "chunks.h"
+#include "text_utils.h"
+
+
+
+static void normalise(char *s)
+{
+    // Search first meaningful character
+    int i = 0;
+    while (is_whitespace(s[i]))
+        i++;
+    
+    // Remove whitespace chars from beginning
+    if (i > 0) {
+        int j = 0;
+        while (s[i])
+            s[j++] = s[i++];
+        s[j] = '\0';
+    }
+
+    // Replace last \n with space 
+    char *nl = strchr(s, '\n');
+    if (nl)
+        *nl = ' ';
+}
+
+
+static char *get_line(FILE *input)
+{
+    static char buffer[MAX_LINE_LENGTH];
+
+    if (!input) return NULL;
+
+    if (fgets(buffer, MAX_LINE_LENGTH, input) == NULL)
+        return NULL;
+    normalise(buffer); 
+    return buffer;
+}
+
+
+void parse_vnrs(FILE *input, FILE *output)
+{
+    if (!input || !output) return;
+    rewind(input);
+
+    int c;
+    while ((c = fgetc(stdin)) != 'q') {
+        char *s = get_line(input);
+        char *t = chunk_type_to_name(get_chunk_type(s));
+        if (s)
+            printf("%10s|%s", t, s);
+        else
+            break;
+    }
+}
+
+/*
+int main(int argc, char *argv[])
+{
+    if (argc < 2) {
+        fprintf(stderr, "Usage: ./parser <filename>.vnrs\n");
+        return 1;
+    }
+
+    FILE *f;
+    f = fopen(argv[1], "r");
+    if (!f) {
+        perror(argv[1]);
+        return 2; 
+    }
+
+    parse_vnrs(f, stdout);
+
+    fclose(f);
+    return 0;
+}
+*/