// 1202501256.010354 a text adventure game that i wrote // initially to test out my editor but then it started // to become actually cool and useful lol. // written by dwrr on 1202501256.010425 // old: // a text-based choose your own adventure game i am coding up to test my using my editor with programming. #include #include #include #include #include #include #include #include #include #include #include typedef uint64_t nat; static const nat max_text_width = 50; static const nat text_speed = 50000; static void say(const char* string) { const nat length = (nat) strlen(string); nat width = 0; putchar(9); for (nat i = 0; i < length; i++) { putchar(string[i]); if (width > max_text_width and isspace(string[i])) { printf("\n\t"); width = 0; } if (string[i] == 10) width = 0; else if (string[i] != 9) width++; usleep((unsigned) (rand() % (int) text_speed)); fflush(stdout); } printf("\033[38;5;238m/\033[0m "); fflush(stdout); getchar(); puts(""); } static nat choose(const char* actions) { ask: printf("\t 0. "); nat number = 0; for (nat i = 0; actions[i]; i++) { if (actions[i] == '.') { i++; number++; if (actions[i] == ' ') printf("\n\t %llu. ", number); else if (not actions[i]) break; } else putchar(actions[i]); } printf("\n\n\t: "); char buffer[4096] = {0}; fgets(buffer, sizeof buffer, stdin); puts(""); if (*buffer == 'q') return (nat) -1; if (isdigit(*buffer)) return (nat) atoi(buffer); goto ask; } int main(int argc, const char** argv) { if (argc == 1) return puts("usage: ./play "); struct story_state { char* name; char* question; char** strings; nat string_count; nat* gotos; char** goto_names; nat goto_count; }; struct story { char* title; struct story_state* states; nat state_count; }; struct story story = { .title = calloc(1, 1), .states = NULL, .state_count = 0, }; { int file = open(argv[1], O_RDONLY); if (file < 0) { perror("open"); exit(1); } size_t length = (size_t) lseek(file, 0, SEEK_END); lseek(file, 0, SEEK_SET); char* contents = calloc(length + 1, 1); read(file, contents, length); close(file); enum appending_states { append_to_title, append_to_state, append_to_string, append_to_question, append_to_choice }; for (nat append_state = 0, start = 0, count = 0, index = 0; index < length; index++) { if (not isspace(contents[index])) { if (not count) start = index; count++; if (index == length - 1) goto process; continue; } else if (not count) continue; process:; char* word = strndup(contents + start, (size_t) count); const nat word_length = count; count = 0; char** dest = NULL; if (not strcmp(word, ".eoi")) break; else if (not strcmp(word, ".title")) append_state = append_to_title; else if (not strcmp(word, ".options")) append_state = append_to_question; else if (not strcmp(word, ".p")) { if (not story.state_count) { puts("expected .state"); abort(); } struct story_state* this = story.states + story.state_count - 1; this->strings = realloc(this->strings, sizeof(char*) * (this->string_count + 1)); this->strings[this->string_count++] = calloc(1, 1); append_state = append_to_string; } else if (not strcmp(word, ".choice")) { if (not story.state_count) { puts("expected .state"); abort(); } struct story_state* this = story.states + story.state_count - 1; this->goto_names = realloc(this->goto_names, sizeof(char*) * (this->goto_count + 1)); this->gotos = realloc(this->gotos, sizeof(nat) * (this->goto_count + 1)); this->goto_names[this->goto_count++] = calloc(1, 1); this->gotos[this->goto_count - 1] = 0; append_state = append_to_choice; } else if (not strcmp(word, ".state")) { struct story_state new = { .name = calloc(1,1), .question = calloc(1,1), .strings = NULL, .string_count = 0, .gotos = NULL, .goto_names = NULL, .goto_count = 0, }; story.states = realloc(story.states, sizeof(struct story_state) * (story.state_count + 1)); story.states[story.state_count++] = new; append_state = append_to_state; } else if (append_state == append_to_title) { dest = &story.title; goto append_word; } else if (append_state == append_to_state) { dest = &(story.states[story.state_count - 1].name); goto append_word; } else if (append_state == append_to_question) { if (not story.state_count) { puts("expected .state directive"); abort(); } dest = &(story.states[story.state_count - 1].question); goto append_word; } else if (append_state == append_to_string) { if (not story.state_count) { puts("expected .state directive"); abort(); } struct story_state* this = story.states + story.state_count - 1; dest = &(this->strings[this->string_count - 1]); goto append_word; } else if (append_state == append_to_choice) { if (not story.state_count) { puts("expected .state directive"); abort(); } struct story_state* this = story.states + story.state_count - 1; dest = &(this->goto_names[this->goto_count - 1]); goto append_word; } else { puts("error: parsing error while reading in story details..."); abort(); } continue; append_word:; const size_t t = strlen(*dest); *dest = realloc(*dest, t + word_length + 2); memcpy(*dest + t, word, word_length); (*dest)[t + word_length] = ' '; (*dest)[t + word_length + 1] = 0; } } for (nat i = 0; i < story.state_count; i++) { for (nat j = 0; j < story.states[i].goto_count; j++) { for (nat k = 1; k < story.state_count; k++) { if (not strcmp(story.states[k].name, story.states[i].goto_names[j])) story.states[i].gotos[j] = k; } } if (story.states[i].question and strlen(story.states[i].question)) story.states[i].question[strlen(story.states[i].question) - 1] = 0; } const bool debug_story = false; if (debug_story) { puts("debugging story..."); printf("story title: %s\n", story.title); printf("story state count: %llu\n", story.state_count); printf("story states\n"); for (nat i = 0; i < story.state_count; i++) { printf("\tstate #%llu: \n\t . name = %s\n", i, story.states[i].name); printf("\t . question = %s\n", story.states[i].question); printf("\t . goto_count = %llu\n", story.states[i].goto_count); printf("\t . string_count = %llu\n", story.states[i].string_count); printf("\t . gotos = { "); for (nat n = 0; n < story.states[i].goto_count; n++) printf("%llu ", story.states[i].gotos[n]); printf("}\n"); printf("\t . strings = {\n"); for (nat n = 0; n < story.states[i].string_count; n++) printf("\t\t<<<%s>>>\n", story.states[i].strings[n]); printf("\t . }\n"); printf("\t . goto_names = {\n"); for (nat n = 0; n < story.states[i].goto_count; n++) printf("\t\t<<<%s>>>\n", story.states[i].goto_names[n]); printf("\t . }\n"); puts("\n\n\n"); } } srand((unsigned) time(0)); puts(""); say("hello! this is a choose your own adventure game. press enter at the \"/\", input numbers to select options, and enjoy the experience! "); nat state = 0; while (1) { for (nat i = 0; i < story.states[state].string_count; i++) say(story.states[state].strings[i]); if (story.states[state].goto_count > 1) { const nat c = choose(story.states[state].question); state = story.states[state].gotos[c]; } else if (story.states[state].goto_count == 1) { state = story.states[state].gotos[0]; } else break; } } /* todo: translate this story that is below, into the file format that we made: say("now playing the story: the darkness"); say("you find yourself in a very dark room, with a single candle on the floor lighting up a small area around it. you see a few stones on the ground, near the candle."); say("what do you do?"); begin_c:; int c = choose("look around the room. go closer to the candle. pick up one of the stones."); if (c == 0) { say("the room is very dark, and you cannot see where you are going."); goto begin_c; } else if (c == 1) { say("the candle is brings with it some warmth as you approach. you see a small hand written note on a scrap of paper on the small metal tray which the candle rests on."); say("it reads \"i'm sorry\""); c = choose("take scrap of paper. blow out candle. say \"is anyone there?\"."); if (c == 0) { say("you put the scrap of paper in the left pocket of your jeans, thinking this might come in handy later."); } else if (c == 1) { say("you blow out the candle. the room turns completely pitch black."); say("shortly after doing so, you hear a noise, like that of a lizard. some foot step noises follow, and you frantically look around to try to find the direction they are coming from. for a couple seconds there is just silence. you wish you did not blow out the candle, as it was the only way of seeing anything in this room."); } else if (c == 2) { say("you ask into the darkness, \"is anyone there?\". there is no reply, but you also notice you cannot hear your voice echo at all, as if there are no walls to this room, which slightly terrifies you."); } } else if (c == 2) { say("you pick up one of the stones near the candle. it is a shiny black color, and somewhat wet and slippery. "); say("you notice a couple more stones on the ground similar to it around the candle, and soon realize that the floor of this room is completely covered in these small black stones."); } say("end of the game. thanks for playing!!!"); } */