diff --git a/.gitignore b/.gitignore index 20a7cf2..ca37570 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ backup *.dll *.world coverage +coverage/* +.venv diff --git a/.vimrc_proj b/.vimrc_proj index 2b745b4..1d04d48 100644 --- a/.vimrc_proj +++ b/.vimrc_proj @@ -1 +1 @@ -set makeprg=meson\ compile\ -C\ . +set makeprg=make\ -f\ ../Makefile\ build diff --git a/Makefile b/Makefile index 6144bd3..ece7c1d 100644 --- a/Makefile +++ b/Makefile @@ -1,47 +1,72 @@ +ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) + all: build test reset: +ifeq '$(OS)' 'Windows_NT' powershell -executionpolicy bypass .\scripts\reset_build.ps1 +else + sh -x ./scripts/reset_build.sh +endif %.cpp : %.rl - ragel -o $@ $< + ragel -I $(ROOT_DIR) -G1 -o $@ $< -build: ansi_parser.cpp lel_parser.cpp - meson compile -j 10 -C builddir +%.dot: %.rl + ragel -Vp -I $(ROOT_DIR) -o $@ $< + +%.png: %.dot + dot -Tpng $< -o $@ + +build: + meson compile -j 10 -C $(ROOT_DIR)/builddir + +asset_build: + ./builddir/icongen release_build: meson --wipe builddir -Db_ndebug=true --buildtype release meson compile -j 10 -C builddir debug_build: - meson setup --wipe builddir --buildtype debug + meson setup --wipe builddir -Db_ndebug=true --buildtype debugoptimized meson compile -j 10 -C builddir tracy_build: meson setup --wipe builddir --buildtype debugoptimized -Dtracy_enable=true -Dtracy:on_demand=true meson compile -j 10 -C builddir -test: build - ./builddir/runtests +test: + ./builddir/runtests -d yes run: build test +ifeq '$(OS)' 'Windows_NT' powershell "cp ./builddir/zedcaster.exe ." ./zedcaster +else + ./builddir/zedcaster +endif debug: build - gdb --nx -x .gdbinit --ex run --args builddir/zedcaster.exe + gdb --nx -x .gdbinit --ex run --args builddir/zedcaster debug_run: build - gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/zedcaster.exe + gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/zedcaster -debug_walk: build - gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/zedcaster.exe t +debug_walk: build test + gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/zedcaster t clean: meson compile --clean -C builddir debug_test: build - gdb --nx -x .gdbinit --ex run --args builddir/runtests.exe -e + gdb --nx -x .gdbinit --ex run --ex bt --ex q --args builddir/runtests -e win_installer: - powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" win_installer.ifp' + powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp' + +coverage_report: + powershell 'scripts/coverage_report.ps1' + +money: + scc --exclude-dir subprojects diff --git a/README.md b/README.md index d1304d8..d489c31 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,9 @@ See? That's how Free Speech works. You don't need a LICENSE. On all platforms you'll need these components: * [Meson](https://mesonbuild.com/) -- which needs Python. -* C++ Compiler -- Tested with Clang and G++. You can use my [Windows C++ Setup Guide](https://git.learnjsthehardway.com/learn-code-the-hard-way/lcthw-windows-installers) which features an automated installer for Windows. +* C++ Compiler -- Tested with Clang and GCC 14.2.0. You can use my [Windows C++ Setup Guide](https://git.learnjsthehardway.com/learn-code-the-hard-way/lcthw-windows-installers) which features an automated installer for Windows. * [GNU make](https://www.gnu.org/software/make/) -- For the convenience Makefile. On Windows you should have this if you used my setup scripts. Otherwise `winget install ezwinports.make` will set you up. +* [Ninja](https://ninja-build.org/) -- Meson uses this to do builds on most systems. * [git](https://git-scm.com/) -- Which should be on almost every platform, and is installed by default with my Windows setup scripts. ### Windows Instructions @@ -88,7 +89,7 @@ cd raycaster # first compile takes a while make -./builddir/raycaster +./builddir/zedcaster ``` You don't need `make run` because Linux and OSX are sane operating systems that don't lock every @@ -127,6 +128,18 @@ I would also like statistics that show it's better, not just your word. It's early so probably a bunch of bugs. +## Linux Build Notes + +Libraries Needed: + +* libxi-dev +* libfreetype-dev + +It uses c++ so you may need to install a libg++ or libc++ for your system. Usually this is all you +need: + +apt install build-essential + ## OSX Build Notes * Quite a bad experience. Need to install Python, cmake, meson, and ninja all which are in homebrew but if you don't use homebrew then this is a problem. diff --git a/ai.cpp b/ai.cpp new file mode 100644 index 0000000..c6b93e6 --- /dev/null +++ b/ai.cpp @@ -0,0 +1,212 @@ +#include "dbc.hpp" +#include "ai.hpp" + +namespace ai { + using namespace nlohmann; + using namespace dbc; + + static AIManager AIMGR; + static bool initialized = false; + + inline void validate_profile(nlohmann::json& profile) { + for(auto& [name_key, value] : profile.items()) { + check(value < STATE_MAX, + fmt::format("profile field {} has value {} greater than STATE_MAX {}", (std::string)name_key, (int)value, STATE_MAX)); + } + } + + Action config_action(AIProfile& profile, nlohmann::json& config) { + check(config.contains("name"), "config_action: action config missing name"); + check(config.contains("cost"), "config_action: action config missing cost"); + + Action result(config["name"], config["cost"]); + + check(config.contains("needs"), + fmt::format("config_action: no 'needs' field", result.name)); + check(config.contains("effects"), + fmt::format("config_action: no 'effects' field", result.name)); + + for(auto& [name_key, value] : config["needs"].items()) { + check(profile.contains(name_key), fmt::format("config_action({}): profile does not have need named {}", result.name, name_key)); + result.needs(profile.at(name_key), bool(value)); + } + + for(auto& [name_key, value] : config["effects"].items()) { + check(profile.contains(name_key), fmt::format("config_action({}): profile does not have effect named {}", result.name, name_key)); + + result.effect(profile.at(name_key), bool(value)); + } + + return result; + } + + State config_state(AIProfile& profile, nlohmann::json& config) { + State result; + + for(auto& [name_key, value] : config.items()) { + check(profile.contains(name_key), fmt::format("config_state: profile does not have name {}", name_key)); + + int name_id = profile.at(name_key); + result[name_id] = bool(value); + } + + return result; + } + + /* + * This is only used in tests so I can load different fixtures. + */ + void reset() { + initialized = false; + AIMGR.actions.clear(); + AIMGR.states.clear(); + AIMGR.scripts.clear(); + AIMGR.profile = json({}); + } + + void init(std::string config_path) { + if(!initialized) { + Config config(config_path); + + // profile specifies what keys (bitset indexes) are allowed + // and how they map to the bitset of State + validate_profile(config["profile"]); + + // relies on json conversion? + AIMGR.profile = config["profile"]; + + // load all actions + auto& actions = config["actions"]; + for(auto& action_vars : actions) { + auto the_action = config_action(AIMGR.profile, action_vars); + AIMGR.actions.insert_or_assign(the_action.name, the_action); + } + + // load all states + auto& states = config["states"]; + for(auto& [name, state_vars] : states.items()) { + auto the_state = config_state(AIMGR.profile, state_vars); + AIMGR.states.insert_or_assign(name, the_state); + } + + auto& scripts = config["scripts"]; + for(auto& [script_name, action_names] : scripts.items()) { + std::vector the_script; + + for(auto name : action_names) { + check(AIMGR.actions.contains(name), + fmt::format("ai::init(): script {} uses action {} that doesn't exist", + (std::string)script_name, (std::string)name)); + + the_script.push_back(AIMGR.actions.at(name)); + } + + AIMGR.scripts.insert_or_assign(script_name, the_script); + } + initialized = true; + } else { + dbc::sentinel("DOUBLE INIT: AI manager should only be intialized once if not in tests."); + } + } + + void check_valid_action(std::string name, std::string msg) { + dbc::check(AIMGR.actions.contains(name), + fmt::format("{} tried to access action that doesn't exist {}", + msg, name)); + } + + State load_state(std::string state_name) { + check(initialized, "you forgot to initialize the AI first."); + check(AIMGR.states.contains(state_name), fmt::format( + "ai::load_state({}): state does not exist in config", + state_name)); + + return AIMGR.states.at(state_name); + } + + Action load_action(std::string action_name) { + check(initialized, "you forgot to initialize the AI first."); + check(AIMGR.states.contains(action_name), fmt::format( + "ai::load_action({}): action does not exist in config", + action_name)); + return AIMGR.actions.at(action_name); + } + + std::vector load_script(std::string script_name) { + check(AIMGR.scripts.contains(script_name), fmt::format( + "ai::load_script(): no script named {} configured", script_name)); + return AIMGR.scripts.at(script_name); + } + + ActionPlan plan(std::string script_name, State start, State goal) { + // BUG: could probably memoize here, since: + // same script+same start+same goal will/should produce the same results + + check(initialized, "you forgot to initialize the AI first."); + auto script = load_script(script_name); + return plan_actions(script, start, goal); + } + + int state_id(std::string name) { + check(AIMGR.profile.contains(name), fmt::format( + "ai::state_id({}): id is not configured in profile", + name)); + return AIMGR.profile.at(name); + } + + void set(State& state, std::string name, bool value) { + // resort by best fit + state.set(state_id(name), value); + } + + bool test(State state, std::string name) { + return state.test(state_id(name)); + } + + void EntityAI::fit_sort() { + if(active()) { + std::sort(plan.script.begin(), plan.script.end(), + [&](auto& l, auto& r) { + int l_cost = l.cost + ai::distance_to_goal(start, goal); + int r_cost = r.cost + ai::distance_to_goal(start, goal); + return l_cost < r_cost; + }); + } + } + + std::string& EntityAI::wants_to() { + return plan.script[0].name; + } + + bool EntityAI::wants_to(std::string name) { + ai::check_valid_action(name, "EntityAI::wants_to"); + return plan.script.size() > 0 && plan.script[0].name == name; + } + + bool EntityAI::active() { + if(plan.script.size() == 1) { + return plan.script[0] != FINAL_ACTION; + } else { + return plan.script.size() != 0; + } + } + + void EntityAI::set_state(std::string name, bool setting) { + fit_sort(); + ai::set(start, name, setting); + } + + bool EntityAI::get_state(std::string name) { + return ai::test(start, name); + } + + void EntityAI::update() { + plan = ai::plan(script, start, goal); + fit_sort(); + } + + AIProfile* profile() { + return &AIMGR.profile; + } + +} diff --git a/ai.hpp b/ai.hpp new file mode 100644 index 0000000..3933b5e --- /dev/null +++ b/ai.hpp @@ -0,0 +1,65 @@ +#pragma once +#include +#include "matrix.hpp" +#include +#include +#include +#include +#include "config.hpp" +#include "goap.hpp" + +namespace ai { + struct EntityAI { + std::string script; + ai::State start; + ai::State goal; + ai::ActionPlan plan; + + EntityAI(std::string script, ai::State start, ai::State goal) : + script(script), start(start), goal(goal) + { + } + + EntityAI() {}; + + bool wants_to(std::string name); + std::string& wants_to(); + void fit_sort(); + + bool active(); + + void set_state(std::string name, bool setting); + bool get_state(std::string name); + + void update(); + + void dump(); + std::string to_string(); + }; + + struct AIManager { + AIProfile profile; + std::unordered_map actions; + std::unordered_map states; + std::unordered_map> scripts; + }; + + /* This is really only used in test to load different fixtures. */ + void reset(); + void init(std::string config_path); + + Action config_action(AIProfile& profile, nlohmann::json& config); + State config_state(AIProfile& profile, nlohmann::json& config); + + int state_id(std::string name); + State load_state(std::string state_name); + Action load_action(std::string action_name); + std::vector load_script(std::string script_name); + + void set(State& state, std::string name, bool value=true); + bool test(State state, std::string name); + ActionPlan plan(std::string script_name, State start, State goal); + + /* Mostly used for debugging and validation. */ + void check_valid_action(std::string name, std::string msg); +} diff --git a/ai_debug.cpp b/ai_debug.cpp new file mode 100644 index 0000000..351df2b --- /dev/null +++ b/ai_debug.cpp @@ -0,0 +1,74 @@ +#include "ai.hpp" +#include "ai_debug.hpp" + +namespace ai { + + /* + * Yeah this is weird but it's only to debug things like + * the preconditions which are weirdly done. + */ + void dump_only(State state, bool matching, bool show_as) { + AIProfile* profile = ai::profile(); + for(auto& [name, name_id] : *profile) { + if(state.test(name_id) == matching) { + fmt::println("\t{}={}", name, show_as); + } + } + } + + void dump_state(State state) { + AIProfile* profile = ai::profile(); + for(auto& [name, name_id] : *profile) { + fmt::println("\t{}={}", name, + state.test(name_id)); + } + } + + void dump_action(Action& action) { + fmt::println(" --ACTION: {}, cost={}", action.name, action.cost); + + fmt::println(" PRECONDS:"); + dump_only(action.$positive_preconds, true, true); + dump_only(action.$negative_preconds, true, false); + + fmt::println(" EFFECTS:"); + dump_only(action.$positive_effects, true, true); + dump_only(action.$negative_effects, true, false); + } + + State dump_script(std::string msg, State start, Script& script) { + fmt::println("--SCRIPT DUMP: {}", msg); + fmt::println("# STATE BEFORE:"); + dump_state(start); + fmt::print("% ACTIONS PLANNED:"); + for(auto& action : script) { + fmt::print("{} ", action.name); + } + fmt::print("\n"); + + for(auto& action : script) { + dump_action(action); + + start = action.apply_effect(start); + fmt::println(" ## STATE AFTER:"); + dump_state(start); + } + + return start; + } + + void EntityAI::dump() { + dump_script(script, start, plan.script); + } + + std::string EntityAI::to_string() { + AIProfile* profile = ai::profile(); + std::string result = wants_to(); + + for(auto& [name, name_id] : *profile) { + result += fmt::format("\n{}={}", name, start.test(name_id)); + } + + return result; + } +} diff --git a/ai_debug.hpp b/ai_debug.hpp new file mode 100644 index 0000000..27f3b54 --- /dev/null +++ b/ai_debug.hpp @@ -0,0 +1,10 @@ +#pragma once +#include "goap.hpp" + +namespace ai { + AIProfile* profile(); + void dump_only(State state, bool matching, bool show_as); + void dump_state(State state); + void dump_action(Action& action); + State dump_script(std::string msg, State start, Script& script); +} diff --git a/animation.cpp b/animation.cpp new file mode 100644 index 0000000..db431ac --- /dev/null +++ b/animation.cpp @@ -0,0 +1,117 @@ +#include "animation.hpp" + +namespace components { + void Animation::play() { + if(!playing) { + current = 0; + subframe = 0.0f; + playing = true; + } + } + + float Animation::twitching() { + float tick = ease::sine(float(frames) / subframe * ease_rate); + + switch(easing) { + case ease::NONE: + return 0.0; + case ease::SINE: + return tick; + case ease::OUT_CIRC: + return ease::out_circ(tick); + case ease::OUT_BOUNCE: + return ease::sine(ease::out_bounce(tick)); + case ease::IN_OUT_BACK: + return ease::sine(ease::in_out_back(tick)); + default: + dbc::sentinel( + fmt::format("Invalid easing {} given to animation", + int(easing))); + } + } + + void Animation::step(sf::Vector2f& scale_out, sf::Vector2f& pos_out, sf::IntRect& rect_out) { + if(playing && current < frames) { + float tick = twitching(); + scale_out.x = std::lerp(scale_out.x, scale_out.x + scale, tick); + scale_out.y = std::lerp(scale_out.y, scale_out.y + scale, tick); + + if(stationary) { + pos_out.y = pos_out.y - (pos_out.y * scale_out.y - pos_out.y); + } + + if(!simple) { + rect_out.position.x += current * frame_width; + } + + subframe += speed; + current = int(subframe); + } else if(!looped) { + playing = false; + current = frames - 1; + subframe = float(frames - 1); + + if(!simple) { + rect_out.position.x += current * frame_width; + } + } else { + playing = false; + current = 0; + subframe = 0.0f; + } + } +} + + +namespace animation { + using namespace components; + using namespace textures; + + static AnimationManager MGR; + static bool initialized = false; + + bool apply(Animation& anim, SpriteTexture& target) { + auto size = target.texture->getSize(); + anim.frame_width = int(size.x) / (unsigned int)anim.frames; + sf::IntRect rect{{0,0}, {anim.frame_width, int(size.y)}}; + sf::Vector2f scale{1.0, 1.0}; + sf::Vector2f pos{0, 0}; + + anim.step(scale, pos, rect); + + target.sprite->setTextureRect(rect); + target.sprite->setPosition(pos); + target.sprite->setScale(scale); + + return anim.playing; + } + + void rotate(sf::Sprite& target, float degrees) { + target.rotate(sf::degrees(degrees)); + } + + void center(sf::Sprite& target, sf::Vector2f pos) { + auto bounds = target.getLocalBounds(); + target.setPosition({pos.x + bounds.size.x / 2, + pos.y + bounds.size.y / 2}); + target.setOrigin({bounds.size.x / 2, bounds.size.y / 2}); + } + + void init() { + if(!initialized) { + Config config("assets/animations.json"); + + for(auto& [name, data] : config.json().items()) { + auto anim = components::convert(data); + MGR.animations.insert_or_assign(name, anim); + } + + initialized = true; + } + } + + Animation load(std::string name) { + dbc::check(initialized, "You forgot to initialize animation."); + return MGR.animations.at(name); + } +} diff --git a/animation.hpp b/animation.hpp new file mode 100644 index 0000000..18264bb --- /dev/null +++ b/animation.hpp @@ -0,0 +1,17 @@ +#pragma once +#include "components.hpp" +#include "textures.hpp" +#include "easings.hpp" + +namespace animation { + struct AnimationManager { + std::unordered_map animations; + }; + + bool apply(components::Animation& anim, textures::SpriteTexture& target); + void rotate(sf::Sprite& target, float degrees); + void center(sf::Sprite& target, sf::Vector2f pos); + + void init(); + components::Animation load(std::string name); +} diff --git a/ansi_parser.cpp b/ansi_parser.cpp deleted file mode 100644 index 19f4d7c..0000000 --- a/ansi_parser.cpp +++ /dev/null @@ -1,376 +0,0 @@ - -#line 1 "ansi_parser.rl" -#include -#include -#include "dbc.hpp" -#include -#include "ansi_parser.hpp" -#include - -using namespace fmt; - - -#line 122 "ansi_parser.rl" - - - -#line 13 "ansi_parser.cpp" -static const char _ansi_parser_actions[] = { - 0, 1, 0, 1, 3, 1, 4, 1, - 5, 1, 6, 1, 7, 1, 8, 1, - 9, 1, 10, 1, 11, 1, 15, 1, - 16, 2, 1, 12, 2, 1, 13, 2, - 6, 7, 2, 16, 5, 3, 1, 14, - 2 -}; - -static const char _ansi_parser_key_offsets[] = { - 0, 0, 1, 2, 11, 12, 14, 17, - 18, 22, 23, 27, 28, 29, 30, 31, - 33, 36, 38, 41, 43, 46, 47, 50, - 51, 52, 53, 54, 55 -}; - -static const int _ansi_parser_trans_keys[] = { - 27, 91, 48, 49, 50, 51, 52, 55, - 57, 53, 54, 109, 48, 109, 34, 48, - 55, 109, 50, 52, 55, 109, 109, 49, - 56, 57, 109, 109, 59, 50, 59, 48, - 57, 59, 48, 57, 48, 57, 59, 48, - 57, 48, 57, 109, 48, 57, 109, 56, - 57, 109, 59, 50, 109, 109, 27, 27, - 0 -}; - -static const char _ansi_parser_single_lengths[] = { - 0, 1, 1, 7, 1, 2, 3, 1, - 4, 1, 4, 1, 1, 1, 1, 0, - 1, 0, 1, 0, 1, 1, 3, 1, - 1, 1, 1, 1, 1 -}; - -static const char _ansi_parser_range_lengths[] = { - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0 -}; - -static const char _ansi_parser_index_offsets[] = { - 0, 0, 2, 4, 13, 15, 18, 22, - 24, 29, 31, 36, 38, 40, 42, 44, - 46, 49, 51, 54, 56, 59, 61, 65, - 67, 69, 71, 73, 75 -}; - -static const char _ansi_parser_trans_targs[] = { - 2, 1, 3, 0, 4, 5, 8, 10, - 22, 26, 6, 7, 0, 28, 0, 6, - 28, 0, 7, 7, 7, 0, 28, 0, - 7, 7, 9, 28, 0, 28, 0, 11, - 12, 21, 28, 0, 28, 0, 13, 0, - 14, 0, 15, 0, 16, 0, 17, 16, - 0, 18, 0, 19, 18, 0, 20, 0, - 28, 20, 0, 28, 0, 23, 25, 28, - 0, 24, 0, 14, 0, 28, 0, 28, - 0, 2, 1, 2, 1, 0 -}; - -static const char _ansi_parser_trans_actions[] = { - 0, 7, 0, 0, 21, 21, 21, 21, - 21, 21, 21, 21, 0, 31, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 17, 0, 15, 0, 0, - 0, 0, 0, 0, 19, 0, 0, 0, - 3, 0, 0, 0, 1, 0, 25, 0, - 0, 1, 0, 28, 0, 0, 1, 0, - 37, 0, 0, 9, 0, 0, 0, 0, - 0, 0, 0, 5, 0, 11, 0, 13, - 0, 0, 7, 23, 34, 0 -}; - -static const char _ansi_parser_eof_actions[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 23 -}; - -static const int ansi_parser_start = 27; -static const int ansi_parser_first_final = 27; -static const int ansi_parser_error = 0; - -static const int ansi_parser_en_main = 27; - - -#line 125 "ansi_parser.rl" - -#include - -ANSIParser::ANSIParser(sf::Color default_fg, sf::Color default_bg) : - $default_fg(default_fg), - $default_bg(default_bg) -{ -} - -bool ANSIParser::parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb) { - const wchar_t *start = nullptr; - int cs = 0; - unsigned int value = 0; - const wchar_t *p = codes.data(); - const wchar_t *pe = p + codes.size(); - const wchar_t *eof = pe; - sf::Color bgcolor($default_bg); - sf::Color color($default_fg); - sf::Color* target = &color; - - -#line 120 "ansi_parser.cpp" - { - cs = ansi_parser_start; - } - -#line 146 "ansi_parser.rl" - -#line 123 "ansi_parser.cpp" - { - int _klen; - unsigned int _trans; - const char *_acts; - unsigned int _nacts; - const int *_keys; - - if ( p == pe ) - goto _test_eof; - if ( cs == 0 ) - goto _out; -_resume: - _keys = _ansi_parser_trans_keys + _ansi_parser_key_offsets[cs]; - _trans = _ansi_parser_index_offsets[cs]; - - _klen = _ansi_parser_single_lengths[cs]; - if ( _klen > 0 ) { - const int *_lower = _keys; - const int *_mid; - const int *_upper = _keys + _klen - 1; - while (1) { - if ( _upper < _lower ) - break; - - _mid = _lower + ((_upper-_lower) >> 1); - if ( (*p) < *_mid ) - _upper = _mid - 1; - else if ( (*p) > *_mid ) - _lower = _mid + 1; - else { - _trans += (unsigned int)(_mid - _keys); - goto _match; - } - } - _keys += _klen; - _trans += _klen; - } - - _klen = _ansi_parser_range_lengths[cs]; - if ( _klen > 0 ) { - const int *_lower = _keys; - const int *_mid; - const int *_upper = _keys + (_klen<<1) - 2; - while (1) { - if ( _upper < _lower ) - break; - - _mid = _lower + (((_upper-_lower) >> 1) & ~1); - if ( (*p) < _mid[0] ) - _upper = _mid - 2; - else if ( (*p) > _mid[1] ) - _lower = _mid + 2; - else { - _trans += (unsigned int)((_mid - _keys)>>1); - goto _match; - } - } - _trans += _klen; - } - -_match: - cs = _ansi_parser_trans_targs[_trans]; - - if ( _ansi_parser_trans_actions[_trans] == 0 ) - goto _again; - - _acts = _ansi_parser_actions + _ansi_parser_trans_actions[_trans]; - _nacts = (unsigned int) *_acts++; - while ( _nacts-- > 0 ) - { - switch ( *_acts++ ) - { - case 0: -#line 14 "ansi_parser.rl" - { - start = p; - } - break; - case 1: -#line 18 "ansi_parser.rl" - { - value = 0; - size_t len = p - start; - dbc::check(start[0] != '-', "negative numbers not supported"); - - switch(len) { - case 10: value += (start[len-10] - '0') * 1000000000; [[fallthrough]]; - case 9: value += (start[len- 9] - '0') * 100000000; [[fallthrough]]; - case 8: value += (start[len- 8] - '0') * 10000000; [[fallthrough]]; - case 7: value += (start[len- 7] - '0') * 1000000; [[fallthrough]]; - case 6: value += (start[len- 6] - '0') * 100000; [[fallthrough]]; - case 5: value += (start[len- 5] - '0') * 10000; [[fallthrough]]; - case 4: value += (start[len- 4] - '0') * 1000; [[fallthrough]]; - case 3: value += (start[len- 3] - '0') * 100; [[fallthrough]]; - case 2: value += (start[len- 2] - '0') * 10; [[fallthrough]]; - case 1: value += (start[len- 1] - '0'); - break; - default: - dbc::sentinel("can't process > 10 digits"); - } - } - break; - case 2: -#line 40 "ansi_parser.rl" - { - color_cb(color, bgcolor); - } - break; - case 3: -#line 43 "ansi_parser.rl" - { - target = &color; - } - break; - case 4: -#line 46 "ansi_parser.rl" - { - target = &bgcolor; - } - break; - case 5: -#line 50 "ansi_parser.rl" - { - write_cb((*p)); - } - break; - case 6: -#line 54 "ansi_parser.rl" - { - color = $default_fg; - color_cb(color, bgcolor); - } - break; - case 7: -#line 58 "ansi_parser.rl" - { - bgcolor = $default_bg; - color_cb(color, bgcolor); - } - break; - case 8: -#line 62 "ansi_parser.rl" - { - color = $default_bg; - bgcolor = $default_fg; - color_cb(color, bgcolor); - } - break; - case 9: -#line 67 "ansi_parser.rl" - { - color = $default_fg; - bgcolor = $default_bg; - color_cb(color, bgcolor); - } - break; - case 10: -#line 72 "ansi_parser.rl" - { - color = sf::Color(100,100,100); - color_cb(color, bgcolor); - } - break; - case 11: -#line 76 "ansi_parser.rl" - { - color = sf::Color::Red; - color_cb(color, bgcolor); - } - break; - case 12: -#line 81 "ansi_parser.rl" - { target->r = value; } - break; - case 13: -#line 82 "ansi_parser.rl" - { target->g = value; } - break; - case 14: -#line 83 "ansi_parser.rl" - { target->b = value; } - break; - case 15: -#line 84 "ansi_parser.rl" - { value = 0; } - break; - case 16: -#line 85 "ansi_parser.rl" - {} - break; -#line 296 "ansi_parser.cpp" - } - } - -_again: - if ( cs == 0 ) - goto _out; - if ( ++p != pe ) - goto _resume; - _test_eof: {} - if ( p == eof ) - { - const char *__acts = _ansi_parser_actions + _ansi_parser_eof_actions[cs]; - unsigned int __nacts = (unsigned int) *__acts++; - while ( __nacts-- > 0 ) { - switch ( *__acts++ ) { - case 16: -#line 85 "ansi_parser.rl" - {} - break; -#line 314 "ansi_parser.cpp" - } - } - } - - _out: {} - } - -#line 147 "ansi_parser.rl" - - bool good = pe - p == 0; - - if(!good) { - p -= 10; - // dear cthuhlu, save me from the pain that is wstring - for(int i = 0; i < 100; i++) { - try { - print("{}", p[i] == 0x1B ? '^' : char(p[i])); - } catch(...) { - print("?=", int(p[i])); - } - } - } - - (void)ansi_parser_first_final; - (void)ansi_parser_error; - (void)ansi_parser_en_main; - - - return good; -} diff --git a/ansi_parser.hpp b/ansi_parser.hpp deleted file mode 100644 index 9dd054c..0000000 --- a/ansi_parser.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include -#include -#include -#include - -typedef std::function ColorCB; - -typedef std::function WriteCB; - -class ANSIParser { - sf::Color $default_fg; - sf::Color $default_bg; - std::wstring_convert> $converter; - -public: - ANSIParser(sf::Color default_fg, sf::Color default_bg); - - // disable copying - ANSIParser(ANSIParser& ap) = delete; - - bool parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb); -}; diff --git a/ansi_parser.rl b/ansi_parser.rl deleted file mode 100644 index cc2219e..0000000 --- a/ansi_parser.rl +++ /dev/null @@ -1,167 +0,0 @@ -#include -#include -#include "dbc.hpp" -#include -#include "ansi_parser.hpp" -#include - -using namespace fmt; - -%%{ - machine ansi_parser; - alphtype int; - - action tstart { - start = fpc; - } - - action number { - value = 0; - size_t len = fpc - start; - dbc::check(start[0] != '-', "negative numbers not supported"); - - switch(len) { - case 10: value += (start[len-10] - '0') * 1000000000; [[fallthrough]]; - case 9: value += (start[len- 9] - '0') * 100000000; [[fallthrough]]; - case 8: value += (start[len- 8] - '0') * 10000000; [[fallthrough]]; - case 7: value += (start[len- 7] - '0') * 1000000; [[fallthrough]]; - case 6: value += (start[len- 6] - '0') * 100000; [[fallthrough]]; - case 5: value += (start[len- 5] - '0') * 10000; [[fallthrough]]; - case 4: value += (start[len- 4] - '0') * 1000; [[fallthrough]]; - case 3: value += (start[len- 3] - '0') * 100; [[fallthrough]]; - case 2: value += (start[len- 2] - '0') * 10; [[fallthrough]]; - case 1: value += (start[len- 1] - '0'); - break; - default: - dbc::sentinel("can't process > 10 digits"); - } - } - - action color_out { - color_cb(color, bgcolor); - } - action is_fg { - target = &color; - } - action is_bg { - target = &bgcolor; - } - - action out { - write_cb(fc); - } - - action reset_fg { - color = $default_fg; - color_cb(color, bgcolor); - } - action reset_bg { - bgcolor = $default_bg; - color_cb(color, bgcolor); - } - action invert { - color = $default_bg; - bgcolor = $default_fg; - color_cb(color, bgcolor); - } - action reset_invert { - color = $default_fg; - bgcolor = $default_bg; - color_cb(color, bgcolor); - } - action half_bright { - color = sf::Color(100,100,100); - color_cb(color, bgcolor); - } - action red_text { - color = sf::Color::Red; - color_cb(color, bgcolor); - } - - action red { target->r = value; } - action blue { target->g = value; } - action green { target->b = value; } - action start { value = 0; } - action end {} - action log { println("command {}", (char)fc); } - - ESC = 0x1B; - start = ESC "["; - fg = "38;" %is_fg; - bg = "48;" %is_bg; - reset = ("39" %reset_fg | "49" %reset_bg); - num = digit+ >tstart %number; - color256 = "5;"; - color24b = "2;"; - - ansi = ( - start %start - ( - reset | - "0" %reset_fg %reset_bg | - "1" | - "2" %half_bright | - "3" | - "4" | - "5" | - "6" | - "7" %invert | - "31" %red_text | - "22" | - "24" | - "27" %reset_invert | - "9" ["0"-"7"] | - "10" ["0"-"7"] | - (fg|bg) (color24b num %red ";" num %blue ";" num %green ) %color_out - ) "m" %end - ); - - other = (any+ @out -- ESC)*; - - main := (other :> ansi)**; -}%% - -%% write data; - -#include - -ANSIParser::ANSIParser(sf::Color default_fg, sf::Color default_bg) : - $default_fg(default_fg), - $default_bg(default_bg) -{ -} - -bool ANSIParser::parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb) { - const wchar_t *start = nullptr; - int cs = 0; - unsigned int value = 0; - const wchar_t *p = codes.data(); - const wchar_t *pe = p + codes.size(); - const wchar_t *eof = pe; - sf::Color bgcolor($default_bg); - sf::Color color($default_fg); - sf::Color* target = &color; - - %% write init; - %% write exec; - - bool good = pe - p == 0; - - if(!good) { - p -= 10; - // dear cthuhlu, save me from the pain that is wstring - for(int i = 0; i < 100; i++) { - try { - print("{}", p[i] == 0x1B ? '^' : char(p[i])); - } catch(...) { - print("?=", int(p[i])); - } - } - } - - (void)ansi_parser_first_final; - (void)ansi_parser_error; - (void)ansi_parser_en_main; - - return good; -} diff --git a/assets/ai.json b/assets/ai.json new file mode 100644 index 0000000..446231e --- /dev/null +++ b/assets/ai.json @@ -0,0 +1,140 @@ +{ + "profile": { + "enemy_found": 0, + "enemy_dead": 1, + "health_good": 2, + "no_more_items": 3, + "no_more_enemies": 4, + "in_combat": 5, + "have_item": 6, + "have_healing": 7, + "detect_enemy": 8, + "tough_personality": 9, + "cant_move": 10 + }, + "actions": [ + { + "name": "find_enemy", + "cost": 5, + "needs": { + "detect_enemy": true, + "in_combat": false, + "no_more_enemies": false, + "enemy_found": false + }, + "effects": { + "in_combat": true, + "enemy_found": true + } + }, + { + "name": "run_away", + "cost": 0, + "needs": { + "tough_personality": false, + "in_combat": true, + "have_healing": false, + "health_good": false, + "cant_move": false + }, + "effects": { + "in_combat": false + } + }, + { + "name": "kill_enemy", + "cost": 10, + "needs": { + "no_more_enemies": false, + "in_combat": true, + "enemy_found": true, + "enemy_dead": false + }, + + "effects": { + "enemy_dead": true + } + }, + { + "name": "collect_items", + "cost": 5, + "needs": { + "no_more_enemies": true, + "no_more_items": false + }, + "effects": { + "no_more_items": true + } + }, + { + "name": "find_healing", + "cost": 2, + "needs": { + "have_healing": false, + "in_combat": false, + "health_good": false + }, + "effects": { + "health_good": true + } + }, + { + "name": "use_healing", + "cost": 1, + "needs": { + "have_item": true, + "have_healing": true, + "health_good": false + }, + "effects": { + "health_good": true + } + } + ], + "states": { + "Host::initial_state": { + "enemy_found": false, + "enemy_dead": false, + "health_good": true, + "no_more_items": false, + "no_more_enemies": false, + "in_combat": false, + "have_item": false, + "have_healing": false, + "detect_enemy": true, + "tough_personality": true + }, + "Host::final_state": { + "enemy_found": true, + "enemy_dead": true, + "health_good": true, + "no_more_items": true, + "in_combat": false, + "no_more_enemies": true + }, + "Enemy::initial_state": { + "detect_enemy": false, + "tough_personality": true, + "enemy_found": false, + "enemy_dead": false, + "health_good": true, + "in_combat": false + }, + "Enemy::final_state": { + "detect_enemy": true, + "enemy_found": true, + "enemy_dead": true, + "health_good": true + } + }, + "scripts": { + "Host::actions": + ["find_enemy", + "kill_enemy", + "collect_items", + "find_healing", + "use_healing"], + "Enemy::actions": + ["find_enemy", "run_away", "kill_enemy", "use_healing"] + } +} diff --git a/assets/animations.json b/assets/animations.json new file mode 100644 index 0000000..bfcb8b8 --- /dev/null +++ b/assets/animations.json @@ -0,0 +1,12 @@ +{ + "ritual_blanket": { + "_type": "Animation", + "easing": 0, + "ease_rate": 0.5, + "scale": 1.0, + "simple": false, + "frames": 3, + "speed": 0.2, + "stationary": true + } +} diff --git a/assets/armored_knight_1-256.png b/assets/armored_knight_1-256.png deleted file mode 100644 index b8617eb..0000000 Binary files a/assets/armored_knight_1-256.png and /dev/null differ diff --git a/assets/armored_knight_1-512.png b/assets/armored_knight_1-512.png deleted file mode 100644 index a8f780a..0000000 Binary files a/assets/armored_knight_1-512.png and /dev/null differ diff --git a/assets/axe_ranger-256.png b/assets/axe_ranger-256.png deleted file mode 100644 index e5387f3..0000000 Binary files a/assets/axe_ranger-256.png and /dev/null differ diff --git a/assets/blood_splatter-256.png b/assets/blood_splatter-256.png deleted file mode 100644 index d80a65f..0000000 Binary files a/assets/blood_splatter-256.png and /dev/null differ diff --git a/assets/bosses.json b/assets/bosses.json index 9e1bfa8..2d18cdb 100644 --- a/assets/bosses.json +++ b/assets/bosses.json @@ -3,7 +3,7 @@ "components": [ {"_type": "BossFight", "background": "boss_fight_background", - "stage": false, + "stage": null, "weapon_sound": "Sword_Hit_2" }, {"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false}, @@ -24,7 +24,7 @@ "components": [ {"_type": "BossFight", "background": "devils_fingers_background", - "stage": false, + "stage": "devils_fingers_stage", "weapon_sound": "Sword_Hit_2" }, {"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false}, diff --git a/assets/devils_fingers_background.jpg b/assets/bossfights/devils_fingers_background.jpg similarity index 100% rename from assets/devils_fingers_background.jpg rename to assets/bossfights/devils_fingers_background.jpg diff --git a/assets/devils_fingers_sprite.png b/assets/bossfights/devils_fingers_sprite.png similarity index 100% rename from assets/devils_fingers_sprite.png rename to assets/bossfights/devils_fingers_sprite.png diff --git a/assets/devils_fingers_stage.png b/assets/bossfights/devils_fingers_stage.png similarity index 100% rename from assets/devils_fingers_stage.png rename to assets/bossfights/devils_fingers_stage.png diff --git a/assets/down_the_well.jpg b/assets/bossfights/down_the_well.jpg similarity index 100% rename from assets/down_the_well.jpg rename to assets/bossfights/down_the_well.jpg diff --git a/assets/rat_king_2_frame_animation.png b/assets/bossfights/rat_king_2_frame_animation.png similarity index 100% rename from assets/rat_king_2_frame_animation.png rename to assets/bossfights/rat_king_2_frame_animation.png diff --git a/assets/rat_king_boss_fight_background.jpg b/assets/bossfights/rat_king_boss_fight_background.jpg similarity index 100% rename from assets/rat_king_boss_fight_background.jpg rename to assets/bossfights/rat_king_boss_fight_background.jpg diff --git a/assets/rat_king_boss_fight_sprite.png b/assets/bossfights/rat_king_boss_fight_sprite.png similarity index 100% rename from assets/rat_king_boss_fight_sprite.png rename to assets/bossfights/rat_king_boss_fight_sprite.png diff --git a/assets/tunnel_with_rocks.png b/assets/bossfights/tunnel_with_rocks.png similarity index 100% rename from assets/tunnel_with_rocks.png rename to assets/bossfights/tunnel_with_rocks.png diff --git a/assets/tunnel_with_rocks_stage.png b/assets/bossfights/tunnel_with_rocks_stage.png similarity index 100% rename from assets/tunnel_with_rocks_stage.png rename to assets/bossfights/tunnel_with_rocks_stage.png diff --git a/assets/ceiling_test-256.png b/assets/ceiling_test-256.png deleted file mode 100644 index 4bcac90..0000000 Binary files a/assets/ceiling_test-256.png and /dev/null differ diff --git a/assets/ceiling_test-512.png b/assets/ceiling_test-512.png deleted file mode 100644 index c1ad1f3..0000000 Binary files a/assets/ceiling_test-512.png and /dev/null differ diff --git a/assets/ceiling_worm-256.png b/assets/ceiling_worm-256.png deleted file mode 100644 index 805dafe..0000000 Binary files a/assets/ceiling_worm-256.png and /dev/null differ diff --git a/assets/cinqueda_1-256.png b/assets/cinqueda_1-256.png deleted file mode 100644 index f40dd84..0000000 Binary files a/assets/cinqueda_1-256.png and /dev/null differ diff --git a/assets/cinqueda_1-512.png b/assets/cinqueda_1-512.png deleted file mode 100644 index 772abd2..0000000 Binary files a/assets/cinqueda_1-512.png and /dev/null differ diff --git a/assets/config.json b/assets/config.json index ab53aec..a38faba 100644 --- a/assets/config.json +++ b/assets/config.json @@ -17,45 +17,233 @@ "Marmot_Scream_1": "assets/sounds/Creature_Sounds-Marmot_Scream_1.ogg", "blank": "assets/sounds/blank.ogg", "pickup": "assets/sounds/pickup.ogg", - "ambient_1": "assets/sounds/ambient_1.ogg" + "ambient_1": "assets/sounds/ambient_1.ogg", + "ui_click": "assets/sounds/ui_click.ogg", + "ui_hover": "assets/sounds/ui_hover.ogg", + "punch_cartoony": "assets/sounds/punch_cartoony.ogg", + "electric_shock_01": "assets/sounds/electric_shock_01.ogg", + "fireball_01": "assets/sounds/fireball_01.ogg", + "hp_status_80": "assets/sounds/hp_status_80.ogg", + "hp_status_60": "assets/sounds/hp_status_60.ogg", + "hp_status_30": "assets/sounds/hp_status_30.ogg", + "hp_status_10": "assets/sounds/hp_status_10.ogg", + "hp_status_00": "assets/sounds/hp_status_00.ogg" }, "sprites": { - "armored_knight": "assets/armored_knight_1-256.png", - "sword": "assets/cinqueda_1-512.png", - "rat_with_sword": "assets/rat_with_sword-256.png", - "rat_king": "assets/rat_king-256.png", - "rat_king_boss": "assets/rat_king_2_frame_animation.png", - "barrel_small": "assets/wood_barrel_small-256.png", - "hanging_brazier": "assets/hanging_brazier-256.png", - "torch_pillar": "assets/torch_pillar-256.png", - "torch_crappy": "assets/torch_crappy-256.png", - "torch_horizontal_floor": "assets/torch_horizontal_floor-256.png", - "evil_eye": "assets/evil_eye-sprites.png", - "peasant_girl": "assets/undead_peasant-256.png", - "grave_stone": "assets/grave_stone-256.png", - "floor": "assets/floor_tile_test-256.png", - "ceiling": "assets/ceiling_test-256.png", - "healing_potion_small": "assets/healing_potion_small-256.png", - "well_down": "assets/well_down-256.png", - "rope_vines_up": "assets/rope_vines_up-256.png", - "tripwire_trap": "assets/tripwire_trap-256.png", - "cinqueda": "assets/cinqueda_1-256.png", - "left_gui": "assets/left_gui.png", - "blood_splatter": "assets/blood_splatter-256.png", - "trash_button": "assets/trash_button.png", - "axe_ranger": "assets/axe_ranger-256.png", - "hairy_spider": "assets/hairy_spider-256.png", - "down_the_well": "assets/down_the_well.jpg", - "boss_fight_background": "assets/rat_king_boss_fight_background.jpg", - "devils_fingers_background": "assets/devils_fingers_background.jpg", - "devils_fingers_sprite": "assets/devils_fingers_sprite.png", - "devils_fingers_stage": "assets/devils_fingers_stage.png", - "tunnel_with_rocks": "assets/tunnel_with_rocks.png", - "tunnel_with_rocks_stage": "assets/tunnel_with_rocks_stage.png" + "gold_savior": + {"path": "assets/sprites/gold_savior.png", + "frame_width": 256, + "frame_height": 256 + }, + "armored_knight": + {"path": "assets/sprites/armored_knight_1.png", + "frame_width": 256, + "frame_height": 256 + }, + "axe_ranger": + {"path": "assets/sprites/axe_ranger.png", + "frame_width": 256, + "frame_height": 256 + }, + "hairy_spider": + {"path": "assets/sprites/hairy_spider.png", + "frame_width": 256, + "frame_height": 256 + }, + "rat_with_sword": + {"path": "assets/sprites/rat_with_sword.png", + "frame_width": 256, + "frame_height": 256 + }, + "rat_king_boss": + {"path": "assets/bossfights/rat_king_2_frame_animation.png", + "frame_width": 720, + "frame_height": 720 + }, + "barrel_small": + {"path": "assets/items/wood_barrel_small.png", + "frame_width": 256, + "frame_height": 256 + }, + "torch_pillar": + {"path": "assets/sprites/torch_pillar.png", + "frame_width": 256, + "frame_height": 256 + }, + "torch_crappy": + {"path": "assets/items/torch_crappy.png", + "frame_width": 256, + "frame_height": 256 + }, + "torch_horizontal_floor": + {"path": "assets/items/torch_horizontal_floor.png", + "frame_width": 256, + "frame_height": 256 + }, + "peasant_girl": + {"path": "assets/sprites/peasant_girl_2.png", + "frame_width": 256, + "frame_height": 256 + }, + "grave_stone": + {"path": "assets/sprites/grave_stone.png", + "frame_width": 256, + "frame_height": 256 + }, + "healing_potion_small": + {"path": "assets/items/healing_potion_small.png", + "frame_width": 256, + "frame_height": 256 + }, + "well_down": + {"path": "assets/sprites/well_down.png", + "frame_width": 256, + "frame_height": 256 + }, + "rope_vines_up": + {"path": "assets/sprites/rope_vines_up.png", + "frame_width": 256, + "frame_height": 256 + }, + "tripwire_trap": + {"path": "assets/sprites/tripwire_trap.png", + "frame_width": 256, + "frame_height": 256 + }, + "boss_fight_background": + {"path": "assets/bossfights/rat_king_boss_fight_background.jpg", + "frame_width": 1080, + "frame_height": 720 + }, + "devils_fingers_background": + {"path": "assets/bossfights/devils_fingers_background.jpg", + "frame_width": 1080, + "frame_height": 720 + }, + "devils_fingers_sprite": + {"path": "assets/bossfights/devils_fingers_sprite.png", + "frame_width": 720, + "frame_height": 720 + }, + "devils_fingers_stage": + {"path": "assets/bossfights/devils_fingers_stage.png", + "frame_width": 1080, + "frame_height": 720 + }, + "tunnel_with_rocks": + {"path": "assets/bossfights/tunnel_with_rocks.png", + "frame_width": 1080, + "frame_height": 720 + }, + "tunnel_with_rocks_stage": + {"path": "assets/bossfights/tunnel_with_rocks_stage.png", + "frame_width": 1080, + "frame_height": 720 + }, + "ritual_crafting_area": + {"path": "assets/ui/ritual_crafting_area.png", + "frame_width": 380, + "frame_height": 720 + }, + "full_screen_paper": + {"path": "assets/ui/full_screen_paper.png", + "frame_width": 1280, + "frame_height": 720 + }, + "broken_locket": + {"path": "assets/items/broken_locket.png", + "frame_width": 256, + "frame_height": 256 + }, + "broken_pen_knife": + {"path": "assets/items/broken_pen_knife.png", + "frame_width": 256, + "frame_height": 256 + }, + "broken_yoyo": + {"path": "assets/items/broken_yoyo.png", + "frame_width": 256, + "frame_height": 256 + }, + "chess_pawn": + {"path": "assets/items/chess_pawn.png", + "frame_width": 256, + "frame_height": 256 + }, + "dirty_kerchief": + {"path": "assets/items/dirty_kerchief.png", + "frame_width": 256, + "frame_height": 256 + }, + "leather_pouch": + {"path": "assets/items/leather_pouch.png", + "frame_width": 256, + "frame_height": 256 + }, + "mushroom": + {"path": "assets/items/mushroom.png", + "frame_width": 256, + "frame_height": 256 + }, + "pocket_watch": + {"path": "assets/items/pocket_watch.png", + "frame_width": 256, + "frame_height": 256 + }, + "rusty_nails": + {"path": "assets/items/rusty_nails.png", + "frame_width": 256, + "frame_height": 256 + }, + "severed_finger": + {"path": "assets/items/severed_finger.png", + "frame_width": 256, + "frame_height": 256 + }, + "stone_doll_cursed": + {"path": "assets/items/stone_doll_cursed.png", + "frame_width": 256, + "frame_height": 256 + }, + "dubious_combination": + {"path": "assets/items/dubious_combination.png", + "frame_width": 256, + "frame_height": 256 + }, + "dead_body": + {"path": "assets/sprites/dead_body.png", + "frame_width": 256, + "frame_height": 256 + }, + "dead_body_lootable": + {"path": "assets/sprites/dead_body_lootable.png", + "frame_width": 256, + "frame_height": 256 + } }, "worldgen": { - "enemy_probability": 80, - "empty_room_probability": 10, + "enemy_probability": 50, "device_probability": 10 + }, + "graphics": { + "smooth_textures": false + }, + "compass": { + "N": 65514, + "NE": 8663, + "E": 8594, + "SE": 8600, + "S": 65516, + "SW": 8665, + "W": 8592, + "NW": 8598 + }, + "theme": { + "NOTE": "colors are in assets/palette.json", + "padding": 3, + "border_px": 1, + "text_size": 20, + "label_size": 20, + "font_file_name": "assets/text.otf" } } diff --git a/assets/devices.json b/assets/devices.json index 44482fc..58b7897 100644 --- a/assets/devices.json +++ b/assets/devices.json @@ -7,13 +7,13 @@ "inventory_count": 0, "randomized": false, "components": [ - {"_type": "Tile", "display": "\u2ac5", - "foreground": [24, 205, 189], - "background": [24, 205, 189] + {"_type": "Tile", "display": 6105, + "foreground": "devices/fg:stairs_down", + "background": "devices/bg:stairs_down" }, {"_type": "Device", - "config": {"test": true}, - "events": ["Events::GUI::STAIRS_DOWN"]}, + "config": {}, + "events": ["STAIRS_DOWN"]}, {"_type": "Sprite", "name": "well_down", "width": 256, "height": 256, "scale": 1.0} ] }, @@ -24,13 +24,13 @@ "inventory_count": 0, "placement": "fixed", "components": [ - {"_type": "Tile", "display": "\u2259", - "foreground": [24, 205, 189], - "background": [24, 205, 189] + {"_type": "Tile", "display": 8793, + "foreground": "devices/fg:stairs_up", + "background": "devices/fg:stairs_up" }, {"_type": "Device", - "config": {"test": true}, - "events": ["Events::GUI::STAIRS_UP"]}, + "config": {}, + "events": ["STAIRS_UP"]}, {"_type": "Sprite", "name": "rope_vines_up", "width": 256, "height": 256, "scale": 1.0} ] }, @@ -40,14 +40,66 @@ "description": "Watch where you're going.", "inventory_count": 0, "components": [ - {"_type": "Tile", "display": "\u1ac7", - "foreground": [24, 205, 189], - "background": [24, 205, 189] + {"_type": "Tile", "display": 95, + "foreground": "devices/fg:tripwire", + "background": "devices/bg:tripwire" }, - {"_type": "Device", - "config": {"test": true}, - "events": ["Events::GUI::TRAP"]}, + {"_type": "Device", "config": {}, "events": ["TRAP"]}, {"_type": "Sprite", "name": "tripwire_trap", "width": 256, "height": 256, "scale": 1.0} ] + }, + "BARREL_SMALL": { + "id": "BARREL_SMALL", + "name": "Small Barrel", + "description": "A small rotten barrel that may hold things.", + "components": [ + {"_type": "Tile", "display": 85, + "foreground": "devices/fg:barrel", + "background": "devices/bg:barrel" + }, + {"_type": "Device", "config": {}, "events": ["LOOT_CONTAINER"]}, + {"_type": "Sprite", "name": "barrel_small", "width": 256, "height": 256, "scale": 1.0}, + {"_type": "Sound", "attack": "pickup", "death": "blank"} + ] + }, + "GRAVE_STONE": { + "id": "GRAVE_STONE", + "name": "Grave Stone", + "description": "Something died here. Was this your doing?", + "components": [ + {"_type": "Tile", "display": 8687, + "foreground": "devices/fg:grave_stone", + "background": "devices/bg:grave_stone" + }, + {"_type": "Device", "config": {}, "events": ["LOOT_CONTAINER"]}, + {"_type": "Sprite", "name": "grave_stone", "width": 256, "height": 256, "scale": 1.0}, + {"_type": "Sound", "attack": "pickup", "death": "blank"} + ] + }, + "DEAD_BODY_LOOTABLE": { + "id": "DEAD_BODY_LOOTABLE", + "name": "Grave Stone", + "description": "Something died here. Was this your doing?", + "components": [ + {"_type": "Tile", "display": 1890, + "foreground": "devices/fg:dead_body_lootable", + "background": "devices/bg:dead_body_lootable" + }, + {"_type": "Device", "config": {}, "events": ["LOOT_CONTAINER"]}, + {"_type": "Sprite", "name": "dead_body_lootable", "width": 256, "height": 256, "scale": 1.0}, + {"_type": "Sound", "attack": "pickup", "death": "blank"} + ] + }, + "DEAD_BODY": { + "id": "DEAD_BODY", + "name": "Something Dead", + "description": "You can't loot this, weirdo.", + "components": [ + {"_type": "Tile", "display": 1939, + "foreground": "devices/fg:dead_body", + "background": "devices/bg:dead_body" + }, + {"_type": "Sprite", "name": "dead_body", "width": 256, "height": 256, "scale": 1.0} + ] } } diff --git a/assets/enemies.json b/assets/enemies.json index ca46990..5fbd8a1 100644 --- a/assets/enemies.json +++ b/assets/enemies.json @@ -2,24 +2,43 @@ "PLAYER_TILE": { "placement": "fixed", "components": [ - {"_type": "Tile", "display": "\ua66b", - "foreground": [255, 200, 125], - "background": [30, 20, 75] + {"_type": "Tile", "display": 10733, + "foreground": "enemies/fg:player", + "background": "color:transparent" }, {"_type": "Combat", "hp": 200, "max_hp": 200, "damage": 10, "dead": false}, {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, - {"_type": "LightSource", "strength": 45, "radius": 2.0} + {"_type": "Collision", "has": true}, + {"_type": "LightSource", "strength": 35, "radius": 2.0} + ] + }, + "GOLD_SAVIOR": { + "components": [ + {"_type": "Tile", "display": 42586, + "foreground": "enemies/fg:gold_savior", + "background": "color:transparent" + }, + {"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 1, "dead": false}, + {"_type": "Collision", "has": true}, + {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, + {"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"}, + {"_type": "Personality", "hearing_distance": 5, "tough": true}, + {"_type": "Animation", "easing": 1, "ease_rate": 0.2, "scale": 0.1, "simple": true, "frames": 10, "speed": 0.3, "stationary": false}, + {"_type": "Sprite", "name": "gold_savior", "width": 256, "height": 256, "width": 256, "height": 256, "scale": 1.0}, + {"_type": "Sound", "attack": "Sword_Hit_2", "death": "Humanoid_Death_1"} ] }, "KNIGHT": { "components": [ - {"_type": "Tile", "display": "\u088d", - "foreground": [131, 213, 238], - "background": [30, 20, 75] + {"_type": "Tile", "display": 2216, + "foreground": "enemies/fg:knight", + "background": "color:transparent" }, {"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 1, "dead": false}, + {"_type": "Collision", "has": true}, {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, - {"_type": "EnemyConfig", "hearing_distance": 5}, + {"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"}, + {"_type": "Personality", "hearing_distance": 5, "tough": true}, {"_type": "Animation", "easing": 1, "ease_rate": 0.2, "scale": 0.1, "simple": true, "frames": 10, "speed": 0.3, "stationary": false}, {"_type": "Sprite", "name": "armored_knight", "width": 256, "height": 256, "width": 256, "height": 256, "scale": 1.0}, {"_type": "Sound", "attack": "Sword_Hit_2", "death": "Humanoid_Death_1"} @@ -27,41 +46,47 @@ }, "AXE_RANGER": { "components": [ - {"_type": "Tile", "display": "\u076a", - "foreground": [156, 172, 197], - "background": [30, 20, 75] + {"_type": "Tile", "display": 1898, + "foreground": "enemies/fg:axe_ranger", + "background": "color:transparent" }, {"_type": "Combat", "hp": 40, "max_hp": 40, "damage": 10, "dead": false}, + {"_type": "Collision", "has": true}, {"_type": "Motion", "dx": 0, "dy": 0, "random": true}, - {"_type": "EnemyConfig", "hearing_distance": 5}, + {"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"}, + {"_type": "Personality", "hearing_distance": 5, "tough": true}, {"_type": "Sprite", "name": "axe_ranger", "width": 256, "height": 256, "scale": 1.0}, - {"_type": "Animation", "easing": 3, "ease_rate": 0.5, "scale": 0.1, "simple": false, "frames": 2, "speed": 0.6, "stationary": false}, + {"_type": "Animation", "easing": 3, "ease_rate": 0.5, "scale": 0.1, "simple": true, "frames": 1, "speed": 0.6, "stationary": false}, {"_type": "Sound", "attack": "Sword_Hit_2", "death": "Ranger_1"} ] }, "RAT_GIANT": { "components": [ - {"_type": "Tile", "display": "\u08ac", - "foreground": [205, 164, 246], - "background": [30, 20, 75] + {"_type": "Tile", "display": 2220, + "foreground": "enemies/fg:rat_giant", + "background": "color:transparent" }, - {"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false}, + {"_type": "Combat", "hp": 50, "max_hp": 50, "damage": 2, "dead": false}, + {"_type": "Collision", "has": true}, {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, - {"_type": "EnemyConfig", "hearing_distance": 10}, - {"_type": "Animation", "easing": 3, "ease_rate": 0.5, "scale": 0.1, "simple": true, "frames": 10, "speed": 1.0, "stationary": false}, + {"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"}, + {"_type": "Personality", "hearing_distance": 5, "tough": true}, + {"_type": "Animation", "easing": 3, "ease_rate": 0.5, "scale": 0.1, "simple": true, "frames": 1, "speed": 1.0, "stationary": false}, {"_type": "Sprite", "name": "rat_with_sword", "width": 256, "height": 256, "scale": 1.0}, {"_type": "Sound", "attack": "Small_Rat", "death": "Creature_Death_1"} ] }, "SPIDER_GIANT_HAIRY": { "components": [ - {"_type": "Tile", "display": "\u08ea", - "foreground": [205, 164, 246], - "background": [30, 20, 75] + {"_type": "Tile", "display": 1218, + "foreground": "enemies/fg:spider_giant", + "background": "color:transparent" }, {"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false}, + {"_type": "Collision", "has": true}, {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, - {"_type": "EnemyConfig", "hearing_distance": 10}, + {"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"}, + {"_type": "Personality", "hearing_distance": 5, "tough": true}, {"_type": "Animation", "easing": 2, "ease_rate": 0.5, "scale": 0.1, "simple": true, "frames": 10, "speed": 1.0, "stationary": false}, {"_type": "Sprite", "name": "hairy_spider", "width": 256, "height": 256, "scale": 1.0}, {"_type": "Sound", "attack": "Spider_1", "death": "Spider_2"} diff --git a/assets/evil_eye-sprites.png b/assets/evil_eye-sprites.png deleted file mode 100644 index 4a38728..0000000 Binary files a/assets/evil_eye-sprites.png and /dev/null differ diff --git a/assets/evil_eye_test-256.png b/assets/evil_eye_test-256.png deleted file mode 100644 index c6364c3..0000000 Binary files a/assets/evil_eye_test-256.png and /dev/null differ diff --git a/assets/evil_eye_test-512.png b/assets/evil_eye_test-512.png deleted file mode 100644 index 0c9ee45..0000000 Binary files a/assets/evil_eye_test-512.png and /dev/null differ diff --git a/assets/floor_tile_test-256.png b/assets/floor_tile_test-256.png deleted file mode 100644 index 56a2c5e..0000000 Binary files a/assets/floor_tile_test-256.png and /dev/null differ diff --git a/assets/floor_tile_test-512.png b/assets/floor_tile_test-512.png deleted file mode 100644 index 26df65f..0000000 Binary files a/assets/floor_tile_test-512.png and /dev/null differ diff --git a/assets/grave_stone-256.png b/assets/grave_stone-256.png deleted file mode 100644 index 4bd9d4b..0000000 Binary files a/assets/grave_stone-256.png and /dev/null differ diff --git a/assets/hairy_spider-256.png b/assets/hairy_spider-256.png deleted file mode 100644 index be2b67f..0000000 Binary files a/assets/hairy_spider-256.png and /dev/null differ diff --git a/assets/hanging_brazier-256.png b/assets/hanging_brazier-256.png deleted file mode 100644 index 005defd..0000000 Binary files a/assets/hanging_brazier-256.png and /dev/null differ diff --git a/assets/healing_potion_small-256.png b/assets/healing_potion_small-256.png deleted file mode 100644 index 0a6c770..0000000 Binary files a/assets/healing_potion_small-256.png and /dev/null differ diff --git a/assets/icons.json b/assets/icons.json new file mode 100644 index 0000000..904cd97 --- /dev/null +++ b/assets/icons.json @@ -0,0 +1,12 @@ +{ + "healing_potion_small": + {"path": "assets/icons/healing_potion_small.png", + "frame_width": 96, + "frame_height": 96 + }, + "torch_horizontal_floor": + {"path": "assets/icons/torch_horizontal_floor.png", + "frame_width": 96, + "frame_height": 96 + } +} diff --git a/assets/icons/healing_potion_small.png b/assets/icons/healing_potion_small.png new file mode 100644 index 0000000..491579a Binary files /dev/null and b/assets/icons/healing_potion_small.png differ diff --git a/assets/icons/torch_horizontal_floor.png b/assets/icons/torch_horizontal_floor.png new file mode 100644 index 0000000..2382d7f Binary files /dev/null and b/assets/icons/torch_horizontal_floor.png differ diff --git a/assets/items.json b/assets/items.json index 5bb7d4d..5c25db3 100644 --- a/assets/items.json +++ b/assets/items.json @@ -6,87 +6,27 @@ "inventory_count": 1, "components": [ {"_type": "LightSource", "strength": 50, "radius": 2.5}, - {"_type": "Tile", "display": "\u0f08", - "foreground": [24, 120, 189], - "background": [230,120, 120] + {"_type": "Tile", "display": 3848, + "foreground": "items/fg:flame", + "background": "color:transparent" }, {"_type": "Sprite", "name": "torch_horizontal_floor", "width": 256, "height": 256, "scale": 1.0}, {"_type": "Sound", "attack": "pickup", "death": "blank"} ] }, - "SWORD_RUSTY": { - "id": "SWORD_RUSTY", - "name": "Rusty Junk Sword", - "description": "A sword left to rot in a deep hole where it acquired a patina of dirt and tetanus. You aren't sure if it's more deadly for you to hold it or for the people you stab with it.", - "inventory_count": 1, - "components": [ - {"_type": "Weapon", "damage": 15}, - {"_type": "Tile", "display": "\u1e37", - "foreground": [24, 120, 189], - "background": [24, 120, 189] - }, - {"_type": "Sprite", "name": "cinqueda", "width": 256, "height": 256, "scale": 1.0}, - {"_type": "Sound", "attack": "pickup", "death": "blank"} - ] - }, - "BARREL_SMALL": { - "id": "BARREL_SMALL", - "name": "Small Barrel", - "description": "A small rotten barrel that may hold things.", - "components": [ - {"_type": "Tile", "display": "\uaaea", - "foreground": [150, 100, 189], - "background": [150, 100, 189] - }, - {"_type": "Loot", "amount": 10}, - {"_type": "Sprite", "name": "barrel_small", "width": 256, "height": 256, "scale": 1.0}, - {"_type": "Sound", "attack": "pickup", "death": "blank"} - ], - "inventory_count": 1 - }, - "TORCH_PILLAR": { - "id": "TORCH_PILLAR", - "name": "Light Hanging from Ceiling", - "description": "Light Hanging from Ceiling", - "inventory_count": 0, - "components": [ - {"_type": "Tile", "display": "\u077e", - "foreground": [24, 205, 210], - "background": [24, 205, 210] - }, - {"_type": "LightSource", "strength": 50, "radius": 2.8}, - {"_type": "Sprite", "name": "torch_pillar", "width": 256, "height": 256, "scale": 1.0}, - {"_type": "Sound", "attack": "pickup", "death": "blank"} - ] - }, "POTION_HEALING_SMALL": { "id": "POTION_HEALING_SMALL", "name": "Small Healing Potion", "description": "A small healing potion.", "inventory_count": 1, "components": [ - {"_type": "Tile", "display": "\u03eb", - "foreground": [255, 205, 189], - "background": [255, 205, 189] + {"_type": "Tile", "display": 1003, + "foreground": "items/fg:potion", + "background": "color:transparent" }, {"_type": "Curative", "hp": 20}, {"_type": "Sprite", "name": "healing_potion_small", "width": 256, "height": 256, "scale": 1.0}, {"_type": "Sound", "attack": "pickup", "death": "blank"} ] - }, - "GRAVE_STONE": { - "id": "GRAVE_STONE", - "name": "Grave Stone", - "description": "Something died here. Was this your doing?", - "inventory_count": 1, - "components": [ - {"_type": "Tile", "display": "\u21ef", - "foreground": [32, 123, 164], - "background": [24, 205, 189] - }, - {"_type": "Loot", "amount": 10}, - {"_type": "Sprite", "name": "grave_stone", "width": 256, "height": 256, "scale": 1.0}, - {"_type": "Sound", "attack": "pickup", "death": "blank"} - ] } } diff --git a/assets/items/broken_locket.png b/assets/items/broken_locket.png new file mode 100644 index 0000000..5c138c2 Binary files /dev/null and b/assets/items/broken_locket.png differ diff --git a/assets/items/broken_pen_knife.png b/assets/items/broken_pen_knife.png new file mode 100644 index 0000000..e802d87 Binary files /dev/null and b/assets/items/broken_pen_knife.png differ diff --git a/assets/items/broken_yoyo.png b/assets/items/broken_yoyo.png new file mode 100644 index 0000000..e848cb2 Binary files /dev/null and b/assets/items/broken_yoyo.png differ diff --git a/assets/items/chess_pawn.png b/assets/items/chess_pawn.png new file mode 100644 index 0000000..c79180b Binary files /dev/null and b/assets/items/chess_pawn.png differ diff --git a/assets/items/cinqueda.png b/assets/items/cinqueda.png new file mode 100644 index 0000000..abf4e34 Binary files /dev/null and b/assets/items/cinqueda.png differ diff --git a/assets/items/dirty_kerchief.png b/assets/items/dirty_kerchief.png new file mode 100644 index 0000000..8618ada Binary files /dev/null and b/assets/items/dirty_kerchief.png differ diff --git a/assets/items/dubious_combination.png b/assets/items/dubious_combination.png new file mode 100644 index 0000000..8247a59 Binary files /dev/null and b/assets/items/dubious_combination.png differ diff --git a/assets/items/healing_postion_small.png b/assets/items/healing_postion_small.png new file mode 100644 index 0000000..a2c6f54 Binary files /dev/null and b/assets/items/healing_postion_small.png differ diff --git a/assets/items/healing_potion_small.png b/assets/items/healing_potion_small.png new file mode 100644 index 0000000..b3c8558 Binary files /dev/null and b/assets/items/healing_potion_small.png differ diff --git a/assets/items/leather_pouch.png b/assets/items/leather_pouch.png new file mode 100644 index 0000000..dc57254 Binary files /dev/null and b/assets/items/leather_pouch.png differ diff --git a/assets/items/mushroom.png b/assets/items/mushroom.png new file mode 100644 index 0000000..f6e8bc4 Binary files /dev/null and b/assets/items/mushroom.png differ diff --git a/assets/items/pocket_watch.png b/assets/items/pocket_watch.png new file mode 100644 index 0000000..07821f7 Binary files /dev/null and b/assets/items/pocket_watch.png differ diff --git a/assets/items/rusty_nails.png b/assets/items/rusty_nails.png new file mode 100644 index 0000000..f1eb884 Binary files /dev/null and b/assets/items/rusty_nails.png differ diff --git a/assets/items/severed_finger.png b/assets/items/severed_finger.png new file mode 100644 index 0000000..187def8 Binary files /dev/null and b/assets/items/severed_finger.png differ diff --git a/assets/items/stone_doll_cursed.png b/assets/items/stone_doll_cursed.png new file mode 100644 index 0000000..e99ef76 Binary files /dev/null and b/assets/items/stone_doll_cursed.png differ diff --git a/assets/items/torch_crappy.png b/assets/items/torch_crappy.png new file mode 100644 index 0000000..c31ee6d Binary files /dev/null and b/assets/items/torch_crappy.png differ diff --git a/assets/items/torch_horizontal_floor.png b/assets/items/torch_horizontal_floor.png new file mode 100644 index 0000000..474ede9 Binary files /dev/null and b/assets/items/torch_horizontal_floor.png differ diff --git a/assets/items/wood_barrel_small.png b/assets/items/wood_barrel_small.png new file mode 100644 index 0000000..a172fd1 Binary files /dev/null and b/assets/items/wood_barrel_small.png differ diff --git a/assets/left_gui.png b/assets/left_gui.png deleted file mode 100644 index a16cdac..0000000 Binary files a/assets/left_gui.png and /dev/null differ diff --git a/assets/map_tiles.json b/assets/map_tiles.json new file mode 100644 index 0000000..a0e2623 --- /dev/null +++ b/assets/map_tiles.json @@ -0,0 +1,140 @@ +[ + { + "centered": false, + "display": 35, + "x": 0, + "y": 0 + }, + { + "centered": false, + "display": 8284, + "x": 64, + "y": 0 + }, + { + "centered": false, + "display": 11590, + "x": 128, + "y": 0 + }, + { + "centered": false, + "display": 10899, + "x": 192, + "y": 0 + }, + { + "centered": false, + "display": 9256, + "x": 256, + "y": 0 + }, + { + "centered": false, + "display": 9608, + "x": 320, + "y": 0 + }, + { + "centered": false, + "display": 10747, + "x": 384, + "y": 0 + }, + { + "centered": false, + "display": 8285, + "x": 448, + "y": 0 + }, + { + "centered": true, + "display": 1003, + "x": 512, + "y": 0 + }, + { + "centered": true, + "display": 3848, + "x": 576, + "y": 0 + }, + { + "centered": true, + "display": 85, + "x": 0, + "y": 64 + }, + { + "centered": true, + "display": 1939, + "x": 64, + "y": 64 + }, + { + "centered": true, + "display": 1890, + "x": 128, + "y": 64 + }, + { + "centered": true, + "display": 8687, + "x": 192, + "y": 64 + }, + { + "centered": true, + "display": 6105, + "x": 256, + "y": 64 + }, + { + "centered": true, + "display": 8793, + "x": 320, + "y": 64 + }, + { + "centered": true, + "display": 95, + "x": 384, + "y": 64 + }, + { + "centered": true, + "display": 1898, + "x": 448, + "y": 64 + }, + { + "centered": true, + "display": 42586, + "x": 512, + "y": 64 + }, + { + "centered": true, + "display": 2216, + "x": 576, + "y": 64 + }, + { + "centered": true, + "display": 10733, + "x": 0, + "y": 128 + }, + { + "centered": true, + "display": 2220, + "x": 64, + "y": 128 + }, + { + "centered": true, + "display": 1218, + "x": 128, + "y": 128 + } +] diff --git a/assets/map_tiles.png b/assets/map_tiles.png new file mode 100644 index 0000000..67c5ae3 Binary files /dev/null and b/assets/map_tiles.png differ diff --git a/assets/palette.json b/assets/palette.json new file mode 100644 index 0000000..3fde82c --- /dev/null +++ b/assets/palette.json @@ -0,0 +1,81 @@ +{ + "color": { + "transparent": [100, 100, 100, 100], + "BAD": [255, 0, 0] + }, + "gui/theme": { + "black": [0, 0, 0, 255], + "dark_dark": [10, 10, 10, 255], + "dark_mid": [30, 30, 30, 255], + "dark_light": [60, 60, 60, 255], + "mid": [100, 100, 100, 255], + "light_dark": [150, 150, 150, 255], + "light_mid": [200, 200, 200, 255], + "light_light": [230, 230, 230, 255], + "white": [255, 255, 255, 255], + "fill_color": "gui/theme:dark_mid", + "text_color": "gui/theme:light_light", + "bg_color": "gui/theme:mid", + "border_color": "gui/theme:dark_dark", + "bg_color_dark": "gui/theme:black" + }, + "map/theme": { + "black": [0, 0, 0, 255], + "dark_dark": [10, 10, 10, 255], + "dark_mid": [30, 30, 30, 255], + "dark_light": [60, 60, 60, 255], + "mid": [100, 100, 100, 255], + "light_dark": [150, 150, 150, 255], + "light_mid": [200, 200, 200, 255], + "light_light": [230, 230, 230, 255], + "white": [255, 255, 255, 255] + }, + "items/fg": { + "flame": "map/theme:white", + "potion": "map/theme:white" + }, + "enemies/fg": { + "player": "map/theme:white", + "gold_savior": "map/theme:white", + "knight": "map/theme:white", + "axe_ranger": "map/theme:white", + "rat_giant": "map/theme:white", + "spider_giant": "map/theme:white" + }, + "tiles/fg": { + "floor_tile": "map/theme:mid", + "wall_plain": "map/theme:dark_mid", + "wall_moss": "map/theme:dark_light", + "ceiling_black": "color:transparent", + "lava_floor": [200, 100, 100], + "gray_stone_floor_light": [40, 60, 180], + "wood_wall": "map/theme:dark_mid" + }, + "tiles/bg": { + "floor_tile": "map/theme:dark_dark", + "wall_plain": "map/theme:dark_dark", + "wall_moss": "map/theme:light_dark", + "ceiling_black": "color:transparent", + "lava_floor": "map/theme:dark_dark", + "gray_stone_floor_light": "map/theme:dark_mid", + "wood_wall": "map/theme:dark_dark" + }, + "devices/fg": { + "stairs_down": [24, 205, 189], + "stairs_up": [24, 205, 189], + "tripwire": [24, 205, 189], + "barrel": [150, 100, 189], + "grave_stone": [32, 123, 164], + "dead_body": [32, 123, 164], + "dead_body_lootable": [32, 123, 164] + }, + "devices/bg": { + "stairs_down": [24, 205, 189], + "stairs_up": [24, 205, 189], + "tripwire": [24, 205, 189], + "barrel": [150, 100, 189], + "grave_stone": [24, 205, 189], + "dead_body": [24, 205, 189], + "dead_body_lootable": [24, 205, 189] + } +} diff --git a/assets/rat-king-boss-fight-test-small.jpg b/assets/rat-king-boss-fight-test-small.jpg deleted file mode 100644 index 5ad3dd6..0000000 Binary files a/assets/rat-king-boss-fight-test-small.jpg and /dev/null differ diff --git a/assets/rat_king-256.png b/assets/rat_king-256.png deleted file mode 100644 index a2315c4..0000000 Binary files a/assets/rat_king-256.png and /dev/null differ diff --git a/assets/rat_with_sword-256.png b/assets/rat_with_sword-256.png deleted file mode 100644 index 86e8ecb..0000000 Binary files a/assets/rat_with_sword-256.png and /dev/null differ diff --git a/assets/rituals.json b/assets/rituals.json new file mode 100644 index 0000000..6b34963 --- /dev/null +++ b/assets/rituals.json @@ -0,0 +1,202 @@ +{ + "profile": { + "has_spikes": 0, + "has_magick": 1, + "shiny_bauble": 2, + "cursed_item": 3, + "$does_physical": 4, + "$does_magick": 5, + "$does_damage": 6, + "$user_cursed": 7, + "$does_healing": 8, + "$damage_boost": 9, + "$large_boost": 10, + "$is_complete": 11 + }, + "actions": [ + { + "name": "pierce_type", + "cost": 100, + "needs": { + "has_spikes": true, + "$is_complete": false + }, + "effects": { + "$does_physical": true, + "$does_damage": true + } + }, + { + "name": "magick_type", + "cost": 100, + "needs": { + "$is_complete": false, + "has_magick": true + }, + "effects": { + "$does_magick": true, + "$does_damage": true + } + }, + { + "name": "combined", + "cost": 0, + "needs": { + "$does_damage": true + }, + "effects": { + "$is_complete": true + } + }, + { + "name": "boost_magick", + "cost": 0, + "needs": { + "shiny_bauble": true, + "$does_magick": true, + "$does_damage": true, + "$is_complete": false, + "$user_cursed": false + }, + "effects": { + "$damage_boost": true + } + }, + { + "name": "boost_damage_large", + "cost": 0, + "needs": { + "$user_cursed": true, + "$is_complete": false, + "$does_damage": true + }, + "effects": { + "$large_boost": true + } + }, + { + "name": "curses_user", + "cost": 1000, + "needs": { + "$is_complete": false, + "cursed_item": true + }, + "effects": { + "$user_cursed": true + } + }, + { + "name": "heals_user", + "cost": 0, + "needs": { + "cursed_item": true, + "$does_damage": false + }, + "effects": { + "$does_healing": true, + "$is_complete": true + } + } + ], + "states": { + "initial": { + "shiny_bauble": false, + "cursed_item": false, + "has_spikes": false, + "has_magick": false, + "$user_cursed": false, + "$does_damage": false, + "$is_complete": false, + "$does_healing": false, + "$does_magick": false, + "$does_physical": false, + "$large_boost": false, + "$damage_boost": false + }, + "final": { + "$user_cursed": true, + "$does_damage": true, + "$is_complete": true, + "$does_healing": true, + "$does_magick": true, + "$does_physical": true, + "$large_boost": true, + "$damage_boost": true + } + }, + "scripts": { + "actions": [ + "boost_magick", + "pierce_type", + "magick_type", + "heals_user", + "curses_user", + "boost_damage_large", + "combined" + ] + }, + "effects": { + "boost_magick": { + "damage": 10, + "kind": 2, + "element": 2, + "probability": 1.0 + }, + "pierce_type": { + "damage": 11, + "kind": 1, + "probability": 1.0 + }, + "magick_type": { + "damage": 12, + "kind": 2, + "element": 1, + "probability": 1.0 + }, + "heals_user": { + "damage": 13, + "probability": 1.0 + }, + "curses_user": { + "damage": 14, + "probability": 0.5 + }, + "boost_damage_large": { + "damage": 15, + "probability": 1.0 + }, + "combined": { + "damage": 16, + "probability": 1.0 + } + }, + "junk": { + "chess_pawn": { + "name": "chess_pawn", + "provides": ["cursed_item"] + }, + "dirty_kerchief": { + "name": "dirty_kerchief", + "provides": ["has_magick"] + }, + "mushroom": { + "name": "mushroom", + "provides": ["has_magick"] + }, + "pocket_watch": { + "name": "pocket_watch", + "provides": ["shiny_bauble"] + }, + "rusty_nails": { + "name": "rusty_nails", + "provides": ["has_spikes"] + }, + "severed_finger": { + "name": "severed_finger", + "provides": ["cursed_item"] + } + }, + "starting_junk": [ + "pocket_watch", "mushroom", "rusty_nails" + ] +} diff --git a/assets/rituals/broken_locket-128.png b/assets/rituals/broken_locket-128.png new file mode 100644 index 0000000..b74c845 Binary files /dev/null and b/assets/rituals/broken_locket-128.png differ diff --git a/assets/rituals/broken_locket-64.png b/assets/rituals/broken_locket-64.png new file mode 100644 index 0000000..c4b692e Binary files /dev/null and b/assets/rituals/broken_locket-64.png differ diff --git a/assets/rituals/broken_pen_knife-128.png b/assets/rituals/broken_pen_knife-128.png new file mode 100644 index 0000000..b82e363 Binary files /dev/null and b/assets/rituals/broken_pen_knife-128.png differ diff --git a/assets/rituals/broken_pen_knife-64.png b/assets/rituals/broken_pen_knife-64.png new file mode 100644 index 0000000..b1cde5f Binary files /dev/null and b/assets/rituals/broken_pen_knife-64.png differ diff --git a/assets/rituals/broken_yoyo-128.png b/assets/rituals/broken_yoyo-128.png new file mode 100644 index 0000000..c2f1630 Binary files /dev/null and b/assets/rituals/broken_yoyo-128.png differ diff --git a/assets/rituals/broken_yoyo-64.png b/assets/rituals/broken_yoyo-64.png new file mode 100644 index 0000000..d900691 Binary files /dev/null and b/assets/rituals/broken_yoyo-64.png differ diff --git a/assets/rituals/chess_pawn-128.png b/assets/rituals/chess_pawn-128.png new file mode 100644 index 0000000..b730d4f Binary files /dev/null and b/assets/rituals/chess_pawn-128.png differ diff --git a/assets/rituals/chess_pawn-64.png b/assets/rituals/chess_pawn-64.png new file mode 100644 index 0000000..6f0e6bc Binary files /dev/null and b/assets/rituals/chess_pawn-64.png differ diff --git a/assets/rituals/dirty_kerchief-128.png b/assets/rituals/dirty_kerchief-128.png new file mode 100644 index 0000000..86d091a Binary files /dev/null and b/assets/rituals/dirty_kerchief-128.png differ diff --git a/assets/rituals/dirty_kerchief-64.png b/assets/rituals/dirty_kerchief-64.png new file mode 100644 index 0000000..f9bb1eb Binary files /dev/null and b/assets/rituals/dirty_kerchief-64.png differ diff --git a/assets/rituals/dubious_combination-128.png b/assets/rituals/dubious_combination-128.png new file mode 100644 index 0000000..7a752b4 Binary files /dev/null and b/assets/rituals/dubious_combination-128.png differ diff --git a/assets/rituals/dubious_combination-64.png b/assets/rituals/dubious_combination-64.png new file mode 100644 index 0000000..6068cc6 Binary files /dev/null and b/assets/rituals/dubious_combination-64.png differ diff --git a/assets/rituals/leather_pouch-128.png b/assets/rituals/leather_pouch-128.png new file mode 100644 index 0000000..0f6ea12 Binary files /dev/null and b/assets/rituals/leather_pouch-128.png differ diff --git a/assets/rituals/leather_pouch-64.png b/assets/rituals/leather_pouch-64.png new file mode 100644 index 0000000..552935d Binary files /dev/null and b/assets/rituals/leather_pouch-64.png differ diff --git a/assets/rituals/mushroom-128.png b/assets/rituals/mushroom-128.png new file mode 100644 index 0000000..8cc7723 Binary files /dev/null and b/assets/rituals/mushroom-128.png differ diff --git a/assets/rituals/mushroom-64.png b/assets/rituals/mushroom-64.png new file mode 100644 index 0000000..3eac5d3 Binary files /dev/null and b/assets/rituals/mushroom-64.png differ diff --git a/assets/rituals/pocket_watch-128.png b/assets/rituals/pocket_watch-128.png new file mode 100644 index 0000000..fb20d80 Binary files /dev/null and b/assets/rituals/pocket_watch-128.png differ diff --git a/assets/rituals/pocket_watch-64.png b/assets/rituals/pocket_watch-64.png new file mode 100644 index 0000000..ecddcd7 Binary files /dev/null and b/assets/rituals/pocket_watch-64.png differ diff --git a/assets/rituals/rusty_nails-128.png b/assets/rituals/rusty_nails-128.png new file mode 100644 index 0000000..feddbfc Binary files /dev/null and b/assets/rituals/rusty_nails-128.png differ diff --git a/assets/rituals/rusty_nails-64.png b/assets/rituals/rusty_nails-64.png new file mode 100644 index 0000000..7f7d8a1 Binary files /dev/null and b/assets/rituals/rusty_nails-64.png differ diff --git a/assets/rituals/severed_finger-128.png b/assets/rituals/severed_finger-128.png new file mode 100644 index 0000000..1672175 Binary files /dev/null and b/assets/rituals/severed_finger-128.png differ diff --git a/assets/rituals/severed_finger-64.png b/assets/rituals/severed_finger-64.png new file mode 100644 index 0000000..bea8dc0 Binary files /dev/null and b/assets/rituals/severed_finger-64.png differ diff --git a/assets/rituals/stone_doll_cursed-128.png b/assets/rituals/stone_doll_cursed-128.png new file mode 100644 index 0000000..e88b7e0 Binary files /dev/null and b/assets/rituals/stone_doll_cursed-128.png differ diff --git a/assets/rituals/stone_doll_cursed-64.png b/assets/rituals/stone_doll_cursed-64.png new file mode 100644 index 0000000..ac753d9 Binary files /dev/null and b/assets/rituals/stone_doll_cursed-64.png differ diff --git a/assets/rope_vines_up-256.png b/assets/rope_vines_up-256.png deleted file mode 100644 index 31159e9..0000000 Binary files a/assets/rope_vines_up-256.png and /dev/null differ diff --git a/assets/shaders.json b/assets/shaders.json new file mode 100644 index 0000000..7a03ad7 --- /dev/null +++ b/assets/shaders.json @@ -0,0 +1,22 @@ +{ + "ui_shader": { + "file_name": "assets/shaders/ui_shader.frag", + "type": "fragment" + }, + "ERROR": { + "file_name": "assets/shaders/ui_error.frag", + "type": "fragment" + }, + "rayview_sprites": { + "file_name": "assets/shaders/rayview_sprites.frag", + "type": "fragment" + }, + "flame": { + "file_name": "assets/shaders/flame_trash.frag", + "type": "fragment" + }, + "lightning": { + "file_name": "assets/shaders/lightning_attack.frag", + "type": "fragment" + } +} diff --git a/assets/shaders/flame_trash.frag b/assets/shaders/flame_trash.frag new file mode 100644 index 0000000..092e4fd --- /dev/null +++ b/assets/shaders/flame_trash.frag @@ -0,0 +1,79 @@ +#version 120 +uniform vec2 u_resolution; +uniform float u_time; +uniform sampler2D source; +uniform float u_mouse; +uniform float value = 0.2; +uniform int octaves=8; + +float random (in vec2 st) { + return fract(sin(dot(st.xy, + vec2(12.9898,78.233)))* + 43758.5453123); +} + +float noise(in vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + + float a = random(i); + float b = random(i + vec2(1.0, 0.0)); + float c = random(i + vec2(0.0, 1.0)); + float d = random(i + vec2(1.0, 1.0)); + + vec2 u = f * f * (3.0 - 2.0 * f); + + return mix(a, b, u.x) + + (c - a) * u.y * (1.0 - u.x) + + (d - b) * u.x * u.y; +} + +float fbm(in vec2 st) { + float v = 0.0; + float a = 0.5; + vec2 shift = vec2(100.0); + mat2 rot = mat2(cos(0.5), sin(0.5), + -sin(0.5), cos(0.5)); + + for(int i = 0; i < octaves; i++) { + v += a * noise(st); + st = rot * st * 2.0 + shift; + a *= 0.5; + } + + return v; +} + +void main() { + vec2 st = gl_FragCoord.xy/u_resolution.xy * 3.0; + vec3 color = vec3(0.0); + + float speed = u_time * 10.0; + float value = 0.8; // cos(u_time) * cos(u_time); + + vec2 q = vec2(0.0); + q.x = fbm(st + 0.00 * speed); + q.y = fbm(st + vec2(1.0)); + + vec2 r = vec2(0,0); + r.x += fbm( st + 1.0*q + vec2(1.0, 0.0)+ 0.15* speed ); + r.y += fbm( st + 1.0*q + vec2(-1.0, 0.0)+ 0.126* speed); + + float f = fbm(st * r); + + color = mix(vec3(0.666667,0.619608, 0.122777), + vec3(0.666667,0.666667,0.498039), + clamp((f*f)*4.0,0.0,1.0)); + + color = mix(color, + vec3(0.666667, 0.122222, 0.0666667), + clamp(length(r.x), 0.0, 1.0)); + + color *= (f*f*f+0.5*f*f+0.6*f) * value; + + vec4 pixel = texture2D(source, gl_TexCoord[0].xy); + + float mask = color.r * pixel.a; + + gl_FragColor = gl_Color * vec4(color, mask) + pixel; +} diff --git a/assets/shaders/lightning_attack.frag b/assets/shaders/lightning_attack.frag new file mode 100644 index 0000000..eb41295 --- /dev/null +++ b/assets/shaders/lightning_attack.frag @@ -0,0 +1,79 @@ +#version 120 +uniform vec2 u_resolution; +uniform float u_time; +uniform sampler2D source; +uniform float u_mouse; +uniform float value = 0.2; +uniform int octaves=8; + +float random (in vec2 st) { + return fract(sin(dot(st.xy, + vec2(12.9898,78.233)))* + 43758.5453123); +} + +float noise(in vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + + float a = random(i); + float b = random(i + vec2(1.0, 0.0)); + float c = random(i + vec2(0.0, 1.0)); + float d = random(i + vec2(1.0, 1.0)); + + vec2 u = f * f * (3.0 - 2.0 * f); + + return mix(a, b, u.x) + + (c - a) * u.y * (1.0 - u.x) + + (d - b) * u.x * u.y; +} + +float fbm(in vec2 st) { + float v = 0.0; + float a = 0.5; + vec2 shift = vec2(100.0); + mat2 rot = mat2(cos(0.5), sin(0.5), + -sin(0.5), cos(0.5)); + + for(int i = 0; i < octaves; i++) { + v += a * noise(st); + st = rot * st * 2.0 + shift; + a *= 0.5; + } + + return v; +} + +void main() { + vec2 st = gl_FragCoord.xy/u_resolution.xy * 3.0; + vec3 color = vec3(0.0); + + float speed = u_time * 40.0; + float value = cos(u_time) * cos(u_time); + + vec2 q = vec2(0.0); + q.x = fbm(st + 0.00 * speed); + q.y = fbm(st + vec2(1.0)); + + vec2 r = vec2(0,0); + r.x += fbm( st + 1.0*q + vec2(1.0, 0.0)+ 0.15* speed ); + r.y += fbm( st + 1.0*q + vec2(-1.0, 0.0)+ 0.126* speed); + + float f = fbm(st / r); + + color = mix(vec3(0.122777,0.619608, 0.666667), + vec3(0.498039,0.666667,0.666667), + clamp((f*f)*4.0,0.0,1.0)); + + color = mix(color, + vec3(0.0666667, 0.122222, 0.666667), + clamp(length(r.x), 0.0, 1.0)); + + color *= (f*f*f+0.5*f*f+0.6*f) * value; + + vec4 pixel = texture2D(source, gl_TexCoord[0].xy); + + float mask = color.r * pixel.a; + + gl_FragColor = gl_Color * vec4(color, mask) + pixel; +} diff --git a/shaders/modal.frag b/assets/shaders/rayview_sprites.frag similarity index 100% rename from shaders/modal.frag rename to assets/shaders/rayview_sprites.frag diff --git a/assets/shaders/ui_error.frag b/assets/shaders/ui_error.frag new file mode 100644 index 0000000..29ccc8c --- /dev/null +++ b/assets/shaders/ui_error.frag @@ -0,0 +1,18 @@ +uniform vec2 u_resolution; +uniform vec2 u_mouse; +uniform float u_duration; +uniform float u_time; +uniform float u_time_end; +uniform sampler2D texture; +uniform bool is_shape; + +void main() { + if(is_shape) { + vec4 color = vec4(1.0, 0.0, 0.0, 1.0); + gl_FragColor = gl_Color * color; + } else { + vec4 pixel = texture2D(texture, gl_TexCoord[0].xy); + vec4 color = vec4(1.0, 0.0, 0.0, 1.0); + gl_FragColor = gl_Color * color * pixel; + } +} diff --git a/assets/shaders/ui_shader.frag b/assets/shaders/ui_shader.frag new file mode 100644 index 0000000..73b77b4 --- /dev/null +++ b/assets/shaders/ui_shader.frag @@ -0,0 +1,29 @@ +uniform vec2 u_resolution; +uniform vec2 u_mouse; +uniform float u_duration; +uniform float u_time; +uniform float u_time_end; +uniform sampler2D texture; +uniform bool is_shape; +uniform bool hover; + +vec4 blink() { + if(hover) { + return vec4(0.95, 0.95, 1.0, 1.0); + } else { + float tick = (u_time_end - u_time) / u_duration; + float blink = mix(0.5, 1.0, tick); + return vec4(blink, blink, blink, 1.0); + } +} + +void main() { + vec4 color = blink(); + + if(!is_shape) { + vec4 pixel = texture2D(texture, gl_TexCoord[0].xy); + color *= pixel; + } + + gl_FragColor = gl_Color * color; +} diff --git a/assets/shaders/ui_shape_shader.frag b/assets/shaders/ui_shape_shader.frag new file mode 100644 index 0000000..c16d6ea --- /dev/null +++ b/assets/shaders/ui_shape_shader.frag @@ -0,0 +1,12 @@ +uniform vec2 u_resolution; +uniform vec2 u_mouse; +uniform float u_duration; +uniform float u_time; +uniform float u_time_end; + +void main() { + float tick = (u_time_end - u_time) / u_duration; + float blink = smoothstep(1.0, 0.5, tick); + vec4 color = vec4(blink, blink, blink, 1.0); + gl_FragColor = gl_Color * color; +} diff --git a/assets/sounds/electric_shock_01.ogg b/assets/sounds/electric_shock_01.ogg new file mode 100644 index 0000000..d9d3b0b Binary files /dev/null and b/assets/sounds/electric_shock_01.ogg differ diff --git a/assets/sounds/fireball_01.ogg b/assets/sounds/fireball_01.ogg new file mode 100644 index 0000000..d819833 Binary files /dev/null and b/assets/sounds/fireball_01.ogg differ diff --git a/assets/sounds/hp_status_00.ogg b/assets/sounds/hp_status_00.ogg new file mode 100644 index 0000000..dad539b Binary files /dev/null and b/assets/sounds/hp_status_00.ogg differ diff --git a/assets/sounds/hp_status_10.ogg b/assets/sounds/hp_status_10.ogg new file mode 100644 index 0000000..08c1dc2 Binary files /dev/null and b/assets/sounds/hp_status_10.ogg differ diff --git a/assets/sounds/hp_status_30.ogg b/assets/sounds/hp_status_30.ogg new file mode 100644 index 0000000..b9c8ddc Binary files /dev/null and b/assets/sounds/hp_status_30.ogg differ diff --git a/assets/sounds/hp_status_60.ogg b/assets/sounds/hp_status_60.ogg new file mode 100644 index 0000000..84849dc Binary files /dev/null and b/assets/sounds/hp_status_60.ogg differ diff --git a/assets/sounds/hp_status_80.ogg b/assets/sounds/hp_status_80.ogg new file mode 100644 index 0000000..fd2d2b6 Binary files /dev/null and b/assets/sounds/hp_status_80.ogg differ diff --git a/assets/sounds/punch_cartoony.ogg b/assets/sounds/punch_cartoony.ogg new file mode 100644 index 0000000..1a812cc Binary files /dev/null and b/assets/sounds/punch_cartoony.ogg differ diff --git a/assets/sounds/ui_click.ogg b/assets/sounds/ui_click.ogg new file mode 100644 index 0000000..7ff2e8c Binary files /dev/null and b/assets/sounds/ui_click.ogg differ diff --git a/assets/sounds/ui_hover.ogg b/assets/sounds/ui_hover.ogg new file mode 100644 index 0000000..be6e679 Binary files /dev/null and b/assets/sounds/ui_hover.ogg differ diff --git a/assets/sprites/armored_knight_1.png b/assets/sprites/armored_knight_1.png new file mode 100644 index 0000000..d32e3f8 Binary files /dev/null and b/assets/sprites/armored_knight_1.png differ diff --git a/assets/sprites/axe_ranger.png b/assets/sprites/axe_ranger.png new file mode 100644 index 0000000..ecd3a5d Binary files /dev/null and b/assets/sprites/axe_ranger.png differ diff --git a/assets/sprites/dead_body.png b/assets/sprites/dead_body.png new file mode 100644 index 0000000..8337eda Binary files /dev/null and b/assets/sprites/dead_body.png differ diff --git a/assets/sprites/dead_body_lootable.png b/assets/sprites/dead_body_lootable.png new file mode 100644 index 0000000..69666a9 Binary files /dev/null and b/assets/sprites/dead_body_lootable.png differ diff --git a/assets/sprites/gold_savior.png b/assets/sprites/gold_savior.png new file mode 100644 index 0000000..4c2b640 Binary files /dev/null and b/assets/sprites/gold_savior.png differ diff --git a/assets/sprites/grave_stone.png b/assets/sprites/grave_stone.png new file mode 100644 index 0000000..4891a30 Binary files /dev/null and b/assets/sprites/grave_stone.png differ diff --git a/assets/sprites/hairy_spider.png b/assets/sprites/hairy_spider.png new file mode 100644 index 0000000..6cee0fb Binary files /dev/null and b/assets/sprites/hairy_spider.png differ diff --git a/assets/sprites/peasant_girl_2.png b/assets/sprites/peasant_girl_2.png new file mode 100644 index 0000000..9288fb9 Binary files /dev/null and b/assets/sprites/peasant_girl_2.png differ diff --git a/assets/sprites/rat_with_sword.png b/assets/sprites/rat_with_sword.png new file mode 100644 index 0000000..bcb52d0 Binary files /dev/null and b/assets/sprites/rat_with_sword.png differ diff --git a/assets/sprites/rope_vines_up.png b/assets/sprites/rope_vines_up.png new file mode 100644 index 0000000..230d2e8 Binary files /dev/null and b/assets/sprites/rope_vines_up.png differ diff --git a/assets/sprites/torch_pillar.png b/assets/sprites/torch_pillar.png new file mode 100644 index 0000000..813aa76 Binary files /dev/null and b/assets/sprites/torch_pillar.png differ diff --git a/assets/sprites/tripwire_trap.png b/assets/sprites/tripwire_trap.png new file mode 100644 index 0000000..edf206b Binary files /dev/null and b/assets/sprites/tripwire_trap.png differ diff --git a/assets/sprites/undead_peasant.png b/assets/sprites/undead_peasant.png new file mode 100644 index 0000000..097227a Binary files /dev/null and b/assets/sprites/undead_peasant.png differ diff --git a/assets/sprites/well_down.png b/assets/sprites/well_down.png new file mode 100644 index 0000000..fe5e74b Binary files /dev/null and b/assets/sprites/well_down.png differ diff --git a/assets/styles.json b/assets/styles.json new file mode 100644 index 0000000..b1822d4 --- /dev/null +++ b/assets/styles.json @@ -0,0 +1,17 @@ +[ + { + "name": "Mossy Blue Ceiling", + "floor": "gray_stone_floor_light", + "walls": "wall_moss" + }, + { + "name": "Plain", + "floor": "floor_tile", + "walls": "wall_plain" + }, + { + "name": "Wood Walls", + "floor": "floor_tile", + "walls": "wood_wall" + } +] diff --git a/assets/textures/ceiling_black.png b/assets/textures/ceiling_black.png new file mode 100644 index 0000000..156cd17 Binary files /dev/null and b/assets/textures/ceiling_black.png differ diff --git a/assets/textures/ceiling_blue_light.png b/assets/textures/ceiling_blue_light.png new file mode 100644 index 0000000..def9a8e Binary files /dev/null and b/assets/textures/ceiling_blue_light.png differ diff --git a/assets/textures/floor_gray_stone.png b/assets/textures/floor_gray_stone.png new file mode 100644 index 0000000..b8d92e9 Binary files /dev/null and b/assets/textures/floor_gray_stone.png differ diff --git a/assets/textures/glowing_moss_wall.png b/assets/textures/glowing_moss_wall.png new file mode 100644 index 0000000..8bbf40a Binary files /dev/null and b/assets/textures/glowing_moss_wall.png differ diff --git a/assets/textures/gray_stone_floor_light.png b/assets/textures/gray_stone_floor_light.png new file mode 100644 index 0000000..388f63d Binary files /dev/null and b/assets/textures/gray_stone_floor_light.png differ diff --git a/assets/textures/lava_floor.png b/assets/textures/lava_floor.png new file mode 100644 index 0000000..da8b46c Binary files /dev/null and b/assets/textures/lava_floor.png differ diff --git a/assets/textures/wall_plain.png b/assets/textures/wall_plain.png new file mode 100644 index 0000000..40286ea Binary files /dev/null and b/assets/textures/wall_plain.png differ diff --git a/assets/textures/wood_wall.png b/assets/textures/wood_wall.png new file mode 100644 index 0000000..8f745c8 Binary files /dev/null and b/assets/textures/wood_wall.png differ diff --git a/assets/tiles.json b/assets/tiles.json index 969b072..c45d559 100644 --- a/assets/tiles.json +++ b/assets/tiles.json @@ -1,23 +1,69 @@ { - "FLOOR_TILE": { - "texture": "assets/floor_tile_test-256.png", - "foreground": [40, 15, 125], - "background": [200, 15, 75], - "collision": false, - "display":"\u289e" + "floor_tile": { + "texture": "assets/textures/floor_gray_stone.png", + "display": 8284, + "ceiling": "ceiling_black", + "light": 0, + "foreground": "tiles/fg:floor_tile", + "background": "tiles/bg:floor_tile", + "id": 0 }, - "WALL_PLAIN": { - "texture": "assets/wall_texture_test-256.png", - "foreground": [230, 20, 30], - "background": [230, 20, 120], - "collision": true, - "display": "\ua5b8" + "wall_plain": { + "texture": "assets/textures/wall_plain.png", + "display": 9608, + "light": 0, + "foreground": "tiles/fg:wall_plain", + "background": "tiles/bg:wall_plain", + "id": 1 }, - "WALL_VINES": { - "texture": "assets/wall_with_vines-256.png", - "foreground": [230, 20, 30], - "background": [230, 20, 120], - "collision": false, - "display":"\u0799" + "wall_moss": { + "texture": "assets/textures/glowing_moss_wall.png", + "display": 9256, + "light": 20, + "foreground": "tiles/fg:wall_moss", + "background": "tiles/bg:wall_moss", + "id": 2 + }, + "ceiling_black": { + "texture": "assets/textures/ceiling_black.png", + "display": 35, + "light": 0, + "foreground": "tiles/fg:ceiling_black", + "background": "tiles/bg:ceiling_black", + "id": 4 + }, + "lava_floor": { + "texture": "assets/textures/lava_floor.png", + "display": 10899, + "ceiling": "ceiling_black", + "light": 20, + "foreground": "tiles/fg:lava_floor", + "background": "tiles/bg:lava_floor", + "id": 5 + }, + "gray_stone_floor_light": { + "texture": "assets/textures/gray_stone_floor_light.png", + "display": 11590, + "ceiling": "zBUGceiling_blue_light", + "light": 40, + "foreground": "tiles/fg:gray_stone_floor_light", + "background": "tiles/bg:gray_stone_floor_light", + "id": 6 + }, + "wood_wall": { + "texture": "assets/textures/wood_wall.png", + "display": 10747, + "light": 0, + "foreground": "tiles/fg:wood_wall", + "background": "tiles/bg:wood_wall", + "id": 7 + }, + "zBUGceiling_blue_light": { + "texture": "assets/textures/ceiling_blue_light.png", + "display": 8285, + "light": 0, + "foreground": "color:BAD", + "background": "color:BAD", + "id": 8 } } diff --git a/assets/torch_crappy-256.png b/assets/torch_crappy-256.png deleted file mode 100644 index 9e7bcdc..0000000 Binary files a/assets/torch_crappy-256.png and /dev/null differ diff --git a/assets/torch_horizontal_floor-256.png b/assets/torch_horizontal_floor-256.png deleted file mode 100644 index 3c7c718..0000000 Binary files a/assets/torch_horizontal_floor-256.png and /dev/null differ diff --git a/assets/torch_pillar-256.png b/assets/torch_pillar-256.png deleted file mode 100644 index 9431f04..0000000 Binary files a/assets/torch_pillar-256.png and /dev/null differ diff --git a/assets/torch_pillar-512.png b/assets/torch_pillar-512.png deleted file mode 100644 index d7f580e..0000000 Binary files a/assets/torch_pillar-512.png and /dev/null differ diff --git a/assets/trash_button.png b/assets/trash_button.png deleted file mode 100644 index ba823ae..0000000 Binary files a/assets/trash_button.png and /dev/null differ diff --git a/assets/tripwire_trap-256.png b/assets/tripwire_trap-256.png deleted file mode 100644 index 1a6b4c0..0000000 Binary files a/assets/tripwire_trap-256.png and /dev/null differ diff --git a/assets/ui/UI/full_screen_paper.png b/assets/ui/UI/full_screen_paper.png new file mode 100644 index 0000000..12b5cfd Binary files /dev/null and b/assets/ui/UI/full_screen_paper.png differ diff --git a/assets/ui/UI/ritual_crafting_area.png b/assets/ui/UI/ritual_crafting_area.png new file mode 100644 index 0000000..120d921 Binary files /dev/null and b/assets/ui/UI/ritual_crafting_area.png differ diff --git a/assets/ui/full_screen_paper.png b/assets/ui/full_screen_paper.png new file mode 100644 index 0000000..12b5cfd Binary files /dev/null and b/assets/ui/full_screen_paper.png differ diff --git a/assets/ui/ritual_crafting_area.png b/assets/ui/ritual_crafting_area.png new file mode 100644 index 0000000..ac8db7d Binary files /dev/null and b/assets/ui/ritual_crafting_area.png differ diff --git a/assets/undead_peasant-256.png b/assets/undead_peasant-256.png deleted file mode 100644 index eed23a3..0000000 Binary files a/assets/undead_peasant-256.png and /dev/null differ diff --git a/assets/undead_peasant-spritesheet.png b/assets/undead_peasant-spritesheet.png deleted file mode 100644 index 9da3fc6..0000000 Binary files a/assets/undead_peasant-spritesheet.png and /dev/null differ diff --git a/assets/wall_simple-256.png b/assets/wall_simple-256.png deleted file mode 100644 index 6c76722..0000000 Binary files a/assets/wall_simple-256.png and /dev/null differ diff --git a/assets/wall_texture_test-256.png b/assets/wall_texture_test-256.png deleted file mode 100644 index 217c625..0000000 Binary files a/assets/wall_texture_test-256.png and /dev/null differ diff --git a/assets/wall_texture_test-512.png b/assets/wall_texture_test-512.png deleted file mode 100644 index e68dd88..0000000 Binary files a/assets/wall_texture_test-512.png and /dev/null differ diff --git a/assets/wall_with_pillars-256.png b/assets/wall_with_pillars-256.png deleted file mode 100644 index 7715274..0000000 Binary files a/assets/wall_with_pillars-256.png and /dev/null differ diff --git a/assets/wall_with_vines-256.png b/assets/wall_with_vines-256.png deleted file mode 100644 index da524ac..0000000 Binary files a/assets/wall_with_vines-256.png and /dev/null differ diff --git a/assets/well_down-256.png b/assets/well_down-256.png deleted file mode 100644 index 514de63..0000000 Binary files a/assets/well_down-256.png and /dev/null differ diff --git a/assets/wood_barrel_large-256.png b/assets/wood_barrel_large-256.png deleted file mode 100644 index 6cf2732..0000000 Binary files a/assets/wood_barrel_large-256.png and /dev/null differ diff --git a/assets/wood_barrel_large-512.png b/assets/wood_barrel_large-512.png deleted file mode 100644 index 37b1898..0000000 Binary files a/assets/wood_barrel_large-512.png and /dev/null differ diff --git a/assets/wood_barrel_small-256.png b/assets/wood_barrel_small-256.png deleted file mode 100644 index 43aede2..0000000 Binary files a/assets/wood_barrel_small-256.png and /dev/null differ diff --git a/assets/wood_wall-256.png b/assets/wood_wall-256.png deleted file mode 100644 index 6851bdc..0000000 Binary files a/assets/wood_wall-256.png and /dev/null differ diff --git a/assets/wood_wall-512.png b/assets/wood_wall-512.png deleted file mode 100644 index 8924713..0000000 Binary files a/assets/wood_wall-512.png and /dev/null differ diff --git a/autowalker.cpp b/autowalker.cpp index 31b6e9a..9f5ac9d 100644 --- a/autowalker.cpp +++ b/autowalker.cpp @@ -1,53 +1,85 @@ #include "autowalker.hpp" -#include "inventory.hpp" +#include "ai_debug.hpp" +#include "gui/ritual_ui.hpp" +#include "game_level.hpp" +#include "systems.hpp" + +struct InventoryStats { + int healing = 0; + int other = 0; +}; template -Pathing compute_paths(gui::FSM& fsm, int& count_out) { - auto& walls_original = fsm.$level.map->$walls; - auto walls_copy = walls_original; +int number_left() { + int count = 0; + auto world = GameDB::current_world(); + auto player = GameDB::the_player(); + + world->query( + [&](const auto ent, auto&, auto&) { + if(ent != player) { + count++; + } + }); + + return count; +} + +template +Pathing compute_paths() { + auto& level = GameDB::current_level(); + auto walls_copy = level.map->$walls; Pathing paths{matrix::width(walls_copy), matrix::height(walls_copy)}; - count_out = 0; - fsm.$level.world->query( - [&](const auto ent, auto& position) { - if(ent != fsm.$level.player) { - if(fsm.$level.world->has(ent)) { - paths.set_target(position.location); - count_out = count_out + 1; - } else { - // this will mark that spot as a wall so we don't path there temporarily - walls_copy[position.location.y][position.location.x] = WALL_PATH_LIMIT; - } - } - }); - - paths.compute_paths(walls_copy); + System::multi_path(level, paths, walls_copy); return paths; } +DinkyECS::Entity Autowalker::camera_aim() { + auto& level = GameDB::current_level(); + auto player_pos = GameDB::player_position(); + + // what happens if there's two things at that spot + if(level.collision->something_there(player_pos.aiming_at)) { + return level.collision->get(player_pos.aiming_at); + } else { + return DinkyECS::NONE; + } +} + +void Autowalker::log(std::wstring msg) { + fsm.$map_ui.log(msg); +} + +void Autowalker::status(std::wstring msg) { + fsm.$main_ui.$overlay_ui.show_text("bottom", msg); +} + +void Autowalker::close_status() { + fsm.$main_ui.$overlay_ui.close_text("bottom"); +} + Pathing Autowalker::path_to_enemies() { - return compute_paths(fsm, enemy_count); + return compute_paths(); } Pathing Autowalker::path_to_items() { - return compute_paths(fsm, item_count); + return compute_paths(); } -Pathing Autowalker::path_to_devices() { - return compute_paths(fsm, device_count); -} - -void Autowalker::window_events() { +void Autowalker::handle_window_events() { fsm.$window.handleEvents( [&](const sf::Event::KeyPressed &) { fsm.autowalking = false; - fmt::println("ABORT AUTOWALK"); + close_status(); + log(L"Aborting autowalk."); }, [&](const sf::Event::MouseButtonPressed &) { fsm.autowalking = false; - fmt::println("ABORT AUTOWALK"); + close_status(); + log(L"Aborting autowalk."); } ); } @@ -64,161 +96,367 @@ void Autowalker::process_combat() { } } -Point Autowalker::get_current_position() { - auto& player_position = fsm.$level.world->get(fsm.$level.player); - return player_position.location; +void Autowalker::path_fail(const std::string& msg, Matrix& bad_paths, Point pos) { + dbc::log(msg); + status(L"PATH FAIL"); + matrix::dump("MOVE FAIL PATHS", bad_paths, pos.x, pos.y); + log(L"Autowalk failed to find a path."); + send_event(gui::Event::STAIRS_DOWN); } bool Autowalker::path_player(Pathing& paths, Point& target_out) { - bool found = paths.random_walk(target_out, false, PATHING_TOWARD); + auto& level = GameDB::current_level(); + auto found = paths.find_path(target_out, PATHING_TOWARD, false); - if(!found) { - dbc::log("no neighbor found, aborting autowalk"); - return false; + if(found == PathingResult::FAIL) { + // failed to find a linear path, try diagonal + if(paths.find_path(target_out, PATHING_TOWARD, true) == PathingResult::FAIL) { + path_fail("random_walk", paths.$paths, target_out); + return false; + } } - if(!fsm.$level.map->can_move(target_out)) { - dbc::log("neighbors is telling me to go to a bad spot."); + if(!level.map->can_move(target_out)) { + path_fail("level_map->can_move", paths.$paths, target_out); return false; } return true; } -void Autowalker::rotate_player(Point current, Point target) { - int delta_x = int(target.x) - int(current.x); - int delta_y = int(target.y) - int(current.y); +void Autowalker::rotate_player(Point target) { + auto &player = GameDB::player_position(); - int facing = fsm.$main_ui.$compass_dir; - int target_facing = 0; - - if(delta_x == -1 && delta_y == 0) { - // west - target_facing = 4; - } else if(delta_x == 1 && delta_y == 0) { - // east - target_facing = 0; - } else if(delta_x == 0 && delta_y == 1) { - // south - target_facing = 2; - } else if(delta_x == 0 && delta_y == -1) { - // north - target_facing = 6; - } else { - dbc::sentinel( - fmt::format("got more than 4 direction result: " - "current={},{} " - "target={},{} " - "delta={},{} ", - current.x, current.y, - target.x, target.y, - delta_x, delta_y)); - } - - auto dir = facing > target_facing ? gui::Event::ROTATE_LEFT : gui::Event::ROTATE_RIGHT; - - while(facing != target_facing) { - send_event(dir); - facing = fsm.$main_ui.$compass_dir; - } - - dbc::check(fsm.$main_ui.$compass_dir == target_facing, - "player isn't facing the correct direction"); -} - -void Autowalker::show_map_overlay(matrix::Matrix& map, Point current) { - auto debug = fsm.$level.world->get_the(); - if(!debug.FPS) { - fsm.$main_ui.$overlay_ui.close_text("top_right"); + if(target == player.location) { + dbc::log("player stuck at a locatoin"); + fsm.autowalking = false; return; } - std::string map_overlay; - for(matrix::box it{map, current.x, current.y, 6}; it.next();) { - if(it.x == it.left) map_overlay += "\n"; - int cell = map[it.y][it.x]; + auto dir = System::shortest_rotate(player.location, player.aiming_at, target); - if(it.x == current.x && it.y == current.y) { - map_overlay += fmt::format("{:x}<", cell); - } else if(cell == WALL_PATH_LIMIT) { - map_overlay += fmt::format("# "); - } else if(cell > 15) { - map_overlay += fmt::format("* "); - } else { - map_overlay += fmt::format("{:x} ", cell); - } + for(int i = 0; player.aiming_at != target; i++) { + if(i > 10) { + dbc::log("HIT OVER ROTATE BUG!"); + break; + } + + send_event(dir); + + while(fsm.in_state(gui::State::ROTATING) || + fsm.in_state(gui::State::COMBAT_ROTATE)) + { + send_event(gui::Event::TICK); + } } - fsm.$main_ui.$overlay_ui.show_text("top_right", map_overlay); + fsm.autowalking = player.aiming_at == target; +} + +void Autowalker::update_state(ai::EntityAI& player_ai) { + int enemy_count = number_left(); + int item_count = number_left(); + + player_ai.set_state("no_more_enemies", enemy_count == 0); + player_ai.set_state("no_more_items", item_count == 0); + + player_ai.set_state("enemy_found", found_enemy()); + player_ai.set_state("health_good", player_health_good()); + + player_ai.set_state("in_combat", + fsm.in_state(gui::State::IN_COMBAT) || + fsm.in_state(gui::State::ATTACKING)); + + auto inv = player_item_count(); + player_ai.set_state("have_item", inv.other > 0 || inv.healing > 0); + player_ai.set_state("have_healing", inv.healing > 0); + + player_ai.update(); +} + +void Autowalker::handle_boss_fight() { + // skip the boss fight for now + if(fsm.in_state(gui::State::NEXT_LEVEL)) { + // eventually we'll have AI handle this too + send_event(gui::Event::STAIRS_DOWN); + face_enemy(); + } +} + +void Autowalker::handle_player_walk(ai::State& start, ai::State& goal) { + ai::EntityAI player_ai("Host::actions", start, goal); + update_state(player_ai); + auto level = GameDB::current_level(); + + if(player_ai.wants_to("find_enemy")) { + status(L"FINDING ENEMY"); + auto paths = path_to_enemies(); + process_move(paths, [&](auto target) -> bool { + return level.collision->occupied(target); + }); + face_enemy(); + } else if(player_ai.wants_to("kill_enemy")) { + status(L"KILLING ENEMY"); + + if(fsm.in_state(gui::State::IN_COMBAT)) { + if(face_enemy()) { + process_combat(); + } + } + } else if(player_ai.wants_to("use_healing")) { + status(L"USING HEALING"); + player_use_healing(); + } else if(player_ai.wants_to("collect_items") || player_ai.wants_to("find_healing")) { + fmt::println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + status(player_ai.wants_to("collect_items") ? L"COLLECTING ITEMS" : L"FIND HEALING"); + player_ai.dump(); + + auto paths = path_to_items(); + + bool found_it = process_move(paths, [&](auto target) -> bool { + if(!level.collision->something_there(target)) return false; + + auto entity = level.collision->get(target); + return ( + level.world->has(entity) || + level.world->has(entity)); + }); + + if(found_it) pickup_item(); + fmt::println("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"); + } else if(!player_ai.active()) { + close_status(); + log(L"FINAL ACTION! Autowalk done."); + fsm.autowalking = false; + } else { + close_status(); + dbc::log(fmt::format("Unknown action: {}", player_ai.to_string())); + } +} + +void Autowalker::craft_weapon() { + if(!weapon_crafted) { + auto& ritual_ui = fsm.$status_ui.$ritual_ui; + fsm.$status_ui.$gui.click_on("ritual_ui"); + + while(!ritual_ui.in_state(gui::ritual::State::OPENED)) { + send_event(gui::Event::TICK); + } + + ritual_ui.$gui.click_on("inv_slot0"); + send_event(gui::Event::TICK); + ritual_ui.$gui.click_on("inv_slot1"); + send_event(gui::Event::TICK); + + while(!ritual_ui.in_state(gui::ritual::State::CRAFTING)) { + send_event(gui::Event::TICK); + } + + ritual_ui.$gui.click_on("result_image", true); + send_event(gui::Event::TICK); + + ritual_ui.$gui.click_on("ritual_ui"); + send_event(gui::Event::TICK); + weapon_crafted = true; + } +} + +void Autowalker::open_map() { + if(!map_opened_once) { + if(!fsm.$map_open) { + send_event(gui::Event::MAP_OPEN); + map_opened_once = true; + } + } +} + +void Autowalker::close_map() { + if(fsm.$map_open) { + send_event(gui::Event::MAP_OPEN); + } } void Autowalker::autowalk() { - window_events(); - if(!fsm.autowalking) return; - - process_combat(); - auto paths = path_to_enemies(); - - if(enemy_count == 0) { - dbc::log("Killed everything, now finding items."); - paths = path_to_items(); - } - - if(enemy_count == 0 && item_count == 0) { - dbc::log("No more items, find the exit."); - paths = path_to_devices(); - } - - if(enemy_count == 0 && - item_count == 0 && - device_count == 0) - { - fsm.autowalking = false; - dbc::log("no more enemies, items, or devices."); + handle_window_events(); + if(!fsm.autowalking) { + close_status(); return; } - Point current = get_current_position(); - Point target = current; - - show_map_overlay(paths.$paths, current); - - - if(!path_player(paths, target)) { - dbc::log("no paths found, aborting autowalk"); - fsm.autowalking = false; - return; - } - - rotate_player(current, target); + craft_weapon(); + open_map(); + face_enemy(); int move_attempts = 0; + + auto start = ai::load_state("Host::initial_state"); + auto goal = ai::load_state("Host::final_state"); + do { - process_combat(); - process_move(); - // BUG: sometimes in idle but there's an enemy near but combat hasn't started - // for now just toss out an ATTACK and it'll be ignored or cause combat - send_event(gui::Event::ATTACK); + handle_window_events(); + handle_boss_fight(); + handle_player_walk(start, goal); + + close_map(); + move_attempts++; - } while(move_attempts < 100 && !player_has_moved(target)); + } while(move_attempts < 100 && fsm.autowalking); } -void Autowalker::process_move() { - send_event(gui::Event::MOVE_FORWARD); - while(fsm.in_state(gui::State::MOVING)) send_event(gui::Event::TICK); +bool Autowalker::process_move(Pathing& paths, std::function is_that_it) { + // target has to start at the player location then... + auto target_out = GameDB::player_position().location; + + // ... target gets modified as an out parameter to find the path + if(!path_player(paths, target_out)) { + close_status(); + log(L"No paths found, aborting autowalk."); + return false; + } + + if(rayview->aiming_at != target_out) rotate_player(target_out); + + bool found_it = is_that_it(target_out); + + if(!found_it) { + send_event(gui::Event::MOVE_FORWARD); + while(fsm.in_state(gui::State::MOVING)) send_event(gui::Event::TICK); + } + + return found_it; } -bool Autowalker::player_has_moved(Point target) { - Point current = get_current_position(); - return current.x == target.x && current.y == target.y; +bool Autowalker::found_enemy() { + auto& level = GameDB::current_level(); + auto player = GameDB::player_position(); + + for(matrix::compass it{level.map->$walls, player.location.x, player.location.y}; it.next();) { + Point aim{it.x, it.y}; + auto aimed_ent = level.collision->occupied_by(player.aiming_at); + if(aim != player.aiming_at || aimed_ent == DinkyECS::NONE) continue; + + if(level.world->has(aimed_ent)) return true; + } + + return false; } -void Autowalker::send_event(gui::Event ev) { - fsm.event(ev); +bool Autowalker::found_item() { + auto world = GameDB::current_world(); + auto aimed_at = camera_aim(); + return aimed_at != DinkyECS::NONE && world->has(aimed_at); +} + +void Autowalker::send_event(gui::Event ev, std::any data) { + fsm.event(ev, data); fsm.render(); fsm.handle_world_events(); } +bool Autowalker::player_health_good() { + auto world = GameDB::current_world(); + auto player = GameDB::the_player(); + auto combat = world->get(player); + float health = float(combat.hp) / float(combat.max_hp); + fmt::println("!!!!!!!!!! HEALTH: {}", health); + return health > 0.5f; +} + +InventoryStats Autowalker::player_item_count() { + InventoryStats stats; + auto& level = GameDB::current_level(); + auto& inventory = level.world->get(level.player); + + if(inventory.has("pocket_r")) { + stats.other += 1; + stats.healing += 1; + } + + if(inventory.has("pocket_l")) { + stats.other += 1; + stats.healing += 1; + } + + return stats; +} + +void Autowalker::player_use_healing() { + auto& level = GameDB::current_level(); + auto& inventory = level.world->get(level.player); + + if(inventory.has("pocket_r")) { + auto gui_id = fsm.$status_ui.$gui.entity("pocket_r"); + send_event(gui::Event::USE_ITEM, gui_id); + } + + if(inventory.has("pocket_l")) { + auto gui_id = fsm.$status_ui.$gui.entity("pocket_l"); + send_event(gui::Event::USE_ITEM, gui_id); + } +} + void Autowalker::start_autowalk() { fsm.autowalking = true; } + +void Autowalker::face_target(Point target) { + if(rayview->aiming_at != target) rotate_player(target); +} + +bool Autowalker::face_enemy() { + auto& level = GameDB::current_level(); + auto player_at = GameDB::player_position(); + + auto [found, neighbors] = level.collision->neighbors(player_at.location, true); + + if(found) { + auto enemy_pos = level.world->get(neighbors[0]); + face_target(enemy_pos.location); + } else { + dbc::log("No enemies nearby, moving on."); + } + + return found; +} + +void Autowalker::click_inventory(const std::string& name, guecs::Modifiers mods) { + auto& cell = fsm.$status_ui.$gui.cell_for(name); + fsm.$status_ui.mouse(cell.mid_x, cell.mid_y, mods); + fsm.handle_world_events(); +} + +void Autowalker::pocket_potion(GameDB::Level &level) { + auto& inventory = level.world->get(level.player); + + if(inventory.has("pocket_r") && inventory.has("pocket_l")) { + player_use_healing(); + } + + send_event(gui::Event::AIM_CLICK); + + if(inventory.has("pocket_r")) { + click_inventory("pocket_l", {1 << guecs::ModBit::left}); + } else { + click_inventory("pocket_r", {1 << guecs::ModBit::left}); + } +} + +void Autowalker::pickup_item() { + auto& level = GameDB::current_level(); + auto& player_pos = GameDB::player_position(); + auto collision = level.collision; + + if(collision->something_there(player_pos.aiming_at)) { + auto entity = collision->get(player_pos.aiming_at); + fmt::println("AIMING AT entity {} @ {},{}", + entity, player_pos.aiming_at.x, player_pos.aiming_at.y); + + if(level.world->has(entity)) { + pocket_potion(level); + status(L"A POTION"); + } else { + send_event(gui::Event::AIM_CLICK); + status(L"I DON'T KNOW"); + } + } +} diff --git a/autowalker.hpp b/autowalker.hpp index 7e9b519..5a51016 100644 --- a/autowalker.hpp +++ b/autowalker.hpp @@ -1,28 +1,54 @@ #pragma once +#include "ai.hpp" +#include "gui/fsm.hpp" +#include -#include "gui_fsm.hpp" +struct InventoryStats; struct Autowalker { int enemy_count = 0; int item_count = 0; int device_count = 0; + bool map_opened_once = false; + bool weapon_crafted = false; gui::FSM& fsm; + std::shared_ptr rayview; Autowalker(gui::FSM& fsm) - : fsm(fsm) {} + : fsm(fsm), rayview(fsm.$main_ui.$rayview) {} void autowalk(); void start_autowalk(); - void send_event(gui::Event ev); - void window_events(); + void craft_weapon(); + void open_map(); + void close_map(); + bool found_enemy(); + bool found_item(); + + void handle_window_events(); + void handle_boss_fight(); + void handle_player_walk(ai::State& start, ai::State& goal); + + void send_event(gui::Event ev, std::any data={}); void process_combat(); + bool process_move(Pathing& paths, std::function cb); bool path_player(Pathing& paths, Point &target_out); - Point get_current_position(); - void rotate_player(Point current, Point target); - bool player_has_moved(Point target); - void process_move(); + void path_fail(const std::string& msg, Matrix& bad_paths, Point pos); + void rotate_player(Point target); + void log(std::wstring msg); + void status(std::wstring msg); + void close_status(); + bool player_health_good(); + void player_use_healing(); + InventoryStats player_item_count(); + void update_state(ai::EntityAI& player_ai); + DinkyECS::Entity camera_aim(); + Pathing path_to_enemies(); Pathing path_to_items(); - Pathing path_to_devices(); - void show_map_overlay(matrix::Matrix& map, Point current); + void face_target(Point target); + bool face_enemy(); + void pickup_item(); + void pocket_potion(GameDB::Level &level); + void click_inventory(const std::string& name, guecs::Modifiers mods); }; diff --git a/backend.cpp b/backend.cpp new file mode 100644 index 0000000..ef0e8e3 --- /dev/null +++ b/backend.cpp @@ -0,0 +1,78 @@ +#include "backend.hpp" +#include "shaders.hpp" +#include "sound.hpp" +#include "textures.hpp" +#include "config.hpp" +#include "palette.hpp" + +namespace sfml { + using namespace nlohmann; + + guecs::SpriteTexture Backend::get_sprite(const string& name) { + auto sp = textures::get_sprite(name); + return {sp.sprite, sp.texture, sp.frame_size}; + } + + guecs::SpriteTexture Backend::get_icon(const string& name) { + auto sp = textures::get_icon(name); + return {sp.sprite, sp.texture, sp.frame_size}; + } + + Backend::Backend() { + sound::init(); + shaders::init(); + textures::init(); + } + + void Backend::sound_play(const string& name) { + sound::play(name); + } + + void Backend::sound_stop(const string& name) { + sound::stop(name); + } + + std::shared_ptr Backend::get_shader(const std::string& name) { + return shaders::get(name); + } + + bool Backend::shader_updated() { + if(shaders::updated($shaders_version)) { + $shaders_version = shaders::version(); + return true; + } else { + return false; + } + } + + guecs::Theme Backend::theme() { + palette::init(); + auto config = Config("assets/config.json")["theme"]; + + guecs::Theme theme { + .BLACK=palette::get("gui/theme:black"), + .DARK_DARK=palette::get("gui/theme:dark_dark"), + .DARK_MID=palette::get("gui/theme:dark_mid"), + .DARK_LIGHT=palette::get("gui/theme:dark_light"), + .MID=palette::get("gui/theme:mid"), + .LIGHT_DARK=palette::get("gui/theme:light_dark"), + .LIGHT_MID=palette::get("gui/theme:light_mid"), + .LIGHT_LIGHT=palette::get("gui/theme:light_light"), + .WHITE=palette::get("gui/theme:white"), + .TRANSPARENT = palette::get("color:transparent") + }; + + theme.PADDING = config["padding"]; + theme.BORDER_PX = config["border_px"]; + theme.TEXT_SIZE = config["text_size"]; + theme.LABEL_SIZE = config["label_size"]; + theme.FILL_COLOR = palette::get("gui/theme:fill_color"); + theme.TEXT_COLOR = palette::get("gui/theme:text_color"); + theme.BG_COLOR = palette::get("gui/theme:bg_color"); + theme.BORDER_COLOR = palette::get("gui/theme:border_color"); + theme.BG_COLOR_DARK = palette::get("gui/theme:bg_color_dark"); + theme.FONT_FILE_NAME = Config::path_to(config["font_file_name"]).string(); + + return theme; + } +} diff --git a/backend.hpp b/backend.hpp new file mode 100644 index 0000000..fc36f35 --- /dev/null +++ b/backend.hpp @@ -0,0 +1,20 @@ +#include "guecs/ui.hpp" + +namespace sfml { + using std::string; + + class Backend : public guecs::Backend { + int $shaders_version = 0; + + public: + + Backend(); + guecs::SpriteTexture get_sprite(const string& name); + guecs::SpriteTexture get_icon(const string& name); + void sound_play(const string& name); + void sound_stop(const string& name); + std::shared_ptr get_shader(const std::string& name); + bool shader_updated(); + guecs::Theme theme(); + }; +} diff --git a/battle.cpp b/battle.cpp new file mode 100644 index 0000000..4709f3d --- /dev/null +++ b/battle.cpp @@ -0,0 +1,62 @@ +#include "rituals.hpp" +#include "battle.hpp" + +namespace combat { + void BattleEngine::add_enemy(Combatant enemy) { + combatants.try_emplace(enemy.entity, enemy); + } + + bool BattleEngine::plan() { + int active = 0; + + for(auto& [entity, enemy] : combatants) { + enemy.ai.update(); + active += enemy.ai.active(); + + if(enemy.ai.active()) { + if(enemy.ai.wants_to("kill_enemy")) { + fmt::println(">> enemy {} wants to KILL", entity); + pending_actions.emplace_back(enemy, BattleAction::ATTACK); + } else if(enemy.ai.wants_to("run_away")) { + fmt::println(">> enemy {} wants to RUN", entity); + pending_actions.emplace_back(enemy, BattleAction::ESCAPE); + } + } + } + + return active > 0; + } + + std::optional BattleEngine::next() { + if(pending_actions.size() == 0) return std::nullopt; + + auto ba = pending_actions.back(); + pending_actions.pop_back(); + return std::make_optional(ba); + } + + void BattleEngine::dump() { + for(auto& [entity, enemy] : combatants) { + fmt::println("\n\n###### ENTITY #{}", entity); + enemy.ai.dump(); + } + } + + void BattleEngine::set(DinkyECS::Entity entity, std::string state, bool setting) { + dbc::check(combatants.contains(entity), "invalid combatant given to BattleEngine"); + auto& action = combatants.at(entity); + action.ai.set_state(state, setting); + } + + void BattleEngine::set_all(std::string state, bool setting) { + for(auto& [ent, action] : combatants) { + action.ai.set_state(state, setting); + } + } + + void BattleEngine::queue(DinkyECS::Entity entity, BattleAction action) { + dbc::check(combatants.contains(entity), "invalid combatant given to BattleEngine"); + auto& enemy = combatants.at(entity); + pending_actions.emplace_back(enemy, action); + } +} diff --git a/battle.hpp b/battle.hpp new file mode 100644 index 0000000..2aeb6f5 --- /dev/null +++ b/battle.hpp @@ -0,0 +1,37 @@ +#pragma once +#include "rituals.hpp" +#include "config.hpp" +#include "dinkyecs.hpp" +#include +#include "components.hpp" + +namespace combat { + + struct Combatant { + DinkyECS::Entity entity; + ai::EntityAI &ai; + components::Combat &combat; + }; + + enum class BattleAction { + ATTACK, BLOCK, ESCAPE + }; + + struct BattleResult { + Combatant &state; + BattleAction action; + }; + + struct BattleEngine { + std::unordered_map combatants; + std::vector pending_actions; + + void add_enemy(Combatant ba); + bool plan(); + std::optional next(); + void dump(); + void set(DinkyECS::Entity entity, std::string state, bool setting); + void set_all(std::string state, bool setting); + void queue(DinkyECS::Entity entity, BattleAction action); + }; +} diff --git a/camera.cpp b/camera.cpp deleted file mode 100644 index d616351..0000000 --- a/camera.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "camera.hpp" -#include -#include - -Point CameraLOL::plan_move(Raycaster &rayview, int dir, bool strafe) { - t = 0.0; - if(strafe) { - target_x = rayview.$pos_x + int(-rayview.$dir_y * 1.5 * dir); - target_y = rayview.$pos_y + int(rayview.$dir_x * 1.5 * dir); - } else { - target_x = rayview.$pos_x + int(rayview.$dir_x * 1.5 * dir); - target_y = rayview.$pos_y + int(rayview.$dir_y * 1.5 * dir); - } - - return {size_t(target_x), size_t(target_y)}; -} - -void CameraLOL::plan_rotate(Raycaster &rayview, int dir) { - t = 0.0; - double angle_dir = std::numbers::pi * 0.25 * dir; - - target_dir_x = rayview.$dir_x * cos(angle_dir) - rayview.$dir_y * sin(angle_dir); - target_dir_y = rayview.$dir_x * sin(angle_dir) + rayview.$dir_y * cos(angle_dir); - - target_plane_x = rayview.$plane_x * cos(angle_dir) - rayview.$plane_y * sin(angle_dir); - target_plane_y = rayview.$plane_x * sin(angle_dir) + rayview.$plane_y * cos(angle_dir); -} - -bool CameraLOL::play_rotate(Raycaster &rayview) { - t += rot_speed; - rayview.$dir_x = std::lerp(rayview.$dir_x, target_dir_x, t); - rayview.$dir_y = std::lerp(rayview.$dir_y, target_dir_y, t); - rayview.$plane_x = std::lerp(rayview.$plane_x, target_plane_x, t); - rayview.$plane_y = std::lerp(rayview.$plane_y, target_plane_y, t); - - return t > 1.0; -} - -bool CameraLOL::play_move(Raycaster &rayview) { - t += move_speed; - rayview.$pos_x = std::lerp(rayview.$pos_x, target_x, t); - rayview.$pos_y = std::lerp(rayview.$pos_y, target_y, t); - return t >= 1.0; -} - -void CameraLOL::abort_plan(Raycaster& rayview) { - target_x = rayview.$pos_x; - target_y = rayview.$pos_y; -} diff --git a/camera.hpp b/camera.hpp index 445324b..9bd8edc 100644 --- a/camera.hpp +++ b/camera.hpp @@ -1,5 +1,4 @@ #pragma once -#include "raycaster.hpp" struct CameraLOL { double t = 0.0; @@ -11,12 +10,4 @@ struct CameraLOL { double target_dir_y = 0.0; double target_plane_x = 0.0; double target_plane_y = 0.0; - - Point plan_move(Raycaster &rayview, int dir, bool strafe); - void plan_rotate(Raycaster &rayview, int dir); - - bool play_rotate(Raycaster &rayview); - bool play_move(Raycaster &rayview); - - void abort_plan(Raycaster& rayview); }; diff --git a/color.hpp b/color.hpp deleted file mode 100644 index 4026ba5..0000000 --- a/color.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include - -namespace ColorValue { - const sf::Color BLACK{0, 0, 0}; - const sf::Color DARK_DARK{10, 10, 10}; - const sf::Color DARK_MID{30, 30, 30}; - const sf::Color DARK_LIGHT{60, 60, 60}; - const sf::Color MID{100, 100, 100}; - const sf::Color LIGHT_DARK{150, 150, 150}; - const sf::Color LIGHT_MID{200, 200, 200}; - const sf::Color LIGHT_LIGHT{230, 230, 230}; - const sf::Color WHITE{255, 255, 255}; - const sf::Color TRANSPARENT = sf::Color::Transparent; -} diff --git a/combat_ui.cpp b/combat_ui.cpp deleted file mode 100644 index 2f1b9f6..0000000 --- a/combat_ui.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "combat_ui.hpp" -#include "constants.hpp" -#include "color.hpp" -#include "events.hpp" - -namespace gui { - using namespace guecs; - - CombatUI::CombatUI(GameLevel level) : - $level(level) - { - $gui.position(COMBAT_UI_X, COMBAT_UI_Y, COMBAT_UI_WIDTH, COMBAT_UI_HEIGHT); - $gui.layout( - "[*%(100,150)button_attack1 | *%(100,150)button_attack2 | *%(100,150)button_attack3 | *%(100,150)button_heal]" - "[ >.%(100,50)label_hp | *%.(198,50)bar_hp | _ ]"); - } - - void CombatUI::init() { - auto& world = $gui.world(); - - world.set_the({$gui.$parser}); - - for(auto& [name, cell] : $gui.cells()) { - if(name.starts_with("button_")) { - auto button = $gui.entity(name); - world.set(button, {}); - world.set(button, - guecs::make_action(*$level.world, Events::GUI::ATTACK)); - world.set