From: Christian Heller Date: Wed, 16 Dec 2015 01:36:32 +0000 (+0100) Subject: Replace old with new client, drop redo system needed to build old one. X-Git-Tag: tce~223 X-Git-Url: https://plomlompom.com/repos/%7B%7B%20web_path%20%7D%7D/%7B%7Bprefix%7D%7D/static/git-logo.png?a=commitdiff_plain;h=a50806df8116a81729220bd79870639b18da9d8c;p=plomrogue Replace old with new client, drop redo system needed to build old one. --- diff --git a/NOTICE b/NOTICE index 02aeb17..377e822 100644 --- a/NOTICE +++ b/NOTICE @@ -1,15 +1,8 @@ Information on the contained programs' copyright, licenses, and warranties ========================================================================== -redo – the scripts below ./build/redo_scripts/ ----------------------------------------------- - -See redo files below ./build/redo_scripts/ for copyright, license, and warranty -information. In the file ./AGPLv3 a copy of the GNU Affero Public License -version 3 is provided. - -PlomRogue – the package's files that are not below ./build/redo_scripts/ ------------------------------------------------------------------------- +PlomRogue +--------- PlomRogue (C) 2013-2015 Christian Heller a.k.a. plomlompom diff --git a/README b/README index 1ab4cec..5759ccd 100644 --- a/README +++ b/README @@ -7,10 +7,11 @@ plomlompom has insanely ambitious long-term plans). You can move around a player on an island and meet different enemies. You have 5 hitpoints to lose before death. Enemies start with different amounts of hitpoints, depending on their species. Dead enemies become dirt, skeletons, or -food to consume (each turn reduces one's "satiation", and the lower it is, the -stronger the chance of suffering from hunger and thereby losing hitpoints). Note -that different kinds of movements/actions take different numbers of turns to -finish. +food to consume (each turn reduces one's "satiation", and the further away from +a healthy center it is, the smaller the chance of regaining lost hitpoints by +healing, and the stronger the chance of suffering from hunger or overfeeding +and thereby losing hitpoints). Note that different kinds of movements/actions +take different numbers of turns to finish. Enemies' AI is very dumb so far: Each turn, they look out for actors of different species to flee from (if their type starts out with more hitpoints @@ -33,50 +34,15 @@ The game is expected to run: To build it, this is furthermore necessary: - gcc (version >= 4.7.2); some llvm masked as gcc was tested successfully on OSX - libc library headers (libc6-dev?) -- ncurses library headers (libncurses5-dev?) To build and start, do the following steps: -$ ./redo +$ ./build.sh $ ./roguelike -If you got a version of djb's redo build system installed and in your $PATH, -you could also do a simple "redo" instead of "./redo". The ./redo script calls a -simple partial shell script implementation of redo stored below -./build/redo_scripts/, written by Nils Dagsson Moskopp a.k.a. erlehmann.) - -./redo generates two executables ./roguelike-server and ./roguelike-client. -./roguelike is a pre-existing shell script that merely executes both of them, -with the server as a background job. You can also ignore the script and start -the two by hand. - -Client's keybindings and window management ------------------------------------------- - -Multiple windows may fill the screen. One of these will be selected as "active" -– identified by dollar signs enclosing its title. Active windows may come with -window-specific keybindings. Furthermore, global keybindings are available no -matter what window is selected. - -Hit "W" (per default global keybindings) to switch the active window to a view -that allows changing its geometry. One more hit on "W" switches the window to a -view that sports (and allows changing its) window-specific keybindings. (One -further "W" will return the window to its default, "normal" view.) The global -keybindings are visible (and can be changed) in the "Set global keys" window, -those of the window geometry configuration in the "Set window geometry keys" -window, and those of the window-specific keybindings configuration in the "Set -window keybinding keys" window; by default, the latter two are not visible, but -may be turned on by (per default keybindings) hitting the keys "F6" and "F7". - -Keybindings and default window selection / visibilities / geometries are read -from the textfile ./confclient/interface_conf by default, or by another one -named by the -i command line option of the client. Some other default window -configurations are stored below ./confclient/single_windows/: "map", "info", -"inventory" and "log". Each of these opens up only a single window into the -client, filling up the entire terminal. This may be useful for running multiple -clients in parallel in multiple terminal windows that can be managed by one's -own window manager choice, instead of relying on plomrogue-client's bizarre -in-client window management. +./roguelike is a shell script that executes a union of ./roguelike-server and +./roguelike-client, with the server as a background job. You may ignore the +script and start either or both of the two by hand if you please. Save files, replay game recording, starting over ------------------------------------------------ @@ -92,11 +58,12 @@ called "record_" plus the save file name. Run "./roguelike -s" to watch the current game's recording from the beginning. Hit any player action key to increment turns (they will not trigger the actions usually mapped to them, only repeat the actions done at that point in the game as defined in the record -file). Keys to manage windows, scroll on the map and quit the program do their -usual thing. Append a number to the -s option (like "-s100") to start the -recording playback at the respective turn number. (Don't forget to delete / -empty a game's record file when deleting its save file, or different game's -moves will get mixed up in one record file.) +file). Other keys do their usual thing. + +Append a number to the -s option (like "-s100") to start the recording playback +at the respective turn number. (Don't forget to delete / empty a game's record +file when deleting its save file, or different game's moves will get mixed up in +one record file.) Hacking / server internals and configuration -------------------------------------------- @@ -107,12 +74,12 @@ The game world is set up and made subject to player commands by (written to by ./roguelike-client), ./confserver/world, ./record_save and ./save. -All source files are thoroughly documented to explain more details of the -PlomRogue engine's internals. The ./roguelike-server executable can be run with -a -v option for helpful debugging info (mostly: what messages the client sends -to the server). Server and client communicate via files in the ./server/ -directory (generated when the server is first run). The ./server/in file is read -by the server for newline-delimited commands. The ./server/out file contains -server messages to be read by clients. The ./server/worldstate file contains a -serialized representation of the game world's data as it is to be visible to -the player / the player's client. +The ./roguelike-server executable can be run with a -v option for possibly +helpful debugging info (mostly: what messages the client sends to the server). + +Server and client communicate via files in the ./server/ directory (generated +when the server is first run). The ./server/in file is read by the server for +newline-delimited commands. The ./server/out file contains server messages to be +read by clients. The ./server/worldstate file contains a serialized +representation of the game world's data as it is to be visible to the player / +the player's client. diff --git a/TODO b/TODO index 212f582..3d7bc4c 100644 --- a/TODO +++ b/TODO @@ -5,20 +5,8 @@ BOTH SERVER/CLIENT: - make server and client communicate by specific world state info requests in server/out, replacing server/worldstate -- quote escaping in token_from_line() - -- think about refactoring occurrences of "free(...); ... = NULL;" - SERVER: - pythonize Python code - old C code: pythonize, then re-C as needed by performance - -CLIENT: - -- re-work unnecessary complex command / keybinding / server message mapping - -- only send "ITEM_HERE" query to server when the dependent window is open - -- draw_win_log() is still buggy (make mem alloc for other line break styles!) diff --git a/all.do b/all.do deleted file mode 100644 index a2340d3..0000000 --- a/all.do +++ /dev/null @@ -1,8 +0,0 @@ -# redo build file to build executables "roguelike-server", "libplomrogue.so". - -# This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 -# or any later version. For details on its copyright, license, and warranties, -# see the file NOTICE in the root directory of the PlomRogue source package. - -redo-ifchange libplomrogue.so -redo-ifchange roguelike-client diff --git a/build/compiler_flags b/build/compiler_flags deleted file mode 100644 index b17dac3..0000000 --- a/build/compiler_flags +++ /dev/null @@ -1,13 +0,0 @@ -# This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 -# or any later version. For details on its copyright, license, and warranties, -# see the file NOTICE in the root directory of the PlomRogue source package. - -OFLAG='-O3' -CFLAGS="-std=c11 -pedantic-errors -Wall -Werror -Wextra -Wformat-security $OFLAG" - -# For non-GNU gcc masks, drop all gcc-specific debugging flags. -test=`stat --version 2>&1 | grep 'Free Software Foundation' | wc -l` -if [ 1 -gt $test ] -then - CFLAGS=$OFLAG -fi diff --git a/build/default.o.do b/build/default.o.do deleted file mode 100644 index be2bb9d..0000000 --- a/build/default.o.do +++ /dev/null @@ -1,10 +0,0 @@ -# This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 -# or any later version. For details on its copyright, license, and warranties, -# see the file NOTICE in the root directory of the PlomRogue source package. - -redo-ifchange compiler_flags -. ./compiler_flags -file=${1#build/} -gcc $CFLAGS -o $3 -c ../src/${file%.o}.c -MD -MF $2.deps -read DEPS <$2.deps -redo-ifchange ${DEPS#*:} diff --git a/build/redo_scripts/redo b/build/redo_scripts/redo deleted file mode 100755 index f4844c1..0000000 --- a/build/redo_scripts/redo +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/sh -# redo – bourne shell implementation of DJB redo -# Copyright © 2014 Nils Dagsson Moskopp (erlehmann) - -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. - -# Dieses Programm hat das Ziel, die Medienkompetenz der Leser zu -# steigern. Gelegentlich packe ich sogar einen handfesten Buffer -# Overflow oder eine Format String Vulnerability zwischen die anderen -# Codezeilen und schreibe das auch nicht dran. - -set +e - -bold="" -green="" -plain="" -red="" -if [ -n "$TERM" -a "$TERM" != "dumb" ] && tty <&2 >/dev/null 2>&1; then - bold="$(printf '\033[1m')" - green="$(printf '\033[32m')" - plain="$(printf '\033[m')" - red="$(printf '\033[31m')" -fi -export bold green plain red - -for argument; do - if [ "$argument" = "-d" ] || [ "$argument" = "--debug" ]; then - export REDO_DEBUG='1' - elif [ "$argument" = "-h" ] || [ "$argument" = "--help" ]; then - cat <&2 -Usage: redo [OPTIONS] [TARGETS...] - - -d, --debug print dependency checks as they happen - -h, --help print usage instructions and exit - -s, --shuffle randomize build order to find dependency bugs - -x, --xtrace print commands as they are executed (variables expanded) - -Report bugs to . -EOF - exit 0 - elif [ "$argument" = "-s" ] || [ "$argument" = "--shuffle" ]; then - export REDO_SHUFFLE='1' - elif [ "$argument" = "-x" ] || [ "$argument" = "--xtrace" ]; then - export REDO_XTRACE='1' - else - REDO_HAS_TARGETS='1' - export REDO_TARGET='' - # If this is build directory, create .redo database directory. - if [ -z "$REDO_BASE" ]; then - export REDO_BASE="$(pwd)" - export REDO_DIR="$REDO_BASE/.redo" - LANG=C mkdir -p $REDO_DIR - fi - redo-ifchange "$argument" - [ "$?" = 0 ] || exit 1 - fi -done - -[ "$REDO_HAS_TARGETS" = "1" ] || exec redo all diff --git a/build/redo_scripts/redo-ifchange b/build/redo_scripts/redo-ifchange deleted file mode 100755 index 532587a..0000000 --- a/build/redo_scripts/redo-ifchange +++ /dev/null @@ -1,321 +0,0 @@ -#!/bin/sh -# redo-ifchange – bourne shell implementation of DJB redo -# Copyright © 2014 Nils Dagsson Moskopp (erlehmann) - -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. - -# Dieses Programm hat das Ziel, die Medienkompetenz der Leser zu -# steigern. Gelegentlich packe ich sogar einen handfesten Buffer -# Overflow oder eine Format String Vulnerability zwischen die anderen -# Codezeilen und schreibe das auch nicht dran. - -# GNU vs BSD. -test=`command -v md5sum | wc -l` -if [ 1 -gt $test ] -then - alias md5sum='md5 -r' -fi -alias stat_cY='stat -c%Y' -test=`stat --version 2>&1 | grep GNU | wc -l` -if [ 1 -gt $test ] -then - alias stat_cY='stat -f%Y' -fi - -_add_ctime_md5sum() { - target=$1 - local base; _dirsplit "$target" - [ -d "$REDO_DIR/$dir" ] || LANG=C mkdir -p "$REDO_DIR/$dir" - LANG=C stat_cY "$target" >"$REDO_DIR/$target".ctime - LANG=C md5sum <"$target" >"$REDO_DIR/$target".md5sum -} - -_add_dependency() { - parent=$1 - dependency=$2 - # Do not record circular dependencies. - [ "$parent" = "$dependency" ] && exit 1 - local base; _dirsplit "$parent" - [ -d "$REDO_DIR/$dir" ] || LANG=C mkdir -p "$REDO_DIR/$dir" - ctime="$(LANG=C stat_cY "$dependency")" - md5sum="$(LANG=C md5sum < "$dependency")" - printf '%s\t%s\t%s\n' "$dependency" "$ctime" "$md5sum" >> \ - "$REDO_DIR"/"$parent".dependencies.tmp -} - -_add_dependency_ne() { - parent="$1" - dependency_ne="$2" - # Do not record circular dependencies. - [ "$parent" = "$dependency_ne" ] && exit 1 - # Do not record existing files as non-existence dependencies. - [ -e "$dependency_ne" ] && return - local base; _dirsplit "$parent" - [ -d "$REDO_DIR/$dir" ] || mkdir -p "$REDO_DIR/$dir" - printf '%s\n' "$dependency_ne" >> "$REDO_DIR/$parent".dependencies_ne.tmp -} - -_dependencies_ne_uptodate() { - target=$1 - # If no non-existence dependencies exist, they are by definition up to date. - if [ ! -s "$REDO_DIR/$target".dependencies_ne ]; then - _echo_debug_message "$target has no non-existence dependencies." - return 0 - fi - _echo_debug_message "$target non-existence dependency check:" - while read dependency_ne; do - _echo_debug_message "\t$dependency_ne should not exist." - # If a non-existence dependency exists, it is out of date. - # Dependencies, e.g. on default.do files may also be out of date. - # Naive implementation: Pretend target is not up to date and rebuild. - if [ -e "$dependency_ne" ]; then - _echo_debug_message "\t$dependency_ne does exist." - return 1 - fi - _echo_debug_message "\t$dependency_ne does not exist." - done < "$REDO_DIR/$target".dependencies_ne - _echo_debug_message "$target non-existence dependencies uptodate." - return 0 -} - -_target_uptodate() { - target=$1 - # If a target is a top-level target, it is not up to date. - if [ -z "$REDO_TARGET" ]; then - return 1 - fi - # If a target does not exist, it is not up to date. - if [ ! -e "$target" ]; then - _echo_debug_message "$target does not exist." - return 1 - fi - # If an .always file exists, the target is out of date. - if [ -e "$REDO_DIR/$target".always ]; then - _echo_debug_message "$target is always out of date." - return 1 - fi - # If .stamp file exists, the target is out of date. - if [ -e "$REDO_DIR/$target".stamp ]; then - _echo_debug_message "$target is out of date due to redo-stamp." - return 1 - fi - if [ -e "$REDO_DIR/$target".ctime ]; then - read ctime_stored <"$REDO_DIR/$target".ctime - ctime_actual="$(LANG=C stat_cY "$target")" - _echo_debug_message "$target stored ctime $ctime_stored" - _echo_debug_message "$target actual ctime $ctime_actual" - if [ "$ctime_stored" = "$ctime_actual" ]; then - _echo_debug_message "$target up to date." - return 0 - elif [ -e "$REDO_DIR/$target".md5sum ]; then - read md5sum_stored <"$REDO_DIR/$target".md5sum - md5sum_actual="$(LANG=C md5sum < "$target")" - _echo_debug_message "$target stored md5sum $md5sum_stored" - _echo_debug_message "$target actual md5sum $md5sum_actual" - if [ "$md5sum_stored" = "$md5sum_actual" ]; then - # If stored md5sum of target matches actual md5sum, but stored - # ctime does not, redo needs to update stored ctime of target. - LANG=C stat_cY "$target" >"$REDO_DIR/$target".md5sum - return 0 - fi - _echo_debug_message "$target out of date." - return 1 - fi - fi -} - -_dependencies_uptodate() { - target=$1 - # If no dependencies exist, they are by definition up to date. - if [ ! -e "$REDO_DIR/$target".dependencies ]; then - _echo_debug_message "$target has no dependencies." - return 0 - fi - if [ "$REDO_SHUFFLE" = "1" ]; then - shuf "$REDO_DIR/$target".dependencies -o "$REDO_DIR/$target".dependencies \ - 2>&- - fi - _echo_debug_message "$target dependency check:" - # If any dependency does not exist, the target is out of date. - LANG=C stat_cY $(LANG=C cut -f1 "$REDO_DIR/$target".dependencies) > \ - "$REDO_DIR/$target".dependencies.ctimes 2>&- || return 1 - exec 3< "$REDO_DIR/$target".dependencies.ctimes - exec 4< "$REDO_DIR/$target".dependencies - while read ctime_actual <&3 && read dependency ctime_stored md5sum_stored <&4; do - # If a .always file exists, the dependency is out of date. - if [ -e "$REDO_DIR/$dependency".always ]; then - _echo_debug_message "\t$dependency is always out of date." - return 1 - fi - # If a .stamp file exists, the dependency is out of date. - if [ -e "$REDO_DIR/$dependency".stamp ]; then - _echo_debug_message "\t$dependency is always out of date." - return 1 - fi - # If a dependency of a dependency is out of date, the dependency is out of date. - if ( ! _dependencies_uptodate "$dependency" ); then - return 1 - fi - # If the ctime of a dependency did not change, the dependency is up to date. - _echo_debug_message "\t$dependency stored ctime $ctime_stored" - _echo_debug_message "\t$dependency actual ctime $ctime_actual" - if [ "$ctime_stored" = "$ctime_actual" ]; then - _echo_debug_message "\t$dependency up to date." - continue - fi - # If the md5sum of a dependency did not change, the dependency is up to date. - md5sum_actual="$(LANG=C md5sum < "$dependency")" - _echo_debug_message "\t$dependency stored md5sum $md5sum_stored" - _echo_debug_message "\t$dependency actual md5sum $md5sum_actual" - if [ "$md5sum_stored" = "$md5sum_actual" ]; then - _echo_debug_message "\t$dependency up to date." - continue - else - # if both ctime and md5sum did change, the dependency is out of date. - _echo_debug_message "\t$dependency out of date." - return 1 - fi - done - exec 4>&- - exec 3>&- - _echo_debug_message "$target dependencies up to date." - # If a non-existence dependency is out of date, the target is out of date. - if ( ! _dependencies_ne_uptodate "$target" ); then - return 1 - fi - return 0 -} - -_dirsplit() { - base=${1##*/} - dir=${1%"$base"} -} - -_do() { - local dir="$1" target="$2" tmp="$3" - target_abspath="$PWD/$target" - target_relpath="${target_abspath##$REDO_BASE/}" - # If target is not up to date or its dependencies are not up to date, build it. - if ( - ! _target_uptodate "$target_abspath" || \ - ! _dependencies_uptodate "$target_abspath" - ); then - dofile="$target".do - base="$target" - ext= - [ -e "$target.do" ] || _find_dofile "$target" - if [ ! -e "$dofile" ]; then - # If .do file does not exist and target exists, it is a source file. - if [ -e "$target_abspath" ]; then - _add_dependency "$REDO_TARGET" "$target_abspath" - _add_ctime_md5sum "$target_abspath" - return 0 - # If .do file does not exist and target does not exist, stop. - else - echo "redo: $target: no .do file" >&2 - exit 1 - fi - fi - printf '%sredo %s%s%s%s%s\n' \ - "$green" "$REDO_DEPTH" "$bold" "$target_relpath" "$plain" >&2 - ( _run_dofile "$target" "${base##*/}" "$tmp.tmp" ) - rv="$?" - # Add non existing .do file to non-existence dependencies so - # target is built when .do file in question is created. - [ -e "$target.do" ] || _add_dependency_ne "$target_abspath" "$PWD/$target.do" - # Add .do file to dependencies so target is built when .do file changes. - _add_dependency "$target_abspath" "$PWD/$dofile" - if [ $rv != 0 ]; then - rm -f "$tmp.tmp" "$tmp.tmp2" - # Exit code 123 conveys that target was considered uptodate at runtime. - if [ $rv != 123 ]; then - printf "%sredo: %s%s: got exit code %s.%s\n" \ - "$red" "$REDO_DEPTH" "$target" "$rv" "$plain" >&2 - exit 1 - fi - fi - mv "$tmp.tmp" "$target" 2>&- || - ! test -s "$tmp.tmp2" || - mv "$tmp.tmp2" "$target" 2>&- - rm -f "$tmp.tmp2" - # After build is finished, update dependencies. - touch "$REDO_DIR/$target_abspath".dependencies.tmp - touch "$REDO_DIR/$target_abspath".dependencies_ne.tmp - mv "$REDO_DIR/$target_abspath".dependencies.tmp \ - "$REDO_DIR/$target_abspath".dependencies >&2 - mv "$REDO_DIR/$target_abspath".dependencies_ne.tmp \ - "$REDO_DIR/$target_abspath".dependencies_ne >&2 - fi - # Some do files (like all.do) do not usually generate output. - if [ -e "$target_abspath" ]; then - # Record dependency on parent target. - _add_dependency "$REDO_TARGET" "$target_abspath" - _add_ctime_md5sum "$target_abspath" - fi -} - -_echo_debug_message() { - [ "$REDO_DEBUG" = "1" ] && echo "$@" >&2 -} - -_find_dofile() { - local prefix= - while :; do - _find_dofile_pwd "$1" - [ -e "$dofile" ] && break - [ "$PWD" = "/" ] && break - target=${PWD##*/}/"$target" - tmp=${PWD##*/}/"$tmp" - prefix=${PWD##*/}/"$prefix" - cd .. - done - base="$prefix""$base" -} - -_find_dofile_pwd() { - dofile=default."$1".do - while :; do - dofile=default.${dofile#default.*.} - [ -e "$dofile" -o "$dofile" = default.do ] && break - done - ext=${dofile#default} - ext=${ext%.do} - base=${1%$ext} -} - -_run_dofile() { - export REDO_DEPTH="$REDO_DEPTH " - export REDO_TARGET="$PWD"/"$target" - local line1 - set -e - read line1 <"$PWD/$dofile" || true - cmd=${line1#"#!"} - # If the first line of a do file does not have a hashbang (#!), use /bin/sh. - if [ "$cmd" = "$line1" ] || [ "$cmd" = "/bin/sh" ]; then - if [ "$REDO_XTRACE" = "1" ]; then - cmd="/bin/sh -ex" - else - cmd="/bin/sh -e" - fi - fi - $cmd "$PWD/$dofile" "$@" >"$tmp.tmp2" -} - -set +e -if [ -n "$1" ]; then - targets="$@" - [ "$REDO_SHUFFLE" = "1" ] && targets=$(LANG=C shuf -e "$@") - for target in $targets; do - # If relative path to target is given, convert to absolute absolute path. - case "$target" in - /*) ;; - *) target="$PWD"/"$target" >&2;; - esac - _dirsplit "$target" - ( cd "$dir" && _do "$dir" "$base" "$base" ) - [ "$?" = 0 ] || exit 1 - done -fi diff --git a/client_prototype.py b/client_prototype.py deleted file mode 100644 index 9458cff..0000000 --- a/client_prototype.py +++ /dev/null @@ -1,610 +0,0 @@ -import curses -import math -import os -import signal -import time - - -def set_window_geometries(): - - def set_window_size(): - win["size"], win["start"] = [0, 0], [0, 0] - win["size"][0] = win["config"][0] - if (win["config"][0] == 0): - win["size"][0] = screen_size[0] - sep_size - elif (win["config"][0] < 0): - win["size"][0] = screen_size[0] + win["config"][0] - sep_size - win["size"][1] = win["config"][1] - if (win["config"][1] == 0): - win["size"][1] = screen_size[1] - elif (win["config"][1] < 0): - win["size"][1] = screen_size[1] + win["config"][1] - - def place_window(): - win_i = windows.index(win) - - # If win is first window, it goes into the top left corner. - win["start"][0] = 0 + sep_size - win["start"][1] = 0 - if (win_i > 0): - - # If not, get win's closest predecessor starting a new stack on the - # screen top,fit win's top left to that win_top's top right corner. - win_top = None - for i in range(win_i - 1, -1, -1): - win_top = windows[i] - if (win_top["start"][0] == 0 + sep_size): - break - win["start"][1] = win_top["start"][1] + win_top["size"][1] \ - + sep_size - - # If enough space is found below win's predecessor, fit win's top - # left corner to that predecessor's bottom left corner. - win_prev = windows[win_i - 1] - next_free_y = win_prev["start"][0] + win_prev["size"][0] + sep_size - if (win["size"][1] <= win_prev["size"][1] and - win["size"][0] <= screen_size[0] - next_free_y): - win["start"][1] = win_prev["start"][1] - win["start"][0] = next_free_y - - # If that fails, try to fit win's top left corner to the top right - # corner of its closest predecessor win_test 1) below win_top (see - # above) 2) and with enough space open to its right between its - # right edge and the lower edge of a win_high located directly - # above win_test to fit win there (without growing further to the - # right than win_high does or surpassing the screen's lower edge). - else: - win_test = win_prev - win_high = None - while (win_test != win_top): - for i in range(win_i - 2, -1, -1): - win_high = windows[i] - if win_test["start"][0] > win_high["start"][0]: - break - next_free_y = win_high["start"][0] + win_high["size"][0] \ - + sep_size - first_free_x = win_test["start"][1] + win_test["size"][1] \ - + sep_size - last_free_x = win_high["start"][1] + win_high["size"][1] - if (win["size"][0] <= screen_size[0] - next_free_y and - win["size"][1] <= last_free_x - first_free_x): - win["start"][1] = first_free_x - win["start"][0] = next_free_y - break - win_test = win_high - - global screen_size, stdscr - curses.endwin() - stdscr = curses.initscr() - screen_size = stdscr.getmaxyx() - for win in windows: - set_window_size() - place_window() - cursed_main.redraw = True - - -def draw_screen(): - - def healthy_addch(y, x, char, attr=0): - """Workaround for .""" - if y == screen_size[0] - 1 and x == screen_size[1] - 1: - char_before = stdscr.inch(y, x - 1) - stdscr.addch(y, x - 1, char, attr) - stdscr.insstr(y, x - 1, " ") - stdscr.addch(y, x - 1, char_before) - else: - stdscr.addch(y, x, char, attr) - - def draw_window_border_lines(): - for win in windows: - for k in range(2): - j = win["start"][int(k == 0)] - sep_size - if (j >= 0 and j < screen_size[int(k == 0)]): - start = win["start"][k] - end = win["start"][k] + win["size"][k] - end = end if end < screen_size[k] else screen_size[k] - if k: - [healthy_addch(j, i, '-') for i in range(start, end)] - else: - [healthy_addch(i, j, '|') for i in range(start, end)] - - def draw_window_border_corners(): - for win in windows: - up = win["start"][0] - sep_size - down = win["start"][0] + win["size"][0] - left = win["start"][1] - sep_size - right = win["start"][1] + win["size"][1] - if (up >= 0 and up < screen_size[0]): - if (left >= 0 and left < screen_size[1]): - healthy_addch(up, left, '+') - if (right >= 0 and right < screen_size[1]): - healthy_addch(up, right, '+') - if (down >= 0 and down < screen_size[0]): - if (left >= 0 and left < screen_size[1]): - healthy_addch(down, left, '+') - if (right >= 0 and right < screen_size[1]): - healthy_addch(down, right, '+') - - def draw_window_titles(): - for win in windows: - title = " " + win["title"] + " " - if len(title) <= win["size"][1]: - y = win["start"][0] - 1 - start_x = win["start"][1] + int((win["size"][1] \ - - len(title))/2) - for x in range(len(title)): - healthy_addch(y, start_x + x, title[x]) - - def draw_window_contents(): - def draw_winmap(): - """Draw winmap in area delimited by offset, winmap_size. - - The individuall cell of a winmap is interpreted as either a single - character element, or as a tuple of character and attribute, - depending on the size len(cell) spits out. - """ - stop = [0, 0] - for i in range(2): - stop[i] = win["size"][i] + offset[i] - if stop[i] >= winmap_size[i]: - stop[i] = winmap_size[i] - for y in range(offset[0], stop[0]): - for x in range(offset[1], stop[1]): - cell = winmap[y * winmap_size[1] + x] - attr = 0 - if len(cell) > 1: - attr = cell[1] - cell = cell[0] - y_in_screen = win["start"][0] + (y - offset[0]) - x_in_screen = win["start"][1] + (x - offset[1]) - if (y_in_screen < screen_size[0] - and x_in_screen < screen_size[1]): - healthy_addch(y_in_screen, x_in_screen, cell, attr) - def draw_scroll_hints(): - def draw_scroll_string(n_lines_outside): - hint = ' ' + str(n_lines_outside + 1) + ' more ' + unit + ' ' - if len(hint) <= win["size"][ni]: - non_hint_space = win["size"][ni] - len(hint) - hint_offset = int(non_hint_space / 2) - for j in range(win["size"][ni] - non_hint_space): - pos_2 = win["start"][ni] + hint_offset + j - x, y = (pos_2, pos_1) if ni else (pos_1, pos_2) - healthy_addch(y, x, hint[j], curses.A_REVERSE) - def draw_scroll_arrows(ar1, ar2): - for j in range(win["size"][ni]): - pos_2 = win["start"][ni] + j - x, y = (pos_2, pos_1) if ni else (pos_1, pos_2) - healthy_addch(y, x, ar1 if ni else ar2, curses.A_REVERSE) - for i in range(2): - ni = int(i == 0) - unit = 'rows' if ni else 'columns' - if (offset[i] > 0): - pos_1 = win["start"][i] - draw_scroll_arrows('^', '<') - draw_scroll_string(offset[i]) - if (winmap_size[i] > offset[i] + win["size"][i]): - pos_1 = win["start"][i] + win["size"][i] - 1 - draw_scroll_arrows('v', '>') - draw_scroll_string(winmap_size[i] - offset[i] - - win["size"][i]) - for win in windows: - offset, winmap_size, winmap = win["func"]() - draw_winmap() - draw_scroll_hints() - - stdscr.erase() - draw_window_border_lines() - draw_window_border_corners() - draw_window_titles() - draw_window_contents() - stdscr.refresh() - - -def read_worldstate(): - if not os.access(io["path_worldstate"], os.F_OK): - msg = "No world state file found at " + io["path_worldstate"] + "." - raise SystemExit(msg) - read_anew = False - worldstate_file = open(io["path_worldstate"], "r") - turn_string = worldstate_file.readline() - if int(turn_string) != world_data["turn"]: - read_anew = True - if not read_anew: # In rare cases, world may change, but not turn number. - mtime = os.stat(io["path_worldstate"]) - if mtime != read_worldstate.last_checked_mtime: - read_worldstate.last_checked_mtime = mtime - read_anew = True - if read_anew: - cursed_main.redraw = True - world_data["turn"] = int(turn_string) - world_data["lifepoints"] = int(worldstate_file.readline()) - world_data["satiation"] = int(worldstate_file.readline()) - world_data["inventory"] = [] - while True: - line = worldstate_file.readline().replace("\n", "") - if line == '%': - break - world_data["inventory"] += [line] - world_data["avatar_position"][0] = int(worldstate_file.readline()) - world_data["avatar_position"][1] = int(worldstate_file.readline()) - if not world_data["look_mode"]: - world_data["map_center"][0] = world_data["avatar_position"][0] - world_data["map_center"][1] = world_data["avatar_position"][1] - world_data["map_size"] = int(worldstate_file.readline()) - world_data["fov_map"] = "" - for i in range(world_data["map_size"]): - line = worldstate_file.readline().replace("\n", "") - world_data["fov_map"] += line - world_data["mem_map"] = "" - for i in range(world_data["map_size"]): - line = worldstate_file.readline().replace("\n", "") - world_data["mem_map"] += line - worldstate_file.close() -read_worldstate.last_checked_mtime = -1 - - -def read_message_queue(): - while (len(message_queue["messages"]) > 1 - or (len(message_queue["messages"]) == 1 - and not message_queue["open_end"])): - message = message_queue["messages"].pop(0) - if message == "THINGS_HERE START": - read_message_queue.parse_thingshere = True - world_data["look"] = [] - elif message == "THINGS_HERE END": - read_message_queue.parse_thingshere = False - if world_data["look"] == []: - world_data["look"] = ["(none known)"] - cursed_main.redraw = True - elif read_message_queue.parse_thingshere: - world_data["look"] += [message] - elif message[0:4] == "LOG ": - world_data["log"] += [message[4:]] - cursed_main.redraw = True - elif message == "WORLD_UPDATED": - query_mapcell() -read_message_queue.parse_thingshere = False - - -def query_mapcell(): - command_sender("THINGS_HERE " + str(world_data["map_center"][0]) + " " - + str(world_data["map_center"][1]))() - world_data["look"] = ["(polling)"] - - -def cursed_main(stdscr): - - def ping_test(): - half_wait_time = 5 - if len(new_data_from_server) > 0: - ping_test.sent = False - elif ping_test.wait_start + half_wait_time < time.time(): - if not ping_test.sent: - io["file_out"].write("PING\n") - io["file_out"].flush() - ping_test.sent = True - ping_test.wait_start = time.time() - elif ping_test.sent: - raise SystemExit("Server not answering anymore.") - ping_test.wait_start = 0 - - def read_into_message_queue(): - if new_data_from_server == "": - return - new_open_end = False - if new_data_from_server[-1] is not "\n": - new_open_end = True - new_messages = new_data_from_server.splitlines() - if message_queue["open_end"]: - message_queue["messages"][-1] += new_messages[0] - del new_messages[0] - message_queue["messages"] += new_messages - if new_open_end: - message_queue["open_end"] = True - - curses.noecho() - curses.curs_set(False) - signal.signal(signal.SIGWINCH, - lambda ignore_1, ignore_2: set_window_geometries()) - set_window_geometries() - delay = 1 - while True: - stdscr.timeout(int(delay)) - delay = delay * 1.1 if delay < 1000 else delay - if cursed_main.redraw: - delay = 1 - draw_screen() - cursed_main.redraw = False - char = stdscr.getch() - if char >= 0: - char = chr(char) - if char in commands: - if len(commands[char]) == 1 or not world_data["look_mode"]: - commands[char][0]() - cursed_main.redraw = True - else: - commands[char][1]() - cursed_main.redraw = True - new_data_from_server = io["file_in"].read() - ping_test() - read_into_message_queue() - read_worldstate() - read_message_queue() - - -def win_map(): - win_size = next(win["size"] for win in windows if win["func"] == win_map) - offset = [0, 0] - for i in range(2): - if world_data["map_center"][i] * (i + 1) > win_size[i] / 2: - if world_data["map_center"][i] * (i + 1) \ - < world_data["map_size"] * (i + 1) - win_size[i] / 2: - offset[i] = world_data["map_center"][i] * (i + 1) \ - - int(win_size[i] / 2) - if i == 1: - offset[1] = offset[1] + world_data["map_center"][0] % 2 - else: - offset[i] = world_data["map_size"] * (i + 1) - win_size[i] + i - winmap_size = [world_data["map_size"], world_data["map_size"] * 2 + 1] - winmap = [] - curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE) - curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK) - for y in range(world_data["map_size"]): - for x in range(world_data["map_size"]): - char = world_data["fov_map"][y * world_data["map_size"] + x] - if world_data["look_mode"] and y == world_data["map_center"][0] \ - and x == world_data["map_center"][1]: - if char == " ": - char = \ - world_data["mem_map"][y * world_data["map_size"] + x] - winmap += [(char, curses.A_REVERSE), (" ", curses.A_REVERSE)] - continue - if char == " ": - char = world_data["mem_map"][y * world_data["map_size"] + x] - attribute = curses.color_pair(1) if char == " " \ - else curses.color_pair(2) - winmap += [(char, attribute), (" ", attribute)] - else: - winmap += char + " " - if y % 2 == 0: - winmap += " " - return offset, winmap_size, winmap - - -def win_inventory(): - win_size = next(win["size"] for win in windows - if win["func"] == win_inventory) - winmap = [] - winmap_size = [0, win_size[1]] - for line in world_data["inventory"]: - winmap_size[1] = winmap_size[1] if len(line) <= winmap_size[1] \ - else len(line) - count = 0 - for line in world_data["inventory"]: - padding_size = winmap_size[1] - len(line) - line += (" " * padding_size) - if count == world_data["inventory_selection"]: - line_new = [] - for x in range(len(line)): - line_new += [(line[x], curses.A_REVERSE)] - line = line_new - winmap += line - winmap_size[0] = winmap_size[0] + 1 - count += 1 - offset = [0, 0] - if world_data["inventory_selection"] > win_size[0]/2: - if world_data["inventory_selection"] < len(world_data["inventory"]) \ - - win_size[0]/2: - offset[0] = world_data["inventory_selection"] - int(win_size[0]/2) - else: - offset[0] = len(world_data["inventory"]) - win_size[0] - return offset, winmap_size, winmap - - -def win_look(): - winmap = "" - winmap_size = [0, 0] - for line in world_data["look"]: - winmap_size[1] = winmap_size[1] if len(line) <= winmap_size[1] \ - else len(line) - for line in world_data["look"]: - padding_size = winmap_size[1] - len(line) - winmap += line + (" " * padding_size) - winmap_size[0] = winmap_size[0] + 1 - offset = [world_data["look_scroll"], 0] - return offset, winmap_size, winmap - - -def win_info(): - winmap = "T: " + str(world_data["turn"]) \ - + " H: " + str(world_data["lifepoints"]) \ - + " S: " + str(world_data["satiation"]) - winmap_size = [1, len(winmap)] - offset = [0, 0] - return offset, winmap_size, winmap - - -def win_log(): - win_size = next(win["size"] for win in windows if win["func"] == win_log) - offset = [0, 0] - winmap = "" - number_of_lines = 0 - for line in world_data["log"]: - number_of_lines += math.ceil(len(line) / win_size[1]) - padding_size = win_size[1] - (len(line) % win_size[1]) - winmap += line + (padding_size * " ") - if number_of_lines < win_size[0]: - winmap = (" " * win_size[1] * (win_size[0] - number_of_lines)) + winmap - number_of_lines = win_size[0] - elif number_of_lines > win_size[0]: - offset[0] = number_of_lines - win_size[0] - winmap_size = [number_of_lines, win_size[1]] - return offset, winmap_size, winmap - - -def command_quit(): - command_sender("QUIT")() - raise SystemExit("Received QUIT command, forwarded to server, leaving.") - - -def command_toggle_look_mode(): - if not world_data["look_mode"]: - world_data["look_mode"] = True - else: - world_data["look_mode"] = False - world_data["map_center"] = world_data["avatar_position"] - query_mapcell() - - -def command_sender(string, int_field=None): - def command_send(): - int_string = "" - if int_field: - int_string = " " + str(world_data[int_field]) - io["file_out"].write(string + int_string + "\n") - io["file_out"].flush() - return command_send - - -def command_looker(string): - def command_look(): - if string == "west" \ - and world_data["map_center"][1] > 0: - world_data["map_center"][1] -= 1 - elif string == "east" \ - and world_data["map_center"][1] < world_data["map_size"] - 1: - world_data["map_center"][1] += 1 - else: - y_unevenness = world_data["map_center"][0] % 2 - y_evenness = int(not(y_unevenness)) - if string[6:] == "west" and \ - world_data["map_center"][1] > -y_unevenness: - if string[:5] == "north" and world_data["map_center"][0] > 0: - world_data["map_center"][0] -= 1 - world_data["map_center"][1] -= y_evenness - if string[:5] == "south" and world_data["map_center"][0] \ - < world_data["map_size"] - 1: - world_data["map_center"][0] += 1 - world_data["map_center"][1] -= y_evenness - elif string[6:] == "east" and world_data["map_center"][1] \ - < world_data["map_size"] - y_unevenness: - if string[:5] == "north" and world_data["map_center"][0] > 0: - world_data["map_center"][0] -= 1 - world_data["map_center"][1] += y_unevenness - if string[:5] == "south" and world_data["map_center"][0] \ - < world_data["map_size"] - 1: - world_data["map_center"][0] += 1 - world_data["map_center"][1] += y_unevenness - query_mapcell() - return command_look - - -def command_look_scroller(string): - def command_look_scroll(): - win_size = next(win["size"] for win in windows - if win["func"] == win_look) - if string == "up" and world_data["look_scroll"] > 0: - world_data["look_scroll"] -= 1 - elif string == "down" and world_data["look_scroll"] \ - < len(world_data["look"]) - win_size[0]: - world_data["look_scroll"] += 1 - return command_look_scroll - - -def command_inventory_selector(string): - def command_inventory_select(): - if string == "up" and world_data["inventory_selection"] > 0: - world_data["inventory_selection"] -= 1 - elif string == "down" and world_data["inventory_selection"] \ - < len(world_data["inventory"]) - 1: - world_data["inventory_selection"] += 1 - return command_inventory_select - - -windows = [ - {"config": [1, 33], "func": win_info, "title": "Info"}, - {"config": [-7, 33], "func": win_log, "title": "Log"}, - {"config": [4, 16], "func": win_inventory, "title": "Inventory"}, - {"config": [4, 16], "func": win_look, "title": "Things here"}, - {"config": [0, -34], "func": win_map, "title": "Map"} -] -io = { - "path_out": "server/in", - "path_in": "server/out", - "path_worldstate": "server/worldstate" -} -commands = { - "A": (command_sender("ai"),), - "D": (command_sender("drop", "inventory_selection"),), - "J": (command_look_scroller("down"),), - "K": (command_look_scroller("up"),), - "P": (command_sender("pick_up"),), - "Q": (command_quit,), - "U": (command_sender("use", "inventory_selection"),), - "W": (command_sender("wait"),), - "c": (command_sender("move south-east"), command_looker("south-east")), - "d": (command_sender("move east"), command_looker("east")), - "e": (command_sender("move north-east"), command_looker("north-east")), - "j": (command_inventory_selector("down"),), - "k": (command_inventory_selector("up"),), - "l": (command_toggle_look_mode,), - "s": (command_sender("move west"), command_looker("west")), - "w": (command_sender("move north-west"), command_looker("north-west")), - "x": (command_sender("move south-west"), command_looker("south-west")), -} -message_queue = { - "open_end": False, - "messages": [] -} -world_data = { - "avatar_position": [-1, -1], - "fov_map": "", - "inventory": [], - "inventory_selection": 0, - "lifepoints": -1, - "look": [], - "look_mode": False, - "look_scroll": 0, - "log": [ -"QUICK COMMAND OVERVIEW: " -"Move through map with 'w', 'e', 's', 'd', 'x', 'c'. " -"Pick up things with 'P', drop a thing selected from the inventory with 'D' " -"or use it with 'P'. " -"Move through inventory selection with 'j' and 'k'. " -"Toggle looking around mode with 'l'. " -"Scroll 'Things here' window with 'J' and 'K'. " -"Let AI decide next move with 'A'. " -"Quit with 'Q'. ", -"STATS OVERVIEW: " -"'T': Turn; 'H': Health (the higher, the better); " -"'S': Satiation (the closer to zero, the better).", -"See README file for more help." -], - "map_center": [-1, -1], - "map_size": 0, - "mem_map": "", - "satiation": -1, - "turn": -1 -} -sep_size = 1 # Width of inter-window borders and title bars. -stdscr = None -screen_size = [0,0] - - -try: - if (not os.access(io["path_out"], os.F_OK)): - msg = "No server input file found at " + io["path_out"] + "." - raise SystemExit(msg) - io["file_out"] = open(io["path_out"], "a") - io["file_in"] = open(io["path_in"], "r") - curses.wrapper(cursed_main) -except SystemExit as exit: - print("ABORTING: " + exit.args[0]) -except: - print("SOMETHING WENT WRONG IN UNEXPECTED WAYS") - raise -finally: - if "file_out" in io: - io["file_out"].close() - if "file_in" in io: - io["file_in"].close() diff --git a/confclient/commands b/confclient/commands deleted file mode 100644 index 865cc3e..0000000 --- a/confclient/commands +++ /dev/null @@ -1,174 +0,0 @@ -COMMAND ai -DESCRIPTION 'let AI decide move' -SERVER_COMMAND ai -SERVER_ARGUMENT 0 - -COMMAND wait -DESCRIPTION 'wait / next turn' -SERVER_COMMAND wait -SERVER_ARGUMENT 0 - -COMMAND move_e -DESCRIPTION 'move north-east' -SERVER_COMMAND move -SERVER_ARGUMENT e - -COMMAND move_d -DESCRIPTION 'move east' -SERVER_COMMAND move -SERVER_ARGUMENT d - -COMMAND move_c -DESCRIPTION 'move south-east' -SERVER_COMMAND move -SERVER_ARGUMENT c - -COMMAND move_x -DESCRIPTION 'move south-west' -SERVER_COMMAND move -SERVER_ARGUMENT x - -COMMAND move_s -DESCRIPTION 'move west' -SERVER_COMMAND move -SERVER_ARGUMENT s - -COMMAND move_w -DESCRIPTION 'move north-west' -SERVER_COMMAND move -SERVER_ARGUMENT w - -COMMAND pick -DESCRIPTION 'pick up' -SERVER_COMMAND pick_up -SERVER_ARGUMENT 0 - -COMMAND use -DESCRIPTION 'use object' -SERVER_COMMAND use -SERVER_ARGUMENT i - -COMMAND drop -DESCRIPTION 'drop' -SERVER_COMMAND drop -SERVER_ARGUMENT i - -COMMAND quit -DESCRIPTION 'quit' - -COMMAND cyc_win_f -DESCRIPTION 'nav down window' - -COMMAND cyc_win_b -DESCRIPTION 'nav up window' - -COMMAND shift_f -DESCRIPTION 'shift down window' - -COMMAND shift_b -DESCRIPTION 'shift up window' - -COMMAND scrl_l -DESCRIPTION 'scroll pad left' - -COMMAND scrl_r -DESCRIPTION 'scroll pad right' - -COMMAND keyb_dG -DESCRIPTION 'nav down global keys' - -COMMAND keyb_uG -DESCRIPTION 'nav up global keys' - -COMMAND keyb_mG -DESCRIPTION 'mod global keys' - -COMMAND winconf -DESCRIPTION 'toggle win config' - -COMMAND grow_h -DESCRIPTION 'grow window width' - -COMMAND shri_h -DESCRIPTION 'shrink window width' - -COMMAND grow_v -DESCRIPTION 'grow window height' - -COMMAND shri_v -DESCRIPTION 'shrink window height' - -COMMAND to_break -DESCRIPTION 'toggle line break type' - -COMMAND to_height_t -DESCRIPTION 'toggle window height type' - -COMMAND to_width_t -DESCRIPTION 'toggle window width type' - -COMMAND inv_d -DESCRIPTION 'nav down object' - -COMMAND inv_u -DESCRIPTION 'nav up object' - -COMMAND keyb_dw -DESCRIPTION 'nav down window keys' - -COMMAND keyb_uw -DESCRIPTION 'nav up window nav' - -COMMAND keyb_mw -DESCRIPTION 'mod window key' - -COMMAND keyb_dg -DESCRIPTION 'nav down win geometry config keys' - -COMMAND keyb_ug -DESCRIPTION 'nav up win geometry config keys' - -COMMAND keyb_mg -DESCRIPTION 'mod win geometry config keys' - -COMMAND keyb_dk -DESCRIPTION 'nav down win keys config keys' - -COMMAND keyb_uk -DESCRIPTION 'nav up win keys config keys' - -COMMAND keyb_mk -DESCRIPTION 'mod win keys config keys' - -COMMAND reload_conf -DESCRIPTION 'reload interface config' - -COMMAND save_conf -DESCRIPTION 'save interface config' - -COMMAND to_inv -DESCRIPTION 'window inventory' - -COMMAND to_terrain -DESCRIPTION 'standing-on window' - -COMMAND to_g_keywin -DESCRIPTION 'window global keys' - -COMMAND to_mapwin -DESCRIPTION 'window map' - -COMMAND to_infowin -DESCRIPTION 'window info' - -COMMAND to_logwin -DESCRIPTION 'window log' - -COMMAND to_wg_keywin -DESCRIPTION 'window geometry keys' - -COMMAND to_wk_keywin -DESCRIPTION 'window keybinding keys' - -COMMAND to_look -DESCRIPTION 'toggle look mode' diff --git a/confclient/interface_conf b/confclient/interface_conf deleted file mode 100644 index 0055141..0000000 --- a/confclient/interface_conf +++ /dev/null @@ -1,108 +0,0 @@ - -KEYBINDINGS 'global' -KEY 81 quit -KEY 87 winconf -KEY 62 cyc_win_f -KEY 60 cyc_win_b -KEY 262 scrl_l -KEY 360 scrl_r -KEY 82 reload_conf -KEY 67 save_conf -KEY 265 to_mapwin -KEY 266 to_infowin -KEY 267 to_inv -KEY 268 to_terrain -KEY 269 to_logwin -KEY 270 to_g_keywin -KEY 271 to_wg_keywin -KEY 272 to_wk_keywin - -KEYBINDINGS 'wingeom' -KEY 258 shift_f -KEY 259 shift_b -KEY 42 grow_h -KEY 95 shri_h -KEY 43 grow_v -KEY 45 shri_v -KEY 98 to_break -KEY 121 to_height_t -KEY 120 to_width_t - -KEYBINDINGS 'winkeys' -KEY 258 keyb_dw -KEY 259 keyb_uw -KEY 10 keyb_mw - -WIN_ORDER 'l0csim' -WIN_FOCUS 'm' - -WINDOW 0 -NAME 'Set global keys' -BREAK 0 -WIDTH 22 -HEIGHT 7 -KEY 258 keyb_dG -KEY 259 keyb_uG -KEY 10 keyb_mG - -WINDOW 1 -NAME 'Set window geometry keys' -BREAK 0 -WIDTH 29 -HEIGHT 9 -KEY 258 keyb_dg -KEY 259 keyb_ug -KEY 10 keyb_mg - -WINDOW 2 -NAME 'Set window keybinding keys' -BREAK 0 -WIDTH 29 -HEIGHT 3 -KEY 258 keyb_dk -KEY 259 keyb_uk -KEY 10 keyb_mk - -WINDOW c -NAME 'Inventory' -BREAK 1 -WIDTH 14 -HEIGHT 11 -KEY 68 drop -KEY 259 inv_u -KEY 258 inv_d -KEY 117 use - -WINDOW s -NAME 'Things here' -BREAK 1 -WIDTH 14 -HEIGHT -12 - -WINDOW i -NAME 'Info' -BREAK 2 -WIDTH -38 -HEIGHT 1 - -WINDOW l -NAME 'Log' -BREAK 0 -WIDTH 22 -HEIGHT -8 - -WINDOW m -NAME 'Map' -BREAK 0 -WIDTH -38 -HEIGHT -2 -KEY 97 ai -KEY 112 pick -KEY 58 wait -KEY 101 move_e -KEY 100 move_d -KEY 99 move_c -KEY 120 move_x -KEY 115 move_s -KEY 119 move_w -KEY 108 to_look diff --git a/confclient/single_wins/info b/confclient/single_wins/info deleted file mode 100644 index 9e899fd..0000000 --- a/confclient/single_wins/info +++ /dev/null @@ -1,119 +0,0 @@ -KEYBINDINGS 'global' -KEY 81 quit -KEY 265 to_mapwin -KEY 266 to_infowin -KEY 267 to_inv -KEY 268 to_terrain -KEY 269 to_logwin -KEY 270 to_g_keywin -KEY 271 to_wg_keywin -KEY 272 to_wk_keywin -KEY 87 winconf -KEY 62 cyc_win_f -KEY 60 cyc_win_b -KEY 262 scrl_l -KEY 360 scrl_r -KEY 82 reload_conf -KEY 67 save_conf -KEY 97 ai -KEY 112 pick -KEY 58 wait -KEY 101 move_e -KEY 100 move_d -KEY 99 move_c -KEY 120 move_x -KEY 115 move_s -KEY 119 move_w -KEY 68 drop -KEY 117 use - -KEYBINDINGS 'wingeom' -KEY 258 shift_f -KEY 259 shift_b -KEY 42 grow_h -KEY 95 shri_h -KEY 43 grow_v -KEY 45 shri_v -KEY 98 to_break -KEY 121 to_height_t -KEY 120 to_width_t - -KEYBINDINGS 'winkeys' -KEY 258 keyb_dw -KEY 259 keyb_uw -KEY 10 keyb_mw - -WIN_ORDER 'i' -WIN_FOCUS 'i' - -WINDOW i -NAME 'Info' -BREAK 2 -WIDTH 0 -HEIGHT 0 - -WINDOW 0 -NAME 'Set global keys' -BREAK 0 -WIDTH 22 -HEIGHT 7 -KEY 258 keyb_dG -KEY 259 keyb_uG -KEY 10 keyb_mG - -WINDOW 1 -NAME 'Set window geometry keys' -BREAK 0 -WIDTH 29 -HEIGHT 9 -KEY 258 keyb_dg -KEY 259 keyb_ug -KEY 10 keyb_mg - -WINDOW 2 -NAME 'Set window keybinding keys' -BREAK 0 -WIDTH 29 -HEIGHT 3 -KEY 258 keyb_dk -KEY 259 keyb_uk -KEY 10 keyb_mk - -WINDOW c -NAME 'Inventory' -BREAK 2 -WIDTH 13 -HEIGHT 4 -KEY 68 drop -KEY 259 inv_u -KEY 258 inv_d -KEY 117 use - -WINDOW s -NAME 'Things here' -BREAK 1 -WIDTH 14 -HEIGHT -12 - -WINDOW l -NAME 'Log' -BREAK 0 -WIDTH 13 -HEIGHT -8 - -WINDOW m -NAME 'Map' -BREAK 0 -WIDTH -37 -HEIGHT 0 -KEY 97 ai -KEY 112 pick -KEY 58 wait -KEY 101 move_e -KEY 100 move_d -KEY 99 move_c -KEY 120 move_x -KEY 115 move_s -KEY 119 move_w -KEY 46 map_c -KEY 70 to_autofocus diff --git a/confclient/single_wins/inventory b/confclient/single_wins/inventory deleted file mode 100644 index 03ef22e..0000000 --- a/confclient/single_wins/inventory +++ /dev/null @@ -1,117 +0,0 @@ -KEYBINDINGS 'global' -KEY 81 quit -KEY 265 to_mapwin -KEY 266 to_infowin -KEY 267 to_inv -KEY 268 to_terrain -KEY 269 to_logwin -KEY 270 to_g_keywin -KEY 271 to_wg_keywin -KEY 272 to_wk_keywin -KEY 87 winconf -KEY 62 cyc_win_f -KEY 60 cyc_win_b -KEY 262 scrl_l -KEY 360 scrl_r -KEY 82 reload_conf -KEY 67 save_conf -KEY 97 ai -KEY 112 pick -KEY 58 wait -KEY 101 move_e -KEY 100 move_d -KEY 99 move_c -KEY 120 move_x -KEY 115 move_s -KEY 119 move_w -KEY 68 drop -KEY 117 use - -KEYBINDINGS 'wingeom' -KEY 258 shift_f -KEY 259 shift_b -KEY 42 grow_h -KEY 95 shri_h -KEY 43 grow_v -KEY 45 shri_v -KEY 98 to_break -KEY 121 to_height_t -KEY 120 to_width_t - -KEYBINDINGS 'winkeys' -KEY 258 keyb_dw -KEY 259 keyb_uw -KEY 10 keyb_mw - -WIN_ORDER 'c' -WIN_FOCUS 'c' - -WINDOW c -NAME 'Inventory' -BREAK 2 -WIDTH 0 -HEIGHT 0 -KEY 259 inv_u -KEY 258 inv_d - -WINDOW 0 -NAME 'Set global keys' -BREAK 0 -WIDTH 22 -HEIGHT 7 -KEY 258 keyb_dG -KEY 259 keyb_uG -KEY 10 keyb_mG - -WINDOW 1 -NAME 'Set window geometry keys' -BREAK 0 -WIDTH 29 -HEIGHT 9 -KEY 258 keyb_dg -KEY 259 keyb_ug -KEY 10 keyb_mg - -WINDOW 2 -NAME 'Set window keybinding keys' -BREAK 0 -WIDTH 29 -HEIGHT 3 -KEY 258 keyb_dk -KEY 259 keyb_uk -KEY 10 keyb_mk - -WINDOW s -NAME 'Things here' -BREAK 1 -WIDTH 14 -HEIGHT -12 - -WINDOW i -NAME 'Info' -BREAK 0 -WIDTH 13 -HEIGHT 2 - -WINDOW l -NAME 'Log' -BREAK 0 -WIDTH 13 -HEIGHT -8 - -WINDOW m -NAME 'Map' -BREAK 0 -WIDTH -37 -HEIGHT 0 -KEY 97 ai -KEY 112 pick -KEY 58 wait -KEY 101 move_e -KEY 100 move_d -KEY 99 move_c -KEY 120 move_x -KEY 115 move_s -KEY 119 move_w -KEY 46 map_c -KEY 70 to_autofocus diff --git a/confclient/single_wins/log b/confclient/single_wins/log deleted file mode 100644 index 6e046a9..0000000 --- a/confclient/single_wins/log +++ /dev/null @@ -1,119 +0,0 @@ -KEYBINDINGS 'global' -KEY 81 quit -KEY 265 to_mapwin -KEY 266 to_infowin -KEY 267 to_inv -KEY 268 to_terrain -KEY 269 to_logwin -KEY 270 to_g_keywin -KEY 271 to_wg_keywin -KEY 272 to_wk_keywin -KEY 87 winconf -KEY 62 cyc_win_f -KEY 60 cyc_win_b -KEY 262 scrl_l -KEY 360 scrl_r -KEY 82 reload_conf -KEY 67 save_conf -KEY 97 ai -KEY 112 pick -KEY 58 wait -KEY 101 move_e -KEY 100 move_d -KEY 99 move_c -KEY 120 move_x -KEY 115 move_s -KEY 119 move_w -KEY 68 drop -KEY 117 use - -KEYBINDINGS 'wingeom' -KEY 258 shift_f -KEY 259 shift_b -KEY 42 grow_h -KEY 95 shri_h -KEY 43 grow_v -KEY 45 shri_v -KEY 98 to_break -KEY 121 to_height_t -KEY 120 to_width_t - -KEYBINDINGS 'winkeys' -KEY 258 keyb_dw -KEY 259 keyb_uw -KEY 10 keyb_mw - -WIN_ORDER 'l' -WIN_FOCUS 'l' - -WINDOW l -NAME 'Log' -BREAK 0 -WIDTH 0 -HEIGHT 0 - -WINDOW 0 -NAME 'Set global keys' -BREAK 0 -WIDTH 22 -HEIGHT 7 -KEY 258 keyb_dG -KEY 259 keyb_uG -KEY 10 keyb_mG - -WINDOW 1 -NAME 'Set window geometry keys' -BREAK 0 -WIDTH 29 -HEIGHT 9 -KEY 258 keyb_dg -KEY 259 keyb_ug -KEY 10 keyb_mg - -WINDOW 2 -NAME 'Set window keybinding keys' -BREAK 0 -WIDTH 29 -HEIGHT 3 -KEY 258 keyb_dk -KEY 259 keyb_uk -KEY 10 keyb_mk - -WINDOW c -NAME 'Inventory' -BREAK 2 -WIDTH 13 -HEIGHT 4 -KEY 68 drop -KEY 259 inv_u -KEY 258 inv_d -KEY 117 use - -WINDOW s -NAME 'Things here' -BREAK 1 -WIDTH 14 -HEIGHT -12 - -WINDOW i -NAME 'Info' -BREAK 0 -WIDTH 13 -HEIGHT 2 - -WINDOW m -NAME 'Map' -BREAK 0 -WIDTH -37 -HEIGHT 0 -KEY 97 ai -KEY 112 pick -KEY 58 wait -KEY 101 move_e -KEY 100 move_d -KEY 99 move_c -KEY 120 move_x -KEY 115 move_s -KEY 119 move_w -KEY 46 map_c -KEY 70 to_autofocus diff --git a/confclient/single_wins/map b/confclient/single_wins/map deleted file mode 100644 index 3b736bd..0000000 --- a/confclient/single_wins/map +++ /dev/null @@ -1,108 +0,0 @@ -KEYBINDINGS 'global' -KEY 81 quit -KEY 265 to_mapwin -KEY 266 to_infowin -KEY 267 to_inv -KEY 268 to_terrain -KEY 269 to_logwin -KEY 270 to_g_keywin -KEY 271 to_wg_keywin -KEY 272 to_wk_keywin -KEY 87 winconf -KEY 62 cyc_win_f -KEY 60 cyc_win_b -KEY 262 scrl_l -KEY 360 scrl_r -KEY 82 reload_conf -KEY 67 save_conf -KEY 97 ai -KEY 112 pick -KEY 58 wait -KEY 101 move_e -KEY 100 move_d -KEY 99 move_c -KEY 120 move_x -KEY 115 move_s -KEY 119 move_w -KEY 68 drop -KEY 117 use - -KEYBINDINGS 'wingeom' -KEY 258 shift_f -KEY 259 shift_b -KEY 42 grow_h -KEY 95 shri_h -KEY 43 grow_v -KEY 45 shri_v -KEY 98 to_break -KEY 121 to_height_t -KEY 120 to_width_t - -KEYBINDINGS 'winkeys' -KEY 258 keyb_dw -KEY 259 keyb_uw -KEY 10 keyb_mw - -WIN_ORDER 'm' -WIN_FOCUS 'm' - -WINDOW m -NAME 'Map' -BREAK 0 -WIDTH 0 -HEIGHT 0 - -WINDOW 0 -NAME 'Set global keys' -BREAK 0 -WIDTH 22 -HEIGHT 7 -KEY 258 keyb_dG -KEY 259 keyb_uG -KEY 10 keyb_mG - -WINDOW 1 -NAME 'Set window geometry keys' -BREAK 0 -WIDTH 29 -HEIGHT 9 -KEY 258 keyb_dg -KEY 259 keyb_ug -KEY 10 keyb_mg - -WINDOW 2 -NAME 'Set window keybinding keys' -BREAK 0 -WIDTH 29 -HEIGHT 3 -KEY 258 keyb_dk -KEY 259 keyb_uk -KEY 10 keyb_mk - -WINDOW c -NAME 'Inventory' -BREAK 2 -WIDTH 13 -HEIGHT 4 -KEY 68 drop -KEY 259 inv_u -KEY 258 inv_d -KEY 117 use - -WINDOW s -NAME 'Things here' -BREAK 1 -WIDTH 14 -HEIGHT -12 - -WINDOW i -NAME 'Info' -BREAK 0 -WIDTH 13 -HEIGHT 2 - -WINDOW l -NAME 'Log' -BREAK 0 -WIDTH 13 -HEIGHT -8 diff --git a/confclient/single_wins/things_here b/confclient/single_wins/things_here deleted file mode 100644 index 2abc3e5..0000000 --- a/confclient/single_wins/things_here +++ /dev/null @@ -1,120 +0,0 @@ - -KEYBINDINGS 'global' -KEY 81 quit -KEY 265 to_mapwin -KEY 266 to_infowin -KEY 267 to_inv -KEY 268 to_terrain -KEY 269 to_logwin -KEY 270 to_g_keywin -KEY 271 to_wg_keywin -KEY 272 to_wk_keywin -KEY 87 winconf -KEY 62 cyc_win_f -KEY 60 cyc_win_b -KEY 262 scrl_l -KEY 360 scrl_r -KEY 82 reload_conf -KEY 67 save_conf -KEY 97 ai -KEY 112 pick -KEY 58 wait -KEY 101 move_e -KEY 100 move_d -KEY 99 move_c -KEY 120 move_x -KEY 115 move_s -KEY 119 move_w -KEY 68 drop -KEY 117 use - -KEYBINDINGS 'wingeom' -KEY 258 shift_f -KEY 259 shift_b -KEY 42 grow_h -KEY 95 shri_h -KEY 43 grow_v -KEY 45 shri_v -KEY 98 to_break -KEY 121 to_height_t -KEY 120 to_width_t - -KEYBINDINGS 'winkeys' -KEY 258 keyb_dw -KEY 259 keyb_uw -KEY 10 keyb_mw - -WIN_ORDER 's' -WIN_FOCUS 's' - -WINDOW 0 -NAME 'Set global keys' -BREAK 0 -WIDTH 22 -HEIGHT 7 -KEY 258 keyb_dG -KEY 259 keyb_uG -KEY 10 keyb_mG - -WINDOW 1 -NAME 'Set window geometry keys' -BREAK 0 -WIDTH 29 -HEIGHT 9 -KEY 258 keyb_dg -KEY 259 keyb_ug -KEY 10 keyb_mg - -WINDOW 2 -NAME 'Set window keybinding keys' -BREAK 0 -WIDTH 29 -HEIGHT 3 -KEY 258 keyb_dk -KEY 259 keyb_uk -KEY 10 keyb_mk - -WINDOW c -NAME 'Inventory' -BREAK 1 -WIDTH 14 -HEIGHT 11 -KEY 68 drop -KEY 259 inv_u -KEY 258 inv_d -KEY 117 use - -WINDOW s -NAME 'Things here' -BREAK 1 -WIDTH 0 -HEIGHT 0 - -WINDOW i -NAME 'Info' -BREAK 2 -WIDTH -38 -HEIGHT 1 - -WINDOW l -NAME 'Log' -BREAK 0 -WIDTH 22 -HEIGHT -8 - -WINDOW m -NAME 'Map' -BREAK 0 -WIDTH -38 -HEIGHT -2 -KEY 97 ai -KEY 112 pick -KEY 58 wait -KEY 101 move_e -KEY 100 move_d -KEY 99 move_c -KEY 120 move_x -KEY 115 move_s -KEY 119 move_w -KEY 46 map_c -KEY 70 to_autofocus diff --git a/libplomrogue.c b/libplomrogue.c new file mode 100644 index 0000000..d0b15a1 --- /dev/null +++ b/libplomrogue.c @@ -0,0 +1,619 @@ +#include /* pow() */ +#include /* NULL */ +#include /* ?(u)int(8|16|32)_t, ?(U)INT8_(MIN|MAX) */ +#include /* free, malloc */ +#include /* memset */ + +/* Number of degrees a circle is divided into. The greater it is, the greater + * the angle precision. But make it one whole zero larger and bizarre FOV bugs + * appear on large maps, probably due to value overflows (TODO: more research!). + */ +#define CIRCLE 3600000 + +/* Angle of a shadow. */ +struct shadow_angle +{ + struct shadow_angle * next; + uint32_t left_angle; + uint32_t right_angle; +}; + +/* To be used as temporary storage for world map array. */ +static char * worldmap = NULL; + +/* Coordinate for maps of max. 256x256 cells. */ +struct yx_uint8 +{ + uint8_t y; + uint8_t x; +}; + +/* Storage for map_length, set by set_maplength(). */ +static uint16_t maplength = 0; +extern void set_maplength(uint16_t maplength_input) +{ + maplength = maplength_input; +} + +/* Pseudo-randomness seed for rrand(), set by seed_rrand(). */ +static uint32_t seed = 0; + +/* Helper to mv_yx_in_dir_legal(). Move "yx" into hex direction "d". */ +static void mv_yx_in_dir(char d, struct yx_uint8 * yx) +{ + if (d == 'e') + { + yx->x = yx->x + (yx->y % 2); + yx->y--; + } + else if (d == 'd') + { + yx->x++; + } + else if (d == 'c') + { + yx->x = yx->x + (yx->y % 2); + yx->y++; + } + else if (d == 'x') + { + yx->x = yx->x - !(yx->y % 2); + yx->y++; + } + else if (d == 's') + { + yx->x--; + } + else if (d == 'w') + { + yx->x = yx->x - !(yx->y % 2); + yx->y--; + } +} + +/* Move "yx" into hex direction "dir". Available hex directions are: 'e' + * (north-east), 'd' (east), 'c' (south-east), 'x' (south-west), 's' (west), 'w' + * (north-west). Returns 1 if the move was legal, 0 if not, and -1 when internal + * wrapping limits were exceeded. + * + * A move is legal if "yx" ends up within the the map and the original wrap + * space. The latter is left to a neighbor wrap space if "yx" moves beyond the + * minimal (0) or maximal (UINT8_MAX) column or row of possible map space – in + * which case "yx".y or "yx".x will snap to the respective opposite side. The + * current wrapping state is kept between successive calls until a "yx" of NULL + * is passed, in which case the function does nothing but zero the wrap state. + * Successive wrapping may move "yx" several wrap spaces into either direction, + * or return it into the original wrap space. + */ +static int8_t mv_yx_in_dir_legal(char dir, struct yx_uint8 * yx) +{ + static int8_t wrap_west_east = 0; + static int8_t wrap_north_south = 0; + if (!yx) + { + wrap_west_east = wrap_north_south = 0; + return 0; + } + if ( INT8_MIN == wrap_west_east || INT8_MIN == wrap_north_south + || INT8_MAX == wrap_west_east || INT8_MAX == wrap_north_south) + { + return -1; + } + struct yx_uint8 original = *yx; + mv_yx_in_dir(dir, yx); + if (('e' == dir || 'd' == dir || 'c' == dir) && yx->x < original.x) + { + wrap_west_east++; + } + else if (('x' == dir || 's' == dir || 'w' == dir) && yx->x > original.x) + { + wrap_west_east--; + } + if (('w' == dir || 'e' == dir) && yx->y > original.y) + { + wrap_north_south--; + } + else if (('x' == dir || 'c' == dir) && yx->y < original.y) + { + wrap_north_south++; + } + if ( !wrap_west_east && !wrap_north_south + && yx->x < maplength && yx->y < maplength) + { + return 1; + } + return 0; +} + +/* Wrapper around mv_yx_in_dir_legal() that stores new coordinate in res_y/x, + * (return with result_y/x()), and immediately resets the wrapping. + */ +static uint8_t res_y = 0; +static uint8_t res_x = 0; +extern uint8_t mv_yx_in_dir_legal_wrap(char dir, uint8_t y, uint8_t x) +{ + struct yx_uint8 yx; + yx.y = y; + yx.x = x; + uint8_t result = mv_yx_in_dir_legal(dir, &yx); + mv_yx_in_dir_legal(0, NULL); + res_y = yx.y; + res_x = yx.x; + return result; +} +extern uint8_t result_y() +{ + return res_y; +} +extern uint8_t result_x() +{ + return res_x; +} + +/* With set_seed set, set seed global to seed_input. In any case, return it. */ +extern uint32_t seed_rrand(uint8_t set_seed, uint32_t seed_input) +{ + if (set_seed) + { + seed = seed_input; + } + return seed; +} + +/* Return 16-bit number pseudo-randomly generated via Linear Congruential + * Generator algorithm with some proven constants. Use instead of any rand() to + * ensure portability of the same pseudo-randomness across systems. + */ +extern uint16_t rrand() +{ /* Constants as recommended by POSIX.1-2001 (see man page rand(3)). */ + seed = ((seed * 1103515245) + 12345) % 4294967296; + return (seed >> 16); /* Ignore less random least significant bits. */ +} + +/* Free shadow angles list "angles". */ +static void free_angles(struct shadow_angle * angles) +{ + if (angles->next) + { + free_angles(angles->next); + } + free(angles); +} + +/* Recalculate angle < 0 or > CIRCLE to a value between these two limits. */ +static uint32_t correct_angle(int32_t angle) +{ + while (angle < 0) + { + angle = angle + CIRCLE; + } + while (angle > CIRCLE) + { + angle = angle - CIRCLE; + } + return angle; +} + +/* Try merging the angle between "left_angle" and "right_angle" to "shadow" if + * it meets the shadow from the right or the left. Returns 1 on success, else 0. + */ +static uint8_t try_merge(struct shadow_angle * shadow, + uint32_t left_angle, uint32_t right_angle) +{ + if ( shadow->right_angle <= left_angle + 1 + && shadow->right_angle >= right_angle) + { + shadow->right_angle = right_angle; + } + else if ( shadow->left_angle + 1 >= right_angle + && shadow->left_angle <= left_angle) + { + shadow->left_angle = left_angle; + } + else + { + return 0; + } + return 1; +} + +/* Try merging the shadow angle between "left_angle" and "right_angle" into an + * existing shadow angle in "shadows". On success, see if this leads to any + * additional shadow angle overlaps and merge these accordingly. Return 1 on + * success, else 0. + */ +static uint8_t try_merging_angles(uint32_t left_angle, uint32_t right_angle, + struct shadow_angle ** shadows) +{ + uint8_t angle_merge = 0; + struct shadow_angle * shadow; + for (shadow = *shadows; shadow; shadow = shadow->next) + { + if (try_merge(shadow, left_angle, right_angle)) + { + angle_merge = 1; + } + } + if (angle_merge) + { + struct shadow_angle * shadow1; + for (shadow1 = *shadows; shadow1; shadow1 = shadow1->next) + { + struct shadow_angle * last_shadow = NULL; + struct shadow_angle * shadow2; + for (shadow2 = *shadows; shadow2; shadow2 = shadow2->next) + { + if ( shadow1 != shadow2 + && try_merge(shadow1, shadow2->left_angle, + shadow2->right_angle)) + { + struct shadow_angle * to_free = shadow2; + if (last_shadow) + { + last_shadow->next = shadow2->next; + shadow2 = last_shadow; + } + else + { + *shadows = shadow2->next; + shadow2 = *shadows; + } + free(to_free); + } + last_shadow = shadow2; + } + } + } + return angle_merge; +} + +/* To "shadows", add shadow defined by "left_angle" and "right_angle", either as + * new entry or as part of an existing shadow (swallowed whole or extending it). + * Return 1 on malloc error, else 0. + */ +static uint8_t set_shadow(uint32_t left_angle, uint32_t right_angle, + struct shadow_angle ** shadows) +{ + struct shadow_angle * shadow_i; + if (!try_merging_angles(left_angle, right_angle, shadows)) + { + struct shadow_angle * shadow; + shadow = malloc(sizeof(struct shadow_angle)); + if (!shadow) + { + return 1; + } + shadow->left_angle = left_angle; + shadow->right_angle = right_angle; + shadow->next = NULL; + if (*shadows) + { + for (shadow_i = *shadows; shadow_i; shadow_i = shadow_i->next) + { + if (!shadow_i->next) + { + shadow_i->next = shadow; + return 0; + } + } + } + *shadows = shadow; + } + return 0; +} + +/* Test whether angle between "left_angle" and "right_angle", or at least + * "middle_angle", is captured inside one of the shadow angles in "shadows". If + * so, set hex in "fov_map" indexed by "pos_in_map" to 'H'. If the whole angle + * and not just "middle_angle" is captured, return 1. Any other case: 0. + */ +static uint8_t shade_hex(uint32_t left_angle, uint32_t right_angle, + uint32_t middle_angle, struct shadow_angle ** shadows, + uint16_t pos_in_map, char * fov_map) +{ + struct shadow_angle * shadow_i; + if (fov_map[pos_in_map] == 'v') + { + for (shadow_i = *shadows; shadow_i; shadow_i = shadow_i->next) + { + if ( left_angle <= shadow_i->left_angle + && right_angle >= shadow_i->right_angle) + { + fov_map[pos_in_map] = 'H'; + return 1; + } + if ( middle_angle < shadow_i->left_angle + && middle_angle > shadow_i->right_angle) + { + fov_map[pos_in_map] = 'H'; + } + } + } + return 0; +} + +/* Evaluate map position "test_pos" in distance "dist" to the view origin, and + * on the circle of that distance to the origin on hex "hex_i" (as counted from + * the circle's rightmost point), for setting shaded hexes in "fov_map" and + * potentially adding a new shadow to linked shadow angle list "shadows". + * Return 1 on malloc error, else 0. + */ +static uint8_t eval_position(uint16_t dist, uint16_t hex_i, char * fov_map, + struct yx_uint8 * test_pos, + struct shadow_angle ** shadows) +{ + int32_t left_angle_uncorrected = ((CIRCLE / 12) / dist) + - (hex_i * (CIRCLE / 6) / dist); + int32_t right_angle_uncorrected = left_angle_uncorrected + - (CIRCLE / (6 * dist)); + uint32_t left_angle = correct_angle(left_angle_uncorrected); + uint32_t right_angle = correct_angle(right_angle_uncorrected); + uint32_t right_angle_1st = right_angle > left_angle ? 0 : right_angle; + uint32_t middle_angle = 0; + if (right_angle_1st) + { + middle_angle = right_angle + ((left_angle - right_angle) / 2); + } + uint16_t pos_in_map = test_pos->y * maplength + test_pos->x; + uint8_t all_shaded = shade_hex(left_angle, right_angle_1st, middle_angle, + shadows, pos_in_map, fov_map); + if (!all_shaded && 'X' == worldmap[pos_in_map]) + { + if (set_shadow(left_angle, right_angle_1st, shadows)) + { + return 1; + } + if (right_angle_1st != right_angle) + { + left_angle = CIRCLE; + if (set_shadow(left_angle, right_angle, shadows)) + { + return 1; + } + } + } + return 0; +} + +/* Update field of view in "fovmap" of "worldmap_input" as seen from "y"/"x". + * Return 1 on malloc error, else 0. + */ +extern uint8_t build_fov_map(uint8_t y, uint8_t x, + char * fovmap, char * worldmap_input) +{ + worldmap = worldmap_input; + struct shadow_angle * shadows = NULL; + struct yx_uint8 test_pos; + test_pos.y = y; + test_pos.x = x; + char * circledirs_string = "xswedc"; + uint16_t circle_i; + uint8_t circle_is_on_map; + for (circle_i = 1, circle_is_on_map = 1; circle_is_on_map; circle_i++) + { + circle_is_on_map = 0; + if (1 < circle_i) /* All circles but the 1st are */ + { /* moved into starting from a */ + mv_yx_in_dir_legal('c', &test_pos);/* previous circle's last hex, */ + } /* i.e. from the upper left. */ + char dir_char = 'd'; /* Circle's 1st hex is entered by rightward move.*/ + uint8_t dir_char_pos_in_circledirs_string = UINT8_MAX; + uint16_t dist_i, hex_i; + for (hex_i=0, dist_i=circle_i; hex_i < 6 * circle_i; dist_i++, hex_i++) + { + if (circle_i < dist_i) + { + dist_i = 1; + dir_char=circledirs_string[++dir_char_pos_in_circledirs_string]; + } + if (mv_yx_in_dir_legal(dir_char, &test_pos)) + { + if (eval_position(circle_i, hex_i, fovmap, &test_pos, &shadows)) + { + return 1; + } + circle_is_on_map = 1; + } + } + } + mv_yx_in_dir_legal(0, NULL); + free_angles(shadows); + return 0; +} + +static uint16_t * score_map = NULL; +static uint16_t neighbor_scores[6]; + +/* Init AI score map. Return 1 on failure, else 0. */ +extern uint8_t init_score_map() +{ + uint32_t map_size = maplength * maplength; + score_map = malloc(map_size * sizeof(uint16_t)); + if (!score_map) + { + return 1; + } + uint32_t i = 0; + for (; i < map_size; i++) + { + score_map[i] = UINT16_MAX; + } + return 0; +} + +/* Set score_map[pos] to score. Return 1 on failure, else 0. */ +extern uint8_t set_map_score(uint16_t pos, uint16_t score) +{ + if (!score_map) + { + return 1; + } + score_map[pos] = score; + return 0; +} + +/* Get score_map[pos]. Return uint16_t value on success, -1 on failure. */ +extern int32_t get_map_score(uint16_t pos) +{ + if (!score_map) + { + return -1; + } + return score_map[pos]; +} + +/* Free score_map. */ +extern void free_score_map() +{ + free(score_map); + score_map = NULL; +} + +/* Write into "neighbors" scores of the immediate neighbors of the score_map + * cell at pos_i (array index), as found in the directions north-east, east, + * south-east etc. (clockwise order). Use kill_score for illegal neighborhoods + * (i.e. if direction would lead beyond the map's border). + */ +static void get_neighbor_scores(uint16_t pos_i, uint16_t kill_score, + uint16_t * neighbors) +{ + uint32_t map_size = maplength * maplength; + uint8_t open_north = pos_i >= maplength; + uint8_t open_east = pos_i + 1 % maplength; + uint8_t open_south = pos_i + maplength < map_size; + uint8_t open_west = pos_i % maplength; + uint8_t is_indented = (pos_i / maplength) % 2; + uint8_t open_diag_west = is_indented || open_west; + uint8_t open_diag_east = !is_indented || open_east; + neighbors[0] = !(open_north && open_diag_east) ? kill_score : + score_map[pos_i - maplength + is_indented]; + neighbors[1] = !(open_east) ? kill_score : score_map[pos_i + 1]; + neighbors[2] = !(open_south && open_diag_east) ? kill_score : + score_map[pos_i + maplength + is_indented]; + neighbors[3] = !(open_south && open_diag_west) ? kill_score : + score_map[pos_i + maplength - !is_indented]; + neighbors[4] = !(open_west) ? kill_score : score_map[pos_i - 1]; + neighbors[5] = !(open_north && open_diag_west) ? kill_score : + score_map[pos_i - maplength - !is_indented]; +} + +/* Call get_neighbor_scores() on neighbor_scores buffer. Return 1 on error. */ +extern uint8_t ready_neighbor_scores(uint16_t pos) +{ + if (!score_map) + { + return 1; + } + get_neighbor_scores(pos, UINT16_MAX, neighbor_scores); + return 0; +} + +/* Return i-th position from neighbor_scores buffer.*/ +extern uint16_t get_neighbor_score(uint8_t i) +{ + return neighbor_scores[i]; +} + +/* Iterate over scored cells in score_map geometry. Compare each cell's score + * against the score of its immediate neighbors in 6 directions. If any + * neighbor's score is at least two points lower than the current cell's score, + * re-set it to 1 point higher than its lowest-scored neighbor. Repeat this + * whole process until all cells have settled on their final score. Ignore cells + * whose score is greater than UINT16_MAX - 1 (treat those as unreachable). Also + * ignore cells whose score is smaller or equal the number of past iterations. + * Return 1 on error, else 0. + */ +extern uint8_t dijkstra_map() +{ + if (!score_map) + { + return 1; + } + uint16_t max_score = UINT16_MAX - 1; + uint32_t map_size = maplength * maplength; + uint32_t pos; + uint16_t i_scans, neighbors[6], min_neighbor; + uint8_t scores_still_changing = 1; + uint8_t i_dirs; + for (i_scans = 0; scores_still_changing; i_scans++) + { + scores_still_changing = 0; + for (pos = 0; pos < map_size; pos++) + { + uint16_t score = score_map[pos]; + if (score <= max_score && score > i_scans) + { + get_neighbor_scores(pos, max_score, neighbors); + min_neighbor = max_score; + for (i_dirs = 0; i_dirs < 6; i_dirs++) + { + if (min_neighbor > neighbors[i_dirs]) + { + min_neighbor = neighbors[i_dirs]; + } + } + if (score_map[pos] > min_neighbor + 1) + { + score_map[pos] = min_neighbor + 1; + scores_still_changing = 1; + } + } + } + } + return 0; +} + +extern uint8_t zero_score_map_where_char_on_memdepthmap(char c, + char * memdepthmap) +{ + if (!score_map) + { + return 1; + } + uint32_t map_size = maplength * maplength; + uint16_t pos; + for (pos = 0; pos < map_size; pos++) + { + if (c == memdepthmap[pos]) + { + score_map[pos] = 0; + } + } + return 0; +} + +extern void age_some_memdepthmap_on_nonfov_cells(char * memdepthmap, + char * fovmap) +{ + uint32_t map_size = maplength * maplength; + uint16_t pos; + for (pos = 0; pos < map_size; pos++) + { + if ('v' != fovmap[pos]) + { + char c = memdepthmap[pos]; + if( '0' <= c && '9' > c && !(rrand() % (uint16_t) pow(2, c - 48))) + { + memdepthmap[pos]++; + } + } + } +} + +extern uint8_t set_cells_passable_on_memmap_to_65534_on_scoremap(char * mem_map) +{ + if (!score_map) + { + return 1; + } + uint32_t map_size = maplength * maplength; + uint16_t pos; + for (pos = 0; pos < map_size; pos++) + { + if ('.' == mem_map[pos]) + { + score_map[pos] = 65534; + } + } + return 0; +} diff --git a/libplomrogue.so.do b/libplomrogue.so.do deleted file mode 100644 index 26ca117..0000000 --- a/libplomrogue.so.do +++ /dev/null @@ -1,10 +0,0 @@ -# redo build file to build the library "libplomrogue.so" - -# This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 -# or any later version. For details on its copyright, license, and warranties, -# see the file NOTICE in the root directory of the PlomRogue source package. - -redo-ifchange build/compiler_flags -. ./build/compiler_flags -redo-ifchange src/server/libplomrogue.c -gcc -shared -fPIC $CFLAGS -o $3 src/server/libplomrogue.c -lm diff --git a/redo b/redo deleted file mode 100755 index f2eef67..0000000 --- a/redo +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh -# -# The purpose of this script is to step in if no version of the "redo" build -# system is installed on your system to interpret the files that (Makefile-like) -# build the PlomRogue system: ./all.do, ./roguelike-client.do and -# ./roguelike-server.do. -# -# If a version of "redo" is installed on your system and in your $PATH, just run -# "redo roguelike-client", "redo roguelike-server" or plain "redo" (to build -# both via all.do). Otherwise, run this very script with the very same -# arguments. It uses parts of a basic "redo" implementation written by Nils -# Dagsson Moskopp a.k.a. erlehmann, snapshots of which are stored in -# build/redo_scripts/. For details on this version, see: -# - -# - - -# Some tests first: for gcc, and certain necessary header files. (This is not -# strictly the responsibility of a mere redo wrapper. But those using a -# pre-installed redo probably will be fine with the error messages thrown by it -# if these miss.) -test=`command -v gcc | wc -l` -if [ 1 != $test ] -then - echo "FAILURE:" - echo "No gcc installed, but it's needed!" - exit 1 -fi -test_header() { - code="#include <$1>" - test=`echo $code | cpp -H -o /dev/null 2>&1 | head -n1 | grep error | wc -l` - if [ 0 != $test ] - then - echo "FAILURE:" - echo "No $1 header file found, but it's needed!" - echo "Maybe install some $2 package?" - exit 1 - fi -} -test_header stdlib.h libc6-dev # Assume stdlib.h guarantees full libc6-dev. -test_header ncurses.h libncurses5-dev - -# The actual redo calling. -export PATH=$PATH:$PWD/build/redo_scripts -redo "$@" diff --git a/roguelike-client b/roguelike-client new file mode 100755 index 0000000..040a2d2 --- /dev/null +++ b/roguelike-client @@ -0,0 +1,612 @@ +#!/usr/bin/python3 + +import curses +import math +import os +import signal +import time + + +def set_window_geometries(): + + def set_window_size(): + win["size"], win["start"] = [0, 0], [0, 0] + win["size"][0] = win["config"][0] + if (win["config"][0] == 0): + win["size"][0] = screen_size[0] - sep_size + elif (win["config"][0] < 0): + win["size"][0] = screen_size[0] + win["config"][0] - sep_size + win["size"][1] = win["config"][1] + if (win["config"][1] == 0): + win["size"][1] = screen_size[1] + elif (win["config"][1] < 0): + win["size"][1] = screen_size[1] + win["config"][1] + + def place_window(): + win_i = windows.index(win) + + # If win is first window, it goes into the top left corner. + win["start"][0] = 0 + sep_size + win["start"][1] = 0 + if (win_i > 0): + + # If not, get win's closest predecessor starting a new stack on the + # screen top,fit win's top left to that win_top's top right corner. + win_top = None + for i in range(win_i - 1, -1, -1): + win_top = windows[i] + if (win_top["start"][0] == 0 + sep_size): + break + win["start"][1] = win_top["start"][1] + win_top["size"][1] \ + + sep_size + + # If enough space is found below win's predecessor, fit win's top + # left corner to that predecessor's bottom left corner. + win_prev = windows[win_i - 1] + next_free_y = win_prev["start"][0] + win_prev["size"][0] + sep_size + if (win["size"][1] <= win_prev["size"][1] and + win["size"][0] <= screen_size[0] - next_free_y): + win["start"][1] = win_prev["start"][1] + win["start"][0] = next_free_y + + # If that fails, try to fit win's top left corner to the top right + # corner of its closest predecessor win_test 1) below win_top (see + # above) 2) and with enough space open to its right between its + # right edge and the lower edge of a win_high located directly + # above win_test to fit win there (without growing further to the + # right than win_high does or surpassing the screen's lower edge). + else: + win_test = win_prev + win_high = None + while (win_test != win_top): + for i in range(win_i - 2, -1, -1): + win_high = windows[i] + if win_test["start"][0] > win_high["start"][0]: + break + next_free_y = win_high["start"][0] + win_high["size"][0] \ + + sep_size + first_free_x = win_test["start"][1] + win_test["size"][1] \ + + sep_size + last_free_x = win_high["start"][1] + win_high["size"][1] + if (win["size"][0] <= screen_size[0] - next_free_y and + win["size"][1] <= last_free_x - first_free_x): + win["start"][1] = first_free_x + win["start"][0] = next_free_y + break + win_test = win_high + + global screen_size, stdscr + curses.endwin() + stdscr = curses.initscr() + screen_size = stdscr.getmaxyx() + for win in windows: + set_window_size() + place_window() + cursed_main.redraw = True + + +def draw_screen(): + + def healthy_addch(y, x, char, attr=0): + """Workaround for .""" + if y == screen_size[0] - 1 and x == screen_size[1] - 1: + char_before = stdscr.inch(y, x - 1) + stdscr.addch(y, x - 1, char, attr) + stdscr.insstr(y, x - 1, " ") + stdscr.addch(y, x - 1, char_before) + else: + stdscr.addch(y, x, char, attr) + + def draw_window_border_lines(): + for win in windows: + for k in range(2): + j = win["start"][int(k == 0)] - sep_size + if (j >= 0 and j < screen_size[int(k == 0)]): + start = win["start"][k] + end = win["start"][k] + win["size"][k] + end = end if end < screen_size[k] else screen_size[k] + if k: + [healthy_addch(j, i, '-') for i in range(start, end)] + else: + [healthy_addch(i, j, '|') for i in range(start, end)] + + def draw_window_border_corners(): + for win in windows: + up = win["start"][0] - sep_size + down = win["start"][0] + win["size"][0] + left = win["start"][1] - sep_size + right = win["start"][1] + win["size"][1] + if (up >= 0 and up < screen_size[0]): + if (left >= 0 and left < screen_size[1]): + healthy_addch(up, left, '+') + if (right >= 0 and right < screen_size[1]): + healthy_addch(up, right, '+') + if (down >= 0 and down < screen_size[0]): + if (left >= 0 and left < screen_size[1]): + healthy_addch(down, left, '+') + if (right >= 0 and right < screen_size[1]): + healthy_addch(down, right, '+') + + def draw_window_titles(): + for win in windows: + title = " " + win["title"] + " " + if len(title) <= win["size"][1]: + y = win["start"][0] - 1 + start_x = win["start"][1] + int((win["size"][1] \ + - len(title))/2) + for x in range(len(title)): + healthy_addch(y, start_x + x, title[x]) + + def draw_window_contents(): + def draw_winmap(): + """Draw winmap in area delimited by offset, winmap_size. + + The individuall cell of a winmap is interpreted as either a single + character element, or as a tuple of character and attribute, + depending on the size len(cell) spits out. + """ + stop = [0, 0] + for i in range(2): + stop[i] = win["size"][i] + offset[i] + if stop[i] >= winmap_size[i]: + stop[i] = winmap_size[i] + for y in range(offset[0], stop[0]): + for x in range(offset[1], stop[1]): + cell = winmap[y * winmap_size[1] + x] + attr = 0 + if len(cell) > 1: + attr = cell[1] + cell = cell[0] + y_in_screen = win["start"][0] + (y - offset[0]) + x_in_screen = win["start"][1] + (x - offset[1]) + if (y_in_screen < screen_size[0] + and x_in_screen < screen_size[1]): + healthy_addch(y_in_screen, x_in_screen, cell, attr) + def draw_scroll_hints(): + def draw_scroll_string(n_lines_outside): + hint = ' ' + str(n_lines_outside + 1) + ' more ' + unit + ' ' + if len(hint) <= win["size"][ni]: + non_hint_space = win["size"][ni] - len(hint) + hint_offset = int(non_hint_space / 2) + for j in range(win["size"][ni] - non_hint_space): + pos_2 = win["start"][ni] + hint_offset + j + x, y = (pos_2, pos_1) if ni else (pos_1, pos_2) + healthy_addch(y, x, hint[j], curses.A_REVERSE) + def draw_scroll_arrows(ar1, ar2): + for j in range(win["size"][ni]): + pos_2 = win["start"][ni] + j + x, y = (pos_2, pos_1) if ni else (pos_1, pos_2) + healthy_addch(y, x, ar1 if ni else ar2, curses.A_REVERSE) + for i in range(2): + ni = int(i == 0) + unit = 'rows' if ni else 'columns' + if (offset[i] > 0): + pos_1 = win["start"][i] + draw_scroll_arrows('^', '<') + draw_scroll_string(offset[i]) + if (winmap_size[i] > offset[i] + win["size"][i]): + pos_1 = win["start"][i] + win["size"][i] - 1 + draw_scroll_arrows('v', '>') + draw_scroll_string(winmap_size[i] - offset[i] + - win["size"][i]) + for win in windows: + offset, winmap_size, winmap = win["func"]() + draw_winmap() + draw_scroll_hints() + + stdscr.erase() + draw_window_border_lines() + draw_window_border_corners() + draw_window_titles() + draw_window_contents() + stdscr.refresh() + + +def read_worldstate(): + if not os.access(io["path_worldstate"], os.F_OK): + msg = "No world state file found at " + io["path_worldstate"] + "." + raise SystemExit(msg) + read_anew = False + worldstate_file = open(io["path_worldstate"], "r") + turn_string = worldstate_file.readline() + if int(turn_string) != world_data["turn"]: + read_anew = True + if not read_anew: # In rare cases, world may change, but not turn number. + mtime = os.stat(io["path_worldstate"]) + if mtime != read_worldstate.last_checked_mtime: + read_worldstate.last_checked_mtime = mtime + read_anew = True + if read_anew: + cursed_main.redraw = True + world_data["turn"] = int(turn_string) + world_data["lifepoints"] = int(worldstate_file.readline()) + world_data["satiation"] = int(worldstate_file.readline()) + world_data["inventory"] = [] + while True: + line = worldstate_file.readline().replace("\n", "") + if line == '%': + break + world_data["inventory"] += [line] + world_data["avatar_position"][0] = int(worldstate_file.readline()) + world_data["avatar_position"][1] = int(worldstate_file.readline()) + if not world_data["look_mode"]: + world_data["map_center"][0] = world_data["avatar_position"][0] + world_data["map_center"][1] = world_data["avatar_position"][1] + world_data["map_size"] = int(worldstate_file.readline()) + world_data["fov_map"] = "" + for i in range(world_data["map_size"]): + line = worldstate_file.readline().replace("\n", "") + world_data["fov_map"] += line + world_data["mem_map"] = "" + for i in range(world_data["map_size"]): + line = worldstate_file.readline().replace("\n", "") + world_data["mem_map"] += line + worldstate_file.close() +read_worldstate.last_checked_mtime = -1 + + +def read_message_queue(): + while (len(message_queue["messages"]) > 1 + or (len(message_queue["messages"]) == 1 + and not message_queue["open_end"])): + message = message_queue["messages"].pop(0) + if message == "THINGS_HERE START": + read_message_queue.parse_thingshere = True + world_data["look"] = [] + elif message == "THINGS_HERE END": + read_message_queue.parse_thingshere = False + if world_data["look"] == []: + world_data["look"] = ["(none known)"] + cursed_main.redraw = True + elif read_message_queue.parse_thingshere: + world_data["look"] += [message] + elif message[0:4] == "LOG ": + world_data["log"] += [message[4:]] + cursed_main.redraw = True + elif message == "WORLD_UPDATED": + query_mapcell() +read_message_queue.parse_thingshere = False + + +def query_mapcell(): + command_sender("THINGS_HERE " + str(world_data["map_center"][0]) + " " + + str(world_data["map_center"][1]))() + world_data["look"] = ["(polling)"] + + +def cursed_main(stdscr): + + def ping_test(): + half_wait_time = 5 + if len(new_data_from_server) > 0: + ping_test.sent = False + elif ping_test.wait_start + half_wait_time < time.time(): + if not ping_test.sent: + io["file_out"].write("PING\n") + io["file_out"].flush() + ping_test.sent = True + ping_test.wait_start = time.time() + elif ping_test.sent: + raise SystemExit("Server not answering anymore.") + ping_test.wait_start = 0 + + def read_into_message_queue(): + if new_data_from_server == "": + return + new_open_end = False + if new_data_from_server[-1] is not "\n": + new_open_end = True + new_messages = new_data_from_server.splitlines() + if message_queue["open_end"]: + message_queue["messages"][-1] += new_messages[0] + del new_messages[0] + message_queue["messages"] += new_messages + if new_open_end: + message_queue["open_end"] = True + + curses.noecho() + curses.curs_set(False) + signal.signal(signal.SIGWINCH, + lambda ignore_1, ignore_2: set_window_geometries()) + set_window_geometries() + delay = 1 + while True: + stdscr.timeout(int(delay)) + delay = delay * 1.1 if delay < 1000 else delay + if cursed_main.redraw: + delay = 1 + draw_screen() + cursed_main.redraw = False + char = stdscr.getch() + if char >= 0: + char = chr(char) + if char in commands: + if len(commands[char]) == 1 or not world_data["look_mode"]: + commands[char][0]() + cursed_main.redraw = True + else: + commands[char][1]() + cursed_main.redraw = True + new_data_from_server = io["file_in"].read() + ping_test() + read_into_message_queue() + read_worldstate() + read_message_queue() + + +def win_map(): + win_size = next(win["size"] for win in windows if win["func"] == win_map) + offset = [0, 0] + for i in range(2): + if world_data["map_center"][i] * (i + 1) > win_size[i] / 2: + if world_data["map_center"][i] * (i + 1) \ + < world_data["map_size"] * (i + 1) - win_size[i] / 2: + offset[i] = world_data["map_center"][i] * (i + 1) \ + - int(win_size[i] / 2) + if i == 1: + offset[1] = offset[1] + world_data["map_center"][0] % 2 + else: + offset[i] = world_data["map_size"] * (i + 1) - win_size[i] + i + winmap_size = [world_data["map_size"], world_data["map_size"] * 2 + 1] + winmap = [] + curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE) + curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK) + for y in range(world_data["map_size"]): + for x in range(world_data["map_size"]): + char = world_data["fov_map"][y * world_data["map_size"] + x] + if world_data["look_mode"] and y == world_data["map_center"][0] \ + and x == world_data["map_center"][1]: + if char == " ": + char = \ + world_data["mem_map"][y * world_data["map_size"] + x] + winmap += [(char, curses.A_REVERSE), (" ", curses.A_REVERSE)] + continue + if char == " ": + char = world_data["mem_map"][y * world_data["map_size"] + x] + attribute = curses.color_pair(1) if char == " " \ + else curses.color_pair(2) + winmap += [(char, attribute), (" ", attribute)] + else: + winmap += char + " " + if y % 2 == 0: + winmap += " " + return offset, winmap_size, winmap + + +def win_inventory(): + win_size = next(win["size"] for win in windows + if win["func"] == win_inventory) + winmap = [] + winmap_size = [0, win_size[1]] + for line in world_data["inventory"]: + winmap_size[1] = winmap_size[1] if len(line) <= winmap_size[1] \ + else len(line) + count = 0 + for line in world_data["inventory"]: + padding_size = winmap_size[1] - len(line) + line += (" " * padding_size) + if count == world_data["inventory_selection"]: + line_new = [] + for x in range(len(line)): + line_new += [(line[x], curses.A_REVERSE)] + line = line_new + winmap += line + winmap_size[0] = winmap_size[0] + 1 + count += 1 + offset = [0, 0] + if world_data["inventory_selection"] > win_size[0]/2: + if world_data["inventory_selection"] < len(world_data["inventory"]) \ + - win_size[0]/2: + offset[0] = world_data["inventory_selection"] - int(win_size[0]/2) + else: + offset[0] = len(world_data["inventory"]) - win_size[0] + return offset, winmap_size, winmap + + +def win_look(): + winmap = "" + winmap_size = [0, 0] + for line in world_data["look"]: + winmap_size[1] = winmap_size[1] if len(line) <= winmap_size[1] \ + else len(line) + for line in world_data["look"]: + padding_size = winmap_size[1] - len(line) + winmap += line + (" " * padding_size) + winmap_size[0] = winmap_size[0] + 1 + offset = [world_data["look_scroll"], 0] + return offset, winmap_size, winmap + + +def win_info(): + winmap = "T: " + str(world_data["turn"]) \ + + " H: " + str(world_data["lifepoints"]) \ + + " S: " + str(world_data["satiation"]) + winmap_size = [1, len(winmap)] + offset = [0, 0] + return offset, winmap_size, winmap + + +def win_log(): + win_size = next(win["size"] for win in windows if win["func"] == win_log) + offset = [0, 0] + winmap = "" + number_of_lines = 0 + for line in world_data["log"]: + number_of_lines += math.ceil(len(line) / win_size[1]) + padding_size = win_size[1] - (len(line) % win_size[1]) + winmap += line + (padding_size * " ") + if number_of_lines < win_size[0]: + winmap = (" " * win_size[1] * (win_size[0] - number_of_lines)) + winmap + number_of_lines = win_size[0] + elif number_of_lines > win_size[0]: + offset[0] = number_of_lines - win_size[0] + winmap_size = [number_of_lines, win_size[1]] + return offset, winmap_size, winmap + + +def command_quit(): + command_sender("QUIT")() + raise SystemExit("Received QUIT command, forwarded to server, leaving.") + + +def command_toggle_look_mode(): + if not world_data["look_mode"]: + world_data["look_mode"] = True + else: + world_data["look_mode"] = False + world_data["map_center"] = world_data["avatar_position"] + query_mapcell() + + +def command_sender(string, int_field=None): + def command_send(): + int_string = "" + if int_field: + int_string = " " + str(world_data[int_field]) + io["file_out"].write(string + int_string + "\n") + io["file_out"].flush() + return command_send + + +def command_looker(string): + def command_look(): + if string == "west" \ + and world_data["map_center"][1] > 0: + world_data["map_center"][1] -= 1 + elif string == "east" \ + and world_data["map_center"][1] < world_data["map_size"] - 1: + world_data["map_center"][1] += 1 + else: + y_unevenness = world_data["map_center"][0] % 2 + y_evenness = int(not(y_unevenness)) + if string[6:] == "west" and \ + world_data["map_center"][1] > -y_unevenness: + if string[:5] == "north" and world_data["map_center"][0] > 0: + world_data["map_center"][0] -= 1 + world_data["map_center"][1] -= y_evenness + if string[:5] == "south" and world_data["map_center"][0] \ + < world_data["map_size"] - 1: + world_data["map_center"][0] += 1 + world_data["map_center"][1] -= y_evenness + elif string[6:] == "east" and world_data["map_center"][1] \ + < world_data["map_size"] - y_unevenness: + if string[:5] == "north" and world_data["map_center"][0] > 0: + world_data["map_center"][0] -= 1 + world_data["map_center"][1] += y_unevenness + if string[:5] == "south" and world_data["map_center"][0] \ + < world_data["map_size"] - 1: + world_data["map_center"][0] += 1 + world_data["map_center"][1] += y_unevenness + query_mapcell() + return command_look + + +def command_look_scroller(string): + def command_look_scroll(): + win_size = next(win["size"] for win in windows + if win["func"] == win_look) + if string == "up" and world_data["look_scroll"] > 0: + world_data["look_scroll"] -= 1 + elif string == "down" and world_data["look_scroll"] \ + < len(world_data["look"]) - win_size[0]: + world_data["look_scroll"] += 1 + return command_look_scroll + + +def command_inventory_selector(string): + def command_inventory_select(): + if string == "up" and world_data["inventory_selection"] > 0: + world_data["inventory_selection"] -= 1 + elif string == "down" and world_data["inventory_selection"] \ + < len(world_data["inventory"]) - 1: + world_data["inventory_selection"] += 1 + return command_inventory_select + + +windows = [ + {"config": [1, 33], "func": win_info, "title": "Info"}, + {"config": [-7, 33], "func": win_log, "title": "Log"}, + {"config": [4, 16], "func": win_inventory, "title": "Inventory"}, + {"config": [4, 16], "func": win_look, "title": "Things here"}, + {"config": [0, -34], "func": win_map, "title": "Map"} +] +io = { + "path_out": "server/in", + "path_in": "server/out", + "path_worldstate": "server/worldstate" +} +commands = { + "A": (command_sender("ai"),), + "D": (command_sender("drop", "inventory_selection"),), + "J": (command_look_scroller("down"),), + "K": (command_look_scroller("up"),), + "P": (command_sender("pick_up"),), + "Q": (command_quit,), + "U": (command_sender("use", "inventory_selection"),), + "W": (command_sender("wait"),), + "c": (command_sender("move south-east"), command_looker("south-east")), + "d": (command_sender("move east"), command_looker("east")), + "e": (command_sender("move north-east"), command_looker("north-east")), + "j": (command_inventory_selector("down"),), + "k": (command_inventory_selector("up"),), + "l": (command_toggle_look_mode,), + "s": (command_sender("move west"), command_looker("west")), + "w": (command_sender("move north-west"), command_looker("north-west")), + "x": (command_sender("move south-west"), command_looker("south-west")), +} +message_queue = { + "open_end": False, + "messages": [] +} +world_data = { + "avatar_position": [-1, -1], + "fov_map": "", + "inventory": [], + "inventory_selection": 0, + "lifepoints": -1, + "look": [], + "look_mode": False, + "look_scroll": 0, + "log": [ +"QUICK COMMAND OVERVIEW: " +"Move through map with 'w', 'e', 's', 'd', 'x', 'c'. " +"Pick up things with 'P', drop a thing selected from the inventory with 'D' " +"or use it with 'P'. " +"Move through inventory selection with 'j' and 'k'. " +"Toggle looking around mode with 'l'. " +"Scroll 'Things here' window with 'J' and 'K'. " +"Let AI decide next move with 'A'. " +"Quit with 'Q'. ", +"STATS OVERVIEW: " +"'T': Turn; 'H': Health (the higher, the better); " +"'S': Satiation (the closer to zero, the better).", +"See README file for more help." +], + "map_center": [-1, -1], + "map_size": 0, + "mem_map": "", + "satiation": -1, + "turn": -1 +} +sep_size = 1 # Width of inter-window borders and title bars. +stdscr = None +screen_size = [0,0] + + +try: + if (not os.access(io["path_out"], os.F_OK)): + msg = "No server input file found at " + io["path_out"] + "." + raise SystemExit(msg) + io["file_out"] = open(io["path_out"], "a") + io["file_in"] = open(io["path_in"], "r") + curses.wrapper(cursed_main) +except SystemExit as exit: + print("ABORTING: " + exit.args[0]) +except: + print("SOMETHING WENT WRONG IN UNEXPECTED WAYS") + raise +finally: + if "file_out" in io: + io["file_out"].close() + if "file_in" in io: + io["file_in"].close() diff --git a/roguelike-client.do b/roguelike-client.do deleted file mode 100644 index af1234b..0000000 --- a/roguelike-client.do +++ /dev/null @@ -1,15 +0,0 @@ -# redo build file to build the executable "roguelike-client". - -# This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 -# or any later version. For details on its copyright, license, and warranties, -# see the file NOTICE in the root directory of the PlomRogue source package. - -redo-ifchange build/compiler_flags -. ./build/compiler_flags -mkdir -p build/client -mkdir -p build/common -for file in src/client/*.c src/common/*.c; do - file=build/${file#src/} - redo-ifchange ${file%.*}.o -done -gcc $CFLAGS -o $3 -g build/client/*.o build/common/*.o -lncurses diff --git a/roguelike-server b/roguelike-server index 753961d..f7b2896 100755 --- a/roguelike-server +++ b/roguelike-server @@ -34,7 +34,7 @@ def prep_library(): """Prepare ctypes library at ./libplomrogue.so""" libpath = ("./libplomrogue.so") if not os.access(libpath, os.F_OK): - raise SystemExit("No library " + libpath + ", run ./redo first?") + raise SystemExit("No library " + libpath + ", run ./build.sh first?") libpr = ctypes.cdll.LoadLibrary(libpath) libpr.seed_rrand.restype = ctypes.c_uint32 return libpr diff --git a/src/client/array_append.c b/src/client/array_append.c deleted file mode 100644 index 52bcf97..0000000 --- a/src/client/array_append.c +++ /dev/null @@ -1,29 +0,0 @@ -/* src/client/array_append.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#include "array_append.h" -#include /* size_t */ -#include /* uint32_t, UINT32_MAX */ -#include /* free() */ -#include /* memcpy() */ -#include "../common/rexit.h" /* exit_trouble() */ -#include "../common/try_malloc.h" /* try_malloc() */ - - - -extern void array_append(uint32_t old_n, size_t region_size, void * new_region, - void ** ptr_old_array) -{ - exit_trouble(UINT32_MAX<(old_n+1)*region_size, __func__, "too large sizes"); - uint32_t old_size = old_n * region_size; - uint32_t new_size = old_size + region_size; - char * new_array = try_malloc(new_size, __func__); - memcpy(new_array, * ptr_old_array, old_size); - memcpy(new_array + (old_n * region_size), new_region, region_size); - free(* ptr_old_array); - *ptr_old_array = new_array; -} diff --git a/src/client/array_append.h b/src/client/array_append.h deleted file mode 100644 index 72a4446..0000000 --- a/src/client/array_append.h +++ /dev/null @@ -1,27 +0,0 @@ -/* src/client/array_append.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Small memory management helper. - */ - -#ifndef ARRAY_APPEND_H -#define ARRAY_APPEND_H - -#include /* size_t */ -#include /* uint32_t */ - - - -/* Append to array pointed to by "ptr_old_array" of "old_n" elements of - * "region_size" "new region". - */ -extern void array_append(uint32_t old_n, size_t region_size, void * new_region, - void ** ptr_old_array); - - - -#endif - diff --git a/src/client/cleanup.c b/src/client/cleanup.c deleted file mode 100644 index 4db02a2..0000000 --- a/src/client/cleanup.c +++ /dev/null @@ -1,58 +0,0 @@ -/* src/client/cleanup.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#include "cleanup.h" -#include /* for endwin() */ -#include /* uint32_t */ -#include /* free() */ -#include "../common/readwrite.h" /* try_fclose() */ -#include "command_db.h" /* free_command_db() */ -#include "interface_conf.h" /* unload_interface_conf() */ -#include "world.h" /* world global */ - - - -/* The clean-up flags set by set_cleanup_flag(). */ -static uint32_t cleanup_flags = 0x0000; - - - -extern void cleanup() -{ - free(world.map.cells); - free(world.mem_map); - free(world.log); - free(world.things_here); - free(world.queue); - free(world.player_inventory); - if (cleanup_flags & CLEANUP_INTERFACE) - { - unload_interface_conf(); - } - if (cleanup_flags & CLEANUP_NCURSES) - { - endwin(); - } - if (cleanup_flags & CLEANUP_COMMANDS) - { - free_command_db(); - } - if (cleanup_flags & CLEANUP_SERVER_IN) - { - try_fclose(world.file_server_in, __func__); - } - if (cleanup_flags & CLEANUP_SERVER_OUT) - { - try_fclose(world.file_server_out, __func__); - } -} - - -extern void set_cleanup_flag(enum cleanup_flag flag) -{ - cleanup_flags = cleanup_flags | flag; -} diff --git a/src/client/cleanup.h b/src/client/cleanup.h deleted file mode 100644 index a9dd2f9..0000000 --- a/src/client/cleanup.h +++ /dev/null @@ -1,35 +0,0 @@ -/* src/client/cleanup.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Stuff defining / performing the cleanup called by rexit.h's exit functions. - */ - -#ifndef CLEANUP_H -#define CLEANUP_H - - - -/* set_cleanup_flag() sets any of the flags defined in cleanup_flag to announce - * the resources that need cleaning up upon program exit. It is to be called at - * the earliest moment possible after resource creation / initialization. - */ -enum cleanup_flag -{ - CLEANUP_NCURSES = 0x0001, - CLEANUP_INTERFACE = 0x0002, - CLEANUP_COMMANDS = 0x0004, - CLEANUP_SERVER_IN = 0x0008, - CLEANUP_SERVER_OUT = 0x0008 -}; - -extern void set_cleanup_flag(enum cleanup_flag flag); - -/* Frees memory and properly winds down ncurses / resets the terminal. */ -extern void cleanup(); - - - -#endif diff --git a/src/client/command_db.c b/src/client/command_db.c deleted file mode 100644 index 68d4ce2..0000000 --- a/src/client/command_db.c +++ /dev/null @@ -1,125 +0,0 @@ -/* src/client/command_db.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#define _POSIX_C_SOURCE 200809L /* strdup() */ -#include "command_db.h" -#include /* NULL */ -#include /* uint8_t */ -#include /* free() */ -#include /* strcmp(), strdup() */ -#include "array_append.h" /* array_append() */ -#include "parse.h" /* EDIT_STARTED, parse_init_entry(), parse_id_uniq(), - * parse_unknown_arg(), parsetest_too_many_values(), - * parse_file(), parse_and_reduce_to_readyflag(), - * parse_flagval() - */ -#include "world.h" /* global world */ -#include "cleanup.h" /* set_cleanup_flag() */ - - - -/* Flags for defining state of command DB config file entries. */ -enum cmd_flag -{ - DESC_SET = 0x02, - SERVERCMD_SET = 0x04, - SERVERARG_SET = 0x08, - READY_CMD = DESC_SET -}; - - - -/* Interpret "token0" and "token1" as data to write into the CommandDB. - * - * Individual CommandDB entries are put together line by line before being - * written. Writing happens after all necessary members of an entry have been - * assembled, and when additionally a) a new entry is started by a "token0" of - * "COMMAND"; or b) of "token0" of NULL is passed. - */ -static void tokens_into_entries(char * token0, char * token1); - - - -static void tokens_into_entries(char * token0, char * token1) -{ - char * str_cmd = "COMMAND"; - static uint8_t cmd_flags = READY_CMD; - static struct Command * cmd = NULL; - if (!token0 || !strcmp(token0, str_cmd)) - { - parse_and_reduce_to_readyflag(&cmd_flags, READY_CMD); - if (cmd) - { - array_append(world.commandDB.n, sizeof(struct Command), - (void *) cmd, (void **) &world.commandDB.cmds); - world.commandDB.n++; - free(cmd); - cmd = NULL; - } - } - if (token0) - { - parsetest_too_many_values(); - if (!strcmp(token0, str_cmd)) - { - cmd = (struct Command *) parse_init_entry(&cmd_flags, - sizeof(struct Command)); - cmd->dsc_short = strdup(token1); - parse_id_uniq(!(!get_command(cmd->dsc_short))); - } - else if (!( parse_flagval(token0, token1, "DESCRIPTION", &cmd_flags, - DESC_SET, 's', (char *) &cmd->dsc_long) - || parse_flagval(token0, token1,"SERVER_COMMAND", &cmd_flags, - SERVERCMD_SET, 's',(char *)&cmd->server_msg) - || parse_flagval(token0, token1,"SERVER_ARGUMENT",&cmd_flags, - SERVERARG_SET, 'c', (char *) &cmd->arg))) - { - parse_unknown_arg(); - } - } -} - - - -extern struct Command * get_command(char * dsc_short) -{ - struct Command * cmd_ptr = world.commandDB.cmds; - uint8_t i = 0; - while (i < world.commandDB.n) - { - if (0 == strcmp(dsc_short, cmd_ptr->dsc_short)) - { - return cmd_ptr; - } - cmd_ptr = &cmd_ptr[1]; - i++; - } - return NULL; -} - - - -extern void init_command_db() -{ - parse_file(world.path_commands, tokens_into_entries); - set_cleanup_flag(CLEANUP_COMMANDS); -} - - - -extern void free_command_db() -{ - uint8_t i = 0; - while (i < world.commandDB.n) - { - free(world.commandDB.cmds[i].dsc_short); - free(world.commandDB.cmds[i].dsc_long); - free(world.commandDB.cmds[i].server_msg); - i++; - } - free(world.commandDB.cmds); -} diff --git a/src/client/command_db.h b/src/client/command_db.h deleted file mode 100644 index ff56984..0000000 --- a/src/client/command_db.h +++ /dev/null @@ -1,48 +0,0 @@ -/* src/client/command_db.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * The Command DB collects the commands available to the user by internal name, - * description and, optionally, components of a message to send to the server - * when calling it. - */ - -#ifndef COMMAND_DB_H -#define COMMAND_DB_H - -#include /* uint8_t */ - - - -struct Command -{ - char * dsc_short; /* short name of command to be used internally */ - char * dsc_long; /* long description of command to be shown to the user */ - char * server_msg; /* optionally start string of message to send to server*/ - char arg; /* defines server message suffix by try_server_commands() rules */ -}; - -struct CommandDB -{ - struct Command * cmds; /* memory area for sequence of all Command structs */ - uint8_t n; /* number of Command structs in database */ -}; - - - -/* Return Command struct for command described by its "dsc_short" member. Return - * NULL if no such command is found. - */ -extern struct Command * get_command(char * dsc_short); - -/* Read in CommandDB config file. */ -extern void init_command_db(); - -/* Free all memory allocated with init_command_db. */ -extern void free_command_db(); - - - -#endif diff --git a/src/client/control.c b/src/client/control.c deleted file mode 100644 index 55dbd9e..0000000 --- a/src/client/control.c +++ /dev/null @@ -1,279 +0,0 @@ -/* src/client/control.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#include "control.h" -#include /* uint8_t, uint16_t, uint32_t, UINT32_MAX */ -#include /* free() */ -#include /* sprintf() */ -#include /* strlen(), strcmp(), strncmp() */ -#include "../common/rexit.h" /* exit_err(), exit_trouble() */ -#include "../common/try_malloc.h" /* try_malloc() */ -#include "interface_conf.h" /* reload_interface_conf(), save_interface_conf() */ -#include "io.h" /* send() */ -#include "keybindings.h" /* get_command_to_keycode(), get_keycode_to_command(), - * mod_selected_keyb(), move_keyb_selection() - */ -#include "map.h" /* toggle_lookmode(), lookmode_nav()*/ -#include "wincontrol.h" /* shift_active_win(), resize_active_win(), - * toggle_win_size_type(), toggle_window(), - * cycle_active_win(), scroll_v_screen(), - * toggle_linebreak_type(), toggle_winconfig() - */ -#include "windows.h" /* get_win_by_id() */ -#include "world.h" /* world */ - - - -/* Move world.inventory_sel up ("dir"="u") or down (else) as far as possible. */ -static void nav_inventory(char dir); - -/* If "command"'s .dsc_short fits "match", apply "f" with provided char - * arguments and return 1; else, return 0. - */ -static uint8_t try_0args(struct Command * command, char * match, void (* f) ()); -static uint8_t try_1args(struct Command * command, char * match, - void (* f) (char), char c); - -/* If "command" fits pattern "keyb_XY" with Y a proper keybinding list ID char - * and X one of "u" (for "up"), "d" (for "down") or "m" (for "modify"), move up - * or down or modify entry in the selected keybinding list. - */ -static uint8_t try_kb_manip(char * command); - -/* Try if "command" matches a hard-coded list of client-only commands and, if - * successful, execute the match and return 1. Else, return 0. - */ -static uint8_t try_client_commands(struct Command * command); - -/* If c == c_to_match, set "string" to "string_to_set". */ -static uint8_t set_string_if_char_match(char c, char c_to_match, - char ** string, char * string_to_set); - -/* Transform "command" to server command + argument string (free externally). */ -static char * build_server_message_with_argument(struct Command * command); - -/* Try out "command" as one for server messaging; sending is .server_msg, - * followed by either a string representing "command"'s .arg, or, if .arg is - * 'i', world.player_inventory_select, or, if .arg is '0', nothing. Return 1 on - * success, 0 on failure. - */ -static uint8_t try_server_commands(struct Command * command); - - - -static void nav_inventory(char dir) -{ - if ('u' == dir) - { - world.player_inventory_select = world.player_inventory_select - - (world.player_inventory_select > 0); - return; - } - uint8_t n_elems = 0; - uint32_t i; - char * err = "Inventory string is too large."; - exit_err(UINT32_MAX <= strlen(world.player_inventory), err); - for (i = 0; '\0' != world.player_inventory[i]; i++) - { - n_elems = n_elems + ('\n' == world.player_inventory[i]); - } - world.player_inventory_select = world.player_inventory_select - + (world.player_inventory_select < n_elems); -} - - - -static uint8_t try_0args(struct Command * command, char * match, void (* f) ()) -{ - if (!strcmp(command->dsc_short, match)) - { - f(); - return 1; - } - return 0; -} - - - -static uint8_t try_1args(struct Command * command, char * match, - void (* f) (char), char c) -{ - if (!strcmp(command->dsc_short, match)) - { - f(c); - return 1; - } - return 0; -} - - - -static uint8_t try_kb_manip(char * command) -{ - char * cmp = "keyb_"; - if (strlen(command) == strlen(cmp)+2 && !strncmp(command, cmp, strlen(cmp))) - { - if ('m' == command[strlen(cmp)]) - { - mod_selected_keyb(command[strlen(cmp) + 1]); - } - else if ('u' == command[strlen(cmp)] || 'd' == command[strlen(cmp)]) - { - move_keyb_selection(command[strlen(cmp) + 1], command[strlen(cmp)]); - } - return 1; - } - return 0; -} - - - -static uint8_t try_client_commands(struct Command * command) -{ - return ( try_1args(command, "inv_u", nav_inventory, 'u') - || try_1args(command, "inv_d", nav_inventory, 'd') - || try_1args(command, "cyc_win_f", cycle_active_win, 'f') - || try_1args(command, "cyc_win_b", cycle_active_win, 'b') - || try_1args(command, "scrl_r", scroll_v_screen, '+') - || try_1args(command, "scrl_l", scroll_v_screen, '-') - || try_1args(command, "to_g_keywin", toggle_window, '0') - || try_1args(command, "to_wg_keywin", toggle_window, '1') - || try_1args(command, "to_wk_keywin", toggle_window, '2') - || try_1args(command, "to_mapwin", toggle_window, 'm') - || try_1args(command, "to_infowin", toggle_window, 'i') - || try_1args(command, "to_inv", toggle_window, 'c') - || try_1args(command, "to_logwin", toggle_window, 'l') - || try_1args(command, "to_terrain", toggle_window, 's') - || try_0args(command, "winconf", toggle_winconfig) - || try_1args(command, "grow_h", resize_active_win, '*') - || try_1args(command, "shri_h", resize_active_win, '_') - || try_1args(command, "grow_v", resize_active_win, '+') - || try_1args(command, "shri_v", resize_active_win, '-') - || try_0args(command, "to_break", toggle_linebreak_type) - || try_1args(command, "to_height_t", toggle_win_size_type, 'y') - || try_1args(command, "to_width_t", toggle_win_size_type, 'x') - || try_1args(command, "shift_f", shift_active_win, 'f') - || try_1args(command, "shift_b", shift_active_win, 'b') - || try_0args(command, "reload_conf", reload_interface_conf) - || try_0args(command, "save_conf", save_interface_conf) - || try_0args(command, "to_look", toggle_lookmode) - || try_kb_manip(command->dsc_short)); -} - - - -static uint8_t set_string_if_char_match(char c, char c_to_match, - char ** string, char * string_to_set) -{ - if (c == c_to_match) - { - *string = string_to_set; - return 1; - } - return 0; -} - - - -static char * build_server_message_with_argument(struct Command * cmd) -{ - uint8_t command_size = strlen(cmd->server_msg); - char * arg_str = ""; - uint8_t arg_size = 0; - if ('i' == cmd->arg) - { - arg_size = 3; - arg_str = try_malloc(arg_size + 1, __func__); - int test = sprintf(arg_str, "%d",world.player_inventory_select); - exit_trouble(test < 0, __func__, "sprintf"); - } - else if ( set_string_if_char_match(cmd->arg, 'd', &arg_str, "east") - || set_string_if_char_match(cmd->arg, 'c', &arg_str, "south-east") - || set_string_if_char_match(cmd->arg, 'x', &arg_str, "south-west") - || set_string_if_char_match(cmd->arg, 's', &arg_str, "west") - || set_string_if_char_match(cmd->arg, 'w', &arg_str, "north-west") - || set_string_if_char_match(cmd->arg, 'e', &arg_str, "north-east")) - { - arg_size = strlen(arg_str); - } - else - { - exit_err(1, "Illegal server command argument."); - } - char * msg = try_malloc(command_size + 1 + arg_size + 1, __func__); - int test = sprintf(msg, "%s %s", cmd->server_msg, arg_str); - exit_trouble(test < 0, __func__, "sprintf"); - if ('i' == cmd->arg) - { - free(arg_str); - } - return msg; -} - - - -static uint8_t try_server_commands(struct Command * command) -{ - if (command->server_msg) - { - if ('0' == command->arg) - { - send(command->server_msg); - } - else - { - char * msg = build_server_message_with_argument(command); - send(msg); - free(msg); - } - return 1; - } - return 0; -} - - - -extern uint8_t try_key(uint16_t key) -{ - struct Command * command = get_command_to_keycode(&world.kb_global, key); - if (!command && world.winDB.active) - { - struct Win * w = get_win_by_id(world.winDB.active); - if (0 == w->view) - { - command = get_command_to_keycode(&w->kb, key); - } - else if (1 == w->view) - { - command = get_command_to_keycode(&world.kb_wingeom, key); - } - else if (2 == w->view) - { - command = get_command_to_keycode(&world.kb_winkeys, key); - } - } - if (command) - { - if (world.look && lookmode_nav(command->dsc_short)) - { - return 1; - } - else if (try_client_commands(command)) - { - return 1; - } - else if (try_server_commands(command)) - { - return 1; - } - else if (!strcmp("quit", command->dsc_short)) - { - return 2; - } - } - return 0; -} diff --git a/src/client/control.h b/src/client/control.h deleted file mode 100644 index b1c7112..0000000 --- a/src/client/control.h +++ /dev/null @@ -1,25 +0,0 @@ -/* src/client/control.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Routines for handling control input from the keyboard. - */ - -#ifndef CONTROL_H -#define CONTROL_H - -#include /* uint8_t, uint16_t */ - - - -/* Try to match "key" to the Command DB and execute appropriate actions if - * successful. Return 0 on failure, 2 for calling a "quit" comand (to be - * handled externally) and 1 for calling any other command. - */ -extern uint8_t try_key(uint16_t key); - - - -#endif diff --git a/src/client/draw_wins.c b/src/client/draw_wins.c deleted file mode 100644 index e409d87..0000000 --- a/src/client/draw_wins.c +++ /dev/null @@ -1,548 +0,0 @@ -/* src/client/draw_wins.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#define _POSIX_C_SOURCE 200809L /* strdup() */ -#include "draw_wins.h" -#include /* attr_t, chtype, init_pair(), A_REVERSE, COLOR_PAIR() */ -#include /* NULL */ -#include /* uint8_t, uint16_t, uint32_t, UINT16_MAX, UINT32_MAX */ -#include /* sprintf() */ -#include /* free() */ -#include /* memset(), strcmp(), strchr(), strdup/(), strlen() */ -#include "../common/rexit.h" /* exit_err(), exit_trouble() */ -#include "../common/try_malloc.h" /* try_malloc() */ -#include "keybindings.h" /* struct KeyBindingDB, get_keyname_to_keycode() */ -#include "windows.h" /* yx_uint16, Win */ -#include "world.h" /* global world */ - - - -/* Apply to the winmap of Win "w" the new sizes "new_size_y" and "new_size_x" - * to the degree that they extend it. Re-shape the window content accordingly. - */ -static void try_resize_winmap(struct Win * w, int new_size_y, int new_size_x); - -/* In Win "w", write "ch" to coordinate "y"/"x". */ -static void set_ch_on_yx(struct Win * w, int y, int x, chtype ch); - -/* Add "line" into window "w" with "attri" set on all chars. add_line() selects - * one of the other three functions based on the "win"->linebreak mode. - * - * "wide" writes lines to full width without in-between breaks. "long" breaks - * lines on the right window border. "compact" replaces newlines with printable - * separator characters. "offset" and "last" are only relevant to the "compact" - * mode. "offset" reports the horizontal offset at which the line is drawn from - * the lowest existing winmap line on; it is updated to the next offset after - * the new line, to be used by the next add_line_compact() call. "last" marks - * the line to add as the last in the window, so no further separator is added. - */ -static void add_line(struct Win * win, char * line, attr_t attri, - uint16_t * offset, uint8_t last); -static void add_line_wide(struct Win * win, char * line, attr_t attri); -static void add_line_long(struct Win * win, char * line, attr_t attri); -static void add_line_compact(struct Win * win, char * line, attr_t attri, - uint16_t * offset, uint8_t last); - -/* Add linebreaks-containing "text" to "win", formatted as per "win"->linebreak. - * - * draw_text_from_bottom() prepends empty lines to "text" to push its last line - * to the low frame edge when the frame is of greater height than the winmap - * would be otherwise; else, set "win"->center.y to the winmap's lowest line. - */ -static void add_text_with_linebreaks(struct Win * win, char * text); -static void draw_text_from_bottom(struct Win * win, char * text); - -/* Draw from line "start" on config view for keybindings defined at "kb". */ -static void draw_keybinding_config(struct Win * win, struct KeyBindingDB * kbdb, - uint16_t offset); - -/* String building helper functions to draw_winconf_geometry(). */ -static char * get_keyname_to_command(char * command_name); -static char * winconf_geom_helper(struct Win * win, char axis, char * sep, - char * newlines, char * value_prefix); - - - -/* try_resize_winmap() only realloc's Win->winmap if this is set. Use this to - * skip inflationary malloc's by successive try_resize_winmap() calls when the - * memory needed can be pre-calculated. - */ -static uint8_t do_realloc_winmap = 1; - - - -static void try_resize_winmap(struct Win * win, int new_size_y, int new_size_x) -{ - if (win->winmap_size.y >= new_size_y && win->winmap_size.x >= new_size_x) - { - return; - } - if (win->winmap_size.y > new_size_y) - { - new_size_y = win->winmap_size.y; - } - else if (win->winmap_size.x > new_size_x) - { - new_size_x = win->winmap_size.x; - } - if (do_realloc_winmap) - { - chtype * old_winmap = win->winmap; - uint32_t new_size = sizeof(chtype) * new_size_y * new_size_x; - win->winmap = try_malloc(new_size, __func__); - uint16_t y, x; - for (y = 0; y < new_size_y; y++) - { - for (x = 0; y < win->winmap_size.y && x < win->winmap_size.x; x++) - { - chtype ch = old_winmap[(y * win->winmap_size.x) + x]; - win->winmap[(y * new_size_x) + x] = ch; - } - for (; x < new_size_x; x++) - { - win->winmap[(y * new_size_x) + x] = ' '; - } - } - free(old_winmap); - } - win->winmap_size.y = new_size_y; - win->winmap_size.x = new_size_x; -} - - - -static void set_ch_on_yx(struct Win * win, int y, int x, chtype ch) -{ - win->winmap[(y * win->winmap_size.x) + x] = ch; -} - - - -static void add_line(struct Win * win, char * line, attr_t attri, - uint16_t * offset, uint8_t last) -{ - exit_err(strlen(line) > UINT16_MAX, "Line in add_line() too big."); - if (0 == win->linebreak) - { - add_line_long(win, line, attri); - } - else if (1 == win->linebreak) - { - add_line_wide(win, line, attri); - } - else if (2 == win->linebreak) - { - add_line_compact(win, line, attri, offset, last); - } -} - - - -static void add_line_wide(struct Win * win, char * line, attr_t attri) -{ - uint16_t y_start = win->winmap_size.y; - uint16_t len_line = strlen(line); - try_resize_winmap(win, y_start + 1, win->frame_size.x); - try_resize_winmap(win, y_start + 1, len_line); - uint16_t x; - for (x = 0; x < len_line; x++) - { - set_ch_on_yx(win, y_start, x, line[x] | attri); - } - if (attri) - { - for (; x < win->frame_size.x; x++) - { - set_ch_on_yx(win, y_start, x, ' ' | attri); - } - } -} - - - -static void add_line_long(struct Win * win, char * line, attr_t attri) -{ - uint16_t y_start = win->winmap_size.y; - uint16_t len_line = strlen(line); - try_resize_winmap(win, y_start + 1, win->frame_size.x); - uint16_t x, y, z; - for (z = 0, y = y_start; z < len_line; y++) - { - for (x = 0; x < win->winmap_size.x && z < len_line; x++, z++) - { - set_ch_on_yx(win, y, x, line[z] | attri); - } - if (z < len_line) - { - try_resize_winmap(win, y + 1 + 1, win->winmap_size.x); - } - } -} - - - -static void add_line_compact(struct Win * win, char * line, attr_t attri, - uint16_t * offset, uint8_t last) -{ - uint16_t y_start = win->winmap_size.y - (win->winmap_size.y > 0); - try_resize_winmap(win, y_start + 1, win->frame_size.x); - uint16_t len_line = strlen(line); - char * separator = last ? "" : " / "; - uint32_t len_line_new = len_line + strlen(separator); - char * line_new = try_malloc(len_line_new + 1, __func__); - int test = sprintf(line_new, "%s%s", line, separator); - exit_trouble(test < 0, __func__, "sprintf"); - uint16_t x = 0; - uint16_t y; - uint32_t z; - for (z = 0, y = y_start; z < len_line_new; y++) - { - for (x = * offset; x < win->winmap_size.x && z < len_line_new; x++, z++) - { - set_ch_on_yx(win, y, x, line_new[z] | attri); - if (z + 1 == (uint32_t) len_line) - { - attri = 0; - } - } - * offset = 0; - if (z < len_line_new) - { - try_resize_winmap(win, y + 1 + 1, win->winmap_size.x); - } - } - free(line_new); - *offset = x; -} - - - -static void add_text_with_linebreaks(struct Win * win, char * text) -{ - char * limit; - uint16_t offset = 0; - char * text_copy = strdup(text); - char * start = text_copy; - while (NULL != (limit = strchr(start, '\n'))) - { - (* limit) = '\0'; - add_line(win, start, 0, &offset, 0); - start = limit + 1; - } - add_line(win, start, 0, &offset, 1); - free(text_copy); -} - - - -static void draw_text_from_bottom(struct Win * win, char * text) -{ - add_text_with_linebreaks(win, text); - if (win->winmap_size.y > win->frame_size.y) - { - win->center.y = win->winmap_size.y - 1; - } - else if (win->winmap_size.y < win->frame_size.y) - { - uint16_t new_y_start = win->frame_size.y - win->winmap_size.y; - memset(&win->winmap_size, 0, sizeof(struct yx_uint16)); - free(win->winmap); - win->winmap = NULL; - do - { - add_line_wide(win, " ", 0); - } - while (new_y_start > win->winmap_size.y); - if (2 == win->linebreak) /* add_text_with_linebreaks() will start not */ - { /* after, but within the last line then. */ - add_line_wide(win, " ", 0); - } - add_text_with_linebreaks(win, text); - } -} - - - -static void draw_keybinding_config(struct Win * win, struct KeyBindingDB * kbdb, - uint16_t offset) -{ - if (0 == kbdb->n_of_kbs) - { - add_line(win, "(none)", 0, &offset, 0); - return; - } - uint8_t kb_n; - for (kb_n = 0; kb_n < kbdb->n_of_kbs; kb_n++) - { - attr_t attri = 0; - if (kb_n == kbdb->select) - { - attri = A_REVERSE; - if (1 == kbdb->edit) - { - attri = attri | A_BLINK; - } - win->center.y = win->winmap_size.y; - } - struct KeyBinding kb = kbdb->kbs[kb_n]; - char * keyname = get_keyname_to_keycode(kb.keycode); - uint16_t size = strlen(keyname) + 3 + strlen(kb.command->dsc_long) + 1; - char * kb_line = try_malloc(size, __func__); - int test = sprintf(kb_line, "%s - %s", keyname, kb.command->dsc_long); - exit_trouble(test < 0, __func__, "sprintf"); - free(keyname); - add_line(win, kb_line, attri, &offset, (kbdb->n_of_kbs == kb_n + 1)); - free(kb_line); - } -} - - - -static char * get_keyname_to_command(char * command_name) -{ - uint8_t i = 0; - for (i = 0; i < world.kb_wingeom.n_of_kbs; i++) - { - if (!strcmp(world.kb_wingeom.kbs[i].command->dsc_short, command_name)) - { - return get_keyname_to_keycode(world.kb_wingeom.kbs[i].keycode); - } - } - return "UNDEFINED"; -} - - - -static char * winconf_geom_helper(struct Win * win, char axis, char * sep, - char * newlines, char * value_prefix) -{ - char * p0 = 'v'==axis? "Height" : "Width"; - char * p1 = " to save (grow/shrink/toggle positivity with "; - char * p2 = get_keyname_to_command('v'==axis? "grow_v" : "grow_h"); - char * p4 = get_keyname_to_command('v'==axis? "shri_v" : "shri_v"); - char * p6 = get_keyname_to_command('v'==axis? "to_height_t" : "to_width_t"); - char p8[6 + 1]; /* 6: int16_t value max strlen */ - int test = sprintf(p8,"%d", 'v'==axis?win->target_height:win->target_width); - exit_trouble(test < 0, __func__, "sprintf"); - char * p9 = " ("; - char * p10 = "in_cells"; - if (1 == ('v'==axis? win->target_height_type : win->target_width_type)) - { - p10 = "non-positive diff: cells to screen maximum"; - } - char * p11 = ")"; - uint8_t size = strlen(p0) + strlen(p1) + strlen(p2) + strlen(sep) - + strlen(p4) + strlen(sep) + strlen(p6) +strlen(value_prefix) - + strlen(p8) + strlen(p9) + strlen(p10) + strlen(p11) - + strlen(newlines) + 1; - char * msg = try_malloc(size, __func__); - sprintf(msg, "%s%s%s%s%s%s%s%s%s%s%s%s%s", p0, p1, p2, sep, p4, sep, p6, - value_prefix, p8, p9, p10, p11, newlines); - return msg; -} - - - -extern void draw_win_log(struct Win * win) -{ - if (!world.log) - { - return; - } - uint32_t x, i, n_postbreak_lines; - for (i = 0, x = 0, n_postbreak_lines = 1; i < strlen(world.log); i++) - { - exit_err(i == UINT32_MAX, "Log too large."); - x++; - if (x > win->frame_size.x || '\n' == world.log[i]) - { - n_postbreak_lines++; - x = 1; - } - } - if (n_postbreak_lines > win->frame_size.y) - { - uint32_t size = n_postbreak_lines * (win->frame_size.x + 1); - win->winmap = try_malloc(sizeof(chtype) * size, __func__); - for (i = 0; i < size; win->winmap[i] = ' ', i++); - /* TODO: This should only be done with "long" line break style. */ - do_realloc_winmap = 0; - draw_text_from_bottom(win, world.log); - do_realloc_winmap = 1; - return; - } - draw_text_from_bottom(win, world.log); -} - - - -extern void draw_win_map(struct Win * win) -{ - uint16_t x, y; - init_pair(1, COLOR_WHITE, COLOR_BLUE); - init_pair(2, COLOR_BLUE, COLOR_BLACK); - attr_t attr_mem = COLOR_PAIR(2); - attr_t attr_sha = COLOR_PAIR(1); - try_resize_winmap(win, world.map.length, world.map.length * 2 + 1); - for (y = 0; y < world.map.length; y++) - { - for (x = 0; x < world.map.length; x++) - { - attr_t a=' '==world.mem_map[y*world.map.length+x]?attr_sha:attr_mem; - char c = world.mem_map[y*world.map.length + x]; - set_ch_on_yx(win, y, x * 2 + (y % 2), c | a); - set_ch_on_yx(win, y, x * 2 + (y % 2) + 1, ' ' | a); - } - } - for (y = 0; y < world.map.length; y++) - { - for (x = 0; x < world.map.length; x++) - { - if (' ' != world.map.cells[y*world.map.length + x]) - { - char c = world.map.cells[y*world.map.length + x]; - set_ch_on_yx(win, y, x * 2 + (y % 2), c); - set_ch_on_yx(win, y, x * 2 + (y % 2) + 1, ' '); - } - } - } - if (world.look) - { - y = world.look_pos.y; - x = world.look_pos.x; - char c = world.map.cells[y * world.map.length + x]; - c = ' ' == c ? world.mem_map[y * world.map.length + x] : c; - set_ch_on_yx(win, y, x * 2 + (y % 2), c | A_REVERSE); - set_ch_on_yx(win, y, x * 2 + (y % 2) + 1, ' ' | A_REVERSE); - } -} - - - -extern void draw_win_info(struct Win * win) -{ - char * dsc_turn = "Turn: "; - char * dsc_hitpoints = "\nHitpoints: "; - char * dsc_satiation = "\nSatiation: "; - uint16_t maxl = strlen(dsc_turn) + 5 + strlen(dsc_hitpoints) - + strlen(dsc_satiation) + 6 + 3; - char * text = try_malloc(maxl + 1, __func__); - int test = sprintf(text, "%s%d%s%d%s%d", dsc_turn, world.turn, dsc_hitpoints, - world.player_lifepoints, dsc_satiation, - world.player_satiation); - exit_trouble(test < 0, __func__, "sprintf"); - add_text_with_linebreaks(win, text); - free(text); -} - - - -extern void draw_win_inventory(struct Win * win) -{ - char * limit; - char * text_copy = strdup(world.player_inventory); - char * start = text_copy; - uint8_t i = 0; - uint8_t found_selection = 0; - uint16_t offset = 0; - while (NULL != (limit = strchr(start, '\n'))) - { - (* limit) = '\0'; - attr_t attri = 0; - if (i == world.player_inventory_select) - { - found_selection = 1; - attri = A_REVERSE; - win->center.y = win->winmap_size.y; - } - add_line(win, start, attri, &offset, 0); - start = limit + 1; - i++; - } - win->center.y = !found_selection ? win->winmap_size.y : win->center.y; - add_line(win, start, !found_selection * A_REVERSE, &offset, 1); - free(text_copy); -} - - - -extern void draw_win_terrain_stack(struct Win * win) -{ - char * wait_response = "(polling)"; - char * text = world.things_here ? world.things_here : wait_response; - add_text_with_linebreaks(win, text); -} - - - -extern void draw_win_keybindings_global(struct Win * win) -{ - win->center.y = world.kb_global.select; - draw_keybinding_config(win, &world.kb_global, 0); -} - - - -extern void draw_win_keybindings_winconf_geometry(struct Win * win) -{ - win->center.y = world.kb_wingeom.select; - draw_keybinding_config(win, &world.kb_wingeom, 0); -} - - - -extern void draw_win_keybindings_winconf_keybindings(struct Win * win) -{ - win->center.y = world.kb_winkeys.select; - draw_keybinding_config(win, &world.kb_winkeys, 0); -} - - - -extern void draw_winconf_keybindings(struct Win * win) -{ - char * title = "Window's keys:"; - uint16_t offset = 0; - add_line(win, title, 0, &offset, 0); - add_line(win, " ", 0, &offset, 0); - draw_keybinding_config(win, &win->kb, offset); -} - - - -extern void draw_winconf_geometry(struct Win * win) -{ - char * sep = "/"; - char * newlines = "\n\n"; - char * value_prefix = "): "; - char * title = "Window's geometry:\n\nShift up/down with "; - char * key_shift_b = get_keyname_to_command("shift_b"); - char * key_shift_f = get_keyname_to_command("shift_f"); - char * height = winconf_geom_helper(win, 'v', sep, newlines, value_prefix); - char * width = winconf_geom_helper(win, 'h', sep, newlines, value_prefix); - char * breaks_title = "Linebreak type (toggle with "; - char * key_to_break = get_keyname_to_command("to_break"); - char * breaks_type = "long"; - if (win->linebreak) - { - breaks_type = (1 == win->linebreak) ? "wide" : "compact"; - } - uint16_t text_size = strlen(title) + strlen(key_shift_b) + strlen(sep) - + strlen(key_shift_f) + strlen(newlines) - + strlen(height) + strlen(width) - + strlen(breaks_title) + strlen(key_to_break) - + strlen(value_prefix) + strlen(breaks_type); - char * text = try_malloc(text_size + 1, __func__); - int test = sprintf(text, "%s%s%s%s%s%s%s%s%s%s%s", title, key_shift_b, sep, - key_shift_f, newlines, height, width, breaks_title, - key_to_break, value_prefix, breaks_type); - free(height); - free(width); - exit_trouble(test < 0, __func__, "sprintf"); - add_text_with_linebreaks(win, text); - free(text); -} diff --git a/src/client/draw_wins.h b/src/client/draw_wins.h deleted file mode 100644 index 88deb6f..0000000 --- a/src/client/draw_wins.h +++ /dev/null @@ -1,33 +0,0 @@ -/* src/client/draw_wins.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Routines for drawing the game's windows' contents. - */ - -#ifndef DRAW_WINS_H -#define DRAW_WINS_H - -struct Win; - - - -/* Default routines to draw the various windows' standard contents. */ -extern void draw_win_log(struct Win * win); -extern void draw_win_map(struct Win * win); -extern void draw_win_info(struct Win * win); -extern void draw_win_inventory(struct Win * win); -extern void draw_win_terrain_stack(struct Win * win); -extern void draw_win_keybindings_global(struct Win * win); -extern void draw_win_keybindings_winconf_geometry(struct Win * win); -extern void draw_win_keybindings_winconf_keybindings(struct Win * win); - -/* Routines to draw windows' configuration views. */ -extern void draw_winconf_keybindings(struct Win * win); -extern void draw_winconf_geometry(struct Win * win); - - - -#endif diff --git a/src/client/interface_conf.c b/src/client/interface_conf.c deleted file mode 100644 index 679f6c7..0000000 --- a/src/client/interface_conf.c +++ /dev/null @@ -1,484 +0,0 @@ -/* src/client/interface_conf.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#define _POSIX_C_SOURCE 200809L /* getopt(), optarg, strdup(), snprintf() */ -#include "interface_conf.h" -#include /* delwin() */ -#include /* NULL, size_t */ -#include /* UINT8_MAX, uint8_t, uint32_t */ -#include /* EXIT_SUCCESS, atoi(), exit(), free() */ -#include /* FILE, sprintf() */ -#include /* strchr(), strcmp(), strdup(), strlen() */ -#include /* optarg, getopt() */ -#include "../common/parse_file.h" /* token_from_line(),parsetset_singlechar() */ -#include "../common/readwrite.h" /* atomic_write_start(), atomic_write_finish(), - * detect_atomic_leftover(), try_fwrite() - */ -#include "../common/rexit.h" /* exit_err(), exit_trouble() */ -#include "../common/try_malloc.h" /* try_malloc() */ -#include "array_append.h" /* array_append() */ -#include "cleanup.h" /* set_cleanup_flag() */ -#include "command_db.h" /* get_command() */ -#include "keybindings.h" /* KeyBinding, KeyBindingDB, get_command_to_keycode()*/ -#include "parse.h" /* EDIT_STARTED, parse_file(), parse_flagval(), - * parse_and_reduce_to_readyflag(), parse_id_uniq() - * parsetest_defcontext(), parse_unknown_arg(), - * parsetest_too_many_values(), parse_init_entry() - */ -#include "wincontrol.h" /* toggle_window() */ -#include "windows.h" /* Win, free_winDB(), make_v_screen_and_init_win_sizes() */ -#include "world.h" /* global world */ - - - -/* Editing state flags set / checked in tokens_into_entries(). */ -enum flag -{ - NAME_SET = 0x02, - WIDTH_SET = 0x04, - HEIGHT_SET = 0x08, - BREAK_SET = 0x10, - FOCUS_SET = 0x02, - READY_WIN = NAME_SET | WIDTH_SET | HEIGHT_SET | BREAK_SET, - READY_ORD = 0x00, - READY_KBD = 0x00 -}; - - - -/* Used in load_interface_conf() to temporarily store designated values for - * world.winDB.order and world.winDB.active. If those were set right away - * without all windows having already been initialized, orderly exits on error - * would be impossible, for windows are cleaned out by working toggling them off - * piece by peice following these values in unload_interfac_conf(). - */ -static char * tmp_order; -static char tmp_active; - - - -/* Write into "file" keybindings in "kbdb" in config file format. */ -static void write_keybindings(FILE * file, struct KeyBindingDB * kbdb); - -/* Write into file "val" interpreted as pointing either to string (type = 's'), - * int16 ('i') or char ('c'), prefixed with "prefix" and put into single quotes - * if 0 != "quotes". - */ -static void write_def(FILE * file, char * prefix, uint8_t quotes, char * val, - char type); - -/* Read in "token0" and "token1" as interface config definition lines, apply - * definitions to WinDB, KeyBindingsDBs and tmp_order/tmp_active. If argument is - * "KEY", get third token via token_from_line() for complete keybinding. - */ -static void tokens_into_entries(char * token0, char * token1); - -/* If "win" points to non-NULL Win struct, append to it to WinDB.wins array and - * add its ID to world.winDB.ids. - */ -static void write_if_win(struct Win ** win); - -/* Start block setting data for window "token1" id if "token0" == "str_win".*/ -static uint8_t start_win(char * token0, char * token1, char * str_win, - uint8_t * win_flags, struct Win ** win); - -/* Start block setting window order and focus if "token0" = "str_ord". - * "tmp_order" stores the designated world.winDB.order string (only later to - * be realized in load_interface_conf() when all windows have been initialized), - * as does "tmp_active" for the designated world.winDB.active char, defaulting - * to the first char in the window order string if not otherwise specified. - */ -static uint8_t start_ord(char * token0, char * token1, char * str_ord, - uint8_t * ord_flags); - -/* Start block setting non-window-specific keybindings if "token0" == "str_kbd". - * "token1" must be "global", "wingeom" or "winkeys" to set KeyBindingDB to edit - * ("kbdb") to world.kb_global, world.kb_wingeom or world.kb_winkeys. - */ -static uint8_t start_kbd(char * token0, char * token1, char * str_kbd, - uint8_t * kbd_flags, struct KeyBindingDB ** kbdb); - -/* Helper to tokens_into_entries(), sets specific entry member data. "token0" - * and the state of flags determine what entry member to edit; "win" and "kbdb" - * are entries to write "token1" data into (as is the global "tmp_active"). If - * "token0" is "str_key", a keybinding is defined and "token2" is read/must not - * be NULL. In that case, the line read is checked against having a 4th token. - */ -static uint8_t set_members(char * token0, char * token1, uint8_t * win_flags, - uint8_t * ord_flags,uint8_t kbd_flags,char * str_key, - struct Win * win, struct KeyBindingDB * kbdb); - -/* Add keybinding defined in "token1" as keycode and "token2" as command to - * "kbdb" if "flags" are set to EDIT_STARTED. - */ -static void set_keybindings(char * token1, uint8_t flags, - struct KeyBindingDB * kbdb); - - - -static void write_keybindings(FILE * file, struct KeyBindingDB * kbdb) -{ - char * sep = " "; - char * tok0 = "KEY"; - uint8_t i_kb; - for (i_kb = 0; i_kb < kbdb->n_of_kbs; i_kb++) - { - size_t size = strlen(tok0) + strlen(sep) + 3 + strlen(sep) - + strlen(kbdb->kbs[i_kb].command->dsc_short) + 1 + 1; - char * line = try_malloc(size, __func__); - int test = snprintf(line, size, "%s%s%d%s%s\n", - tok0, sep, kbdb->kbs[i_kb].keycode, sep, - kbdb->kbs[i_kb].command->dsc_short); - exit_trouble(test < 0, __func__, "snprintf"); - try_fwrite(line, sizeof(char), strlen(line), file, __func__); - free(line); - } -} - - - -static void write_def(FILE * file, char * prefix, uint8_t quotes, char * val, - char type) -{ - char * val_str = NULL; - int test_val_str = 1; - if ('s' == type) - { - val_str = strdup(val); - } - if ('i' == type) - { - size_t size_val_str = 6 + 1; - val_str = try_malloc(size_val_str, __func__); - test_val_str = snprintf(val_str, size_val_str, "%d", (int16_t) *val); - } - else if ('c' == type) - { - size_t size_val_str = 1 + 1; - val_str = try_malloc(size_val_str, __func__); - test_val_str = snprintf(val_str, size_val_str, "%c", * val); - } - exit_trouble(test_val_str < 0, __func__, "snprintf"); - char * quote = quotes ? "'": ""; - char * affix = "\n"; - size_t size = strlen(prefix) + strlen(val_str) + (2 * strlen(quote)) - + strlen(affix) + 1; - char * line = try_malloc(size, __func__); - int test = snprintf(line, size, "%s%s%s%s%s", - prefix, quote, val_str, quote, affix); - exit_trouble(test < 0, __func__, "snprintf"); - free(val_str); - try_fwrite(line, sizeof(char), strlen(line), file, __func__); - free(line); -} - - - -static void tokens_into_entries(char * token0, char * token1) -{ - char * str_win = "WINDOW"; - char * str_ord = "WIN_ORDER"; - char * str_kbd = "KEYBINDINGS"; - char * str_key = "KEY"; - static uint8_t win_flags = READY_WIN; - static uint8_t ord_flags = READY_ORD; - static uint8_t kbd_flags = READY_KBD; - static struct Win * win = NULL; - static struct KeyBindingDB * kbdb = NULL; - if (!token0 || !strcmp(token0, str_win) || !strcmp(token0, str_ord) - || !strcmp(token0, str_kbd)) - { - parse_and_reduce_to_readyflag(&win_flags, READY_WIN); - parse_and_reduce_to_readyflag(&ord_flags, READY_ORD); - parse_and_reduce_to_readyflag(&kbd_flags, READY_KBD); - write_if_win(&win); - } - if (token0) - { - if (strcmp(token0, str_key)) - { - parsetest_too_many_values(); - } - if (!( start_win(token0, token1, str_win, &win_flags, &win) - || start_ord(token0, token1, str_ord, &ord_flags) - || start_kbd(token0, token1, str_kbd, &kbd_flags, &kbdb) - || set_members(token0, token1, &win_flags, &ord_flags, kbd_flags, - str_key, win, kbdb))) - { - parse_unknown_arg(); - } - } -} - - - -static void write_if_win(struct Win ** win) -{ - if (*win) - { - (*win)->target_height_type = (0 >= (*win)->target_height); - (*win)->target_width_type = (0 >= (*win)->target_width);; - size_t old_ids_size = strlen(world.winDB.ids); - size_t new_size = old_ids_size + 1 + 1; - char * new_ids = try_malloc(new_size, __func__); - int test = snprintf(new_ids,new_size,"%s%c",world.winDB.ids,(*win)->id); - exit_trouble(test < 0, __func__, "snprintf"); - free(world.winDB.ids); - world.winDB.ids = new_ids; - array_append(old_ids_size, sizeof(struct Win), *win, - (void **) &world.winDB.wins); - free(*win); - *win = NULL; - } -} - - - -static uint8_t start_win(char * token0, char * token1, char * str_win, - uint8_t * win_flags, struct Win ** win) -{ - if (strcmp(token0, str_win)) - { - return 0; - } - *win = (struct Win *) parse_init_entry(win_flags, sizeof(struct Win)); - parsetest_singlechar(token1); - parse_id_uniq(world.winDB.ids && strchr(world.winDB.ids,token1[0])); - (*win)->id = token1[0]; - return 1; -} - - - -static uint8_t start_ord(char * token0, char * token1, char * str_ord, - uint8_t * ord_flags) -{ - if (strcmp(token0, str_ord)) - { - return 0; - } - *ord_flags = EDIT_STARTED; - uint32_t i = 0; - for (; i < strlen(token1); i++) - { - err_line(!strchr(world.winDB.legal_ids, token1[i]), "Illegal ID(s)."); - } - free(tmp_order); - tmp_order = strdup(token1); - if (0 < strlen(tmp_order)) - { - tmp_active = tmp_order[0]; - } - return 1; -} - - - -static uint8_t start_kbd(char * token0, char * token1, char * str_kbd, - uint8_t * kbd_flags, struct KeyBindingDB ** kbdb) -{ - if (strcmp(token0, str_kbd)) - { - return 0; - } - *kbd_flags = EDIT_STARTED; - if (!strcmp(token1, "global")) - { - *kbdb = &world.kb_global; - } - else if (!strcmp(token1, "wingeom")) - { - *kbdb = &world.kb_wingeom; - } - else if (!strcmp(token1, "winkeys")) - { - *kbdb = &world.kb_winkeys; - } - else - { - err_line(1, "Value must be 'global', 'wingeom' or 'winkeys'."); - } - return 1; -} - - - -static uint8_t set_members(char * token0, char * token1, uint8_t * win_flags, - uint8_t * ord_flags,uint8_t kbd_flags,char * str_key, - struct Win * win, struct KeyBindingDB * kbdb) -{ - if ( parse_flagval(token0, token1, "NAME", win_flags, - NAME_SET, 's', (char *) &win->title) - || parse_flagval(token0, token1, "WIDTH", win_flags, - WIDTH_SET, 'i', (char *) &win->target_width) - || parse_flagval(token0, token1, "HEIGHT", win_flags, - HEIGHT_SET, 'i', (char *) &win->target_height)); - else if (parse_flagval(token0, token1, "BREAK", win_flags, - BREAK_SET, '8', (char *) &win->linebreak)) - { - err_line(2 < win->linebreak, "Value must be 0, 1 or 2."); - } - else if (parse_flagval(token0, token1, "WIN_FOCUS", ord_flags, - FOCUS_SET, 'c', &tmp_active)) - { - char * err_null = "Value not empty as it should be."; - char * err_outside = "ID not found in WIN_ORDER ID series."; - err_line(!strlen(tmp_order) && tmp_active, err_null); - err_line(!strchr(tmp_order, tmp_active), err_outside); - } - else if (!strcmp(token0, str_key)) - { - if (*win_flags & EDIT_STARTED) - { - set_keybindings(token1, *win_flags, &win->kb); - } - else - { - set_keybindings(token1, kbd_flags, kbdb); - } - } - else - { - return 0; - } - return 1; -} - - - -static void set_keybindings(char * token1, uint8_t flags, - struct KeyBindingDB * kbdb) -{ - char * token2 = token_from_line(NULL); - err_line(!token2, "No binding to key given."); - parsetest_too_many_values(); - char * err_code = "Invalid keycode. Must be >= 0 and < 1000."; - parsetest_defcontext(flags); - char * err_many = "No more than 255 keybindings allowed in one section."; - err_line(UINT8_MAX == kbdb->n_of_kbs, err_many); - struct KeyBinding kb; - uint8_t test = strlen(token1); - err_line(!test, err_code); - uint8_t i; - for (i = 0; '\0' != token1[i]; i++) - { - test= i > 2 || '0' > token1[i] || '9' < token1[i]; - err_line(test, err_code); - } - kb.keycode = atoi(token1); - char * err_uniq = "Binding to key already defined."; - err_line(!(!get_command_to_keycode(kbdb, kb.keycode)), err_uniq); - kb.command = get_command(token2); - err_line(!(kb.command), "No such command in command DB."); - array_append(kbdb->n_of_kbs, sizeof(struct KeyBinding), (void *) &kb, - (void **) kbdb); - kbdb->n_of_kbs++; -} - - - -extern void obey_argv(int argc, char * argv[]) -{ - int opt; - while (-1 != (opt = getopt(argc, argv, "i:"))) - { - if ('i' == opt) - { - world.path_interface = optarg; - } - else - { - exit(EXIT_FAILURE); - } - } -} - - - -extern void save_interface_conf() -{ - char * path_tmp; - FILE * file = atomic_write_start(world.path_interface, &path_tmp); - char * str_keybs = "\nKEYBINDINGS "; - write_def(file, str_keybs, 1, "global", 's'); - write_keybindings(file, &world.kb_global); - write_def(file, str_keybs, 1, "wingeom", 's'); - write_keybindings(file, &world.kb_wingeom); - write_def(file, str_keybs, 1, "winkeys", 's'); - write_keybindings(file, &world.kb_winkeys); - write_def(file, "\nWIN_ORDER ", 1, world.winDB.order, 's'); - if (world.winDB.active) - { - write_def(file, "WIN_FOCUS ", 1, &world.winDB.active, 'c'); - } - uint8_t i; - for (i = 0; i < strlen(world.winDB.ids); i++) - { - write_def(file, "\nWINDOW ", 0, &world.winDB.ids[i], 'c'); - struct Win * win = get_win_by_id(world.winDB.ids[i]); - write_def(file, "NAME ", 1, win->title, 's'); - write_def(file, "BREAK ", 0, (char *) &win->linebreak, 'i'); - write_def(file, "WIDTH ", 0, (char *) &win->target_width, 'i'); - write_def(file, "HEIGHT ", 0, (char *) &win->target_height, 'i'); - write_keybindings(file, &win->kb); - } - atomic_write_finish(file, world.path_interface, path_tmp); -} - - - -extern void load_interface_conf() -{ - world.winDB.ids = try_malloc(1, __func__); - world.winDB.ids[0] = '\0'; - world.winDB.wins = NULL; - tmp_order = try_malloc(1, __func__); - tmp_order[0] = '\0'; - tmp_active = '\0'; - detect_atomic_leftover(world.path_interface); - parse_file(world.path_interface, tokens_into_entries); - char * err = "Not all expected windows defined in config file."; - exit_err(strlen(world.winDB.legal_ids) != strlen(world.winDB.ids), err); - make_v_screen_and_init_win_sizes(); - world.winDB.order = try_malloc(1, __func__); - world.winDB.order[0] = '\0'; - uint8_t i; - for (i = 0; i < strlen(tmp_order); toggle_window(tmp_order[i]), i++); - world.winDB.active = tmp_active; - free(tmp_order); - set_cleanup_flag(CLEANUP_INTERFACE); -} - - - -extern void unload_interface_conf() -{ - free(world.kb_global.kbs); - world.kb_global.kbs = NULL; - world.kb_global.n_of_kbs = 0; - free(world.kb_wingeom.kbs); - world.kb_wingeom.kbs = NULL; - world.kb_wingeom.n_of_kbs = 0; - free(world.kb_winkeys.kbs); - world.kb_winkeys.kbs = NULL; - world.kb_winkeys.n_of_kbs = 0; - while ('\0' != world.winDB.active) - { - toggle_window(world.winDB.active); - } - free_winDB(); - delwin(world.winDB.v_screen); -} - - - -extern void reload_interface_conf() -{ - unload_interface_conf(); - load_interface_conf(); - world.winDB.v_screen_offset = 0; -} diff --git a/src/client/interface_conf.h b/src/client/interface_conf.h deleted file mode 100644 index 1d1c44c..0000000 --- a/src/client/interface_conf.h +++ /dev/null @@ -1,31 +0,0 @@ -/* src/client/interface_conf.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Read/unread/write interface configuration file. - */ - -#ifndef INTERFACE_CONF_H -#define INTERFACE_CONF_H - - - -/* Parses command line argument -i into client configuration. */ -extern void obey_argv(int argc, char * argv[]); - -/* Save / load (init) / unload (free/dissolve) / reload interface configuration - * data, world.wins.pad (initialized before opening any windows to the height of - * the terminal screen and a width of 1) and window chains. - * - * reload_interface_conf() also re-sets world.winDB.v_screen_offset to zero. - */ -extern void save_interface_conf(); -extern void load_interface_conf(); -extern void unload_interface_conf(); -extern void reload_interface_conf(); - - - -#endif diff --git a/src/client/io.c b/src/client/io.c deleted file mode 100644 index f47de03..0000000 --- a/src/client/io.c +++ /dev/null @@ -1,361 +0,0 @@ -/* src/client/io.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#define _POSIX_C_SOURCE 1 /* PIPE_BUF */ -#include "io.h" -#include /* PIPE_BUF */ -#include /* timeout(), getch() */ -#include /* NULL */ -#include /* uint8_t, uint16_t, uint32_t, UINT32_MAX */ -#include /* FILE, sprintf(), fseek(), fflush() */ -#include /* strcmp(), strlen(), memcpy() */ -#include /* free(), atoi() */ -#include /* stat() */ -#include /* time_t */ -#include /* time() */ -#include /* access() */ -#include "../common/try_malloc.h" /* try_malloc() */ -#include "../common/rexit.h" /* exit_trouble(), exit_err() */ -#include "../common/readwrite.h" /* try_fopen(), try_fclose(), try_fgets(), - * try_fgetc(), textfile_width(), try_fputc(), - * read_file_into_queue(), - * get_message_from_queue(), - */ -#include "../common/yx_uint8.h" /* yx_uint8 */ -#include "control.h" /* try_key() */ -#include "map.h" /* query_mapcell() */ -#include "windows.h" /* Win, reset_windows_on_winch(), draw_all_wins(), - * get_win_by_id() - */ -#include "world.h" /* world global */ - - - -/* Read next lines of "file" up to (and excluding) a line "%\n" into the - * world.player_inventory string. - */ -static void read_inventory(char * read_buf, uint32_t linemax, FILE * file); - -/* Read the next characters in "file" into "map". In detail: Read - * world.map.length times world.map.length characters, followed by one ignored - * character (that we assume is a newline). - */ -static void read_map_cells(FILE * file, char ** map); - -/* Return value seen by atoi() in next line of "file" when passed to try_fgets() - * with the given arguments. - */ -static int32_t read_value_from_line(char* read_buf,int32_t linemax,FILE* file); - -/* If the server's worldstate file has changed since the last read_worldstate(), - * return a pointer to its file descriptor; else, return NULL. - * - * Two tests are performed to check for a file change. The file's last data - * modification time in seconds via stat() is compared against world.last_update - * (and if it is changed, world.last_update is re-set to it). If this does not - * verify a change, the first bytes of the file are read to compare the game - * turn number described therein to the last read turn number in world.turn. - * - * The stat() check is mostly useless, for it only detects file updates once a - * second. But the turn check fails if a new world is generated from turn 1 on: - * the new world also starts in turn 1, not signifying any world change to the - * turn check. The stat() check detects this change with at most 1 second delay. - */ -static FILE * changed_worldstate_file(char * path); - -/* Attempt to read the server's worldstate file as representation of the game - * world in a hard-coded serialization format. Returns 1 on success, or 0 if the - * out file wasn't read for supposedly not having changed since a last - * read_worldstate() call. - */ -static uint8_t read_worldstate(); - -/* Poll server for queue input. If no new input, send ping, or, if ping already - * sent but unanswered for some time, abort. - */ -static void test_and_poll_server(); - -/* If "string", append \n-prefixed "append", else write "append" as "string". */ -static void nl_append_string(char * append, char ** string); - -/* Read messages from queue, act on them. */ -static uint8_t read_queue(); - - - -static void read_inventory(char * read_buf, uint32_t linemax, FILE * file) -{ - char * delimiter = "%\n"; - free(world.player_inventory); - world.player_inventory = NULL; /* Avoids illegal strlen() below. */ - while (1) - { - try_fgets(read_buf, linemax + 1, file, __func__); - if (!(strcmp(read_buf, delimiter))) - { - break; - } - int old_size = 0; - if (world.player_inventory) - { - old_size = strlen(world.player_inventory); - } - int new_size = strlen(read_buf); - char * new_inventory = try_malloc(old_size + new_size + 1, __func__); - memcpy(new_inventory, world.player_inventory, old_size); - int test = sprintf(new_inventory + old_size, "%s", read_buf); - exit_trouble(test < 0, __func__, "sprintf"); - free(world.player_inventory); - world.player_inventory = new_inventory; - } - world.player_inventory[strlen(world.player_inventory) - 1] = '\0'; - world.player_inventory_select = 0; -} - - - -static void read_map_cells(FILE * file, char ** map) -{ - if (*map) - { - free(*map); - *map = NULL; - } - *map = try_malloc(world.map.length * world.map.length, __func__); - char * map_cells = *map; - uint16_t y, x; - for (y = 0; y < world.map.length; y++) - { - for (x = 0; x < world.map.length; x++) - { - char c = try_fgetc(file, __func__); - map_cells[y * world.map.length + x] = c; - } - try_fgetc(file, __func__); - } -} - - - -static int32_t read_value_from_line(char * read_buf,int32_t linemax,FILE * file) -{ - try_fgets(read_buf, linemax + 1, file, __func__); - return atoi(read_buf); -} - - - -static FILE * changed_worldstate_file(char * path) -{ - struct stat stat_buf; - exit_trouble(stat(path, &stat_buf), __func__, "stat"); - if (stat_buf.st_mtime != world.last_update) - { - world.last_update = stat_buf.st_mtime; - return try_fopen(path, "r", __func__); - } - FILE * file = try_fopen(path, "r", __func__); - char turn_string[6]; - try_fgets(turn_string, 6, file, __func__); - if (world.turn == atoi(turn_string)) - { - try_fclose(file, __func__); - return NULL; - } - exit_trouble(fseek(file, 0, SEEK_SET), __func__, "fseek"); - return file; -} - - - -static uint8_t read_worldstate() -{ - char * path = "server/worldstate"; - char * quit_msg = "No worldstate file found to read. Server may be down."; - exit_err(access(path, F_OK), quit_msg); - FILE * file = changed_worldstate_file(path); - if (!file) - { - return 0; - } - uint32_t linemax = textfile_width(file); - char * read_buf = try_malloc(linemax + 1, __func__); - world.turn = (uint16_t) read_value_from_line(read_buf, linemax, file); - world.player_lifepoints = (uint16_t) read_value_from_line(read_buf, linemax, - file); - world.player_satiation = (int16_t) read_value_from_line(read_buf, linemax, - file); - read_inventory(read_buf, linemax, file); - world.player_pos.y = (uint8_t) read_value_from_line(read_buf,linemax,file); - world.player_pos.x = (uint8_t) read_value_from_line(read_buf,linemax,file); - world.map.length = (uint16_t) read_value_from_line(read_buf, linemax, file); - read_map_cells(file, &world.map.cells); - read_map_cells(file, &world.mem_map); - free(read_buf); - try_fclose(file, __func__); - return 1; -} - - - -static void test_and_poll_server() -{ - static time_t last_server_answer_time = 0; - static time_t last_pong_time = 0; - static uint8_t ping_sent = 0; - uint8_t wait = 15; - if (read_file_into_queue(world.file_server_out, &world.queue)) - { - last_server_answer_time = time(0); - return; - } - time_t now = time(0); - if (ping_sent && last_server_answer_time > now - wait)/* Re-set if last */ - { /* ping was answered*/ - ping_sent = 0; /* with server */ - return; /* activity. */ - } - if (!ping_sent && last_server_answer_time < now - wait) - { - send("PING"); - ping_sent = 1; - last_pong_time = now; - return; - } - exit_err(ping_sent && last_pong_time < now - wait, "Server not answering."); -} - - - -static void nl_append_string(char * append, char ** string) -{ - char * err = "too large sizes"; - exit_trouble(UINT32_MAX < strlen(append), __func__, err); - uint32_t new_size = strlen(append); - uint32_t old_size = 0; - uint8_t add_nl = 0; - if (*string) - { - exit_trouble(UINT32_MAX < new_size + strlen(*string), __func__, err); - old_size = strlen(*string); - add_nl = 1; - } - char * new_string = try_malloc(old_size + add_nl + new_size + 1, __func__); - memcpy(new_string, *string, old_size); - char * pattern = add_nl ? "\n%s" : "%s"; - int test = sprintf(new_string + old_size, pattern, append); - exit_trouble(test < 0, __func__, "sprintf"); - free(*string); - *string = new_string; -} - - - -static uint8_t read_queue() -{ - static uint8_t things_here_parsing = 0; - uint8_t ret = 0; - char * msg; - while (NULL != (msg = get_message_from_queue(&world.queue))) - { - char * log_prefix = "LOG "; - if (!strcmp(msg, "THINGS_HERE START")) - { - ret = 1; - things_here_parsing = 1; - free(world.things_here); - world.things_here = NULL; - } - else if (!strcmp(msg, "THINGS_HERE END")) - { - things_here_parsing = 0; - if (!world.things_here) - { - nl_append_string("(none known)", &world.things_here); - } - } - else if (things_here_parsing) - { - ret = 1; - nl_append_string(msg, &world.things_here); - } - else if (!strncmp(msg, log_prefix, strlen(log_prefix))) - { - ret = 1; - nl_append_string(msg + strlen(log_prefix), &world.log); - } - else if (!strcmp(msg, "NEW_WORLD")) - { - ret = 1; - free(world.log); - world.log = NULL; - } - else if (!strcmp(msg, "WORLD_UPDATED")) - { - query_mapcell(); - } - free(msg); - } - return ret; -} - - - -extern void send(char * msg) -{ - uint32_t msg_size = strlen(msg) + 1; - char * err = "send() tried to send message larger than PIPE_BUF bytes."; - exit_err(msg_size > PIPE_BUF, err); - try_fwrite(msg, strlen(msg), 1, world.file_server_in, __func__); - try_fputc('\n', world.file_server_in, __func__); - fflush(world.file_server_in); -} - - - -extern char * io_loop() -{ - uint16_t delay = 1; /* Greater delay: less redundant server files reading.*/ - uint8_t change_in_client = 0; - while (1) - { - timeout(delay); - delay = delay < 1000 ? delay * 2 : delay; - test_and_poll_server(); - if (world.winch) - { - reset_windows_on_winch(); - world.winch = 0; - change_in_client++; - } - if (change_in_client || read_worldstate() || read_queue()) - { - delay = 1; /* Keep client alert even if it's not getch()'d. */ - struct Win * win_map = get_win_by_id('m'); - if (0 == win_map->view) /* So the map window's winconfig views */ - { /* don't get confused by the centering. */ - struct yx_uint8 pos=world.look?world.look_pos:world.player_pos; - win_map->center.y = pos.y; - win_map->center.x = pos.x * 2 + (pos.y % 2); - } - draw_all_wins(); - } - change_in_client = 0; - int key = getch(); - if (ERR != key) - { - delay = 1; /* Alert client if it's getch()'d. */ - change_in_client = try_key((uint16_t) key); - if (2 == change_in_client) - { - break; - } - } - } - send("QUIT"); - return "Sent QUIT to server."; -} diff --git a/src/client/io.h b/src/client/io.h deleted file mode 100644 index 12a56c0..0000000 --- a/src/client/io.h +++ /dev/null @@ -1,42 +0,0 @@ -/* src/client/io.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Communication of the client with the server (by reading and writing files) - * and the user (by writing to the screen and reading keypresses). - */ - -#ifndef IO_H -#define IO_H - - - -/* Write "msg" plus newline to server input file at world.path_server_in. - * - * "msg" must fit into size defined by PIPE_BUF so that no race conditiosn - * arise by many clients writing to the file in parallel. - */ -extern void send(char * msg); - -/* Keep checking for user input, a changed worldstate file, and queue input from - * the server's out file. Update client's world representation on worldstate - * file changes. Manipulate the client and send commands to server based on the - * user input as interpreted by the control.h library. - * - * On each change / activity, re-draw the windows with draw_all_wins(). When the - * loop ends regularly (due to the user sending a quit command), return an - * appropriate quit message to write to stdout when the client winds down. Call - * reset_windows() on receiving a SIGWINCH. Abort on assumed server death if the - * server's out file does not get updated, even on PING requests. Re-focus map - * view on player. Messages from the out file are put together on the queue - * first, from which only complete (\n-delimited) messages are read. Queues of - * messages are worked through completely / emptied before any re-drawing or - * further server polling happens. - */ -extern char * io_loop(); - - - -#endif diff --git a/src/client/keybindings.c b/src/client/keybindings.c deleted file mode 100644 index fc371e8..0000000 --- a/src/client/keybindings.c +++ /dev/null @@ -1,153 +0,0 @@ -/* src/client/keybindings.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#include "keybindings.h" -#include /* keycode defines, cbreak(), getch(), timeout() */ -#include /* NULL */ -#include /* uint8_t, uint16_t, uint32_t */ -#include /* FILE, sprintf() */ -#include "../common/rexit.h" /* exit_trouble() */ -#include "../common/try_malloc.h" /* try_malloc() */ -#include "windows.h" /* draw_all_wins() */ -#include "world.h" /* global world */ -struct Command; - - -/* Return pointer to global keybindings or to keybindings for wingeometry config - * (c = "g") or winkeys config (c = "k") or active window's keybindings ("w"). - */ -static struct KeyBindingDB * char_selected_kb_db(char c); - -/* If "keycode_given" equals "keycode_match", copy "keyname_match" to "keyname" - * and return 1; otherwise return 0. - */ -static uint8_t try_keycode(uint16_t keycode_given, char * keyname, - uint16_t keycode_match, char * keyname_match); - - - -static struct KeyBindingDB * char_selected_kb_db(char c) -{ - struct KeyBindingDB * kbdb; - kbdb = &world.kb_global; - if ('g' == c) - { - kbdb = &world.kb_wingeom; - } - else if ('k' == c) - { - kbdb = &world.kb_winkeys; - } - else if ('w' == c) - { - struct Win * w = get_win_by_id(world.winDB.active); - kbdb = &w->kb; - } - return kbdb; -} - - - -static uint8_t try_keycode(uint16_t keycode_given, char * keyname, - uint16_t keycode_match, char * keyname_match) -{ - if (keycode_given == keycode_match) - { - int test = sprintf(keyname, "%s", keyname_match); - exit_trouble(test < 0, __func__, "sprintf"); - return 1; - } - return 0; -} - - - -extern struct Command * get_command_to_keycode(struct KeyBindingDB * kbdb, - uint16_t keycode) -{ - uint8_t n_kb; - for (n_kb = 0; n_kb < kbdb->n_of_kbs; n_kb++) - { - if (keycode == kbdb->kbs[n_kb].keycode) - { - return kbdb->kbs[n_kb].command; - } - } - return NULL; -} - - - -extern char * get_keyname_to_keycode(uint16_t keycode) -{ - char * keyname = try_malloc(10, __func__); /* max keyname length + 1 */ - if (32 < keycode && keycode < 127) - { - exit_trouble(sprintf(keyname, "%c", keycode) < 0, __func__, "sprintf"); - } - else if (keycode >= KEY_F0 && keycode <= KEY_F(63)) - { - uint16_t f = keycode - KEY_F0; - exit_trouble(sprintf(keyname, "F%d", f) < 0, __func__, "sprintf");; - } - else if ( try_keycode(keycode, keyname, 9, "TAB") - || try_keycode(keycode, keyname, 10, "RETURN") - || try_keycode(keycode, keyname, 27, "ESCAPE") - || try_keycode(keycode, keyname, 32, "SPACE") - || try_keycode(keycode, keyname, KEY_UP, "UP") - || try_keycode(keycode, keyname, KEY_DOWN, "DOWN") - || try_keycode(keycode, keyname, KEY_LEFT, "LEFT") - || try_keycode(keycode, keyname, KEY_RIGHT, "RIGHT") - || try_keycode(keycode, keyname, KEY_HOME, "HOME") - || try_keycode(keycode, keyname, KEY_BACKSPACE, "BACKSPACE") - || try_keycode(keycode, keyname, KEY_DC, "DELETE") - || try_keycode(keycode, keyname, KEY_IC, "INSERT") - || try_keycode(keycode, keyname, KEY_NPAGE, "NEXT PAGE") - || try_keycode(keycode, keyname, KEY_PPAGE, "PREV PAGE") - || try_keycode(keycode, keyname, KEY_END, "END")) - { - ; - } - else - { - exit_trouble(sprintf(keyname, "(unknown)") < 0, __func__, "sprintf"); - } - return keyname; -} - - - -extern void mod_selected_keyb(char kb_c) -{ - struct KeyBindingDB * kbdb = char_selected_kb_db(kb_c); - kbdb->edit = 1; - draw_all_wins(); - cbreak(); - timeout(-1); - int keycode = getch(); - timeout(0); - if (keycode < 1000) - { - kbdb->kbs[kbdb->select].keycode = keycode; - } - kbdb->edit = 0; -} - - - -extern void move_keyb_selection(char kb_c, char dir) -{ - struct KeyBindingDB * kbdb = char_selected_kb_db(kb_c); - if ('u' == dir && kbdb->select > 0) - { - kbdb->select--; - } - else if ('d' == dir && kbdb->select < kbdb->n_of_kbs - 1) - { - kbdb->select++; - } -} diff --git a/src/client/keybindings.h b/src/client/keybindings.h deleted file mode 100644 index fa08de1..0000000 --- a/src/client/keybindings.h +++ /dev/null @@ -1,56 +0,0 @@ -/* src/client/keybindings.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Database of keybindings and functions to read and manipulate it. - */ - -#ifndef KEYBINDINGS_H -#define KEYBINDINGS_H - -#include /* uint8_t, uint16_t */ -struct Command; - - - -struct KeyBinding -{ - uint16_t keycode; - struct Command * command; /* command in command DB to which key is bound */ -}; - -struct KeyBindingDB -{ - struct KeyBinding * kbs; - uint8_t n_of_kbs; /* how many KeyBinding structs are stored below .kbs? */ - uint8_t select; /* linear list index of keybinding selected for editing */ - uint8_t edit; /* 1 if currently editing a keybinding, else 0 */ -}; - - - -/* Return command bound to "keycode" in "kbdb"; NULL if none found. */ -extern struct Command * get_command_to_keycode(struct KeyBindingDB * kbdb, - uint16_t keycode); - -/* Return human-readable name (of maximum 9 chars) for "keycode" as matched by - * ncurses.h; if none is found, return "(unknown)". - */ -extern char * get_keyname_to_keycode(uint16_t keycode); - -/* Mark keybinding in KeybindingDB (char_selected_kb_db()-) selected by "kb_c" - * as being edited, get user input to modify it, then unmark it again. Ensure - * there are max. three digits in the ASCII string of the kecode read from user. - */ -extern void mod_selected_keyb(char kb_c); - -/* Move .select in KeybindingDB (char-selected_kb_db()-) selected by "kb_c" - * upwards ("dir"=="u") or downwards ("dir"=="d") within KeyBindingDB limits. - */ -extern void move_keyb_selection(char kb_c, char dir); - - - -#endif diff --git a/src/client/main.c b/src/client/main.c deleted file mode 100644 index c40d745..0000000 --- a/src/client/main.c +++ /dev/null @@ -1,80 +0,0 @@ -/* main.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#define _POSIX_C_SOURCE 1 /* sigaction, sigaction() */ -#define _DARWIN_C_SOURCE 1 /* SIGWINCH on OS X */ -#include /* keypad(), start_color() */ -#include /* SIGWINCH, sigaction, sigaction() */ -#include /* NULL */ -#include /* exit() */ -#include /* memset() */ -#include /* access() */ -#include "../common/readwrite.h" /* try_fopen() */ -#include "../common/rexit.h" /* set_cleanup_func(), exit_trouble(),exit_err() */ -#include "cleanup.h" /* cleanup(), set_cleanup_flag() */ -#include "command_db.h" /* init_command_db() */ -#include "interface_conf.h" /* load_interface_conf(), obey_argv() */ -#include "io.h" /* io_loop() */ -#include "windows.h" /* winch_called() */ -#include "world.h" /* struct World */ - - - -struct World world; - - - -int main(int argc, char * argv[]) -{ - /* Declare hard-coded paths and values here. */ - world.path_commands = "confclient/commands"; - world.path_interface = "confclient/interface_conf"; - world.winDB.legal_ids = "012cilms"; - char * path_server_in = "server/in"; - char * path_server_out = "server/out"; - - /* Parse command line arguments. */ - obey_argv(argc, argv); - - /* So error exits also go through the client's cleanup() function. */ - set_cleanup_func(cleanup); - - /* Check existence of config files. */ - exit_err(access(world.path_commands, F_OK), "No commands config file."); - exit_err(access(world.path_interface, F_OK), "No interface config file."); - - /* Initialize the whole interface. */ - world.winDB.t_screen = initscr(); - start_color(); - set_cleanup_flag(CLEANUP_NCURSES); - noecho(); - curs_set(0); - keypad(world.winDB.t_screen, TRUE); - init_command_db(); /* The command DB needs to be initialized before */ - load_interface_conf(); /* the interface, whose keybindings depend on it. */ - - /* Set handler for terminal window resizing. */ - struct sigaction act; - memset(&act, 0, sizeof(act)); - act.sa_handler = &winch_called; - exit_trouble(sigaction(SIGWINCH, &act, NULL), __func__, "sigaction"); - - /* Open file streams for communicating with the server. */ - exit_err(access(path_server_in, F_OK), "No server input file found."); - world.file_server_in = try_fopen(path_server_in, "a", __func__); - set_cleanup_flag(CLEANUP_SERVER_IN); - world.file_server_out = try_fopen(path_server_out, "r", __func__); - set_cleanup_flag(CLEANUP_SERVER_OUT); - - /* This is where most everything happens. */ - char * quit_msg = io_loop(); - - /* Leave properly. */ - cleanup(); - exit_trouble(printf("%s\n", quit_msg) < 0, __func__, "printf"); - exit(EXIT_SUCCESS); -} diff --git a/src/client/map.c b/src/client/map.c deleted file mode 100644 index ab8148d..0000000 --- a/src/client/map.c +++ /dev/null @@ -1,99 +0,0 @@ -/* src/client/map.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#include "map.h" -#include /* uint8_t */ -#include /* free() */ -#include /* sprintf() */ -#include /* strlen(), strncmp() */ -#include "../common/try_malloc.h" /* try_malloc() */ -#include "../common/rexit.h" /* exit_trouble() */ -#include "io.h" /* send() */ -#include "world.h" /* for world */ - - - -extern void toggle_lookmode() -{ - if (!world.look) - { - world.look_pos = world.player_pos; - world.look = 1; - } - else - { - world.look = 0; - } - query_mapcell(); -} - - - -extern uint8_t lookmode_nav(char * command) -{ - char * prefix = "move_"; - uint8_t len_pref = strlen(prefix); - if (!strncmp(prefix, command, len_pref) && strlen(command) - 1 == len_pref) - { - uint8_t open_north = world.look_pos.y > 0; - uint8_t open_south = world.look_pos.y < world.map.length - 1; - uint8_t open_west = world.look_pos.x > 0; - uint8_t open_east = world.look_pos.x < world.map.length - 1; - uint8_t indent = world.look_pos.y % 2; - if ('s' == command[len_pref]) - { - world.look_pos.x = world.look_pos.x - open_west; - } - else if ('d' == command[len_pref]) - { - world.look_pos.x = world.look_pos.x + open_east; - } - else if ('w' == command[len_pref]) - { - world.look_pos.y = world.look_pos.y - open_north; - world.look_pos.x = world.look_pos.x - !indent * open_west; - } - else if ('e' == command[len_pref]) - { - world.look_pos.y = world.look_pos.y - open_north; - world.look_pos.x = world.look_pos.x + indent * open_east; - } - else if ('x' == command[len_pref]) - { - world.look_pos.y = world.look_pos.y + open_south; - world.look_pos.x = world.look_pos.x - !indent * open_west; - } - else if ('c' == command[len_pref]) - { - world.look_pos.y = world.look_pos.y + open_south; - world.look_pos.x = world.look_pos.x + indent * open_east; - } - else - { - return 0; - } - query_mapcell(); - return 1; - } - return 0; -} - - - -extern void query_mapcell() -{ - free(world.things_here); - world.things_here = NULL; - char * stack = "THINGS_HERE"; - char * stack_query = try_malloc(strlen(stack) +1+3 +1+3 +1, __func__); - uint8_t y = world.look ? world.look_pos.y : world.player_pos.y; - uint8_t x = world.look ? world.look_pos.x : world.player_pos.x; - int test = sprintf(stack_query, "%s %d %d", stack, y, x); - exit_trouble(test < 0, __func__, "sprintf"); - send(stack_query); - free(stack_query); -} diff --git a/src/client/map.h b/src/client/map.h deleted file mode 100644 index 8a36750..0000000 --- a/src/client/map.h +++ /dev/null @@ -1,27 +0,0 @@ -/* src/client/map.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Routines for the game map window. - */ - -#ifndef MAP_H_CLIENT -#define MAP_H_CLIENT - -#include /* uint8_t */ - - -/* Toggle world.look (moving look cursor instead of player over map). */ -extern void toggle_lookmode(); - -/* Read "command" as look cursor move command, act on it.*/ -extern uint8_t lookmode_nav(char * command); - -/* Send THINGS_HERE query message to server.*/ -extern void query_mapcell(); - - - -#endif diff --git a/src/client/parse.c b/src/client/parse.c deleted file mode 100644 index 5bf2bd7..0000000 --- a/src/client/parse.c +++ /dev/null @@ -1,122 +0,0 @@ -/* src/client/parse.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#define _POSIX_C_SOURCE 200809L /* strdup(), snprintf() */ -#include "parse.h" -#include /* size_t, NULL */ -#include /* FILE, snprintf() */ -#include /* uint8_t, uint32_t */ -#include /* free() */ -#include /* strdup(), strlen() */ -#include /* access(), F_OK */ -#include "../common/parse_file.h" /* set_err_line_options(), err_line_inc(), - * err_line_zero(), token_from_line() - */ -#include "../common/readwrite.h" /* try_fopen(),try_fclose(),textfile_width() */ -#include "../common/rexit.h" /* exit_err(), exit_trouble() */ -#include "../common/try_malloc.h" /* try_malloc() */ - - - -extern void parse_file(char * path, void (* token_to_entry) (char *, char *)) -{ - char * prefix = "Failed reading config file: \""; - char * affix = "\". "; - size_t size = strlen(prefix) + strlen(path) + strlen(affix) + 1; - char * errline_intro = try_malloc(size, __func__); - int test = snprintf(errline_intro, size, "%s%s%s", prefix, path, affix); - exit_trouble(test < 0, __func__, "snprintf"); - exit_err(access(path, F_OK), errline_intro); - FILE * file = try_fopen(path, "r", __func__); - uint32_t linemax = textfile_width(file); - char * errline_line = try_malloc(linemax + 1, __func__); - set_err_line_options(errline_intro, errline_line, 1); - err_line_zero(); - err_line(!linemax, "File is empty."); - char * token0 = NULL; /* For final token_to_entry() if while() stagnates. */ - char * token1 = NULL; - char * err_val = "No value given."; - while (try_fgets(errline_line, linemax + 1, file, __func__)) - { - err_line_inc(); - char * line_copy = strdup(errline_line); - token0 = token_from_line(line_copy); - if (token0) - { - err_line(0 == (token1 = token_from_line(NULL)), err_val); - token_to_entry(token0, token1); - token0 = NULL; - } - free(line_copy); - } - token_to_entry(token0, token1); - try_fclose(file, __func__); - free(errline_line); - free(errline_intro); -} - - - -extern void parsetest_defcontext(uint8_t flags) -{ - err_line(!(flags & EDIT_STARTED),"Outside appropriate definition context."); -} - - - -extern void parsetest_too_many_values() -{ - err_line(!(!token_from_line(NULL)), "Too many values."); -} - - - -extern void parse_id_uniq(int test) -{ - err_line(0 != test, "Declaration of ID already used."); -} - - - -extern void parse_unknown_arg() -{ - err_line(1, "Unknown argument."); -} - - - -extern char * parse_init_entry(uint8_t * flags, size_t size) -{ - *flags = EDIT_STARTED; - char * p = try_malloc(size, __func__); - memset(p, 0, size); - return p; -} - - - -extern uint8_t parse_flagval(char * token0, char * token1, char * comparand, - uint8_t * flags, uint8_t set_flag, char type, - char * element) -{ - if (parse_val(token0, token1, comparand, type, element)) - { - parsetest_defcontext(*flags); - *flags = *flags | set_flag; - return 1; - } - return 0; -} - - - -extern void parse_and_reduce_to_readyflag(uint8_t * flags, uint8_t ready_flag) -{ - char * err_fin = "Last definition block not finished yet."; - err_line((*flags & ready_flag) ^ ready_flag, err_fin); - *flags = ready_flag; -} diff --git a/src/client/parse.h b/src/client/parse.h deleted file mode 100644 index 31c2fb5..0000000 --- a/src/client/parse.h +++ /dev/null @@ -1,59 +0,0 @@ -/* src/client/parse.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Routines for file parsing. - */ - -#ifndef PARSE_H -#define PARSE_H - -#include /* size_t */ -#include /* uint8_t */ - - - -enum parse_flags -{ - EDIT_STARTED = 0x01 -}; - - - -/* Parse file at "path" by passing each line's first two tokens to - * "token_to_entry". Ignore empty lines. Non-empty lines must feature at least - * two tokens as delimited either be whitespace or single quotes (to allow for - * tokens featuring whitespaces). When EOF is reached, token_to_entry() is - * called a last time with a first token of NULL. - */ -extern void parse_file(char * path, void ( *token_to_entry) (char *, char *)); - -/* Calls err_line() with fitting message if EDIT_STARTED not set in "flags". */ -extern void parsetest_defcontext(uint8_t flags); - -/* Ensure token_from_line() does not find any more tokens on the line. */ -extern void parsetest_too_many_values(); - -/* Trigger err_line() with "Unknown argument" message. */ -extern void parse_unknown_arg(); - -/* If "test"!=0 call err_line() with "Declaration of ID already used" message.*/ -extern void parse_id_uniq(int test); - -/* Set "flags"=EDIT_STARTED and return pointer to new zeroed array of "size". */ -extern char * parse_init_entry(uint8_t * flags, size_t size); - -/* Wrapper to parse_val() that sets "flags" to "flags"|"set_flag" on success. */ -extern uint8_t parse_flagval(char * token0, char * token1, char * comparand, - uint8_t * flags, uint8_t set_flag, char type, - char * element); - -/* Check "ready_flag" is set in "flags", re-set "flags" to "ready_flag" only. */ -extern void parse_and_reduce_to_readyflag(uint8_t * flags, uint8_t ready_flag); - - - -#endif - diff --git a/src/client/wincontrol.c b/src/client/wincontrol.c deleted file mode 100644 index eb9270b..0000000 --- a/src/client/wincontrol.c +++ /dev/null @@ -1,415 +0,0 @@ -/* src/client/wincontrol.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#include "wincontrol.h" -#include /* getmaxx(), getmaxy(), wresize() */ -#include /* NULL */ -#include /* uint8_t, uint32_t, UINT16_MAX */ -#include /* sprintf() */ -#include /* free() */ -#include /* memcpy(), memset(), strchr(), strlen() */ -#include "../common/rexit.h" /* exit_err(), exit_trouble() */ -#include "../common/try_malloc.h" /* try_malloc() */ -#include "windows.h" /* Win,yx_uint16, get_win_by_id(),get_win_pos_in_order() */ -#include "world.h" /* global world */ - - - -/* Get Win before window identified by "c" or NULL if there is none. */ -static struct Win * get_win_before(char c); - -/* Make .v_screen just wide enough to contain all visible windows. */ -static void refit_v_screen(); - -/* Update geometry (sizes, positions) of window "w" and its successors in the - * window chain. Use place_win() for the positioning algorithm. - */ -static void update_wins(struct Win * w); -static void place_win(struct Win * w); - -/* Write "win"'s size back to .target_(height/width) as per .target_*_type. */ -static void set_win_target_size(struct Win * win); - -/* Append/suspend window "w" to/from chain of visible windows. Appended windows - * will become active. Suspended active windows will move the active window - * selection to their successor in the window chain or, failing that, their - * predecessor, or, failing that, to 0 (no window active). - */ -static void append_win(struct Win * w); -static void suspend_win(struct Win * w); - - - -extern struct Win * get_win_before(char c) -{ - uint8_t i = get_win_pos_in_order(c); - if (i > 0) - { - return get_win_by_id(world.winDB.order[i - 1]); - } - return NULL; -} - - - -static void refit_v_screen() -{ - /* Determine rightmost window column. */ - uint32_t lastwcol = 0; - struct Win * wp = get_win_by_id(world.winDB.order[0]); - while (wp != 0) - { - if ((uint32_t) wp->start.x + (uint32_t) wp->frame_size.x > lastwcol + 1) - { - lastwcol = (uint32_t) wp->start.x + (uint32_t) wp->frame_size.x - 1; - } - wp = get_win_after(wp->id); - } - - /* Only resize .v_screen if the rightmost window column has changed. */ - char * err_s = "refit_v_screen() grows virtual screen beyond legal sizes."; - char * err_m = "refit_v_screen() triggers memory alloc error in wresize()."; - if ((uint32_t) getmaxx(world.winDB.v_screen) + 1 != lastwcol) - { - uint8_t t = (lastwcol + 2 > UINT16_MAX); - exit_err(t, err_s); - t = wresize(world.winDB.v_screen, getmaxy(world.winDB.v_screen), - lastwcol + 2); - exit_err(t, err_m); - } -} - - - -static void update_wins(struct Win * w) -{ - place_win(w); - refit_v_screen(); - struct Win * next = get_win_after(w->id); - if (next) - { - update_wins(next); - } -} - - - -static void place_win(struct Win * w) -{ - uint8_t sep = 1; /* Width of inter-window borders and title bars. */ - - /* If w is first window, it goes into the top left corner. */ - w->start.x = 0; - w->start.y = 0 + sep; - struct Win * w_prev = get_win_before(w->id); - if (w_prev) - { - - /* If not, get w's next predecessor starting a new stack on the screen - * top, fit w's top left corner to that predecessor's top right corner. - */ - struct Win * w_top = w_prev; - for (; w_top->start.y != 0 + sep; w_top = get_win_before(w_top->id)); - w->start.x = w_top->start.x + w_top->frame_size.x + sep; - - /* If enough space is found below w's predecessor, fit w's top left - * corner to that predecessor's bottom left corner. - */ - uint16_t next_free_y = w_prev->start.y + w_prev->frame_size.y + sep; - if ( w->frame_size.x <= w_prev->frame_size.x - && w->frame_size.y <= world.winDB.v_screen_size.y - next_free_y) - { - w->start.x = w_prev->start.x; - w->start.y = next_free_y; - return; - } - - /* If that fails, try to fit w's top left corner to the top right corner - * of its next predecessor w_test 1) below w_top (w's next predecessor - * starting a new stack on the screen top) 2) and the most rightward - * lower neighbor of a window w_high, itself throning over enough free - * space for w to fit below it, rightwards of its lower neighbor w_test. - */ - struct Win * w_test = w_prev; - struct Win * w_high; - while (w_test != w_top) - { - for (w_high = get_win_before(w_test->id); /* Walk down chain */ - w_test->start.y <= w_high->start.y; /* until w_high starts */ - w_high = get_win_before(w_high->id)); /* higher than w_test. */ - next_free_y = w_high->start.y + w_high->frame_size.y + sep; - uint16_t first_free_x = w_test->start.x + w_test->frame_size.x +sep; - uint16_t last_free_x = w_high->start.x + w_high->frame_size.x; - if ( w->frame_size.y <= world.winDB.v_screen_size.y - next_free_y - && w->frame_size.x <= last_free_x - first_free_x) - { - w->start.x = first_free_x; - w->start.y = next_free_y; - break; - } - w_test = w_high; - } - } -} - - - -static void set_win_target_size(struct Win * w) -{ - if (0 == w->target_height_type) - { - w->target_height = w->frame_size.y; - } - else if (1 == w->target_height_type) - { - w->target_height = w->frame_size.y - world.winDB.v_screen_size.y +1; - } - if (0 == w->target_width_type) - { - w->target_width = w->frame_size.x; - } - else if (1 == w->target_width_type) - { - w->target_width = w->frame_size.x - world.winDB.v_screen_size.x; - } -} - - - -static void append_win(struct Win * w) -{ - uint8_t old_size = strlen(world.winDB.order) + 1; - char * new_order = try_malloc(old_size + 1, __func__); - memcpy(new_order, world.winDB.order, old_size - 1); - new_order[old_size - 1] = w->id; - new_order[old_size] = '\0'; - free(world.winDB.order); - world.winDB.order = new_order; - world.winDB.active = w->id; - update_wins(w); -} - - - -static void suspend_win(struct Win * w) -{ - uint8_t new_size = strlen(world.winDB.order); - char * new_order = try_malloc(new_size, __func__); - uint8_t i = get_win_pos_in_order(w->id); - char next_char = world.winDB.order[i + 1]; - world.winDB.order[i] = '\0'; - char * second_part = &world.winDB.order[i + 1]; - int test = sprintf(new_order, "%s%s", world.winDB.order, second_part); - exit_trouble(test < 0, __func__, "sprintf"); - free(world.winDB.order); - world.winDB.order = new_order; - world.winDB.active = world.winDB.order[i]; - if (!world.winDB.order[i] && 0 < i) - { - world.winDB.active = world.winDB.order[i - 1]; - } - if (world.winDB.order[i]) - { - update_wins(get_win_by_id(next_char)); /* Already calls */ - return; /* refit_v_screen(), so leave. */ - } - refit_v_screen(); -} - - - -extern void toggle_window(char id) -{ - struct Win * win = get_win_by_id(id); - if (!strchr(world.winDB.order, id)) - { - append_win(win); - return; - } - suspend_win(win); -} - - - -extern void toggle_winconfig() -{ - if (!world.winDB.active) - { - return; - } - struct Win * w = get_win_by_id(world.winDB.active); - if (0 == w->view) - { - w->view = 1; - w->target_center = w->center; - memset(&w->center, 0, sizeof(struct yx_uint16)); - return; - } - else if (1 == w->view) - { - w->view = 2; - w->center.x = 0; - return; - } - w->view = 0; - w->center = w->target_center; -} - - - -extern void toggle_win_size_type(char axis) -{ - struct Win * w = get_win_by_id(world.winDB.active); - if ('y' == axis) - { - w->target_height_type = (0 == w->target_height_type); - set_win_target_size(w); - return; - } - w->target_width_type = ( 0 == w->target_width_type - && w->frame_size.x <= world.winDB.v_screen_size.x); - set_win_target_size(w); -} - - - -extern void toggle_linebreak_type() -{ - struct Win * w = get_win_by_id(world.winDB.active); - if (0 == w->linebreak) - { - w->linebreak = 1; - } - else if (1 == w->linebreak) - { - w->linebreak = 2; - } - else if (2 == w->linebreak) - { - w->linebreak = 0; - } -} - - - -extern void resize_active_win(char change) -{ - if (world.winDB.active) - { - struct Win * w = get_win_by_id(world.winDB.active); - if (change == '-' && w->frame_size.y > 1) - { - w->frame_size.y--; - } - else if (change == '_' && w->frame_size.x > 1) - { - w->frame_size.x--; - } - else if ( change == '+' - && w->frame_size.y < world.winDB.v_screen_size.y - 1) - { - w->frame_size.y++; - } - else if (change == '*' && w->frame_size.y < UINT16_MAX) - { - w->frame_size.x++; - } - if ( 1 == w->target_width_type - && w->frame_size.x > world.winDB.v_screen_size.x) - { - w->target_width_type = 0; - } - set_win_target_size(w); - update_wins(w); - } -} - - - -extern void shift_active_win(char dir) -{ - uint8_t len_order = strlen(world.winDB.order); - if (1 < len_order) - { - char * tmp = try_malloc(len_order + 1, __func__); - tmp[len_order] = '\0'; - uint8_t pos = get_win_pos_in_order(world.winDB.active); - if ('f' == dir) - { - if (pos == len_order - 1) - { - memcpy(tmp + 1, world.winDB.order, len_order - 1); - tmp[0] = world.winDB.active; - memcpy(world.winDB.order, tmp, len_order + 1); - } - else - { - world.winDB.order[pos] = world.winDB.order[pos + 1]; - world.winDB.order[pos + 1] = world.winDB.active; - } - } - else - { - if (pos == 0) - { - memcpy(tmp, world.winDB.order + 1, len_order - 1); - tmp[len_order - 1] = world.winDB.active; - memcpy(world.winDB.order, tmp, len_order + 1); - } - else - { - world.winDB.order[pos] = world.winDB.order[pos - 1]; - world.winDB.order[pos - 1] = world.winDB.active; - } - } - free(tmp); - update_wins(get_win_by_id(world.winDB.order[0])); - } -} - - - -extern void scroll_v_screen(char dir) -{ - if ( '+' == dir - && world.winDB.v_screen_offset + world.winDB.v_screen_size.x + 1 - < getmaxx(world.winDB.v_screen)) - { - world.winDB.v_screen_offset++; - } - else if ( '-' == dir - && world.winDB.v_screen_offset > 0) - { - world.winDB.v_screen_offset--; - } -} - - - -extern void cycle_active_win(char dir) -{ - uint8_t len_order = strlen(world.winDB.order); - if (1 < len_order) - { - uint8_t pos = get_win_pos_in_order(world.winDB.active); - if ('f' == dir) - { - world.winDB.active = world.winDB.order[pos + 1]; - if ('\0' == world.winDB.active) - { - world.winDB.active = world.winDB.order[0]; - } - return; - } - if (pos > 0) - { - world.winDB.active = world.winDB.order[pos - 1]; - return; - } - world.winDB.active = world.winDB.order[len_order - 1]; - } -} diff --git a/src/client/wincontrol.h b/src/client/wincontrol.h deleted file mode 100644 index 417d09f..0000000 --- a/src/client/wincontrol.h +++ /dev/null @@ -1,66 +0,0 @@ -/* src/client/wincontrol.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Window manipulation functions directly called by user actions. - */ - -#ifndef WINCONTROL_H -#define WINCONTROL_H - - - -/* Toggle display of a window of "id". */ -extern void toggle_window(char id); - -/* Toggle "window configuration" view for active window. Sets sensible .center - * values for each configuration view (for winconf_geometry: y=0, x=0; for - * winconf_keys: x=0 (y is set by draw_winconf_keybindings()); stores default - * view's .center in .target_center to return to it when toggling back. - */ -extern void toggle_winconfig(); - -/* Toggle active window's .target_(height/width)_type ("axis" = "y": height; - * else: width). Don't toggle to .target_width_type of 1 (saving the width as a - * diff to the .t_screen's width) if window's width is larger than .t_screen's - * width, for such width is better saved directly with .target_width_type of 0. - */ -extern void toggle_win_size_type(char axis); - -/* Toggle window's line break type. (0: line breaks only at newlines; 1: - * linebreaks at newlines and when the text hits the right window border; 2: - * linebreaks only when the text hits the right window border, newlines are - * replaced by another string). - */ -extern void toggle_linebreak_type(); - -/* Grow or shrink active window horizontally ("change" = "*"/"_") or vertically - * ("change" = "+"/"-") if the new size was at least 1x1, the height at least - * one cell smaller than .v_screen's vertical hight (to provide space for the - * title bar) and the width max. (2^16) - 1 cells. If a new window width would - * surpass that of .t_screen, set active window's .target_width_type to 0. - */ -extern void resize_active_win(char c); - -/* Move active window forwards ("dir" == "f") or backwards (any other "dir") in - * window chain. Wrap around in the window chain if start / end of it is met. - */ -extern void shift_active_win(char dir); - -/* Sroll .v_screen one cell to the left if "dir" is "-" and .v_screen_offset is - * more than 1, or to the right if "dir" is "+" and .v_screen's right edge would - * not move (further, if suspension of windows has moved it to the left already) - * leftwards to .t_screen's right edge. - */ -extern void scroll_v_screen(char dir); - -/* Cycle active window selection forwards ("dir" == "f") or backwards (any - * other "dir"). Wrap around in the windows chain if start / end of it is met. - */ -extern void cycle_active_win(char dir); - - - -#endif diff --git a/src/client/windows.c b/src/client/windows.c deleted file mode 100644 index fa62684..0000000 --- a/src/client/windows.c +++ /dev/null @@ -1,501 +0,0 @@ -/* src/client/windows.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#define _POSIX_C_SOURCE 200809L /* strnlen() */ -#include "windows.h" -#include /* chtype, getmaxx(), getmaxy(), erase(), werase(), - * endwin(), delwin(), wnoutrefresh(), pnoutrefresh(), - * doupdate(), refresh(), delwin(), newpad(), mvwaddch(), - * mvwaddstr() - */ -#include /* NULL */ -#include /* uint8_t, uint16_t, uint32_t, UINT16_MAX */ -#include /* sprintf() */ -#include /* free() */ -#include /* memcpy(), strlen(), strnlen(), memset() */ -#include "../common/rexit.h" /* exit_trouble(), exit_err() */ -#include "../common/try_malloc.h" /* try_malloc() */ -#include "draw_wins.h" /* draw_winconf_geometry(), draw_winconf_keybindings(), - * draw_win_inventory(), draw_win_info(), draw_win_log(), - * draw_win_keybindings_winconf_keybindings(), - * draw_win_keybindings_winconf_geometry(), - * draw_win_keybindings_global(), draw_win_map(), - * draw_win_terrain_stack() - */ -#include "wincontrol.h" /* toggle_window() */ -#include "world.h" /* world */ - - - -/* Calculate "id"'s window's size from v_screen size and .target_(height/width). - * A .target_width == 0 makes the window as wide as .t_screen. .target_height == - * 0 sets the maximum allowed height: one cell smaller than that of .v_screen. - * Negative values make width/height so many cells smaller than what 0 would - * set. Values that would reduce the window height or width to less than 1 cell - * according to the aforementioned rules set the height/width as if they were 0. - */ -static void init_win_size_from_winconf_and_v_screen_size(char id); - -/* Get (Win->draw) function identified by "c"; NULL if c not mapped to one. - * match_func() is just a little helper to it. - */ -static uint8_t match_func(char c, void (** f) (), char c_m, void (* f_m) ()); -static void (* get_drawfunc_by_char(char c)) (); - -/* Iterate over chars of world.winDB.ids array / string. Restart after \0.*/ -static char get_next_win_id(); - -/* Draw scroll hint (a line saying that there are "dist" more elements of "unit" - * further into the direction symbolized by "dir") into .v_screen, onto an - * appropriate edge of a window or .v_screen; the left/right edge if "dir" is - * "<"/">", or the top/bottom edge if it is "^"/"v". Use "start" as the start - * coordinate of the frame that is to contain the scroll hints. winscroll_hint() - * is a wrapper that uses the border of window "w" as this frame. - */ -static void scroll_hint(struct yx_uint16 fsize, char dir, uint16_t dist, - char * unit, struct yx_uint16 start); -static void winscroll_hint(struct Win * w, char dir, uint16_t dist); - -/* draw_win_borderlines() draws vertical/horizontal borders of window "w" sans - * corners into .v_screen. It draws the top border line as the windows' title - * bar (highlighted if the window is selected as active). It is called - * recursively by draw_wins_borderlines() on all windows from "w" on. - * draw_wins_bordercorners() draws the border corners of "w" and its successors. - */ -static void draw_win_borderlines(struct Win * w); -static void draw_wins_borderlines(struct Win * w); -static void draw_wins_bordercorners(struct Win * w); - -/* Draw contents of all windows in window chain from window "w" onwards. */ -static void draw_wins(struct Win * w); - - - -static void init_win_size_from_winconf_and_v_screen_size(char id) -{ - struct Win * w = get_win_by_id(id); - w->frame_size.y = world.winDB.v_screen_size.y - 1; - if ( 0 < w->target_height - && w->target_height <= world.winDB.v_screen_size.y - 1) - { - w->frame_size.y = w->target_height; - } - else if ( 0 > w->target_height - && world.winDB.v_screen_size.y + (w->target_height - 1) > 0) - { - w->frame_size.y = world.winDB.v_screen_size.y + (w->target_height - 1); - } - w->frame_size.x = world.winDB.v_screen_size.x; - if (0 < w->target_width) - { - w->frame_size.x = w->target_width; - } - else if ( 0 > w->target_width - && world.winDB.v_screen_size.x + w->target_width > 0) - { - w->frame_size.x = world.winDB.v_screen_size.x + w->target_width; - } -} - - - -static uint8_t match_func(char c, void (** f) (), char c_m, void (* f_m) ()) -{ - if (c == c_m) - { - * f = f_m; - return 1; - } - return 0; -} - - - -static void (* get_drawfunc_by_char(char c)) () -{ - void (* f) (struct Win *) = NULL; - if ( match_func(c, &f, 'c', draw_win_inventory) - || match_func(c, &f, 's', draw_win_terrain_stack) - || match_func(c, &f, 'i', draw_win_info) - || match_func(c, &f, 'l', draw_win_log) - || match_func(c, &f, 'm', draw_win_map) - || match_func(c, &f, '0', draw_win_keybindings_global) - || match_func(c, &f, '1', draw_win_keybindings_winconf_geometry) - || match_func(c, &f, '2', draw_win_keybindings_winconf_keybindings)) - { - ; - } - return f; -} - - - -static char get_next_win_id() -{ - static uint8_t i = 0; - char c = world.winDB.ids[i]; - if (0 == c) - { - i = 0; - return c; - } - i++; - return c; -} - - - -static void scroll_hint(struct yx_uint16 fsize, char dir, uint16_t dist, - char * unit, struct yx_uint16 start) -{ - /* Decide on alignment (vertical/horizontal?), thereby hint text space. */ - char * more = "more"; - uint16_t dsc_space = fsize.x; - if ('<' == dir || '>' == dir) - { - dsc_space = fsize.y; - } /* vv-- 10 = max strlen for uint16_t */ - uint16_t size = 1 + strlen(more) + 1 + 10 + 1 + strlen(unit) + 1 + 1; - char * scrolldsc = try_malloc(size, __func__); - int test = sprintf(scrolldsc, " %d %s %s ", dist, more, unit); - exit_trouble(test < 0, __func__, "sprintf"); - - /* Decide on offset of the description text inside the scroll hint line. */ - uint16_t dsc_offset = 1; - if (dsc_space > strlen(scrolldsc) + 1) - { - dsc_offset = (dsc_space - strlen(scrolldsc)) / 2; - } - - /* Draw scroll hint line as dir symbols bracketing description text. */ - uint16_t draw_offset = 0; - if ('>' == dir) - { - draw_offset = fsize.x - 1; - } - else if ('v' == dir) - { - draw_offset = fsize.y - 1; - } - uint16_t q = 0; - for (; q < dsc_space; q++) - { - chtype c = dir | A_REVERSE; - if (q >= dsc_offset && q < strlen(scrolldsc) + dsc_offset) - { - c = scrolldsc[q - dsc_offset] | A_REVERSE; - } - if ('<' == dir || '>' == dir) - { - mvwaddch(world.winDB.v_screen, start.y+q, start.x+draw_offset, c); - continue; - } - mvwaddch(world.winDB.v_screen, start.y + draw_offset, start.x + q, c); - } - free(scrolldsc); -} - - - -static void winscroll_hint(struct Win * w, char dir, uint16_t dist) -{ - char * unit = "lines"; - if ('<' == dir || '>' == dir) - { - unit = "columns"; - } - struct yx_uint16 start = w->start; - scroll_hint(w->frame_size, dir, dist, unit, start); -} - - - -static void draw_win_borderlines(struct Win * w) -{ - /* Draw vertical and horizontal border lines. */ - uint16_t y, x; - for (y = w->start.y; y <= w->start.y + w->frame_size.y; y++) - { - mvwaddch(world.winDB.v_screen, y, w->start.x - 1, '|'); - mvwaddch(world.winDB.v_screen, y, w->start.x + w->frame_size.x, '|'); - } - for (x = w->start.x; x <= w->start.x + w->frame_size.x; x++) - { - mvwaddch(world.winDB.v_screen, w->start.y - 1, x, '-'); - mvwaddch(world.winDB.v_screen, w->start.y + w->frame_size.y, x, '-'); - } - - /* Draw as much as possible of the title into center of top border line. */ - uint8_t min_title_length_visible = 3;/* min. 1 char +2 padding/decoration*/ - if (w->frame_size.x >= min_title_length_visible) - { - uint16_t offset = 0; - if (w->frame_size.x > strlen(w->title) + 2) - { - offset = (w->frame_size.x - (strlen(w->title) + 2)) / 2; - } /* +2 is for padding/decoration */ - uint16_t length_visible = strnlen(w->title, w->frame_size.x - 2); - char * title = try_malloc(length_visible + 3, "draw_win_borderlines"); - char decoration = ' '; - if (w->id == world.winDB.active) - { - decoration = '$'; - } - memcpy(title + 1, w->title, length_visible); - title[0] = title[length_visible + 1] = decoration; - title[length_visible + 2] = '\0'; - mvwaddstr(world.winDB.v_screen, w->start.y-1, w->start.x+offset, title); - free(title); - } -} - - - -static void draw_wins_borderlines(struct Win * w) -{ - draw_win_borderlines(w); - struct Win * next = get_win_after(w->id); - if (next) - { - draw_wins_borderlines(next); - } -} - - - -static void draw_wins_bordercorners(struct Win * w) -{ - mvwaddch(world.winDB.v_screen, - w->start.y - 1, w->start.x - 1, '+'); - mvwaddch(world.winDB.v_screen, - w->start.y - 1, w->start.x + w->frame_size.x, '+'); - mvwaddch(world.winDB.v_screen, - w->start.y + w->frame_size.y, w->start.x - 1, '+'); - mvwaddch(world.winDB.v_screen, w->start.y + w->frame_size.y, - w->start.x + w->frame_size.x, '+'); - struct Win * next = get_win_after(w->id); - if (next) - { - draw_wins_bordercorners(next); - } -} - - - -static void draw_wins(struct Win * w) -{ - void (* drawfunc) (struct Win *) = get_drawfunc_by_char(w->id); - if (1 == w->view) - { - drawfunc = draw_winconf_geometry; - } - else if (2 == w->view) - { - drawfunc = draw_winconf_keybindings; - } - drawfunc(w); - uint16_t size_y = w->winmap_size.y; - uint16_t size_x = w->winmap_size.x; - uint16_t offset_y = center_offset(w->center.y, size_y, w->frame_size.y); - uint16_t offset_x = center_offset(w->center.x, size_x, w->frame_size.x); - uint16_t y, x; - for (y = offset_y; y < w->frame_size.y + offset_y && y < size_y; y++) - { - for (x = offset_x; x < w->frame_size.x + offset_x && x < size_x; x++) - { - chtype ch = w->winmap[(y * w->winmap_size.x) + x]; - mvwaddch(world.winDB.v_screen, w->start.y + (y - offset_y), - w->start.x + (x - offset_x), ch); - } - } - free(w->winmap); /* NULL so draw_wins.c's try_resize_winmap() may always */ - w->winmap = NULL;/* free() it before (re-)allocation, even the first time.*/ - memset(&w->winmap_size, 0, sizeof(struct yx_uint16)); - if (offset_y > 0) - { - winscroll_hint(w, '^', offset_y + 1); - } - if (size_y > offset_y + w->frame_size.y) - { - winscroll_hint(w, 'v', size_y - ((offset_y + w->frame_size.y) - 1)); - } - if (offset_x > 0) - { - winscroll_hint(w, '<', offset_x + 1); - } - if (size_x > offset_x + w->frame_size.x) - { - winscroll_hint(w, '>', size_x - ((offset_x + w->frame_size.x) - 1)); - } - struct Win * next = get_win_after(w->id); - if (next) - { - draw_wins(next); - } -} - - - -extern uint8_t get_win_pos_in_order(char c) -{ - uint8_t i; - for (i = 0; c != world.winDB.order[i]; i++); - return i; -} - - - -extern struct Win * get_win_after(char c) -{ - return get_win_by_id(world.winDB.order[get_win_pos_in_order(c) + 1]); -} - - - -extern uint16_t center_offset(uint16_t position, uint32_t mapsize, - uint32_t frame_size) -{ - uint16_t offset = 0; - if (mapsize > frame_size) - { - if (position > frame_size / 2) - { - if (position < mapsize - (frame_size / 2)) - { - offset = position - (frame_size / 2); - } - else - { - offset = mapsize - frame_size; - } - } - } - return offset; -} - - - -extern struct Win * get_win_by_id(char id) -{ - uint8_t i = 0; - while ('\0' != world.winDB.ids[i]) - { - if (id == world.winDB.ids[i]) - { - return &world.winDB.wins[i]; - } - i++; - } - return NULL; -} - - - -extern void make_v_screen_and_init_win_sizes() -{ - char * err_s = "creating an illegaly large virtual screen"; - char * err_m = "triggering a memory allocation error via newpad"; - uint32_t maxy_test = getmaxy(world.winDB.t_screen); - uint32_t maxx_test = getmaxx(world.winDB.t_screen); - exit_trouble(maxy_test>UINT16_MAX || maxx_test>UINT16_MAX, __func__, err_s); - world.winDB.v_screen_size.y = maxy_test; - world.winDB.v_screen_size.x = maxx_test; - world.winDB.v_screen = newpad(world.winDB.v_screen_size.y, 1); - exit_trouble(!world.winDB.v_screen, __func__, err_m); - char id; - while (0 != (id = get_next_win_id())) - { - init_win_size_from_winconf_and_v_screen_size(id); - } -} - - - -extern void free_winDB() -{ - char id; - while (0 != (id = get_next_win_id())) - { - struct Win * wc = get_win_by_id(id); - free(wc->title); - free(wc->kb.kbs); - } - free(world.winDB.ids); - free(world.winDB.wins); - free(world.winDB.order); -} - - - -extern void winch_called() -{ - world.winch = 1; -} - - - -extern void reset_windows_on_winch() -{ - endwin(); /* "[S]tandard way" to recalibrate ncurses post SIGWINCH, says */ - refresh(); /* . */ - char * tmp_order = try_malloc(strlen(world.winDB.order) + 1, __func__); - int test = sprintf(tmp_order, "%s", world.winDB.order); - exit_trouble(test < 0, __func__, "sprintf"); - uint8_t i; - char tmp_active = world.winDB.active; - for (i = 0; i < strlen(tmp_order); toggle_window(tmp_order[i]), i++); - delwin(world.winDB.v_screen); - make_v_screen_and_init_win_sizes(); - for (i = 0; i < strlen(tmp_order); toggle_window(tmp_order[i]), i++); - free(tmp_order); - world.winDB.active = tmp_active; -} - - - -extern void draw_all_wins() -{ - /* Empty everything before filling it a-new. */ - erase(); - wnoutrefresh(world.winDB.t_screen); - werase(world.winDB.v_screen); - if (world.winDB.active) - { - - /* Draw borders, wins. Order matters: corners should overwrite lines. */ - draw_wins_borderlines(get_win_by_id(world.winDB.order[0])); - draw_wins_bordercorners(get_win_by_id(world.winDB.order[0])); - draw_wins(get_win_by_id(world.winDB.order[0])); - - /* Draw .v_screen scroll hints. */ - struct yx_uint16 start; - start.y = 0; - start.x = world.winDB.v_screen_offset; - char * cols_string = "columns"; - if (world.winDB.v_screen_offset > 0) - { - scroll_hint(world.winDB.v_screen_size, '<', - world.winDB.v_screen_offset + 1, cols_string, start); - } - uint16_t size_x = getmaxx(world.winDB.v_screen); - uint16_t right_edge = world.winDB.v_screen_offset - + world.winDB.v_screen_size.x; - if (right_edge < size_x - 1) - { - scroll_hint(world.winDB.v_screen_size, '>', - size_x - right_edge, cols_string, start); - } - - /* Put .v_screen segment to be shown on .t_screen to .t_screen buffer.*/ - pnoutrefresh(world.winDB.v_screen, 0, world.winDB.v_screen_offset, 0, 0, - world.winDB.v_screen_size.y, - world.winDB.v_screen_size.x - 1); - } - - /* Only at the end write accumulated changes to .t_screen. */ - doupdate(); -} diff --git a/src/client/windows.h b/src/client/windows.h deleted file mode 100644 index 91f3f7b..0000000 --- a/src/client/windows.h +++ /dev/null @@ -1,110 +0,0 @@ -/* src/client/windows.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * A tiled window manager for the terminal. - * - * It provides a virtual screen that can be scrolled horizontally and may carry - * any number of windows to be appeared, disappeared, resized and moved around. - * They have borders and a title bar and are positioned automatically. - * - * Windows can be any width between 1 and 2^16 cells. The virtual screen grows - * with them as needed -- but only horizontally and only up to 2^16 cells. Their - * height is limited by the height of the terminal screen (maximum 2^16 cells). - * - * Windows' positioning can be influenced only indirectly: by resizing them, and - * by shifting their relative position inside the (currently invisible) chain - * that the window manager treats their plurality as. The first window goes into - * the top left corner of the virtual screen. Further windows are fitted as - * left-aligned as possible below their (chain-wise) closest predecessor that - * thrones over enough space to contain them and that is open to the right. If - * that fails, they're fitted up-right to the window with the rightmost border. - * - * TODO: Think up a more intuitive window positioning algorithm. - */ - -#ifndef WINDOWS_H -#define WINDOWS_H - -#include /* WINDOW, chtype */ -#include /* uint8_t, int16_t, uint16_t, uint32_t */ -#include "keybindings.h" /* struct KeyBindingDB */ - - - -struct yx_uint16 -{ - uint16_t y; - uint16_t x; -}; - -struct WinDB -{ - WINDOW * t_screen; /* ncurses' pointer to the terminal screen */ - WINDOW * v_screen; /* virtual screen (ncurses pad) */ - struct Win * wins; /* array of windows */ - struct yx_uint16 v_screen_size; /* virtual screen size */ - char * legal_ids; /* ids allowed to be used */ - char * ids; /* all windows' ids, followed by \0 */ - char * order; /* visible windows' id's order, followed by \0 */ - uint16_t v_screen_offset; /* how many cells v_screen view moved rightwards*/ - char active; /* id of window selected as active */ -}; - -struct Win -{ - struct KeyBindingDB kb; /* window-specific keybindings */ - char * title; /* title to be used in window title bar */ - struct yx_uint16 target_center; /* saves .center when toggling .view */ - struct yx_uint16 frame_size; /* size of window/frame to see winmap through*/ - struct yx_uint16 start; /* upper left corner of window in v_screen */ - struct yx_uint16 center; /* winmap cell to center frame on if < winmap */ - struct yx_uint16 winmap_size; /* delimits .winmap, sorts it into lines */ - chtype * winmap; /* window content in sequence of chtype's to write */ - int16_t target_height; /* window size / .frame_size description in config */ - int16_t target_width; /* file format, i.e. values <= 0 may be used */ - char id; /* Win identifier; also maps to default window drawing function. */ - uint8_t target_height_type; /* 0: read .height/.width as positive size; */ - uint8_t target_width_type; /* 1: as negative diff to v_screen size */ - uint8_t linebreak; /* linebreaking modes: 0: wide; 1: long; 2: compact */ - uint8_t view; /* window view mode: 0: use .id- set default draw function */ -}; /* 1/2: use one of the two config view draw function */ - - - -/* Get position of id "c" in world.winDB.order*/ -extern uint8_t get_win_pos_in_order(char c); - -/* Get Win after window identified by "c" or NULL if there is none. */ -extern struct Win * get_win_after(char c); - -/* Return yx offset to focus map of "mapsize" on "position" in "frame_size". */ -extern uint16_t center_offset(uint16_t position, - uint32_t mapsize, uint32_t frame_size); - -/* Get Win of "id". */ -extern struct Win * get_win_by_id(char id); - -/* Builds virtual sreen from .t_screen's size, fits win's sizes to them.*/ -extern void make_v_screen_and_init_win_sizes(); - -/* Free all winDB data. */ -extern void free_winDB(); - -/* The SIGWINCH handler winch_called() merely sets world.winch to 1. This info - * is used by io_loop() to call reset_windows_on_winch(), which adapts the - * currently loaded interface configuration to the new .t_screen size. - */ -extern void winch_called(); -extern void reset_windows_on_winch(); - -/* Draw .v_screen and its windows. Add scroll hints where edges of .t_screen hit - * .non-edges inside the virtual screen. Then update .t_screen. - */ -extern void draw_all_wins(); - - - -#endif diff --git a/src/client/world.h b/src/client/world.h deleted file mode 100644 index 5681210..0000000 --- a/src/client/world.h +++ /dev/null @@ -1,58 +0,0 @@ -/* src/client/world.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Contains the World struct holding all quasi-global game data together. - */ - -#ifndef WORLD_H -#define WORLD_H - -#include /* uint8_t, uint16_t */ -#include /* FILE */ -#include /* time_t */ -#include "../common/map.h" /* struct Map */ -#include "../common/yx_uint8.h" /* struct yx_uint8 */ -#include "keybindings.h" /* stuct KeyBindingDB */ -#include "command_db.h" /* struct CommandDB */ -#include "windows.h" /* WinDB */ - - - -struct World -{ - FILE * file_server_in; /* server input file to write commands to */ - FILE * file_server_out; /* server output file to read messages from */ - struct WinDB winDB; /* data for window management and individual windows */ - struct CommandDB commandDB; /* data on commands from commands config file */ - struct KeyBindingDB kb_global; /* globally availabe keybindings */ - struct KeyBindingDB kb_wingeom; /* Win geometry config view keybindings */ - struct KeyBindingDB kb_winkeys; /* Win keybindings config view keybindings*/ - struct Map map; /* game map geometry and content of player's map view */ - time_t last_update; /* used for comparison with worldstate file's mtime */ - char * log; /* log of player's activities */ - char * things_here; /* list of things below the player */ - char * path_interface; /* path of interface configuration file */ - char * path_commands; /* path of commands config file */ - char * player_inventory; /* one-item-per-line string list of owned items */ - char * mem_map; /* map cells of player's map memory */ - char * queue; /* stores un-processed messages read from the input file */ - struct yx_uint8 player_pos; /* coordinates of player on map */ - struct yx_uint8 look_pos; /* coordinates of look cursor */ - uint16_t turn; /* world/game turn */ - int16_t player_satiation; /* player's belly fullness */ - uint8_t player_inventory_select; /* index of selected item in inventory */ - uint8_t player_lifepoints; /* how alive the player is */ - uint8_t winch; /* if set, SIGWINCH was registered; trigger reset_windows()*/ - uint8_t look; /* if set, move look cursor over map intead of player */ -}; - - - -extern struct World world; - - - -#endif diff --git a/src/common/map.h b/src/common/map.h deleted file mode 100644 index 2a37793..0000000 --- a/src/common/map.h +++ /dev/null @@ -1,25 +0,0 @@ -/* src/common/map.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Game map. - */ - -#ifndef MAP_H -#define MAP_H - -#include /* uint16_t */ - - - -struct Map -{ - char * cells; /* sequence of bytes encoding map cells */ - uint16_t length; /* map's edge length, i.e. both height and width */ -}; - - - -#endif diff --git a/src/common/parse_file.c b/src/common/parse_file.c deleted file mode 100644 index ded2d5a..0000000 --- a/src/common/parse_file.c +++ /dev/null @@ -1,232 +0,0 @@ -/* src/common/parse_file.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#define _POSIX_C_SOURCE 200809L /* strdup(), snprintf() */ -#include "parse_file.h" -#include /* size_t, NULL */ -#include /* FILE, snprintf() */ -#include /* int16_t,uint8_t,uint32_t, INT16_MIN, UINT{8,16,32}_MAX */ -#include /* atoi(), free() */ -#include /* strchr, strcmp(), strdup(), strlen() */ -#include "rexit.h" /* exit_err(), exit_trouble() */ -#include "try_malloc.h" /* try_malloc() */ - - - -/* Set by parse_file(), helps err_line() deciding what to do/output on error. */ -static uint32_t err_line_count = 0; -static char * err_line_line = NULL; -static char * err_line_intro = NULL; -static uint8_t err_line_exit = 1; - - - -/* Determines the end of the token_from_line() token. */ -static void set_token_end(char ** start, char ** limit_char); - - - -static void set_token_end(char ** start, char ** limit_char) -{ - char * end_quote = ('\'' == (* start)[0]) ? strchr(* start + 1, '\''): NULL; - * start = (end_quote) ? * start + 1 : *start; - if (end_quote) - { - * end_quote = '\0'; - * limit_char = end_quote; - return; - } - char * space = strchr(*start, ' '); - char * tab = strchr(*start, '\t'); - space = (!space || (tab && tab < space)) ? tab : space; - if (space) - { - * space = '\0'; - } - *limit_char = strchr(*start, '\0'); -} - - - -extern void set_err_line_options(char * intro, char * line, uint8_t exit) -{ - err_line_line = line; - err_line_intro = intro; - err_line_exit = exit; -} - - - -extern void err_line_inc() -{ - err_line_count++; - err_line(UINT32_MAX == err_line_count, "Line reaches max lines limit."); -} - - - -extern void err_line_zero() -{ - err_line_count = 0; -} - - - -extern uint8_t err_line(uint8_t test, char * msg) -{ - if (!test) - { - return 0; - } - char * prefix = " Offending line "; - char * affix = ": "; - size_t size = strlen(err_line_intro) + strlen(msg) + strlen(prefix) - + 10 /* strlen for uint32_t representations */ - + strlen(affix) + strlen(err_line_line) + 1; - char * err = try_malloc(size, __func__); - int ret = snprintf(err, size, "%s%s%s%d%s%s", err_line_intro, msg, prefix, - err_line_count, affix, err_line_line); - exit_trouble(ret < 0, __func__, "snprintf"); - if (err_line_exit) - { - exit_err(1, err); - } - exit_trouble(0 > printf("%s\n", err), __func__, "printf"); - exit_trouble(EOF == fflush(stdout), __func__, "fflush"); - free(err); - return 1; -} - - - -extern char * token_from_line(char * line) -{ - static char * final_char = NULL; - static char * limit_char = NULL; - char * start = limit_char + 1; - if (line) - { - start = line; - limit_char = start; - final_char = &(line[strlen(line)]); - if ('\n' == *(final_char - 1)) - { - *(--final_char) = '\0'; - } - } - if (final_char < start) - { - return NULL; - } - uint8_t empty = 1; - uint32_t i; - for (i = 0; '\0' != start[i]; i++) - { - if (' ' != start[i] && '\t' != start[i]) - { - start = &start[i]; - empty = 0; - break; - } - } - if (empty) - { - return NULL; - } - set_token_end(&start, &limit_char); - return start; -} - - - -extern uint8_t parsetest_int(char * string, char type) -{ - char * err_8 = "Value must represent proper unsigned 8 bit integer."; - char * err_i = "Value must represent proper signed 16 bit integer."; - char * err_u = "Value must represent proper unsigned 16 bit integer."; - char * err_U = "Value must represent proper unsigned 32 bit integer."; - char * err = ('8' == type) ? err_8 : err_U; - err = ('i' == type) ? err_i : err; - err = ('u' == type) ? err_u : err; - uint8_t ret = err_line(strlen(string) < 1, err); - uint8_t i; - uint8_t test; - for (i = 0; '\0' != string[i]; i++) - { - char * err_many = "Value of too many characters."; - ret = ret + err_line(string[i + 1] && UINT8_MAX == i, err_many); - test = ( (0 == i && ('-' == string[i] || '+' == string[i])) - || ('0' <= string[i] && string[i] <= '9')); - ret = ret + err_line(!test, err); - } - ret = ret + err_line( strlen(string) < 2 - && ('-' == string[i] || '+' == string[i]), err); - test = ( '8' == type - && ( strlen(string) > 4 - || atoi(string) < 0 || atoi(string) > UINT8_MAX)) - || ( 'i' == type - && ( strlen(string) > 6 - || atol(string) < INT16_MIN || atol(string) > INT16_MAX)) - || ( 'u' == type - && ( strlen(string) > 6 - || atoll(string) < 0 || atol(string) > UINT16_MAX)) - || ( 'U' == type - && ( strlen(string) > 11 - || atoll(string) < 0 || atoll(string) > UINT32_MAX)); - ret = ret + err_line(test, err); - return ret; -} - - - -extern uint8_t parsetest_singlechar(char * string) -{ - return err_line(1 !=strlen(string),"Value must be single ASCII character."); -} - - - -extern uint8_t parse_val(char * token0, char * token1, char * comparand, - char type, char * element) -{ - if (!strcmp(token0, comparand)) - { - if ('s' == type) - { - free(* (char **) element); - * (char **) element = strdup(token1); - } - else if ('c' == type) - { - if (!parsetest_singlechar(token1)) - { - *element = (token1)[0]; - } - } - else if (!parsetest_int(token1, type)) - { - if ('8' == type) - { - * (uint8_t *) element = atoi(token1); - } - else if ('i' == type) - { - * (int16_t *) element = atoi(token1); - } - else if ('u' == type) - { - * (uint16_t *) element = atol(token1); - } - else if ('U' == type) - { - * (uint32_t *) element = atoll(token1); - } - } - return 1; - } - return 0; -} diff --git a/src/common/parse_file.h b/src/common/parse_file.h deleted file mode 100644 index e0e3c7a..0000000 --- a/src/common/parse_file.h +++ /dev/null @@ -1,59 +0,0 @@ -/* src/common/parse_file.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Tools for parsing files. - */ - -#ifndef PARSE_FILE_H -#define PARSE_FILE_H - -#include /* uint8_t */ - - - -/* Set err_line() options: "intro" message, char array used to store analyzed - * lines ("line"), and whether to "exit" on error message. - */ -extern void set_err_line_options(char * intro, char * line, uint8_t exit); - -/* Increment and reset (to zero) err_line() line counter. */ -extern void err_line_inc(); -extern void err_line_zero(); - -/* If "test", output "msg", faulty line, its number and exit if so defined by - * set_err_line_options(), else return 1 on "test" and 0 if "test" is 0. - */ -extern uint8_t err_line(uint8_t test, char * msg); - -/* Return next token from "line", or NULL if none is found. Tokens either a) - * start at the first non-whitespace character and end before the next - * whitespace character after that; or b) if the first non-whitespace character - * is a single quote followed by at least one other single quote some time later - * on the line, the token starts after that first single quote and ends before - * the second, with the next token_from_line() call starting its token search - * after that second quote. The only way to return an empty string (instead of - * NULL) as a token is to delimit the token by two succeeding single quotes. - */ -extern char * token_from_line(char * line); - -/* Test for "string" to represent proper int16 (type: "i"), uint8 ("8"), uint16 - * ("u") or uint32 ("U"). Returns 0 if proper value, else >0. - */ -extern uint8_t parsetest_int(char * string, char type); - -/* Test for "string" to be of length 1 (excluding "\0"). Return 1 on failure. */ -extern uint8_t parsetest_singlechar(char * string); - -/* If "token0" fits "comparand", set "element" to value read from "token1" as - * string (type: "s"), char ("c") uint8 ("8"), uint16 ("u"), uint32 ("U") or - * int16 ("i"), and return 1; else 0. - */ -extern uint8_t parse_val(char * token0, char * token1, char * comparand, - char type, char * element); - - - -#endif diff --git a/src/common/readwrite.c b/src/common/readwrite.c deleted file mode 100644 index 94150f1..0000000 --- a/src/common/readwrite.c +++ /dev/null @@ -1,236 +0,0 @@ -/* src/common/readwrite.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#include "readwrite.h" -#include /* NULL, size_t */ -#include /* uint8_t, uint16_t, uint32_t, UINT32_MAX */ -#include /* FILE, fseek(), sprintf(), fgets(), fgetc(), ferror(), - * fputc(), fwrite(), fclose(), fopen(), clearerr() - */ -#include /* free() */ -#include /* strlen(), memcpy(), strchr() */ -#include /* access(), unlink() */ -#include "rexit.h" /* exit_err(), exit_trouble() */ -#include "try_malloc.h" /* try_malloc() */ - - - -extern FILE * try_fopen(char * path, char * mode, const char * f) -{ - char * msg1 = "Trouble in "; - char * msg2 = " with fopen (mode '"; - char * msg3 = "') on path '"; - char * msg4 = "'."; - uint16_t size = strlen(msg1) + strlen(msg2) + strlen(msg3) + strlen(msg4) - + strlen(f) + strlen(path) + strlen(mode) + 1; - char * msg = try_malloc(size, __func__); - int test = sprintf(msg, "%s%s%s%s%s%s%s", msg1,f,msg2,mode,msg3,path,msg4); - exit_trouble(test < 0, __func__, "sprintf"); - FILE * file_p = fopen(path, mode); - exit_err(!file_p, msg); - free(msg); - return file_p; -} - - - -extern void try_fclose(FILE * file, const char * f) -{ - exit_trouble(fclose(file), f, "fclose"); -} - - - -extern void try_fwrite(void * ptr, size_t size, size_t nmemb, FILE * stream, - const char * f) -{ - exit_trouble(0 == fwrite(ptr, size, nmemb, stream), f, "fwrite"); -} - - - -extern void try_fputc(uint8_t c, FILE * file, const char * f) -{ - exit_trouble(EOF == fputc(c, file), f, "fputc"); -} - - - -extern int try_fgetc(FILE * file, const char * f) -{ - clearerr(file); /* OSX' (BSD?) fgetc() needs this to undo previous EOFs. */ - int test = fgetc(file); - exit_trouble(EOF == test && ferror(file), f, "fgetc"); - return test; -} - - - -extern char * try_fgets(char * line, int linemax, FILE * file, const char * f) -{ - char * test = fgets(line, linemax, file); - exit_trouble(!test && ferror(file), f, "fgets"); - return test; -} - - - -extern char * build_temp_path(char * path) -{ - char * suffix_tmp = "_tmp"; - uint16_t size = strlen(path) + strlen(suffix_tmp) + 1; - char * path_tmp = try_malloc(size, __func__); - int test = sprintf(path_tmp, "%s%s", path, suffix_tmp); - exit_trouble(test < 0, __func__, "sprintf"); - return path_tmp; -} - - - -extern FILE * atomic_write_start(char * path, char ** path_tmp) -{ - *path_tmp = build_temp_path(path); - return try_fopen(*path_tmp, "w", __func__); -} - - - -extern void atomic_write_finish(FILE * file, char * path, char * path_tmp) -{ - try_fclose(file, __func__); - char * msg1 = "Trouble in "; - char * msg4 = "'."; - if (!access(path, F_OK)) - { - char * msg2 = " with unlink on path '"; - uint16_t size = strlen(msg1) + strlen(msg2) + strlen(msg4) - + strlen(__func__) + strlen(path) + 1; - char * msg = try_malloc(size, __func__); - int test = sprintf(msg, "%s%s%s%s%s", msg1, __func__, msg2, path, msg4); - exit_trouble(test < 0, __func__, "sprintf"); - exit_err(unlink(path), msg); - free(msg); - } - char * msg2 = " with rename from '"; - char * msg3 = "' to '"; - uint16_t size = strlen(msg1) + strlen(__func__) + strlen(msg2) + - + strlen(path_tmp) + strlen(msg3) + strlen(path) - + strlen(msg4) + 1; - char * msg = try_malloc(size, __func__); - int test = sprintf(msg, "%s%s%s%s%s%s%s", - msg1, __func__, msg2, path_tmp, msg3, path, msg4); - exit_trouble(test < 0, __func__, "sprintf"); - exit_err(rename(path_tmp, path), msg); - free(msg); - free(path_tmp); -} - - - -extern void detect_atomic_leftover(char * path) -{ - char * path_tmp = build_temp_path(path); - char * part1 = "Found file '"; - char * part2 = "' that may be a leftover from an aborted previous attempt " - "to write '"; - char * part3 = "'. Aborting until the matter is solved by (re-)moving it."; - uint32_t size = strlen(part1) + strlen(path_tmp) + strlen(part2) - + strlen(path) + strlen(part3) + 1; - char * msg = try_malloc(size, __func__); - int test = sprintf(msg, "%s%s%s%s%s", part1, path_tmp, part2, path, part3); - exit_trouble(test < 0, __func__, "sprintf"); - exit_err(!access(path_tmp, F_OK), msg); - free(msg); - free(path_tmp); -} - - - -extern uint32_t textfile_width(FILE * file) -{ - int c = 0; - uint32_t c_count = 0; - uint32_t linemax = 0; - while (1) - { - c = try_fgetc(file, __func__); - if (EOF == c) - { - break; - } - c_count++; - exit_trouble(UINT32_MAX==c_count, __func__, "too large text file line"); - if ('\n' == c) - { - if (c_count > linemax) - { - linemax = c_count; - } - c_count = 0; - } - } - if (0 == linemax && 0 < c_count) /* Handle files that consist of only one */ - { /* line / lack newline chars. */ - linemax = c_count; - } - exit_trouble(-1 == fseek(file, 0, SEEK_SET), __func__, "fseek"); - return linemax; -} - - - -extern uint8_t read_file_into_queue(FILE * file, char ** queue) -{ - uint8_t ret = 0; - int test; - while (EOF != (test = try_fgetc(file, __func__))) - { - ret = 1; - if ('\0' != test) - { - if (*queue) - { - char * new_queue = try_malloc(strlen(*queue) + 1 + 1, __func__); - memcpy(new_queue, *queue, strlen(*queue)); - new_queue[strlen(*queue)] = (char) test; - new_queue[strlen(*queue) + 1] = '\0'; - free(*queue); - *queue = new_queue; - } - else - { - *queue = try_malloc(1 + 1, __func__); - (*queue)[0] = (char) test; - (*queue)[1] = '\0'; - } - } - } - return ret; -} - - - -extern char * get_message_from_queue(char ** queue) -{ - if (!(*queue)) - { - return NULL; - } - char * first_nl = strchr(*queue, '\n'); - if (!first_nl) - { - return NULL; - } - char * msg = try_malloc(first_nl - (*queue) + 1, __func__); - memcpy(msg, *queue, first_nl - (*queue)); - msg[first_nl - (*queue)] = '\0'; - char * new_queue = try_malloc(strlen(first_nl + 1) + 1, __func__); - memcpy(new_queue, first_nl + 1, strlen(first_nl + 1) + 1); - free(*queue); - *queue = new_queue; - return msg; -} diff --git a/src/common/readwrite.h b/src/common/readwrite.h deleted file mode 100644 index ce7b749..0000000 --- a/src/common/readwrite.h +++ /dev/null @@ -1,66 +0,0 @@ -/* src/common/readwrite.h: - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Routines for reading and writing files. - */ - -#ifndef READWRITE_H -#define READWRITE_H - -#include /* uint8_t, uint32_t */ -#include /* FILE */ - - - -/* Wrappers to fopen(), fclose(), fgets() and fwrite() from function called "f", - * calling exit_err() upon error with appropriate error messages. - */ -extern FILE * try_fopen(char * path, char * mode, const char * f); -extern void try_fclose(FILE * file, const char * f); -extern void try_fwrite(void * ptr, size_t size, size_t nmemb, FILE * stream, - const char * f); -extern void try_fputc(uint8_t c, FILE * file, const char * f); - -/* Wrapper to calling fgetc() and fgets() from function "f". The return code is - * returned unless ferror() indicates an error (i.e. to signify an end of file, - * fgetc() may return an EOF and fgets() a NULL). try_fgetc() calls clearerr() - * on "file" before fgetc(), because some Unixes fgetc() remember old EOFs and - * only return those until explicitely cleared. - */ -extern int try_fgetc(FILE * file, const char * f); -extern char * try_fgets(char * line, int size, FILE * file, const char * f); - -/* Return "path" + suffix "_tmp". Value is malloc'd, must be freed externally.*/ -extern char * build_temp_path(char * path); - -/* Write to "path_tmp" "path" + "_tmp" and return a new file at that "path_tmp" - * open for writing. "path_tmp" is malloc()'d, must be freed externally. -*/ -extern FILE * atomic_write_start(char * path, char ** path_tmp); - -/* Finish atomic writing started in atomic_write_start(). Wraps successive calls - * of fclose() on "file", then unlink() on file at path "path" if it exists, - * then rename() from "path_tmp" to "path", then free() on "path_tmp". - */ -extern void atomic_write_finish(FILE * file, char * path, char * path_tmp); - -/* Check for temp file leftover of atomic writing of "path", abort if found. */ -extern void detect_atomic_leftover(char * path); - -/* Return largest line length from "file" (including newline chars). */ -extern uint32_t textfile_width(FILE * file); - -/* Read "file" for load of non-zero bytes to put onto "queue" string. */ -extern uint8_t read_file_into_queue(FILE * file, char ** queue); - -/* Return message from "queue" (identified as \n-delimited string, with \n as - * \0 in the returned message; return nothing if no \n delimiter found). - */ -extern char * get_message_from_queue(char ** queue); - - - -#endif diff --git a/src/common/rexit.c b/src/common/rexit.c deleted file mode 100644 index 7eeaccb..0000000 --- a/src/common/rexit.c +++ /dev/null @@ -1,62 +0,0 @@ -/* src/common/rexit.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#include "rexit.h" -#include /* global errno */ -#include /* uint16_t */ -#include /* printf(), perror(), sprintf() */ -#include /* exit(), free(), EXIT_FAILURE */ -#include /* strlen() */ -#include "try_malloc.h" /* try_malloc() */ - - - -void (* cleanup_func) (); - - - -extern void set_cleanup_func(void (* f)()) -{ - cleanup_func = f; -} - - - -extern void exit_err(int err, const char * msg) -{ - if (0 == err) - { - return; - } - cleanup_func(); - if (!msg) - { - msg = "Details unknown."; - } - printf("Aborted program due to error. %s\n", msg); - if (0 != errno) - { - perror("errno states"); - } - exit(EXIT_FAILURE); -} - - - -extern void exit_trouble(int err, const char * parent, const char * child) -{ - char * p1 = "Trouble in "; - char * p2 = " with "; - char * p3 = "."; - uint16_t size = strlen(p1) + strlen(parent) + strlen(p2) + strlen(child) - + strlen(p3) + 1; - char * msg = try_malloc(size, __func__); - int test = sprintf(msg, "%s%s%s%s%s", p1, parent, p2, child, p3); - exit_err(test < 0, "Trouble in exit_trouble with sprintf."); - exit_err(err, msg); - free(msg); -} diff --git a/src/common/rexit.h b/src/common/rexit.h deleted file mode 100644 index 85fb648..0000000 --- a/src/common/rexit.h +++ /dev/null @@ -1,30 +0,0 @@ -/* src/common/rexit.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Routines to exit the game orderly on error, with cleaning up and a somewhat - * informative message. - */ - -#ifndef REXIT_H -#define REXIT_H - - - -/* Set "f" as the cleanup function to be called on exit. */ -extern void set_cleanup_func(void (* f)()); - -/* If "err" == 0, do nothing. Else, clean up and exit with an error message that - * consists of "msg" or (if "msg" is NULL pointer) a generic "Details unknown" - * and of errno's content (if it is non-zero). - */ -extern void exit_err(int err, const char * msg); - -/* Do exit_err() with "msg" as: "Trouble in ".parent." with ".child."." */ -extern void exit_trouble(int err, const char * parent, const char * child); - - - -#endif diff --git a/src/common/try_malloc.c b/src/common/try_malloc.c deleted file mode 100644 index 053d430..0000000 --- a/src/common/try_malloc.c +++ /dev/null @@ -1,29 +0,0 @@ -/* src/common/try_malloc.c - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - */ - -#include "try_malloc.h" -#include /* for malloc */ -#include /* sprintf() */ -#include /* strlen() */ -#include /* for size_t */ -#include "rexit.h" /* for exit_err() */ - - - -extern void * try_malloc(size_t size, const char * f) -{ - char * prefix = "Trouble with malloc in "; - char * msg = malloc(strlen(prefix) + strlen(f) + 1 + 1); - exit_err(!msg, - "Trouble in try_malloc with malloc for error message string."); - int test = sprintf(msg, "%s%s.", prefix, f); - exit_err(test < 0, "Trouble in try_malloc with sprintf."); - void * p = malloc(size); - exit_err(!p, msg); /* Bypass exit_trouble() calling try_malloc(). */ - free(msg); - return p; -} diff --git a/src/common/try_malloc.h b/src/common/try_malloc.h deleted file mode 100644 index 14c5169..0000000 --- a/src/common/try_malloc.h +++ /dev/null @@ -1,22 +0,0 @@ -/* src/common/try_malloc.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * malloc() wrapper. - */ - -#ifndef TRY_MALLOC_H -#define TRY_MALLOC_H - -#include /* for size_t */ - - - -/* Call malloc("size") from function called "f"; exit_trouble() on error. */ -extern void * try_malloc(size_t size, const char * f); - - - -#endif diff --git a/src/common/yx_uint8.h b/src/common/yx_uint8.h deleted file mode 100644 index 61916cd..0000000 --- a/src/common/yx_uint8.h +++ /dev/null @@ -1,26 +0,0 @@ -/* src/server/yx_uint8.h - * - * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3 - * or any later version. For details on its copyright, license, and warranties, - * see the file NOTICE in the root directory of the PlomRogue source package. - * - * Struct used for game map representations. - */ - -#ifndef YX_UINT8_H -#define YX_UINT8_H - -#include /* uint8_t */ - - - -/* Coordinate for maps of max. 256x256 cells. */ -struct yx_uint8 -{ - uint8_t y; - uint8_t x; -}; - - - -#endif diff --git a/src/server/libplomrogue.c b/src/server/libplomrogue.c deleted file mode 100644 index d0b15a1..0000000 --- a/src/server/libplomrogue.c +++ /dev/null @@ -1,619 +0,0 @@ -#include /* pow() */ -#include /* NULL */ -#include /* ?(u)int(8|16|32)_t, ?(U)INT8_(MIN|MAX) */ -#include /* free, malloc */ -#include /* memset */ - -/* Number of degrees a circle is divided into. The greater it is, the greater - * the angle precision. But make it one whole zero larger and bizarre FOV bugs - * appear on large maps, probably due to value overflows (TODO: more research!). - */ -#define CIRCLE 3600000 - -/* Angle of a shadow. */ -struct shadow_angle -{ - struct shadow_angle * next; - uint32_t left_angle; - uint32_t right_angle; -}; - -/* To be used as temporary storage for world map array. */ -static char * worldmap = NULL; - -/* Coordinate for maps of max. 256x256 cells. */ -struct yx_uint8 -{ - uint8_t y; - uint8_t x; -}; - -/* Storage for map_length, set by set_maplength(). */ -static uint16_t maplength = 0; -extern void set_maplength(uint16_t maplength_input) -{ - maplength = maplength_input; -} - -/* Pseudo-randomness seed for rrand(), set by seed_rrand(). */ -static uint32_t seed = 0; - -/* Helper to mv_yx_in_dir_legal(). Move "yx" into hex direction "d". */ -static void mv_yx_in_dir(char d, struct yx_uint8 * yx) -{ - if (d == 'e') - { - yx->x = yx->x + (yx->y % 2); - yx->y--; - } - else if (d == 'd') - { - yx->x++; - } - else if (d == 'c') - { - yx->x = yx->x + (yx->y % 2); - yx->y++; - } - else if (d == 'x') - { - yx->x = yx->x - !(yx->y % 2); - yx->y++; - } - else if (d == 's') - { - yx->x--; - } - else if (d == 'w') - { - yx->x = yx->x - !(yx->y % 2); - yx->y--; - } -} - -/* Move "yx" into hex direction "dir". Available hex directions are: 'e' - * (north-east), 'd' (east), 'c' (south-east), 'x' (south-west), 's' (west), 'w' - * (north-west). Returns 1 if the move was legal, 0 if not, and -1 when internal - * wrapping limits were exceeded. - * - * A move is legal if "yx" ends up within the the map and the original wrap - * space. The latter is left to a neighbor wrap space if "yx" moves beyond the - * minimal (0) or maximal (UINT8_MAX) column or row of possible map space – in - * which case "yx".y or "yx".x will snap to the respective opposite side. The - * current wrapping state is kept between successive calls until a "yx" of NULL - * is passed, in which case the function does nothing but zero the wrap state. - * Successive wrapping may move "yx" several wrap spaces into either direction, - * or return it into the original wrap space. - */ -static int8_t mv_yx_in_dir_legal(char dir, struct yx_uint8 * yx) -{ - static int8_t wrap_west_east = 0; - static int8_t wrap_north_south = 0; - if (!yx) - { - wrap_west_east = wrap_north_south = 0; - return 0; - } - if ( INT8_MIN == wrap_west_east || INT8_MIN == wrap_north_south - || INT8_MAX == wrap_west_east || INT8_MAX == wrap_north_south) - { - return -1; - } - struct yx_uint8 original = *yx; - mv_yx_in_dir(dir, yx); - if (('e' == dir || 'd' == dir || 'c' == dir) && yx->x < original.x) - { - wrap_west_east++; - } - else if (('x' == dir || 's' == dir || 'w' == dir) && yx->x > original.x) - { - wrap_west_east--; - } - if (('w' == dir || 'e' == dir) && yx->y > original.y) - { - wrap_north_south--; - } - else if (('x' == dir || 'c' == dir) && yx->y < original.y) - { - wrap_north_south++; - } - if ( !wrap_west_east && !wrap_north_south - && yx->x < maplength && yx->y < maplength) - { - return 1; - } - return 0; -} - -/* Wrapper around mv_yx_in_dir_legal() that stores new coordinate in res_y/x, - * (return with result_y/x()), and immediately resets the wrapping. - */ -static uint8_t res_y = 0; -static uint8_t res_x = 0; -extern uint8_t mv_yx_in_dir_legal_wrap(char dir, uint8_t y, uint8_t x) -{ - struct yx_uint8 yx; - yx.y = y; - yx.x = x; - uint8_t result = mv_yx_in_dir_legal(dir, &yx); - mv_yx_in_dir_legal(0, NULL); - res_y = yx.y; - res_x = yx.x; - return result; -} -extern uint8_t result_y() -{ - return res_y; -} -extern uint8_t result_x() -{ - return res_x; -} - -/* With set_seed set, set seed global to seed_input. In any case, return it. */ -extern uint32_t seed_rrand(uint8_t set_seed, uint32_t seed_input) -{ - if (set_seed) - { - seed = seed_input; - } - return seed; -} - -/* Return 16-bit number pseudo-randomly generated via Linear Congruential - * Generator algorithm with some proven constants. Use instead of any rand() to - * ensure portability of the same pseudo-randomness across systems. - */ -extern uint16_t rrand() -{ /* Constants as recommended by POSIX.1-2001 (see man page rand(3)). */ - seed = ((seed * 1103515245) + 12345) % 4294967296; - return (seed >> 16); /* Ignore less random least significant bits. */ -} - -/* Free shadow angles list "angles". */ -static void free_angles(struct shadow_angle * angles) -{ - if (angles->next) - { - free_angles(angles->next); - } - free(angles); -} - -/* Recalculate angle < 0 or > CIRCLE to a value between these two limits. */ -static uint32_t correct_angle(int32_t angle) -{ - while (angle < 0) - { - angle = angle + CIRCLE; - } - while (angle > CIRCLE) - { - angle = angle - CIRCLE; - } - return angle; -} - -/* Try merging the angle between "left_angle" and "right_angle" to "shadow" if - * it meets the shadow from the right or the left. Returns 1 on success, else 0. - */ -static uint8_t try_merge(struct shadow_angle * shadow, - uint32_t left_angle, uint32_t right_angle) -{ - if ( shadow->right_angle <= left_angle + 1 - && shadow->right_angle >= right_angle) - { - shadow->right_angle = right_angle; - } - else if ( shadow->left_angle + 1 >= right_angle - && shadow->left_angle <= left_angle) - { - shadow->left_angle = left_angle; - } - else - { - return 0; - } - return 1; -} - -/* Try merging the shadow angle between "left_angle" and "right_angle" into an - * existing shadow angle in "shadows". On success, see if this leads to any - * additional shadow angle overlaps and merge these accordingly. Return 1 on - * success, else 0. - */ -static uint8_t try_merging_angles(uint32_t left_angle, uint32_t right_angle, - struct shadow_angle ** shadows) -{ - uint8_t angle_merge = 0; - struct shadow_angle * shadow; - for (shadow = *shadows; shadow; shadow = shadow->next) - { - if (try_merge(shadow, left_angle, right_angle)) - { - angle_merge = 1; - } - } - if (angle_merge) - { - struct shadow_angle * shadow1; - for (shadow1 = *shadows; shadow1; shadow1 = shadow1->next) - { - struct shadow_angle * last_shadow = NULL; - struct shadow_angle * shadow2; - for (shadow2 = *shadows; shadow2; shadow2 = shadow2->next) - { - if ( shadow1 != shadow2 - && try_merge(shadow1, shadow2->left_angle, - shadow2->right_angle)) - { - struct shadow_angle * to_free = shadow2; - if (last_shadow) - { - last_shadow->next = shadow2->next; - shadow2 = last_shadow; - } - else - { - *shadows = shadow2->next; - shadow2 = *shadows; - } - free(to_free); - } - last_shadow = shadow2; - } - } - } - return angle_merge; -} - -/* To "shadows", add shadow defined by "left_angle" and "right_angle", either as - * new entry or as part of an existing shadow (swallowed whole or extending it). - * Return 1 on malloc error, else 0. - */ -static uint8_t set_shadow(uint32_t left_angle, uint32_t right_angle, - struct shadow_angle ** shadows) -{ - struct shadow_angle * shadow_i; - if (!try_merging_angles(left_angle, right_angle, shadows)) - { - struct shadow_angle * shadow; - shadow = malloc(sizeof(struct shadow_angle)); - if (!shadow) - { - return 1; - } - shadow->left_angle = left_angle; - shadow->right_angle = right_angle; - shadow->next = NULL; - if (*shadows) - { - for (shadow_i = *shadows; shadow_i; shadow_i = shadow_i->next) - { - if (!shadow_i->next) - { - shadow_i->next = shadow; - return 0; - } - } - } - *shadows = shadow; - } - return 0; -} - -/* Test whether angle between "left_angle" and "right_angle", or at least - * "middle_angle", is captured inside one of the shadow angles in "shadows". If - * so, set hex in "fov_map" indexed by "pos_in_map" to 'H'. If the whole angle - * and not just "middle_angle" is captured, return 1. Any other case: 0. - */ -static uint8_t shade_hex(uint32_t left_angle, uint32_t right_angle, - uint32_t middle_angle, struct shadow_angle ** shadows, - uint16_t pos_in_map, char * fov_map) -{ - struct shadow_angle * shadow_i; - if (fov_map[pos_in_map] == 'v') - { - for (shadow_i = *shadows; shadow_i; shadow_i = shadow_i->next) - { - if ( left_angle <= shadow_i->left_angle - && right_angle >= shadow_i->right_angle) - { - fov_map[pos_in_map] = 'H'; - return 1; - } - if ( middle_angle < shadow_i->left_angle - && middle_angle > shadow_i->right_angle) - { - fov_map[pos_in_map] = 'H'; - } - } - } - return 0; -} - -/* Evaluate map position "test_pos" in distance "dist" to the view origin, and - * on the circle of that distance to the origin on hex "hex_i" (as counted from - * the circle's rightmost point), for setting shaded hexes in "fov_map" and - * potentially adding a new shadow to linked shadow angle list "shadows". - * Return 1 on malloc error, else 0. - */ -static uint8_t eval_position(uint16_t dist, uint16_t hex_i, char * fov_map, - struct yx_uint8 * test_pos, - struct shadow_angle ** shadows) -{ - int32_t left_angle_uncorrected = ((CIRCLE / 12) / dist) - - (hex_i * (CIRCLE / 6) / dist); - int32_t right_angle_uncorrected = left_angle_uncorrected - - (CIRCLE / (6 * dist)); - uint32_t left_angle = correct_angle(left_angle_uncorrected); - uint32_t right_angle = correct_angle(right_angle_uncorrected); - uint32_t right_angle_1st = right_angle > left_angle ? 0 : right_angle; - uint32_t middle_angle = 0; - if (right_angle_1st) - { - middle_angle = right_angle + ((left_angle - right_angle) / 2); - } - uint16_t pos_in_map = test_pos->y * maplength + test_pos->x; - uint8_t all_shaded = shade_hex(left_angle, right_angle_1st, middle_angle, - shadows, pos_in_map, fov_map); - if (!all_shaded && 'X' == worldmap[pos_in_map]) - { - if (set_shadow(left_angle, right_angle_1st, shadows)) - { - return 1; - } - if (right_angle_1st != right_angle) - { - left_angle = CIRCLE; - if (set_shadow(left_angle, right_angle, shadows)) - { - return 1; - } - } - } - return 0; -} - -/* Update field of view in "fovmap" of "worldmap_input" as seen from "y"/"x". - * Return 1 on malloc error, else 0. - */ -extern uint8_t build_fov_map(uint8_t y, uint8_t x, - char * fovmap, char * worldmap_input) -{ - worldmap = worldmap_input; - struct shadow_angle * shadows = NULL; - struct yx_uint8 test_pos; - test_pos.y = y; - test_pos.x = x; - char * circledirs_string = "xswedc"; - uint16_t circle_i; - uint8_t circle_is_on_map; - for (circle_i = 1, circle_is_on_map = 1; circle_is_on_map; circle_i++) - { - circle_is_on_map = 0; - if (1 < circle_i) /* All circles but the 1st are */ - { /* moved into starting from a */ - mv_yx_in_dir_legal('c', &test_pos);/* previous circle's last hex, */ - } /* i.e. from the upper left. */ - char dir_char = 'd'; /* Circle's 1st hex is entered by rightward move.*/ - uint8_t dir_char_pos_in_circledirs_string = UINT8_MAX; - uint16_t dist_i, hex_i; - for (hex_i=0, dist_i=circle_i; hex_i < 6 * circle_i; dist_i++, hex_i++) - { - if (circle_i < dist_i) - { - dist_i = 1; - dir_char=circledirs_string[++dir_char_pos_in_circledirs_string]; - } - if (mv_yx_in_dir_legal(dir_char, &test_pos)) - { - if (eval_position(circle_i, hex_i, fovmap, &test_pos, &shadows)) - { - return 1; - } - circle_is_on_map = 1; - } - } - } - mv_yx_in_dir_legal(0, NULL); - free_angles(shadows); - return 0; -} - -static uint16_t * score_map = NULL; -static uint16_t neighbor_scores[6]; - -/* Init AI score map. Return 1 on failure, else 0. */ -extern uint8_t init_score_map() -{ - uint32_t map_size = maplength * maplength; - score_map = malloc(map_size * sizeof(uint16_t)); - if (!score_map) - { - return 1; - } - uint32_t i = 0; - for (; i < map_size; i++) - { - score_map[i] = UINT16_MAX; - } - return 0; -} - -/* Set score_map[pos] to score. Return 1 on failure, else 0. */ -extern uint8_t set_map_score(uint16_t pos, uint16_t score) -{ - if (!score_map) - { - return 1; - } - score_map[pos] = score; - return 0; -} - -/* Get score_map[pos]. Return uint16_t value on success, -1 on failure. */ -extern int32_t get_map_score(uint16_t pos) -{ - if (!score_map) - { - return -1; - } - return score_map[pos]; -} - -/* Free score_map. */ -extern void free_score_map() -{ - free(score_map); - score_map = NULL; -} - -/* Write into "neighbors" scores of the immediate neighbors of the score_map - * cell at pos_i (array index), as found in the directions north-east, east, - * south-east etc. (clockwise order). Use kill_score for illegal neighborhoods - * (i.e. if direction would lead beyond the map's border). - */ -static void get_neighbor_scores(uint16_t pos_i, uint16_t kill_score, - uint16_t * neighbors) -{ - uint32_t map_size = maplength * maplength; - uint8_t open_north = pos_i >= maplength; - uint8_t open_east = pos_i + 1 % maplength; - uint8_t open_south = pos_i + maplength < map_size; - uint8_t open_west = pos_i % maplength; - uint8_t is_indented = (pos_i / maplength) % 2; - uint8_t open_diag_west = is_indented || open_west; - uint8_t open_diag_east = !is_indented || open_east; - neighbors[0] = !(open_north && open_diag_east) ? kill_score : - score_map[pos_i - maplength + is_indented]; - neighbors[1] = !(open_east) ? kill_score : score_map[pos_i + 1]; - neighbors[2] = !(open_south && open_diag_east) ? kill_score : - score_map[pos_i + maplength + is_indented]; - neighbors[3] = !(open_south && open_diag_west) ? kill_score : - score_map[pos_i + maplength - !is_indented]; - neighbors[4] = !(open_west) ? kill_score : score_map[pos_i - 1]; - neighbors[5] = !(open_north && open_diag_west) ? kill_score : - score_map[pos_i - maplength - !is_indented]; -} - -/* Call get_neighbor_scores() on neighbor_scores buffer. Return 1 on error. */ -extern uint8_t ready_neighbor_scores(uint16_t pos) -{ - if (!score_map) - { - return 1; - } - get_neighbor_scores(pos, UINT16_MAX, neighbor_scores); - return 0; -} - -/* Return i-th position from neighbor_scores buffer.*/ -extern uint16_t get_neighbor_score(uint8_t i) -{ - return neighbor_scores[i]; -} - -/* Iterate over scored cells in score_map geometry. Compare each cell's score - * against the score of its immediate neighbors in 6 directions. If any - * neighbor's score is at least two points lower than the current cell's score, - * re-set it to 1 point higher than its lowest-scored neighbor. Repeat this - * whole process until all cells have settled on their final score. Ignore cells - * whose score is greater than UINT16_MAX - 1 (treat those as unreachable). Also - * ignore cells whose score is smaller or equal the number of past iterations. - * Return 1 on error, else 0. - */ -extern uint8_t dijkstra_map() -{ - if (!score_map) - { - return 1; - } - uint16_t max_score = UINT16_MAX - 1; - uint32_t map_size = maplength * maplength; - uint32_t pos; - uint16_t i_scans, neighbors[6], min_neighbor; - uint8_t scores_still_changing = 1; - uint8_t i_dirs; - for (i_scans = 0; scores_still_changing; i_scans++) - { - scores_still_changing = 0; - for (pos = 0; pos < map_size; pos++) - { - uint16_t score = score_map[pos]; - if (score <= max_score && score > i_scans) - { - get_neighbor_scores(pos, max_score, neighbors); - min_neighbor = max_score; - for (i_dirs = 0; i_dirs < 6; i_dirs++) - { - if (min_neighbor > neighbors[i_dirs]) - { - min_neighbor = neighbors[i_dirs]; - } - } - if (score_map[pos] > min_neighbor + 1) - { - score_map[pos] = min_neighbor + 1; - scores_still_changing = 1; - } - } - } - } - return 0; -} - -extern uint8_t zero_score_map_where_char_on_memdepthmap(char c, - char * memdepthmap) -{ - if (!score_map) - { - return 1; - } - uint32_t map_size = maplength * maplength; - uint16_t pos; - for (pos = 0; pos < map_size; pos++) - { - if (c == memdepthmap[pos]) - { - score_map[pos] = 0; - } - } - return 0; -} - -extern void age_some_memdepthmap_on_nonfov_cells(char * memdepthmap, - char * fovmap) -{ - uint32_t map_size = maplength * maplength; - uint16_t pos; - for (pos = 0; pos < map_size; pos++) - { - if ('v' != fovmap[pos]) - { - char c = memdepthmap[pos]; - if( '0' <= c && '9' > c && !(rrand() % (uint16_t) pow(2, c - 48))) - { - memdepthmap[pos]++; - } - } - } -} - -extern uint8_t set_cells_passable_on_memmap_to_65534_on_scoremap(char * mem_map) -{ - if (!score_map) - { - return 1; - } - uint32_t map_size = maplength * maplength; - uint16_t pos; - for (pos = 0; pos < map_size; pos++) - { - if ('.' == mem_map[pos]) - { - score_map[pos] = 65534; - } - } - return 0; -} diff --git a/start_server_client_union.sh b/start_server_client_union.sh index c56bf06..74872c7 100755 --- a/start_server_client_union.sh +++ b/start_server_client_union.sh @@ -19,14 +19,9 @@ then fi # Give helpful message to players that want to start without compiling first. -if [ ! -e ./roguelike-client ] -then - echo 'No ./roguelike-client executable found. Try "./redo" first?' - false -fi if [ ! -e ./libplomrogue.so ] then - echo 'No ./libplomrogue.so library found. Try "./redo" first?' + echo 'No ./libplomrogue.so library found. Try "./build.sh" first?' false fi @@ -57,4 +52,4 @@ fi # Only start the interface when everything else went well. kill -0 $! 2> /dev/null -./roguelike-client +python3 ./roguelike-client diff --git a/start_server_python_client_union.sh b/start_server_python_client_union.sh deleted file mode 100755 index 5894947..0000000 --- a/start_server_python_client_union.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh - -# Abort the script on error. -set -e - -# Don't let any log leftovers from before interfere. -if [ -e ./log ] -then - rm log -fi - -# Give helpful message to players that want to start without compiling first. -if [ ! -e ./roguelike-client ] -then - echo 'No ./roguelike-client file found to execute. Try "./redo" first?' - false -fi - -# Use shell script's arguments for server and pipe server output to log file. -# This script's wrapper script will read it out on exit. -python3 ./plomrogue-server.py "$@" > log 2>&1 & - -# Give server some time to start up and exit on error. -sleep 0.01 - -# The client should not start if the server is not running. (If the server was -# running in the foreground, any error exit of it so far would be caught by "set -# -e" above. But "set -e" is blind to error codes generated in the background.) -kill -0 $! 2> /dev/null - -# Give server some time (max. 10 seconds) to generate its worldstate file. -i=0 -while [ ! -e server/worldstate ] && [ $i -le 1000 ] -do - sleep 0.01 - i=`expr $i + 1` -done -if [ ! -e server/worldstate ] -then - echo "Server failed generating worldstate file within given time limit." - false -fi - -# Only start the interface when everything else went well. -kill -0 $! 2> /dev/null -./roguelike-client diff --git a/test_server.sh b/test_server.sh index 16f52fa..e154959 100755 --- a/test_server.sh +++ b/test_server.sh @@ -1,6 +1,6 @@ #!/bin/sh -./redo +./build.sh echo "Copying tested server script to ./tested_server.py." cp ./roguelike-server ./testing/tested_server.py echo "Copying tested C library source to ./tested_server_lib.c."