diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 7a3094a..0000000 --- a/LICENSE +++ /dev/null @@ -1,11 +0,0 @@ -DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -Version 2, December 2004 - -Copyright (C) 2004 Sam Hocevar - -Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. - -DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README.md b/README.md deleted file mode 100644 index e781560..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# L.P.T - -Visual Novel Engine \ No newline at end of file diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..d5ae1b1 --- /dev/null +++ b/README.txt @@ -0,0 +1,563 @@ +Installing +========== + +Windows +------- + +In the ttf folder, double-click each font file, click “Install font”; to install all at once, select all files, right-click, and choose “Install” + +OR + +Use https://chocolatey.org: + + choco install firacode + + +macOS +----- + +In the downloaded TTF folder: + +1. Select all font files +2. Right click and select `Open` (alternatively `Open With Font Book`) +3. Select "Install Font" + +OR + +Use http://brew.sh: + + `brew tap homebrew/cask-fonts` + `brew install font-fira-code` + + +Ubuntu Zesty (17.04), Debian Stretch (9) or newer +------------------------------------------------- + +1. Make sure that the `universe` (for Ubuntu) or `contrib` (for Debian) repository is enabled (see https://askubuntu.com/questions/148638/how-do-i-enable-the-universe-repository or https://wiki.debian.org/SourcesList#Component) +2. Install `fonts-firacode` package either by executing `sudo apt install fonts-firacode` in the terminal or via GUI tool (like “Software Center”) + + +Arch Linux +---------- + +Fira Code package is available in the official repository: https://www.archlinux.org/packages/community/any/ttf-fira-code/. + +Variant of Fira Code package is available in the AUR: https://aur.archlinux.org/packages/otf-fira-code-git/. + + +Gentoo +------ + + emerge -av media-fonts/fira-code + + +Fedora +------ + +To install, perform the following commands: + + dnf install fira-code-fonts + + +Solus +----- + +Fira Code package is available in the official repository: `font-firacode-ttf` and `font-firacode-otf`. +They can be installed by running: + + sudo eopkg install font-firacode-ttf font-firacode-otf + + +Void linux +---------- + + xbps-install font-firacode + + +Linux Manual Installation +------------------------- + +With most desktop-oriented distributions, double-clicking each font file in the ttf folder and selecting “Install font” should be enough. If it isn’t, create and run `download_and_install.sh` script: + + #!/usr/bin/env bash + + fonts_dir="${HOME}/.local/share/fonts" + if [ ! -d "${fonts_dir}" ]; then + echo "mkdir -p $fonts_dir" + mkdir -p "${fonts_dir}" + else + echo "Found fonts dir $fonts_dir" + fi + + for type in Bold Light Medium Regular Retina; do + file_path="${HOME}/.local/share/fonts/FiraCode-${type}.ttf" + file_url="https://github.com/tonsky/FiraCode/blob/master/distr/ttf/FiraCode-${type}.ttf?raw=true" + if [ ! -e "${file_path}" ]; then + echo "wget -O $file_path $file_url" + wget -O "${file_path}" "${file_url}" + else + echo "Found existing file $file_path" + fi; + done + + echo "fc-cache -f" + fc-cache -f + +More details: https://github.com/tonsky/FiraCode/issues/4 + + +FreeBSD +------- + +Using pkg(8): + + pkg install firacode + +OR + +Using ports: + + cd /usr/ports/x11-fonts/firacode && make install clean + + +Enabling ligatures +================== + +Atom +---- + +To change your font to Fira Code, open Atom's preferences (`cmd + ,` on a Mac, `ctrl + ,` on PC), make sure the "Settings" tab is selected, or the "Editor" in Atom 1.10+, and scroll down to "Editor Settings". In the "Font Family" field, enter `Fira Code`. + +If you wish to specify a font weight, for example, Light, use `Fira Code Light` as a font name (Windows) or `FiraCode-Light` (macOS). + +Ligatures are enabled by default in Atom 1.9 and above. + + +VS Code +------- + +To open the settings editor, first from the File menu choose Preferences, Settings or use keyboard shortcut `Ctrl + ,` (Windows) or `Cmd + ,` (macOS). + +To enable FiraCode in the settings editor, under "Commonly Used", expand the "Text Editor" settings and then click on "Font". In the "Font Family" input box type `Fira Code`, replacing any content. Tick the check box "Enables/Disables font ligatures" under "Font Ligatures" to enable the special ligatures. + +If you wish to specify a font weight, for example, Light, use `Fira Code Light` as a font name (Windows) or `FiraCode-Light` (macOS). + + +IntelliJ products +----------------- + +1. Enable in Settings → Editor → Font → Enable Font Ligatures +2. Select `Fira Code` as "Primary font" under Settings → Editor → Font + +Additionally, if a Color Scheme is selected: + +3. Enable in Settings → Editor → Color Scheme → Color Scheme Font → Enable Font Ligatures +4. Select Fira Code as "Primary font" under Settings → Editor → Color Scheme → Color Scheme Font + + +BBEdit, TextWrangler +-------------------- + +Run in your terminal: + + defaults write com.barebones.bbedit "EnableFontLigatures_Fira Code" -bool YES + +Source: https://www.barebones.com/support/bbedit/ExpertPreferences.html + + +Brackets +-------- + +1. From the `View` menu choose `Themes....` +2. Paste `'Fira Code'`, at the beginning of `Font Family` + + +Emacs +----- + +There are a few options when it comes down to using ligatures in +Emacs. They are listed in order of preferred to less-preferred. Pick one! + +1. Using composition mode in Emacs Mac port + +If you're using the latest Mac port of Emacs (https://bitbucket.org/mituharu/emacs-mac by Mitsuharu Yamamoto) for macOS, you can use: + + (mac-auto-operator-composition-mode) + +This is generally the easiest solution, but can only be used on macOS. + +2. Using prettify-symbols + +These instructions are pieced together by https://github.com/Triavanicus, taking some pieces from https://github.com/minad/hasklig-mode. + +This method requires you to install the Fira Code Symbol font, made by https://github.com/siegebell: +https://github.com/tonsky/FiraCode/issues/211#issuecomment-239058632 + + (defun fira-code-mode--make-alist (list) + "Generate prettify-symbols alist from LIST." + (let ((idx -1)) + (mapcar + (lambda (s) + (setq idx (1+ idx)) + (let* ((code (+ #Xe100 idx)) + (width (string-width s)) + (prefix ()) + (suffix '(?\s (Br . Br))) + (n 1)) + (while (< n width) + (setq prefix (append prefix '(?\s (Br . Bl)))) + (setq n (1+ n))) + (cons s (append prefix suffix (list (decode-char 'ucs code)))))) + list))) + + (defconst fira-code-mode--ligatures + '("www" "**" "***" "**/" "*>" "*/" "\\\\" "\\\\\\" + "{-" "[]" "::" ":::" ":=" "!!" "!=" "!==" "-}" + "--" "---" "-->" "->" "->>" "-<" "-<<" "-~" + "#{" "#[" "##" "###" "####" "#(" "#?" "#_" "#_(" + ".-" ".=" ".." "..<" "..." "?=" "??" ";;" "/*" + "/**" "/=" "/==" "/>" "//" "///" "&&" "||" "||=" + "|=" "|>" "^=" "$>" "++" "+++" "+>" "=:=" "==" + "===" "==>" "=>" "=>>" "<=" "=<<" "=/=" ">-" ">=" + ">=>" ">>" ">>-" ">>=" ">>>" "<*" "<*>" "<|" "<|>" + "<$" "<$>" "\\)" #Xe113) + ("[^-]\\(->\\)" #Xe114) + ("\\(->>\\)" #Xe115) + ("\\(-<\\)" #Xe116) + ("\\(-<<\\)" #Xe117) + ("\\(-~\\)" #Xe118) + ("\\(#{\\)" #Xe119) + ("\\(#\\[\\)" #Xe11a) + ("\\(##\\)" #Xe11b) + ("\\(###\\)" #Xe11c) + ("\\(####\\)" #Xe11d) + ("\\(#(\\)" #Xe11e) + ("\\(#\\?\\)" #Xe11f) + ("\\(#_\\)" #Xe120) + ("\\(#_(\\)" #Xe121) + ("\\(\\.-\\)" #Xe122) + ("\\(\\.=\\)" #Xe123) + ("\\(\\.\\.\\)" #Xe124) + ("\\(\\.\\.<\\)" #Xe125) + ("\\(\\.\\.\\.\\)" #Xe126) + ("\\(\\?=\\)" #Xe127) + ("\\(\\?\\?\\)" #Xe128) + ("\\(;;\\)" #Xe129) + ("\\(/\\*\\)" #Xe12a) + ("\\(/\\*\\*\\)" #Xe12b) + ("\\(/=\\)" #Xe12c) + ("\\(/==\\)" #Xe12d) + ("\\(/>\\)" #Xe12e) + ("\\(//\\)" #Xe12f) + ("\\(///\\)" #Xe130) + ("\\(&&\\)" #Xe131) + ("\\(||\\)" #Xe132) + ("\\(||=\\)" #Xe133) + ("[^|]\\(|=\\)" #Xe134) + ("\\(|>\\)" #Xe135) + ("\\(\\^=\\)" #Xe136) + ("\\(\\$>\\)" #Xe137) + ("\\(\\+\\+\\)" #Xe138) + ("\\(\\+\\+\\+\\)" #Xe139) + ("\\(\\+>\\)" #Xe13a) + ("\\(=:=\\)" #Xe13b) + ("[^!/]\\(==\\)[^>]" #Xe13c) + ("\\(===\\)" #Xe13d) + ("\\(==>\\)" #Xe13e) + ("[^=]\\(=>\\)" #Xe13f) + ("\\(=>>\\)" #Xe140) + ("\\(<=\\)" #Xe141) + ("\\(=<<\\)" #Xe142) + ("\\(=/=\\)" #Xe143) + ("\\(>-\\)" #Xe144) + ("\\(>=\\)" #Xe145) + ("\\(>=>\\)" #Xe146) + ("[^-=]\\(>>\\)" #Xe147) + ("\\(>>-\\)" #Xe148) + ("\\(>>=\\)" #Xe149) + ("\\(>>>\\)" #Xe14a) + ("\\(<\\*\\)" #Xe14b) + ("\\(<\\*>\\)" #Xe14c) + ("\\(<|\\)" #Xe14d) + ("\\(<|>\\)" #Xe14e) + ("\\(<\\$\\)" #Xe14f) + ("\\(<\\$>\\)" #Xe150) + ("\\( Settings + +Add before "ignored_packages": + + "font_face": "Fira Code", + "font_options": ["subpixel_antialias"], + +If you want enable antialias, add in font_options: "gray_antialias" + + +Visual Studio +------------- + +1. Launch Visual Studio (2015 or later). +2. Launch the Options dialog by opening the "Tools" menu and selecting "Options". +3. In the Options dialog, under the "Environment" category, you'll find "Fonts and Colors". Click on that. You'll see a combo-box on the right hand side of the dialog labelled "Font". Select "Fira Code" from that combo-box. +4. Click "OK" to dismiss. +5. Restart Visual Studio. + +Now, most FiraCode ligatures will work. A notable exception is the hyphen-based ligatures (e.g. the C++ dereference '->'). See https://github.com/tonsky/FiraCode/issues/422 for details. + + +Troubleshooting +=============== + +See https://github.com/tonsky/FiraCode/wiki/Troubleshooting diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..6a06323 --- /dev/null +++ b/build.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +cc -Wall -Wextra -I src -o obj/parser.o -c src/parser.c +cc -Wall -Wextra -I src -o obj/sdl.o -c src/sdl.c +cc -Wall -Wextra -I src -o obj/interface.o -c src/interface.c +cc -Wall -Wextra -I src -o obj/lpt_parser.o -c src/lpt_parser.c +cc -Wall -Wextra -I src -o obj/input.o -c src/input.c +cc -Wall -Wextra -I src -o game obj/*.o src/main.c -l SDL2 -l SDL2_image -l SDL2_ttf -g3 diff --git a/files/backgrounds/chem1.png b/files/backgrounds/chem1.png new file mode 100644 index 0000000..af5d8b2 Binary files /dev/null and b/files/backgrounds/chem1.png differ diff --git a/files/backgrounds/pc1.png b/files/backgrounds/pc1.png new file mode 100644 index 0000000..4f2d457 Binary files /dev/null and b/files/backgrounds/pc1.png differ diff --git a/files/fonts/FiraCode-Medium.ttf b/files/fonts/FiraCode-Medium.ttf new file mode 100644 index 0000000..2c0ecdf Binary files /dev/null and b/files/fonts/FiraCode-Medium.ttf differ diff --git a/files/fonts/FiraCode-Regular.ttf b/files/fonts/FiraCode-Regular.ttf new file mode 100644 index 0000000..bd73685 Binary files /dev/null and b/files/fonts/FiraCode-Regular.ttf differ diff --git a/files/images/lpt.png b/files/images/lpt.png new file mode 100644 index 0000000..270bc39 Binary files /dev/null and b/files/images/lpt.png differ diff --git a/files/scenes/0.lpt b/files/scenes/0.lpt new file mode 100644 index 0000000..22c4ec1 --- /dev/null +++ b/files/scenes/0.lpt @@ -0,0 +1,20 @@ +Background: + files/backgrounds/chem1.png + +AddEntity: + files/sprites/u.png + -0.5 0.5 + +AddEntity: + files/sprites/x.png + 0.5 0.2 + +AddMessage: + ME + TEST + +AddMessage: + U + Я ГОВОРЮЮЮЮЮюю!! + +ClearEntity diff --git a/files/scenes/1.lpt b/files/scenes/1.lpt new file mode 100644 index 0000000..29554e4 --- /dev/null +++ b/files/scenes/1.lpt @@ -0,0 +1,16 @@ +#Фон +Background: + files/backgrounds/chem1.png + +#Добавляет сущность +AddEntity: + files/sprites/x.png + 0.3 0.1 + +#Добавляет сообщение +AddMessage: + ME + TEST + +#Очищает экран от сущностей +ClearEntity diff --git a/files/sprites/u.png b/files/sprites/u.png new file mode 100644 index 0000000..270bc39 Binary files /dev/null and b/files/sprites/u.png differ diff --git a/files/sprites/x.png b/files/sprites/x.png new file mode 100644 index 0000000..fa01d16 Binary files /dev/null and b/files/sprites/x.png differ diff --git a/settings b/settings new file mode 100644 index 0000000..973f84f --- /dev/null +++ b/settings @@ -0,0 +1,8 @@ +fullscreen yes +fullscreen_custom no +vsync yes +sound_volume 100 +music_volume 22 +screen_h 1080 +screen_w 1900 +saved_scene 1 diff --git a/src/input.c b/src/input.c new file mode 100644 index 0000000..1c0f60b --- /dev/null +++ b/src/input.c @@ -0,0 +1,67 @@ +#include "sdl.h" +#include "interface.h" +#include "input.h" + +int Input(APP *app, char *run, FONTS *fonts, char flag) { + SDL_Event event; + + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_KEYDOWN: + if (event.key.keysym.sym == SDLK_q) { + *run = 0; + return EXIT; + } + + else if (event.key.keysym.sym == SDLK_k) + return SAVE; + + else if (flag == IN_MENU) { + if (event.key.keysym.sym == SDLK_s) + return SETTINGS; + + else if (event.key.keysym.sym == SDLK_c) + return CONTINUE; + + else if (event.key.keysym.sym == SDLK_n) + return NEW_GAME; + } + + else if (flag == IN_SETTINGS || flag == IN_GAME) { + if (event.key.keysym.sym == SDLK_DOWN) + return KEY_DOWN; + + else if (event.key.keysym.sym == SDLK_UP) + return KEY_UP; + + else if (event.key.keysym.sym == SDLK_RIGHT) + return KEY_RIGHT; + + else if (event.key.keysym.sym == SDLK_LEFT) + return KEY_LEFT; + } + + break; + + case SDL_QUIT: + if (flag == IN_MENU) + *run = 0; + break; + + case SDL_WINDOWEVENT: + if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { + SDL_GetWindowSize(app->window, &app->scr_width, &app->scr_height); + SDL_UpdateWindowSurface(app->window); + } + + CloseFonts(fonts); + InitFonts(fonts, *app); + break; + + default: + break; + } + } + + return INPUT_NORMAL; +} diff --git a/src/input.h b/src/input.h new file mode 100644 index 0000000..eba1229 --- /dev/null +++ b/src/input.h @@ -0,0 +1,29 @@ +#ifndef _INPUT_H +#define _INPUT_H + +#include "sdl.h" +#include "interface.h" + +/* Game modes */ +enum { + IN_MENU, + IN_SETTINGS, + IN_GAME +}; + +/* Input events */ +enum { + INPUT_NORMAL, + EXIT, + SETTINGS, + KEY_DOWN, + KEY_UP, + KEY_LEFT, + KEY_RIGHT, + SAVE, + CONTINUE, + NEW_GAME +}; + +int Input(APP *app, char *run, FONTS *fonts, char flag); +#endif diff --git a/src/interface.c b/src/interface.c new file mode 100644 index 0000000..e310920 --- /dev/null +++ b/src/interface.c @@ -0,0 +1,95 @@ +#include +#include "sdl.h" +#include "interface.h" + +int TextArray(TEXT text[], size_t n, char flag) { + for (size_t i = 0; i < n; i++) { + if (flag == TEXT_ARRAY_FREE && text[i].texture) + SDL_DestroyTexture(text[i].texture); + + else if (flag == TEXT_ARRAY_SCAN && text[i].init == 0) { + fputs("Found uninitialized font\n", stderr); + return 1; + } + } + + return 0; +} + +void PrintText(TTF_Font *font, APP *app, TEXT *dst, const char *text) { + SDL_Surface *temp = TTF_RenderUTF8_Solid(font, text, dst->color); + if (!temp) { + fprintf(stderr, "AddBanner Font: %s\n", TTF_GetError()); + return; + } + + dst->texture = SDL_CreateTextureFromSurface(app->render, temp); + if (!dst->texture) { + SDL_FreeSurface(temp); + fprintf(stderr, "AddBanner Font: %s\n", TTF_GetError()); + return; + } + + SDL_FreeSurface(temp); + + /* Write text size in to a structure */ + if (TTF_SizeText(font, text, &dst->w, &dst->h) < 0) { + SDL_DestroyTexture(dst->texture); + return; + } + + dst->init = 1; +} + +void AddBanner(APP *app, TEXT text, SDL_Rect rect) { + /* Box */ + SDL_SetRenderDrawColor(app->render, 0, 0, 0, 0); + + SDL_RenderDrawRect(app->render, &rect); + + /* Text */ + if (text.w < rect.w) + rect.w = text.w; + + SDL_RenderCopy(app->render, text.texture, NULL, &rect); +} + +void AddPseudoButton(APP *app, TEXT text, const int bttn_num) { + int rect_x = APPROX2ORIG(app->scr_width, -0.8); + SDL_Rect rect = { + .x = rect_x, + .y = APPROX2ORIG(app->scr_height, (0.75 - (0.2 * bttn_num))), + .w = app->scr_width - (rect_x * 2), + .h = 45 + }; + + AddBanner(app, text, rect); +} + +void CloseFonts(FONTS *fonts) { + if (fonts->font1) + TTF_CloseFont(fonts->font1); + + if (fonts->font2) + TTF_CloseFont(fonts->font2); + + memset(fonts, 0, sizeof(FONTS)); +} + +int InitFonts(FONTS *fonts, const APP app) { + memset(fonts, 0, sizeof(FONTS)); + + size_t font_size = app.scr_width * app.scr_height * 8 / 403200; + fonts->font1 = TTF_OpenFont(FONT1, font_size); + + font_size = app.scr_width * app.scr_height * 15 / 403200; + fonts->font2 = TTF_OpenFont(FONT1, font_size); + + if (fonts->font1 == NULL || fonts->font2 == NULL) { + fprintf(stderr, "Can not open font: %s\n", TTF_GetError()); + CloseFonts(fonts); + return 1; + } + + return 0; +} diff --git a/src/interface.h b/src/interface.h new file mode 100644 index 0000000..ed2c206 --- /dev/null +++ b/src/interface.h @@ -0,0 +1,36 @@ +#ifndef _INTERFACE_SDL +#define _INTERFACE_SDL + +#include "sdl.h" + +#define FONT1 "files/fonts/FiraCode-Regular.ttf" +#define FONT2 "files/fonts/FiraCode-Medium.ttf" + +enum { + TEXT_ARRAY_FREE = 1, + TEXT_ARRAY_SCAN +}; + +typedef struct { + TTF_Font *font1; + TTF_Font *font2; +} FONTS; + +typedef struct { + SDL_Texture *texture; + SDL_Color color; + + int w; + int h; + + /* Status of initialization */ + int init; +} TEXT; + +int TextArray(TEXT text[], size_t n, char flag); +void PrintText(TTF_Font *font, APP *app, TEXT *dst, const char *text); +void AddBanner(APP *app, TEXT text, SDL_Rect rect); +void AddPseudoButton(APP *app, TEXT text, const int bttn_num); +void CloseFonts(FONTS *fonts); +int InitFonts(FONTS *fonts, const APP app); +#endif diff --git a/src/lpt_parser.c b/src/lpt_parser.c new file mode 100644 index 0000000..341d4fc --- /dev/null +++ b/src/lpt_parser.c @@ -0,0 +1,265 @@ +#include +#include +#include +#include +#include +#include "interface.h" +#include "sdl.h" +#include "parser.h" +#include "lpt_parser.h" +#include "input.h" + +enum { + PARSER_NORMAL, + BACKGROUND, + + ADD_ENTITY, + ADD_ENTITY_ARG2, + + ADD_MESSAGE, + ADD_MESSAGE_ARG2 +}; + +struct ENTITY { + float x; + float scale; + SDL_Texture *texture; + + struct ENTITY *next; +}; + +void free_chain(struct ENTITY **ent) { + while (*ent != NULL) { + struct ENTITY *i = (*ent)->next; + + if ((*ent)->texture) + SDL_DestroyTexture((*ent)->texture); + + free(*ent); + *ent = i; + } +} + +int DisplayScene(SDL_Texture *background, TEXT author, TEXT message, APP *app, FONTS *fonts, CONFIG cfg, struct ENTITY *ent) { + char run = LPTP_NORMAL; + + while (run == LPTP_NORMAL) { + int status = Input(app, &run, fonts, IN_GAME); + switch (status) { + case EXIT: + run = MENU; + /* fallthrough */ + + case SAVE: + WriteCfg(cfg); + break; + + case KEY_RIGHT: + run = NEXT; + break; + + default: + break; + } + + /* Background */ + SDL_SetRenderDrawColor(app->render, 0xFF, 0xFF, 0xFF, 0xFF); + SDL_RenderClear(app->render); + + SDL_RenderCopy(app->render, background, NULL, NULL); + + /* Entities */ + struct ENTITY *bckp = ent; + while (bckp != NULL) { + struct ENTITY *i = bckp->next; + + int w = 0, h = 0; + SDL_QueryTexture(bckp->texture, NULL, NULL, &w, &h); + + SDL_Rect rect = { + .x = APPROX2ORIG(app->scr_width, bckp->x), + .y = APPROX2ORIG(app->scr_height, 0), + .w = w * bckp->scale, + .h = h * bckp->scale + }; + + if (bckp->texture) + SDL_RenderCopy(app->render, bckp->texture, NULL, &rect); + + bckp = i; + } + + /* Dialog */ + AddPseudoButton(app, message, 0); + AddPseudoButton(app, author, 1); + + /* Render */ + SDL_RenderPresent(app->render); + SDL_Delay(20); + } + + return run; +} + +int lpt_parser(APP *app, FONTS *fonts, CONFIG *cfg) { + char path[PATH_MAX + 1]; + snprintf(path, sizeof(path), "files/scenes/%d.lpt", cfg->saved_scene); + + FILE *fp = OpenFile(path, "r"); + if (fp == NULL) { + fputs("Maybe it's error?? Or endgame\n", stderr); + return ENDGAME; + } + + char ret = ERROR; + + /* Parser */ + char *raw_str = NULL; + ssize_t bytes = 0; + size_t n = 0; + size_t line = 0; + char flag = PARSER_NORMAL; + + /* For scene */ + SDL_Texture *background = NULL; + + TEXT author, msg; + memset(&author, 0, sizeof(author)); + msg = author; + + struct ENTITY *entities = NULL; + struct ENTITY *ptr = NULL; + + while ((bytes = getline(&raw_str, &n, fp)) > 0) { + line++; + + raw_str[strcspn(raw_str, "\n")] = '\0'; + + char *str = raw_str; + while (isspace(*str)) + str++; + + if (*str == '#') + continue; + + if (flag == BACKGROUND) { + if (background) + SDL_DestroyTexture(background); + + background = LoadIMG_SDL(str, app); + if (background == NULL) + goto LPT_PARSER_CLOSE_UP; + } + + else if (flag == ADD_MESSAGE) { + if (author.init) + SDL_DestroyTexture(author.texture); + + memset(&author, 0, sizeof(author)); + + PrintText(fonts->font1, app, &author, str); + if (author.init == 0) + goto LPT_PARSER_CLOSE_UP; + } + + else if (flag == ADD_MESSAGE_ARG2) { + if (msg.init) + SDL_DestroyTexture(msg.texture); + + memset(&msg, 0, sizeof(msg)); + + PrintText(fonts->font1, app, &msg, str); + if (msg.init == 0) + goto LPT_PARSER_CLOSE_UP; + + if (DisplayScene(background, author, msg, app, fonts, *cfg, entities) == MENU) { + ret = MENU; + break; + } + } + + else if (flag == ADD_ENTITY) { + entities = malloc(sizeof(struct ENTITY)); + if (entities == NULL) { + fprintf(stderr, "LPT_parser: failed to add entitiy: malloc: %s\n", strerror(errno)); + goto LPT_PARSER_CLOSE_UP; + } + + memset(entities, 0, sizeof(struct ENTITY)); + entities->texture = LoadIMG_SDL(str, app); + if (entities->texture == NULL) + goto LPT_PARSER_CLOSE_UP; + } + + else if (flag == ADD_ENTITY_ARG2) { + char *tok_rest = str; + char *x_pos = strtok_r(tok_rest, " ", &tok_rest); + char *scale = strtok_r(tok_rest, " ", &tok_rest); + if (scale == NULL || x_pos == NULL) { + fprintf(stderr, "SCENE %d:%ld: invalid syntax: %s\n", cfg->saved_scene, line, str); + goto LPT_PARSER_CLOSE_UP; + } + + entities->x = atof(x_pos); + entities->scale = atof(scale); + + entities->next = ptr; + ptr = entities; + } + + /* Parsing file */ + if (flag == PARSER_NORMAL && bytes > 1) { + if (!strcmp(str, "Background:")) + flag = BACKGROUND; + + else if (!strcmp(str, "AddEntity:")) + flag = ADD_ENTITY; + + else if (!strcmp(str, "AddMessage:")) + flag = ADD_MESSAGE; + + else if (!strcmp(str, "ClearEntity")) { + free_chain(&entities); + ptr = NULL; + } + + else { + fprintf(stderr, "SCENE %d:%ld : invalid syntax: %s\n", cfg->saved_scene, line, str); + goto LPT_PARSER_CLOSE_UP; + } + } + + else { + if (flag == ADD_MESSAGE) + flag = ADD_MESSAGE_ARG2; + + else if (flag == ADD_ENTITY) + flag = ADD_ENTITY_ARG2; + + else + flag = PARSER_NORMAL; + } + } + + if (ret != MENU) + ret = LPTP_NORMAL; + + /* Close up */ +LPT_PARSER_CLOSE_UP: + if (raw_str) + free(raw_str); + + if (background) + SDL_DestroyTexture(background); + + if (author.init) + SDL_DestroyTexture(author.texture); + + if (msg.init) + SDL_DestroyTexture(msg.texture); + + free_chain(&entities); + + fclose(fp); + return ret; +} diff --git a/src/lpt_parser.h b/src/lpt_parser.h new file mode 100644 index 0000000..8c55b4c --- /dev/null +++ b/src/lpt_parser.h @@ -0,0 +1,17 @@ +#ifndef _LPT_PARSER_H +#define _LPT_PARSER_H + +#include "sdl.h" +#include "interface.h" +#include "parser.h" + +enum { + LPTP_NORMAL = 1, + ERROR, + ENDGAME, + MENU, + NEXT +}; + +int lpt_parser(APP *app, FONTS *fonts, CONFIG *cfg); +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..9200866 --- /dev/null +++ b/src/main.c @@ -0,0 +1,318 @@ +#include +#include +#include +#include +#include +#include "lpt_parser.h" +#include "parser.h" +#include "sdl.h" +#include "interface.h" +#include "input.h" + +void AddOption(TTF_Font *font, APP *app, TEXT text, const int bttn_num, const int opt_value, const int flag) { + AddPseudoButton(app, text, bttn_num); + + + /* Create texture for value */ + char str[16]; + if (flag == 1) + snprintf(str, sizeof(str), "%s", (opt_value) ? "Y" : "N"); + + else if (flag == 2) + snprintf(str, sizeof(str), "%d%%", opt_value); + + else + snprintf(str, sizeof(str), "%d", opt_value); + + TEXT value; + memset(&value, 0, sizeof(value)); + + PrintText(font, app, &value, str); + if (value.init == 0) + return; + + + /* Print it */ + int rect_x = APPROX2ORIG(app->scr_width, -0.8); + SDL_Rect rect = { + .x = app->scr_width - rect_x - value.w - 20, + .y = APPROX2ORIG(app->scr_height, (0.75 - (0.2 * bttn_num))), + .w = value.w + 20, + .h = 45 + }; + + SDL_RenderCopy(app->render, value.texture, NULL, &rect); + SDL_DestroyTexture(value.texture); +} + +int main_menu(APP *app, FONTS *fonts, CONFIG *cfg) { + char run = 1; + char mode = IN_MENU; + size_t index = 0; + int *value = NULL; + char changed = 0; + + + + /* LPT LOGO */ + SDL_Texture *logo = LoadIMG_SDL("files/images/lpt.png", app); + if (logo == NULL) + run = -1; + + + + /* Setup menu text */ + TEXT text_new_game, text_continue, text_settings, text_quit, text_save; + memset(&text_new_game, 0, sizeof(text_new_game)); + text_continue = text_settings = text_quit = text_save = text_new_game; + + PrintText(fonts->font1, app, &text_new_game, "[n] New Game"); + PrintText(fonts->font1, app, &text_continue, "[c] Continue"); + PrintText(fonts->font1, app, &text_settings, "[s] Settings"); + PrintText(fonts->font1, app, &text_save, "[k] Save"); + PrintText(fonts->font1, app, &text_quit, "[q] Quit"); + + TEXT menu_msgs[] = {text_quit, text_settings, text_new_game, text_save, text_continue}; + size_t menu_size = sizeof(menu_msgs) / sizeof(TEXT); + if (TextArray(menu_msgs, menu_size, TEXT_ARRAY_SCAN)) + run = -1; + + + + /* Setup settings text */ + TEXT settings, fullscreen, fullscreen_custom, vsync, sound_vol, music_vol, screen_h, screen_w, pointer, text_back; + memset(&settings, 0, sizeof(settings)); + fullscreen = fullscreen_custom = vsync = sound_vol = music_vol = screen_h = screen_w = pointer = text_back = settings; + + PrintText(fonts->font1, app, &fullscreen, "Fullscreen mode:"); + PrintText(fonts->font1, app, &fullscreen_custom, "Custom resolution for fullscreen: Y"); + PrintText(fonts->font1, app, &vsync, "VSync:"); + PrintText(fonts->font1, app, &sound_vol, "Sound volume:"); + PrintText(fonts->font1, app, &music_vol, "Music volume:"); + PrintText(fonts->font1, app, &screen_h, "Screen height:"); + PrintText(fonts->font1, app, &screen_w, "Screen width:"); + PrintText(fonts->font1, app, &text_back, "[q] Back"); + PrintText(fonts->font1, app, &settings, "[settings] Restart requied..."); + PrintText(fonts->font2, app, &pointer, ">"); + + TEXT settings_msgs[] = {fullscreen, fullscreen_custom, vsync, sound_vol, music_vol, screen_h, screen_w, text_save, text_back, pointer, settings}; + int *settings_opt[] = {&cfg->fullscreen, &cfg->fullscreen_custom, &cfg->vsync, &cfg->sound_volume, &cfg->music_volume, &cfg->screen_h, &cfg->screen_w}; + size_t settings_size = sizeof(settings_msgs) / sizeof(TEXT); + if (TextArray(settings_msgs, settings_size, TEXT_ARRAY_SCAN)) + run = -1; + + + + /* Game loop */ + while (run > 0) { + if (value == NULL) + value = settings_opt[1]; + + + /* Input and navigation */ + int status = Input(app, &run, fonts, mode); + switch (status) { + case SETTINGS: + mode = IN_SETTINGS; + break; + + case KEY_DOWN: + if (index > 0) { + index--; + value = settings_opt[index]; + } + + break; + + case KEY_UP: + if (index < settings_size - 5) { + index++; + value = settings_opt[index]; + } + + break; + + case KEY_LEFT: + changed = 1; + if ((value == &cfg->screen_w && *value > 639) || (value == &cfg->screen_h && *value > 479)) + (*value)--; + + else if (*value > 0) + (*value)--; + + break; + + case KEY_RIGHT: + changed = 1; + if (value == &cfg->fullscreen || value == &cfg->fullscreen_custom || value == &cfg->vsync) + *value = 1; + + else if ((value == &cfg->music_volume && *value < 100) || (value == &cfg->sound_volume && *value < 100)) + (*value) += 2; + + else if (value == &cfg->screen_w || value == &cfg->screen_h) + (*value) += 5; + + break; + + case SAVE: + changed = 0; + if (WriteCfg(*cfg)) + run = -1; + break; + + case NEW_GAME: + cfg->saved_scene = 0; + /* fallthrough */ + + case CONTINUE: + char game_run = 1; + while (game_run > 0) { + game_run = lpt_parser(app, fonts, cfg); + if (game_run == ERROR) { + run = -1; + break; + } + + else if (game_run == MENU) + break; + + else if (game_run == LPTP_NORMAL) + cfg->saved_scene++; + + else if (game_run == ENDGAME) { + cfg->saved_scene = 0; + break; + } + } + + break; + + default: + break; + } + + + /* Background */ + SDL_SetRenderDrawColor(app->render, 0xFF, 0xFF, 0xFF, 0xFF); + SDL_RenderClear(app->render); + + + /* Create GUI */ + if (mode == IN_MENU) { + /* Logo */ + SDL_Rect rect = { + .x = APPROX2ORIG(app->scr_width, -0.5), + .y = APPROX2ORIG(app->scr_height, -0.5), + .w = 128 * 2, + .h = 64 * 2 + }; + + SDL_RenderCopy(app->render, logo, NULL, &rect); + + /* Menu */ + size_t i; + for (i = 0; i < menu_size - 1; i++) + AddPseudoButton(app, menu_msgs[i], i); + + if (cfg->saved_scene > 0) + AddPseudoButton(app, menu_msgs[i], i); + } + + else if (mode == IN_SETTINGS) { + /* Menu */ + AddOption(fonts->font1, app, fullscreen, 0, cfg->fullscreen, 1); + AddOption(fonts->font1, app, fullscreen_custom, 1, cfg->fullscreen_custom, 1); + AddOption(fonts->font1, app, vsync, 2, cfg->vsync, 1); + AddOption(fonts->font1, app, sound_vol, 3, cfg->sound_volume, 2); + AddOption(fonts->font1, app, music_vol, 4, cfg->music_volume, 2); + AddOption(fonts->font1, app, screen_h, 5, cfg->screen_h, 0); + AddOption(fonts->font1, app, screen_w, 6, cfg->screen_w, 0); + AddPseudoButton(app, text_back, 7); + + if (changed) + AddPseudoButton(app, text_save, 8); + + /* Pointer */ + SDL_Rect rect = { + .x = 0, + .y = APPROX2ORIG(app->scr_height, (0.75 - (0.2 * index))), + .w = pointer.w, + .h = pointer.h + }; + + SDL_RenderCopy(app->render, pointer.texture, NULL, &rect); + + /* Title */ + rect.y = 0; + rect.w = settings.w; + rect.h = settings.h; + SDL_RenderCopy(app->render, settings.texture, NULL, &rect); + } + + if (run == 0 && mode == IN_SETTINGS) { + mode = IN_MENU; + run = 1; + } + + + /* Render */ + SDL_RenderPresent(app->render); + SDL_Delay(20); + } + + + + /* Free structures */ + if (logo) + SDL_DestroyTexture(logo); + + TextArray(menu_msgs, menu_size, TEXT_ARRAY_FREE); + TextArray(settings_msgs, menu_size, TEXT_ARRAY_FREE); + return (run == -1) ? 1 : 0; +} + +int main(void) { + /* Config */ + CONFIG cfg = { + .fullscreen = 1, + .fullscreen_custom = 0, + .screen_w = 480, + .screen_h = 640, + .sound_volume = 100, + .music_volume = 20, + .vsync = 0, + }; + + if (access(CONFIG_FILE, R_OK) < 0) { + fprintf(stderr, "Failed to open %s: %s. Creating...\n", CONFIG_FILE, strerror(errno)); + WriteCfg(cfg); + } + + else + ReadCfg(&cfg); + + /* Init SDL */ + APP app; + if (Init_SDL(cfg, &app)) + return 1; + + int ret = 0; + + FONTS fonts; + if (InitFonts(&fonts, app)) + ret = 1; + + /* Game loop*/ + if (!ret) + ret = main_menu(&app, &fonts, &cfg); + + /* Clean up */ + CloseFonts(&fonts); + CleanUp_SDL(&app); + if (ret) + fputs("Game returned 1: interal error\n", stderr); + + fputs("Thanks for playing\n", stderr); + return ret; +} diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 0000000..2dce5b3 --- /dev/null +++ b/src/parser.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include "parser.h" + +#define YesOrNo() ((strcmp(val, "yes") > 0) ? 1 : 0) + +FILE *OpenFile(const char *file, const char *mode) { + FILE *fp = fopen(file, mode); + if (fp == NULL) + fprintf(stderr, "Can not open %s with %s mode\n", file, mode); + + return fp; +} + +int ReadCfg(CONFIG *cfg) { + memset(cfg, 0, sizeof(CONFIG)); + + FILE *fp = OpenFile(CONFIG_FILE, "r"); + if (fp == NULL) + return 1; + + char *str = NULL; + ssize_t bytes = 0; + size_t n = 0; + + while ((bytes = getline(&str, &n, fp)) > 0) { + char *tok_rest = str; + char *var = strtok_r(tok_rest, " ", &tok_rest); + char *val = strtok_r(tok_rest, " ", &tok_rest); + + if (var == NULL || val == NULL) + continue; + + if (var[0] == '#') + continue; + + else if (!strcmp(var, "fullscreen")) + cfg->fullscreen = YesOrNo(); + + else if (!strcmp(var, "fullscreen_custom")) + cfg->fullscreen_custom = YesOrNo(); + + else if (!strcmp(var, "vsync")) + cfg->vsync = YesOrNo(); + + else if (!strcmp(var, "sound_volume")) + cfg->sound_volume = atoi(val); + + else if (!strcmp(var, "music_volume")) + cfg->music_volume = atoi(val); + + else if (!strcmp(var, "screen_h")) + cfg->screen_h = atoi(val); + + else if (!strcmp(var, "screen_w")) + cfg->screen_w = atoi(val); + + else if (!strcmp(var, "saved_scene")) + cfg->saved_scene = atoi(val); + } + + if (str) + free(str); + + fclose(fp); + return 0; +} + +int WriteCfg(const CONFIG cfg) { + FILE *fp = OpenFile(CONFIG_FILE, "w"); + if (fp == NULL) + return 1; + + fprintf(fp, "fullscreen %s\n", (cfg.fullscreen) ? "yes" : "no"); + fprintf(fp, "fullscreen_custom %s\n", (cfg.fullscreen_custom) ? "yes" : "no"); + fprintf(fp, "vsync %s\n", (cfg.vsync) ? "yes" : "no"); + fprintf(fp, "sound_volume %d\n", cfg.sound_volume); + fprintf(fp, "music_volume %d\n", cfg.music_volume); + fprintf(fp, "screen_h %d\n", cfg.screen_h); + fprintf(fp, "screen_w %d\n", cfg.screen_w); + fprintf(fp, "saved_scene %d\n", cfg.saved_scene); + + fclose(fp); + return 0; +} diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 0000000..db1f8c0 --- /dev/null +++ b/src/parser.h @@ -0,0 +1,25 @@ +#ifndef _PARSER_H +#define _PARSER_H + +#include + +#define CONFIG_FILE "settings" + +typedef struct { + int fullscreen; + int fullscreen_custom; + int vsync; + + int screen_w; + int screen_h; + + int sound_volume; + int music_volume; + + int saved_scene; +} CONFIG; + +FILE *OpenFile(const char *file, const char *mode); +int ReadCfg(CONFIG *cfg); +int WriteCfg(const CONFIG cfg); +#endif diff --git a/src/sdl.c b/src/sdl.c new file mode 100644 index 0000000..bcb03de --- /dev/null +++ b/src/sdl.c @@ -0,0 +1,95 @@ +#include +#include +#include "sdl.h" +#include "parser.h" + +void CleanUp_SDL(APP *app) { + if (app->window) + SDL_DestroyWindow(app->window); + + if (app->render) + SDL_DestroyRenderer(app->render); + + SDL_Quit(); + IMG_Quit(); + TTF_Quit(); +} + +int Init_SDL(const CONFIG cfg, APP *app) { + memset(app, 0, sizeof(APP)); + + int status = 0; + if (SDL_Init(SDL_INIT_VIDEO) < 0) + goto Init_SDL_CLOSE; + + /* Get display mode if fullscreen enabled in config */ + app->scr_width = cfg.screen_w; + app->scr_height = cfg.screen_h; + int fullscreen = 0; + + if (cfg.fullscreen) { + if (!cfg.fullscreen_custom) { + SDL_DisplayMode dm; + if (SDL_GetDesktopDisplayMode(0, &dm) < 0) + goto Init_SDL_CLOSE; + + app->scr_width = dm.w; + app->scr_height = dm.h; + } + + fullscreen = SDL_WINDOW_FULLSCREEN; + } + + /* Creating window */ + app->window = SDL_CreateWindow("LPT", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, app->scr_width, app->scr_height, SDL_WINDOW_RESIZABLE | fullscreen); + if (!app->window) + goto Init_SDL_CLOSE; + SDL_SetWindowMinimumSize(app->window, 640, 480); + + /* Renderer */ + app->render = SDL_CreateRenderer(app->window, -1, SDL_RENDERER_ACCELERATED | (cfg.vsync) ? SDL_RENDERER_PRESENTVSYNC : 0); + if (!app->render) + goto Init_SDL_CLOSE; + + /* Image subsystem */ + status = 1; + int flags = IMG_INIT_PNG; + if (!(IMG_Init(flags) & flags)) + goto Init_SDL_CLOSE; + + /* Font subsystem */ + status = 2; + if (TTF_Init() < 0) + goto Init_SDL_CLOSE; + + return 0; + + +Init_SDL_CLOSE: + if (status == 1) + fprintf(stderr, "Could not initialize SDL_IMG: %s\n", IMG_GetError()); + + if (status == 2) + fprintf(stderr, "Could not initialize SDL_TTF: %s\n", TTF_GetError()); + + else + fprintf(stderr, "Could not initialize SDL: %s\n", SDL_GetError()); + + CleanUp_SDL(app); + return 1; +} + +SDL_Texture *LoadIMG_SDL(const char *path, APP *app) { + if (path == NULL) { + fputs("LoadIMG: path is NULL. Maybe incorrect scene syntax\n", stderr); + return NULL; + } + + SDL_Texture *text = IMG_LoadTexture(app->render, path); + if (text == NULL) { + fprintf(stderr, "LoadIMG: %s\n", SDL_GetError()); + return NULL; + } + + return text; +} diff --git a/src/sdl.h b/src/sdl.h new file mode 100644 index 0000000..a41c82e --- /dev/null +++ b/src/sdl.h @@ -0,0 +1,24 @@ +#ifndef _LPT_SDL +#define _LPT_SDL + +#include +#include +#include +#include +#include "parser.h" + +#define APPROX2ORIG(res, appr) ((res * appr + res) / 2) +#define ORIG2APPROX(res, pix) (pix / res * 2 - 1) + +typedef struct { + SDL_Window *window; + SDL_Renderer *render; + + int scr_height; + int scr_width; +} APP; + +void CleanUp_SDL(APP *app); +int Init_SDL(const CONFIG cfg, APP *app); +SDL_Texture *LoadIMG_SDL(const char *path, APP *app); +#endif