Init commit
This commit is contained in:
commit
706e87a123
9
Makefile
Normal file
9
Makefile
Normal 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
|
250
cyoa.leg
Normal file
250
cyoa.leg
Normal 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
61
demo.story
Normal 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
13
simple.story
Normal 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
57
spec.txt
Normal 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.
|
Loading…
Reference in New Issue
Block a user