Compare commits
No commits in common. 'master' and 'version-0.1' have entirely different histories.
master
...
version-0.
@ -1 +1 @@ |
|||||||
set makeprg=make\ -f\ ../Makefile\ build |
set makeprg=meson\ compile\ -C\ . |
||||||
|
@ -1,72 +1,47 @@ |
|||||||
ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
|
|
||||||
|
|
||||||
all: build test |
all: build test |
||||||
|
|
||||||
reset: |
reset: |
||||||
ifeq '$(OS)' 'Windows_NT' |
|
||||||
powershell -executionpolicy bypass .\scripts\reset_build.ps1
|
powershell -executionpolicy bypass .\scripts\reset_build.ps1
|
||||||
else |
|
||||||
sh -x ./scripts/reset_build.sh
|
|
||||||
endif |
|
||||||
|
|
||||||
%.cpp : %.rl |
%.cpp : %.rl |
||||||
ragel -I $(ROOT_DIR) -G1 -o $@ $<
|
ragel -o $@ $<
|
||||||
|
|
||||||
%.dot: %.rl |
|
||||||
ragel -Vp -I $(ROOT_DIR) -o $@ $<
|
|
||||||
|
|
||||||
%.png: %.dot |
|
||||||
dot -Tpng $< -o $@
|
|
||||||
|
|
||||||
build: |
|
||||||
meson compile -j 10 -C $(ROOT_DIR)/builddir
|
|
||||||
|
|
||||||
asset_build: build |
build: ansi_parser.cpp lel_parser.cpp |
||||||
./builddir/icongen
|
meson compile -j 10 -C builddir
|
||||||
|
|
||||||
release_build: |
release_build: |
||||||
meson --wipe builddir -Db_ndebug=true --buildtype release
|
meson --wipe builddir -Db_ndebug=true --buildtype release
|
||||||
meson compile -j 10 -C builddir
|
meson compile -j 10 -C builddir
|
||||||
|
|
||||||
debug_build: |
debug_build: |
||||||
meson setup --wipe builddir -Db_ndebug=true --buildtype debugoptimized
|
meson setup --wipe builddir --buildtype debug
|
||||||
meson compile -j 10 -C builddir
|
meson compile -j 10 -C builddir
|
||||||
|
|
||||||
tracy_build: |
tracy_build: |
||||||
meson setup --wipe builddir --buildtype debugoptimized -Dtracy_enable=true -Dtracy:on_demand=true
|
meson setup --wipe builddir --buildtype debugoptimized -Dtracy_enable=true -Dtracy:on_demand=true
|
||||||
meson compile -j 10 -C builddir
|
meson compile -j 10 -C builddir
|
||||||
|
|
||||||
test: asset_build build |
test: build |
||||||
./builddir/runtests -d yes "[pathing]"
|
./builddir/runtests
|
||||||
|
|
||||||
run: build test |
run: build test |
||||||
ifeq '$(OS)' 'Windows_NT' |
|
||||||
powershell "cp ./builddir/zedcaster.exe ."
|
powershell "cp ./builddir/zedcaster.exe ."
|
||||||
./zedcaster
|
./zedcaster
|
||||||
else |
|
||||||
./builddir/zedcaster
|
|
||||||
endif |
|
||||||
|
|
||||||
debug: build |
debug: build |
||||||
gdb --nx -x .gdbinit --ex run --args builddir/zedcaster
|
gdb --nx -x .gdbinit --ex run --args builddir/zedcaster.exe
|
||||||
|
|
||||||
debug_run: build |
debug_run: build |
||||||
gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/zedcaster
|
gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/zedcaster.exe
|
||||||
|
|
||||||
debug_walk: build test |
debug_walk: build |
||||||
gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/zedcaster t
|
gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/zedcaster.exe t
|
||||||
|
|
||||||
clean: |
clean: |
||||||
meson compile --clean -C builddir
|
meson compile --clean -C builddir
|
||||||
|
|
||||||
debug_test: build |
debug_test: build |
||||||
gdb --nx -x .gdbinit --ex run --ex bt --ex q --args builddir/runtests -e "[pathing]"
|
gdb --nx -x .gdbinit --ex run --args builddir/runtests.exe -e
|
||||||
|
|
||||||
win_installer: |
win_installer: |
||||||
powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp'
|
powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" win_installer.ifp'
|
||||||
|
|
||||||
coverage_report: |
|
||||||
powershell 'scripts/coverage_report.ps1'
|
|
||||||
|
|
||||||
money: |
|
||||||
scc --exclude-dir subprojects
|
|
||||||
|
@ -1,5 +1,134 @@ |
|||||||
# Repository Moved |
# The Artisanal Handcrafted Retro-Future "3D" Dungeon Crawler |
||||||
|
|
||||||
This repository is archived here and moved to https://git.zedshaw.games/games/raycaster |
Welcome to my latest obsession, and turn based dungeon crawler in the style of old school raycasted |
||||||
|
games like _Wizardry_, _Might and Magic_, _Ultima_, and similar games. The game uses SFML 3.x as |
||||||
|
it's "cross platform layer" but other than that everything is hand coded by me. It's fully |
||||||
|
artisinal, created manually, with nothing but a terminal and vim. No LSPs, AI, or anything. |
||||||
|
|
||||||
|
This code is truly a work of art. Like an espresso at that Speakeasy Coffee bar in Brooklyn nobody |
||||||
|
talks about. You know the one? You don't? Oh sorry, I thought you were cool. |
||||||
|
|
||||||
|
## STATUS |
||||||
|
|
||||||
|
Currently it's only officially tested on Windows, but I'm not really using anything OS specific (I |
||||||
|
think). |
||||||
|
|
||||||
|
## Where's the LICENSE? |
||||||
|
|
||||||
|
You don't need a LICENSE that gives everything away to thieving corporations just to publish your |
||||||
|
works online. Nobody makes artists, musicians, painters, photographers, or sculptors get a license |
||||||
|
before posting online, so why do programmers need one? You worried you'll get sued? Ok, so just put |
||||||
|
a disclaimer but why do you _also_ have to give your hard work away for anyone to steal and profit |
||||||
|
from just so they don't sue you? |
||||||
|
|
||||||
|
You don't, and no matter what the OSI says, nobody can sue you if they steal your code and cause a |
||||||
|
plane to crash. _They_ would get sued for stealing your code and putting it in a plane, not you. |
||||||
|
Requiring _only_ programmers to release their code with a license to avoid lawsuits creates a |
||||||
|
[Chilling Effect](https://www.thefire.org/research-learn/chilling-effect-overview) on programmer free speech and that violates the First Amendment. |
||||||
|
|
||||||
|
So this code isn't licensed, it's copyright by default. I'm publishing it using my free speech |
||||||
|
rights to express myself and that means you can look at it the same as if I posted a painting or an |
||||||
|
essay on my blog. I obviously can't sue you for just looking at it and playing the game because I |
||||||
|
published it so you can, but _that doesn't mean you own it._ You can't resell it, fork it, |
||||||
|
nothing. |
||||||
|
|
||||||
|
Just grab the code and play it. That's it. Tell people about it. Fair use says you can even record |
||||||
|
videos reviewing it and talking about it. |
||||||
|
|
||||||
|
See? That's how Free Speech works. You don't need a LICENSE. |
||||||
|
|
||||||
|
## Build Instructions |
||||||
|
|
||||||
|
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. |
||||||
|
* [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. |
||||||
|
* [git](https://git-scm.com/) -- Which should be on almost every platform, and is installed by default with my Windows setup scripts. |
||||||
|
|
||||||
|
### Windows Instructions |
||||||
|
|
||||||
|
I primarily develop in Windows using the above setup, so this should work the best. Open [Windows |
||||||
|
Terminal](https://github.com/microsoft/terminal) and run these commands _one at a time_. Don't |
||||||
|
copy-past bomb this: |
||||||
|
|
||||||
|
```shell |
||||||
|
git clone https://git.learnjsthehardway.com/learn-code-the-hard-way/raycaster.git |
||||||
|
|
||||||
|
cd raycaster |
||||||
|
|
||||||
|
# ignore the errors the first time |
||||||
|
./scripts/reset_build.ps1 |
||||||
|
|
||||||
|
# first compile takes a while |
||||||
|
make |
||||||
|
|
||||||
|
# this copies the binary so you can run it |
||||||
|
make run |
||||||
|
``` |
||||||
|
|
||||||
|
After that the game should be running. It'll be in different states depending on how far I've |
||||||
|
pushed it, but you should at least have a few enemies, some loot, and rooms light in it. Go find them. |
||||||
|
|
||||||
|
## Linux and OSX |
||||||
|
|
||||||
|
Linux and OSX have the same requirements as Windows and almost the same install steps. The only |
||||||
|
difference is that once you get your developer tools installed then you only need [Meson](https://mesonbuild.com/). Linux and OSX should have everything else you need or there's a package for it. |
||||||
|
|
||||||
|
Once you have that installed you can run these commands: |
||||||
|
|
||||||
|
```shell |
||||||
|
git clone https://git.learnjsthehardway.com/learn-code-the-hard-way/raycaster.git |
||||||
|
|
||||||
|
cd raycaster |
||||||
|
|
||||||
|
# ignore the errors the first time |
||||||
|
./scripts/reset_build.sh |
||||||
|
|
||||||
|
# first compile takes a while |
||||||
|
make |
||||||
|
|
||||||
|
./builddir/raycaster |
||||||
|
``` |
||||||
|
|
||||||
|
You don't need `make run` because Linux and OSX are sane operating systems that don't lock every |
||||||
|
damn thing a process touches. |
||||||
|
|
||||||
|
### Other Platforms |
||||||
|
|
||||||
|
No testing done on other platforms but let me know if you get it to build somewhere fun and I'll |
||||||
|
mention it. |
||||||
|
|
||||||
|
## Development Guide |
||||||
|
|
||||||
|
You can look in the `notes.txt` file for my informal TODO list of things to fix and make. I'm not |
||||||
|
really accepting contributions from others, but if you want to follow along then that's what I'm |
||||||
|
doing. |
||||||
|
|
||||||
|
If you're just starting out in C++ or programming then the project is designed to be readable by |
||||||
|
someone who knows very little. Every file is small and should be easy to read. I don't use any |
||||||
|
insane tricks or weird C++ idioms. I also try to avoid too many external libraries so I'll use |
||||||
|
plain old [std::vector]() and [std::unordered_map]() rather than external libraries that might be |
||||||
|
faster. This is done _on purpose_ so people (myself included) can learn about the basics of C++ and |
||||||
|
the STL. |
||||||
|
|
||||||
|
I also don't do a lot of performance tuning or obsession over _THE CACHE_. Clean, simple, readable |
||||||
|
code is more important than squeezing 4% performance out of the code. I do however attempt to |
||||||
|
design things so that it doesn't do useless work because the fastest thing you can do in a computer |
||||||
|
is nothing. If I can architect away a performance issue and not make the code too complex then I'll |
||||||
|
do that instead. |
||||||
|
|
||||||
|
That means if you have a suggestion for a micro-benchmark improvement that will dramatically boost |
||||||
|
performance, but the code is convoluted and hard to understand, then it won't work. If your |
||||||
|
suggestion is interesting and provides a massive boost then let me know and I'll check it out. But, |
||||||
|
I would also like statistics that show it's better, not just your word. |
||||||
|
|
||||||
|
## Known Bugs |
||||||
|
|
||||||
|
It's early so probably a bunch of bugs. |
||||||
|
|
||||||
|
## 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. |
||||||
|
* You need to run the .command script in Application/your python that updates the SSL certs. |
||||||
|
* You have to give iTerm access to your keystrokes...because wtf it already has them? |
||||||
|
@ -1,212 +0,0 @@ |
|||||||
#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<Action> 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<Action> 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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,65 +0,0 @@ |
|||||||
#pragma once |
|
||||||
#include <vector> |
|
||||||
#include "matrix.hpp" |
|
||||||
#include <bitset> |
|
||||||
#include <limits> |
|
||||||
#include <optional> |
|
||||||
#include <nlohmann/json.hpp> |
|
||||||
#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<std::string, Action> actions; |
|
||||||
std::unordered_map<std::string, State> states; |
|
||||||
std::unordered_map<std::string, std::vector<Action>> 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<Action> 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); |
|
||||||
} |
|
@ -1,74 +0,0 @@ |
|||||||
#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; |
|
||||||
} |
|
||||||
} |
|
@ -1,10 +0,0 @@ |
|||||||
#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); |
|
||||||
} |
|
@ -1,117 +0,0 @@ |
|||||||
#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<Animation>(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); |
|
||||||
} |
|
||||||
} |
|
@ -1,17 +0,0 @@ |
|||||||
#pragma once |
|
||||||
#include "components.hpp" |
|
||||||
#include "textures.hpp" |
|
||||||
#include "easings.hpp" |
|
||||||
|
|
||||||
namespace animation { |
|
||||||
struct AnimationManager { |
|
||||||
std::unordered_map<std::string, components::Animation> 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); |
|
||||||
} |
|
@ -0,0 +1,376 @@ |
|||||||
|
|
||||||
|
#line 1 "ansi_parser.rl" |
||||||
|
#include <fmt/core.h> |
||||||
|
#include <string_view> |
||||||
|
#include "dbc.hpp" |
||||||
|
#include <SFML/Graphics.hpp> |
||||||
|
#include "ansi_parser.hpp" |
||||||
|
#include <iostream> |
||||||
|
|
||||||
|
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 <ftxui/screen/terminal.hpp> |
||||||
|
|
||||||
|
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; |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
#pragma once |
||||||
|
#include <string_view> |
||||||
|
#include <SFML/Graphics.hpp> |
||||||
|
#include <codecvt> |
||||||
|
#include <functional> |
||||||
|
|
||||||
|
typedef std::function<void(sf::Color bgcolor, sf::Color color)> ColorCB; |
||||||
|
|
||||||
|
typedef std::function<void(wchar_t ch)> WriteCB; |
||||||
|
|
||||||
|
class ANSIParser { |
||||||
|
sf::Color $default_fg; |
||||||
|
sf::Color $default_bg; |
||||||
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $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); |
||||||
|
}; |
@ -0,0 +1,167 @@ |
|||||||
|
#include <fmt/core.h> |
||||||
|
#include <string_view> |
||||||
|
#include "dbc.hpp" |
||||||
|
#include <SFML/Graphics.hpp> |
||||||
|
#include "ansi_parser.hpp" |
||||||
|
#include <iostream> |
||||||
|
|
||||||
|
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 <ftxui/screen/terminal.hpp> |
||||||
|
|
||||||
|
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; |
||||||
|
} |
@ -1,141 +0,0 @@ |
|||||||
{ |
|
||||||
"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": { |
|
||||||
"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": "face_enemy", |
|
||||||
"cost": 10, |
|
||||||
"needs": { |
|
||||||
"no_more_enemies": false, |
|
||||||
"in_combat": false, |
|
||||||
"enemy_found": true |
|
||||||
}, |
|
||||||
"effects": { |
|
||||||
"in_combat": true, |
|
||||||
"enemy_dead": true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
"name": "collect_items", |
|
||||||
"cost": 5, |
|
||||||
"needs": { |
|
||||||
"no_more_enemies": true, |
|
||||||
"no_more_items": false |
|
||||||
}, |
|
||||||
"effects": { |
|
||||||
"no_more_items": true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
"name": "use_healing", |
|
||||||
"cost": 0, |
|
||||||
"needs": { |
|
||||||
"have_item": true, |
|
||||||
"have_healing": true, |
|
||||||
"in_combat": false, |
|
||||||
"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", |
|
||||||
"face_enemy", |
|
||||||
"collect_items", |
|
||||||
"use_healing"], |
|
||||||
"Enemy::actions": |
|
||||||
["find_enemy", "run_away", "kill_enemy", "use_healing"] |
|
||||||
} |
|
||||||
} |
|
@ -1,12 +0,0 @@ |
|||||||
{ |
|
||||||
"ritual_blanket": { |
|
||||||
"_type": "Animation", |
|
||||||
"easing": 0, |
|
||||||
"ease_rate": 0.5, |
|
||||||
"scale": 1.0, |
|
||||||
"simple": false, |
|
||||||
"frames": 3, |
|
||||||
"speed": 0.2, |
|
||||||
"stationary": true |
|
||||||
} |
|
||||||
} |
|
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 117 KiB |
After Width: | Height: | Size: 107 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 249 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 665 KiB After Width: | Height: | Size: 665 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 316 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 204 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 350 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 11 KiB |
@ -1,12 +0,0 @@ |
|||||||
{ |
|
||||||
"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 |
|
||||||
} |
|
||||||
} |
|
Before Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 85 KiB |
@ -1,140 +0,0 @@ |
|||||||
[ |
|
||||||
{ |
|
||||||
"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 |
|
||||||
} |
|
||||||
] |
|
Before Width: | Height: | Size: 9.5 KiB |
@ -1,81 +0,0 @@ |
|||||||
{ |
|
||||||
"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] |
|
||||||
} |
|
||||||
} |
|
After Width: | Height: | Size: 211 KiB |
After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 1011 KiB After Width: | Height: | Size: 1011 KiB |
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
Before Width: | Height: | Size: 466 KiB After Width: | Height: | Size: 466 KiB |
After Width: | Height: | Size: 20 KiB |
@ -1,202 +0,0 @@ |
|||||||
{ |
|
||||||
"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" |
|
||||||
] |
|
||||||
} |
|
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 8.9 KiB |