Compare commits
265 Commits
version-0.
...
master
@ -1 +1 @@ |
|||||||
set makeprg=meson\ compile\ -C\ . |
set makeprg=make\ -f\ ../Makefile\ build |
||||||
|
@ -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<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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
#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(); |
||||||
|
}; |
||||||
|
|
||||||
|
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); |
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
#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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
@ -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<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); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
#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); |
||||||
|
} |
@ -1,376 +0,0 @@ |
|||||||
|
|
||||||
#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; |
|
||||||
} |
|
@ -1,23 +0,0 @@ |
|||||||
#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); |
|
||||||
}; |
|
@ -1,167 +0,0 @@ |
|||||||
#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; |
|
||||||
} |
|
@ -0,0 +1,125 @@ |
|||||||
|
{ |
||||||
|
"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 |
||||||
|
}, |
||||||
|
"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 |
||||||
|
}, |
||||||
|
"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": "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", |
||||||
|
"collect_items", |
||||||
|
"use_healing"], |
||||||
|
"Enemy::actions": |
||||||
|
["find_enemy", "run_away", "kill_enemy", "use_healing"] |
||||||
|
} |
||||||
|
} |
@ -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 |
||||||
|
} |
||||||
|
} |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 117 KiB |
Before Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 82 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 |
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 |
Before Width: | Height: | Size: 818 KiB After Width: | Height: | Size: 818 KiB |
Before Width: | Height: | Size: 413 KiB After Width: | Height: | Size: 413 KiB |
Before Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 249 KiB |
Before Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 316 KiB |
Before Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 204 KiB |
Before Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 350 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 211 KiB |
Before Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 20 KiB |
@ -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" |
||||||
|
] |
||||||
|
} |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 28 KiB |
@ -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" |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
@ -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; |
||||||
|
} |