Initial include of fuc2 to eventually replace catch2.
This commit is contained in:
parent
a58439a289
commit
4deda37665
5 changed files with 228 additions and 187 deletions
|
|
@ -60,6 +60,7 @@ elif build_machine.system() == 'darwin'
|
|||
endif
|
||||
|
||||
catch2 = subproject('catch2').get_variable('catch2_with_main_dep')
|
||||
fuc2 = subproject('fuc2').get_variable('fuc2_dep')
|
||||
fmt = subproject('fmt').get_variable('fmt_dep')
|
||||
json = subproject('nlohmann_json').get_variable('nlohmann_json_dep')
|
||||
freetype2 = subproject('freetype2').get_variable('freetype_dep')
|
||||
|
|
@ -114,6 +115,13 @@ executable('runtests', sources + tests,
|
|||
override_options: exe_defaults,
|
||||
dependencies: dependencies + [catch2])
|
||||
|
||||
executable('fuc2it', sources + fuc2_tests,
|
||||
cpp_args: cpp_args,
|
||||
link_args: link_args,
|
||||
include_directories: inc_dirs,
|
||||
override_options: exe_defaults,
|
||||
dependencies: dependencies + [fuc2])
|
||||
|
||||
executable('zedcaster',
|
||||
[ 'src/main.cpp' ],
|
||||
cpp_args: cpp_args,
|
||||
|
|
|
|||
|
|
@ -38,15 +38,17 @@ union ColorConv {
|
|||
* and I guess the compiler can handle it better than shifting
|
||||
* bits around.
|
||||
*/
|
||||
inline RGBA lighting_calc(RGBA pixel, float dist, int level) {
|
||||
inline float intensity_calc(float dist, int level) {
|
||||
return (float(level) * PERCENT) / (dist + 1) * LIGHT_MULTIPLIER;
|
||||
}
|
||||
|
||||
inline RGBA lighting_calc(RGBA pixel, float intensity) {
|
||||
ColorConv conv{.as_int=pixel};
|
||||
|
||||
if(conv.as_color.b < GLOW_LIMIT
|
||||
&& conv.as_color.r < GLOW_LIMIT
|
||||
&& conv.as_color.g < GLOW_LIMIT)
|
||||
{
|
||||
float intensity = (float(level) * PERCENT) / (dist + 1) * LIGHT_MULTIPLIER;
|
||||
|
||||
conv.as_color.r *= intensity;
|
||||
conv.as_color.g *= intensity;
|
||||
conv.as_color.b *= intensity;
|
||||
|
|
@ -55,6 +57,7 @@ inline RGBA lighting_calc(RGBA pixel, float dist, int level) {
|
|||
return conv.as_int;
|
||||
}
|
||||
|
||||
|
||||
Raycaster::Raycaster(int x, int y, int width, int height) :
|
||||
$view_texture(sf::Vector2u{(unsigned int)width, (unsigned int)height}),
|
||||
$view_sprite($view_texture),
|
||||
|
|
@ -336,12 +339,14 @@ void Raycaster::cast_rays() {
|
|||
// Starting texture coordinate
|
||||
double tex_pos = (draw_start - $pitch - $height / 2 + line_height / 2) * step;
|
||||
|
||||
int light_level = lights[map_y][map_x];
|
||||
float intensity = intensity_calc(perp_wall_dist, light_level);
|
||||
|
||||
for(int y = draw_start; y < draw_end; y++) {
|
||||
int tex_y = (int)tex_pos & (texture_height - 1);
|
||||
tex_pos += step;
|
||||
RGBA pixel = texture[texture_height * tex_y + tex_x];
|
||||
int light_level = lights[map_y][map_x];
|
||||
$pixels[pixcoord(x, y)] = lighting_calc(pixel, perp_wall_dist, light_level);
|
||||
$pixels[pixcoord(x, y)] = lighting_calc(pixel, intensity);
|
||||
}
|
||||
|
||||
// SET THE ZBUFFER FOR THE SPRITE CASTING
|
||||
|
|
@ -422,14 +427,15 @@ void Raycaster::draw_ceiling_floor() {
|
|||
}
|
||||
|
||||
// NOTE: use map_x/y to get the floor, ceiling texture.
|
||||
float intensity = intensity_calc(row_distance, light_level);
|
||||
|
||||
// FLOOR
|
||||
color = floor_texture[texture_width * ty + tx];
|
||||
$pixels[pixcoord(x, y)] = lighting_calc(color, row_distance, light_level);
|
||||
$pixels[pixcoord(x, y)] = lighting_calc(color, intensity);
|
||||
|
||||
// CEILING
|
||||
color = ceiling_texture[texture_width * ty + tx];
|
||||
$pixels[pixcoord(x, $height - y - 1)] = lighting_calc(color, row_distance, light_level);
|
||||
$pixels[pixcoord(x, $height - y - 1)] = lighting_calc(color, intensity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
96
tests/ai.cpp
96
tests/ai.cpp
|
|
@ -1,14 +1,16 @@
|
|||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "dbc.hpp"
|
||||
#include "ai/ai.hpp"
|
||||
#include "ai/ai_debug.hpp"
|
||||
#include <iostream>
|
||||
#include "game/rituals.hpp"
|
||||
#include <fuc2/testing.hpp>
|
||||
|
||||
using namespace dbc;
|
||||
using namespace nlohmann;
|
||||
using namespace fuc2;
|
||||
|
||||
TEST_CASE("state and actions work", "[ai]") {
|
||||
namespace ai_tests {
|
||||
void test_state_and_actions() {
|
||||
enum StateNames {
|
||||
ENEMY_IN_RANGE,
|
||||
ENEMY_DEAD
|
||||
|
|
@ -29,38 +31,38 @@ TEST_CASE("state and actions work", "[ai]") {
|
|||
move_closer.needs(ENEMY_IN_RANGE, false);
|
||||
move_closer.effect(ENEMY_IN_RANGE, true);
|
||||
|
||||
REQUIRE(move_closer.can_effect(start));
|
||||
CHECK(move_closer.can_effect(start));
|
||||
auto after_move_state = move_closer.apply_effect(start);
|
||||
REQUIRE(start[ENEMY_IN_RANGE] == false);
|
||||
REQUIRE(after_move_state[ENEMY_IN_RANGE] == true);
|
||||
REQUIRE(after_move_state[ENEMY_DEAD] == false);
|
||||
CHECK(start[ENEMY_IN_RANGE] == false);
|
||||
CHECK(after_move_state[ENEMY_IN_RANGE] == true);
|
||||
CHECK(after_move_state[ENEMY_DEAD] == false);
|
||||
// start is clean but after move is dirty
|
||||
REQUIRE(move_closer.can_effect(start));
|
||||
REQUIRE(!move_closer.can_effect(after_move_state));
|
||||
REQUIRE(ai::distance_to_goal(start, after_move_state) == 1);
|
||||
CHECK(move_closer.can_effect(start));
|
||||
CHECK(!move_closer.can_effect(after_move_state));
|
||||
CHECK(ai::distance_to_goal(start, after_move_state) == 1);
|
||||
|
||||
ai::Action kill_it("kill_it", 10);
|
||||
kill_it.needs(ENEMY_IN_RANGE, true);
|
||||
kill_it.needs(ENEMY_DEAD, false);
|
||||
kill_it.effect(ENEMY_DEAD, true);
|
||||
|
||||
REQUIRE(!kill_it.can_effect(start));
|
||||
REQUIRE(kill_it.can_effect(after_move_state));
|
||||
CHECK(!kill_it.can_effect(start));
|
||||
CHECK(kill_it.can_effect(after_move_state));
|
||||
|
||||
auto after_kill_state = kill_it.apply_effect(after_move_state);
|
||||
REQUIRE(!kill_it.can_effect(after_kill_state));
|
||||
REQUIRE(ai::distance_to_goal(after_move_state, after_kill_state) == 1);
|
||||
CHECK(!kill_it.can_effect(after_kill_state));
|
||||
CHECK(ai::distance_to_goal(after_move_state, after_kill_state) == 1);
|
||||
|
||||
kill_it.ignore(ENEMY_IN_RANGE);
|
||||
REQUIRE(kill_it.can_effect(after_move_state));
|
||||
CHECK(kill_it.can_effect(after_move_state));
|
||||
|
||||
actions.push_back(kill_it);
|
||||
actions.push_back(move_closer);
|
||||
|
||||
REQUIRE(start != goal);
|
||||
}
|
||||
CHECK(start != goal);
|
||||
}
|
||||
|
||||
TEST_CASE("basic feature tests", "[ai]") {
|
||||
void test_basic_features() {
|
||||
enum StateNames {
|
||||
ENEMY_IN_RANGE,
|
||||
ENEMY_DEAD
|
||||
|
|
@ -97,7 +99,7 @@ TEST_CASE("basic feature tests", "[ai]") {
|
|||
actions.push_back(move_closer);
|
||||
|
||||
auto result = ai::plan_actions(actions, start, goal);
|
||||
REQUIRE(result.complete);
|
||||
CHECK(result.complete);
|
||||
|
||||
auto state = start;
|
||||
|
||||
|
|
@ -105,11 +107,11 @@ TEST_CASE("basic feature tests", "[ai]") {
|
|||
state = action.apply_effect(state);
|
||||
}
|
||||
|
||||
REQUIRE(state[ENEMY_DEAD]);
|
||||
}
|
||||
CHECK(state[ENEMY_DEAD]);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("ai as a module like sound/sprites", "[ai]") {
|
||||
void test_ai_module_like_sound() {
|
||||
ai::reset();
|
||||
ai::init("tests/ai_fixture.json");
|
||||
|
||||
|
|
@ -117,7 +119,7 @@ TEST_CASE("ai as a module like sound/sprites", "[ai]") {
|
|||
auto goal = ai::load_state("test_goal");
|
||||
|
||||
auto a_plan = ai::plan("test1", start, goal);
|
||||
REQUIRE(a_plan.complete);
|
||||
CHECK(a_plan.complete);
|
||||
|
||||
auto state = start;
|
||||
for(auto& action : a_plan.script) {
|
||||
|
|
@ -125,10 +127,10 @@ TEST_CASE("ai as a module like sound/sprites", "[ai]") {
|
|||
state = action.apply_effect(state);
|
||||
}
|
||||
|
||||
REQUIRE(ai::test(state, "target_dead"));
|
||||
}
|
||||
CHECK(ai::test(state, "target_dead"));
|
||||
}
|
||||
|
||||
TEST_CASE("ai autowalker ai test", "[ai]") {
|
||||
void test_ai_autowalker() {
|
||||
ai::reset();
|
||||
ai::init("ai");
|
||||
auto start = ai::load_state("Host::initial_state");
|
||||
|
|
@ -139,11 +141,11 @@ TEST_CASE("ai autowalker ai test", "[ai]") {
|
|||
|
||||
// find an enemy and kill them
|
||||
auto a_plan = ai::plan("Host::actions", start, goal);
|
||||
REQUIRE(!a_plan.complete);
|
||||
CHECK(!a_plan.complete);
|
||||
|
||||
auto result = ai::dump_script("\n\nWALKER KILL STUFF", start, a_plan.script);
|
||||
REQUIRE(ai::test(result, "enemy_found"));
|
||||
REQUIRE(!ai::test(result, "no_more_enemies"));
|
||||
CHECK(ai::test(result, "enemy_found"));
|
||||
CHECK(!ai::test(result, "no_more_enemies"));
|
||||
|
||||
// health is low, go heal
|
||||
ai::set(result, "health_good", false);
|
||||
|
|
@ -151,24 +153,24 @@ TEST_CASE("ai autowalker ai test", "[ai]") {
|
|||
ai::set(result, "enemy_found", false);
|
||||
ai::set(result, "have_healing", true);
|
||||
ai::set(result, "have_item", true);
|
||||
REQUIRE(!ai::test(result, "health_good"));
|
||||
CHECK(!ai::test(result, "health_good"));
|
||||
|
||||
auto health_plan = ai::plan("Host::actions", result, goal);
|
||||
result = ai::dump_script("\n\nWALKER NEED HEALTH", result, health_plan.script);
|
||||
REQUIRE(!health_plan.complete);
|
||||
REQUIRE(ai::test(result, "health_good"));
|
||||
CHECK(!health_plan.complete);
|
||||
CHECK(ai::test(result, "health_good"));
|
||||
|
||||
// health is good, enemies dead, go get stuff
|
||||
ai::set(result, "no_more_enemies", true);
|
||||
REQUIRE(ai::test(result, "no_more_enemies"));
|
||||
CHECK(ai::test(result, "no_more_enemies"));
|
||||
|
||||
auto new_plan = ai::plan("Host::actions", result, goal);
|
||||
result = ai::dump_script("\n\nWALKER COLLECT ITEMS", result, new_plan.script);
|
||||
REQUIRE(ai::test(result, "no_more_items"));
|
||||
REQUIRE(ai::test(result, "no_more_enemies"));
|
||||
}
|
||||
CHECK(ai::test(result, "no_more_items"));
|
||||
CHECK(ai::test(result, "no_more_enemies"));
|
||||
}
|
||||
|
||||
TEST_CASE("Confirm EntityAI behaves as expected", "[ai]") {
|
||||
void test_confirm_EntityAI_behaves() {
|
||||
ai::reset();
|
||||
ai::init("ai");
|
||||
auto ai_start = ai::load_state("Enemy::initial_state");
|
||||
|
|
@ -178,26 +180,26 @@ TEST_CASE("Confirm EntityAI behaves as expected", "[ai]") {
|
|||
|
||||
enemy.set_state("detect_enemy", true);
|
||||
enemy.update();
|
||||
REQUIRE(enemy.wants_to("find_enemy"));
|
||||
CHECK(enemy.wants_to("find_enemy"));
|
||||
|
||||
enemy.set_state("enemy_found", true);
|
||||
enemy.set_state("in_combat", true);
|
||||
enemy.update();
|
||||
REQUIRE(enemy.wants_to("kill_enemy"));
|
||||
CHECK(enemy.wants_to("kill_enemy"));
|
||||
|
||||
enemy.set_state("have_item", true);
|
||||
enemy.set_state("have_healing", true);
|
||||
enemy.set_state("in_combat", false);
|
||||
enemy.set_state("health_good", false);
|
||||
enemy.update();
|
||||
REQUIRE(enemy.wants_to("use_healing"));
|
||||
CHECK(enemy.wants_to("use_healing"));
|
||||
|
||||
enemy.set_state("have_healing", false);
|
||||
enemy.set_state("tough_personality", true);
|
||||
enemy.set_state("in_combat", true);
|
||||
enemy.set_state("health_good", true);
|
||||
enemy.update();
|
||||
REQUIRE(enemy.wants_to("kill_enemy"));
|
||||
CHECK(enemy.wants_to("kill_enemy"));
|
||||
|
||||
fmt::println("\n\n\n\n=============================\n\n\n\n");
|
||||
enemy.set_state("have_healing", false);
|
||||
|
|
@ -205,5 +207,17 @@ TEST_CASE("Confirm EntityAI behaves as expected", "[ai]") {
|
|||
enemy.set_state("in_combat", true);
|
||||
enemy.set_state("health_good", false);
|
||||
enemy.update();
|
||||
REQUIRE(enemy.wants_to("run_away"));
|
||||
CHECK(enemy.wants_to("run_away"));
|
||||
}
|
||||
|
||||
fuc2::Set TESTS{
|
||||
.name="ai functionality",
|
||||
.tests={
|
||||
TEST(test_state_and_actions),
|
||||
TEST(test_basic_features),
|
||||
TEST(test_ai_module_like_sound),
|
||||
TEST(test_ai_autowalker),
|
||||
TEST(test_confirm_EntityAI_behaves),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
tests = files(
|
||||
'ai.cpp',
|
||||
'animation.cpp',
|
||||
'base.cpp',
|
||||
'battle.cpp',
|
||||
|
|
@ -26,3 +25,8 @@ tests = files(
|
|||
'systems.cpp',
|
||||
'textures.cpp',
|
||||
)
|
||||
|
||||
fuc2_tests = files(
|
||||
'ai.cpp',
|
||||
'main.cpp'
|
||||
)
|
||||
|
|
|
|||
9
wraps/fuc2.wrap
Normal file
9
wraps/fuc2.wrap
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[wrap-git]
|
||||
directory=fuc2-0.1.0
|
||||
url=https://lcthw.dev/cpp/fuc2.git
|
||||
revision=HEAD
|
||||
depth=1
|
||||
method=meson
|
||||
|
||||
[provide]
|
||||
fuc2 = fuc2_dep
|
||||
Loading…
Add table
Add a link
Reference in a new issue