Init commit

This commit is contained in:
Dane Johnson 2021-12-17 13:54:05 -06:00
commit 706e87a123
7 changed files with 1407 additions and 0 deletions

9
Makefile Normal file
View File

@ -0,0 +1,9 @@
CC = gcc
CFLAGS = -g
all: cyoa
cyoa: cyoa.c
cyoa.c: cyoa.leg
leg -o $@ $^
clean:
rm -f cyoa.c cyoa

BIN
cyoa Executable file

Binary file not shown.

1017
cyoa.c Normal file

File diff suppressed because it is too large Load Diff

250
cyoa.leg Normal file
View File

@ -0,0 +1,250 @@
%{
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef struct page_t page_t;
typedef struct footer_t footer_t;
typedef struct page_node_t page_node_t;
typedef struct choicelist choicelist;
struct page_node_t {
page_t *page;
page_node_t *next;
};
#define FOOTER_END 1
#define FOOTER_GOTO 2
#define FOOTER_CHOICES 3
struct footer_t {
int type;
union {
choicelist *choices;
char *link;
};
};
struct page_t {
char *id;
char *body;
footer_t footer;
};
typedef struct statcheck_t statcheck_t;
typedef struct statchange_t statchange_t;
typedef struct choice_t choice_t;
#define STATCHECK_GT 1
#define STATCHECK_LT 2
struct statcheck_t {
char *stat;
int value;
int rel;
};
struct statchange_t {
char *stat;
int addend;
};
#define CHOICE_STATCHECK 0x1
#define CHOICE_STATCHANGE 0x2
struct choice_t {
int flags;
int option;
char *flavor;
statcheck_t statcheck;
char *redirect;
statchange_t statchange;
};
struct choicelist {
choice_t *choice;
choicelist *next;
};
page_node_t *pages;
choice_t *make_choice();
void add_page(page_t*);
void add_choice(page_t*, choice_t*);
page_t *emit_ending();
page_t *emit_goto(char*);
page_t *emit_choices();
void append_body(page_t *page, char *str);
union value {
page_t *page;
char *string;
choice_t *choice;
};
#define YYSTYPE union value
%}
Story = BlankLine* (p:Page { add_page(p.page); })+ EndOfFile;
Page = h:Header b:Body { $$.page = b.page; $$.page->id = h.string}
BlankLine*;
Header = < Identifier > { $$.string = strndup(yytext, yyleng); }
Newline BlankLine*;
Identifier = [A-Z][A-Z0-9_]+;
Body = Footer | t:TextLine b:Body { $$ = b; append_body(b.page, t.string);};
TextLine = < (!Newline .)* Newline > { $$.string = strndup(yytext, yyleng); };
Footer = Ending { $$.page = emit_ending(); }
| g:Goto { $$.page = emit_goto(g.string); }
| Choice+ { $$.page = emit_ending(); }
;
Goto = 'GOTO' Spacing < i:Identifier > Newline
{ $$.string = strndup(yytext, yyleng); }
;
Ending = 'THE END' Newline;
Choice = { $$.choice = make_choice(); } [0-9]+ ')' Spacing < (!Redirect .)+ > Redirect;
Redirect = StatCheck? Spacing '[' Identifier ']' Spacing StatChange? Newline;
StatCheck = '<' StatName Spacing [0-9]+ ('+' | '-')? '>';
StatChange = '(' ('+' | '-') [0-9]+ Spacing StatName ')';
StatName = [A-Za-z]+;
EndOfFile = !.;
BlankLine = Spacing Newline;
Spacing = (' ' | '\t')*;
Newline = '\r\n' | '\r' | '\n';
%%
void add_page(page_t *page) {
page_node_t *mynode = (page_node_t*) malloc(sizeof(page_node_t));
mynode->page = page;
mynode->next = NULL;
page_node_t *curr = pages;
page_node_t *prev = NULL;
if (curr) {
for(; curr; prev = curr, curr = curr->next);
prev->next = mynode;
} else {
pages = mynode;
}
}
void add_choice(page_t *page, choice_t* choice) {
assert(page != choice);
choicelist *mychoices = (choicelist *) malloc(sizeof(choicelist));
mychoices->choice = choice;
mychoices->next = NULL;
choicelist *curr = page->footer.choices;
choicelist *prev = NULL;
if (curr) {
for(; curr; prev = curr, curr = curr->next);
prev->next = mychoices;
} else {
page->footer.choices = mychoices;
}
}
static inline page_t *make_page() {
page_t *mypage = (page_t*) malloc(sizeof(page_t));
mypage->id = NULL;
mypage->body = NULL;
return mypage;
}
inline choice_t *make_choice() {
choice_t *mychoice = (choice_t*) malloc(sizeof(choice_t));
mychoice->flags = 0x0;
printf("choice 0x%x\n", mychoice);
return mychoice;
}
page_t *emit_goto(char *id) {
page_t *mypage = make_page();
mypage->footer.type = FOOTER_GOTO;
mypage->footer.link = id;
return mypage;
}
page_t *emit_ending() {
page_t *mypage = make_page();
mypage->footer.type = FOOTER_END;
return mypage;
}
page_t *emit_choices() {
page_t *mypage = make_page();
mypage->footer.type = FOOTER_CHOICES;
mypage->footer.choices = NULL;
printf("page 0x%x\n", mypage);
return mypage;
}
void append_body(page_t *page, char *str) {
if (page->body) {
int size = strlen(page->body) + strlen(str) + 1;
char *newstring = (char*) calloc(size, sizeof(char));
strcpy(newstring, str);
strcat(newstring, page->body);
free(page->body);
page->body = newstring;
} else {
page->body = str;
}
}
void print_page(page_t *page) {
printf("HEADER: %s\nBODY: %s\n", page->id, page->body);
switch (page->footer.type) {
case FOOTER_END:
printf("THE END\n");
break;
case FOOTER_GOTO:
printf("GOTO %s\n", page->footer.link);
break;
case FOOTER_CHOICES:
printf("CHOICES:\n");
break;
}
}
int main() {
if (!yyparse()) {
printf("Parsing Error!\n");
return 1;
} else {
for (page_node_t *curr = pages; curr; curr = curr->next) {
print_page(curr->page);
}
}
return 0;
}
/* Local Variables: */
/* mode: text */
/* End: */

61
demo.story Normal file
View File

@ -0,0 +1,61 @@
START
You are a young boy at a carnival. The Ferris wheel is in front of you and looks tempting. You can also see the dark ride.
1) Ride the Ferris wheel [WHEEL]
2) Ride the Dark ride [RIDE]
RIDE
You ride the dark ride. It's not really that interesting and by the time it's over you have to go home.
THE END
WHEEL
You get to the front of the line, and you are placed in a gondola with your school crush. The two of you have
been playmates for some time, but you think that you are old enough to be more.
1) Lean in and kiss them at the top [KISS] (+1 Romance)
2) Let the moment pass [NOKISS] (+1 Willpower)
KISS
You lean in and give them a quick kiss on the cheek. It's very romantic in the moonlight.
GOTO AFTERWHEEL
NOKISS
You let the moment pass, and instead look out over the hills towards the moon. You feel like
anything is possible in your life.
GOTO AFTERWHEEL
AFTERWHEEL
After you get off the Ferris Wheel, it is getting a bit late. You still want to go on the dark ride, maybe with
your school crush. You can also go ride the teacups. Or you could just go home.
1) Go with your crush to the dark ride <Romance 1+> [RIDE_TOGETHER]
2) Ride the teacups <Willpower 1+> [TEACUPS]
3) Go home [HOME]
RIDE_TOGETHER
You ride into the dark tunnel and you can feel your friend squeeze your hand. A warm feeling covers your body.
THE END
TEACUPS
The swirling vortex envelopes you, but you are undeterred. You leave the park feeling like you are ready to take
on the world.
THE END
HOME
You return home to your bed. A soft pillow greets you and you dream of the night's festivities.
THE END

13
simple.story Normal file
View File

@ -0,0 +1,13 @@
START
One day Dane took a really big shit.
Dane: "Wow, that was a really big shit"
1) Don't die [NEXT]
NEXT
Then he died.
Fuck you Dane
THE END

57
spec.txt Normal file
View File

@ -0,0 +1,57 @@
###################################
# Untitled Storybook Game Project #
###################################
This will be a project to create a system whereby "Choose Your Own Adventure" (CYOA) style stories can be easily written.
Each page will read out story elements, and then prompt the user to select a new page based on a decision
they would like to make.
Making certain choices will also award ad-hoc stat bonuses i.e. +1 Knowledge or something, all
stats are presumed to be 0 until points are awarded. This stat system also includes the ability
to make checks when certain choices become available, or once they are selected.
Requirements:
1. A language description and recognizer for creating CYOA-style stories.
2. An interpreter and interface for interacting with these stories.
3. Both story-writing and story-interaction tools need to be cross platform and relatively straightforwards for
a non technical user to operate.
Story language PEG: (with apologies, this is my first PEG)
Story <- BlankLine* Page+ EndOfFile
Page <- Header Body BlankLine*
Header <- Identifier Newline BlankLine*
Identifier <- [A-Z][A-Z0-9_]*
Body <- Footer / TextLine Body
TextLine <- (!Newline .)+ Newline
Footer <- Ending / Goto / Choice+
Goto <- 'GOTO' Spacing Identifier Newline
Ending <- 'THE END' Newline
Choice <- [0-9]+ ')' Spacing (!Redirect .)+ Redirect
Redirect <- StatCheck? Spacing '[' Identifier ']' Spacing StatChange? Newline
StatCheck <- '<' StatName Spacing [0-9]+ ('+' / '-')? '>'
StatChange <- '(' ('+' / '-') [0-9]+ Spacing StatName ')'
StatName <- [A-Za-z]+
EndOfFile <- !.
BlankLine <- Spacing Newline
Spacing <- (' ' / '\t')*
Newline <- '\r\n' / '\r' / '\n'
Work log:
12/13/21
Right now I need to make a decision before any of the work has started on how the user would interact with the system.
I think for a MVP it would be okay to just write a console application, but I think for the long term there needs to
be a decision made on how this will work. I'm leaning towards one of three options.
1) We put the whole thing on the JVM and make it a library or something.
Maybe in the future we can make a frontend for making and playing stories
as a web interface, but we could make the interface pluggable as far as the backend is concerned.
2) I build this non-traditional game in a traditional game engine (Godot probably). This ensures cross-platform
availibility. I'm not 100% sure about my ability to use parsers in Godot, but this looks like a good option.
3) I build this as a reactive web page. I know this sounds like a terrible idea, but there are actually a lot
of cool image and text transition libraries out there. It does mean setting up something like React tho, which
like, just kill me now
Out of the three of these I think the Java thing would be the easiest and I think the Godot thing would be the best.