From 6e0204e3ab8cbf32c393cb322d646702aa9deccd Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Fri, 2 Nov 2018 00:21:58 +0100 Subject: [PATCH 01/28] gamestate: move old state to new folder --- libopenage/engine.cpp | 4 +- libopenage/game_control.cpp | 2 +- libopenage/game_control.h | 2 +- libopenage/game_renderer.cpp | 4 +- libopenage/gamestate/CMakeLists.txt | 20 +- libopenage/gamestate/entity.cpp | 7 + libopenage/gamestate/entity.h | 11 + libopenage/gamestate/game.cpp | 8 + libopenage/gamestate/game.h | 14 + libopenage/gamestate/old/CMakeLists.txt | 14 + .../gamestate/{ => old}/civilisation.cpp | 6 +- libopenage/gamestate/{ => old}/civilisation.h | 0 libopenage/gamestate/{ => old}/cost.cpp | 0 libopenage/gamestate/{ => old}/cost.h | 0 libopenage/gamestate/{ => old}/game_main.cpp | 8 +- libopenage/gamestate/{ => old}/game_main.h | 8 +- libopenage/gamestate/{ => old}/game_save.cpp | 12 +- libopenage/gamestate/{ => old}/game_save.h | 0 libopenage/gamestate/{ => old}/game_spec.cpp | 26 +- libopenage/gamestate/{ => old}/game_spec.h | 21 +- libopenage/gamestate/{ => old}/generator.cpp | 10 +- libopenage/gamestate/{ => old}/generator.h | 4 +- libopenage/gamestate/{ => old}/market.cpp | 0 libopenage/gamestate/{ => old}/market.h | 0 libopenage/gamestate/old/player.cpp | 291 ++++++++++++++++++ libopenage/gamestate/old/player.h | 244 +++++++++++++++ .../{ => old}/population_tracker.cpp | 0 .../gamestate/{ => old}/population_tracker.h | 0 libopenage/gamestate/{ => old}/resource.cpp | 0 libopenage/gamestate/{ => old}/resource.h | 0 libopenage/gamestate/{ => old}/score.cpp | 2 +- libopenage/gamestate/{ => old}/score.h | 0 libopenage/gamestate/{ => old}/team.cpp | 0 libopenage/gamestate/{ => old}/team.h | 0 libopenage/gamestate/player.cpp | 289 +---------------- libopenage/gamestate/player.h | 236 +------------- libopenage/gamestate/terrain.cpp | 8 + libopenage/gamestate/terrain.h | 12 + libopenage/gamestate/terrain_chunk.cpp | 8 + libopenage/gamestate/terrain_chunk.h | 12 + libopenage/gamestate/types.cpp | 8 + libopenage/gamestate/types.h | 16 + libopenage/gamestate/world.cpp | 8 + libopenage/gamestate/world.h | 11 + libopenage/gui/category_contents_list_model.h | 4 +- libopenage/gui/game_creator.cpp | 6 +- libopenage/gui/game_main_link.h | 4 +- libopenage/gui/game_saver.cpp | 6 +- libopenage/gui/game_spec_link.h | 4 +- libopenage/gui/generator_link.h | 4 +- ...e_spec_image_provider_by_filename_impl.cpp | 2 +- ...spec_image_provider_by_graphic_id_impl.cpp | 2 +- ...ui_game_spec_image_provider_by_id_impl.cpp | 2 +- ...spec_image_provider_by_terrain_id_impl.cpp | 2 +- .../gui_game_spec_image_provider_impl.cpp | 2 +- libopenage/gui/resources_list_model.h | 4 +- libopenage/main.cpp | 3 +- libopenage/unit/ability.cpp | 4 +- libopenage/unit/ability.h | 1 - libopenage/unit/action.h | 2 +- libopenage/unit/attribute.h | 2 +- libopenage/unit/producer.h | 2 +- libopenage/unit/research.cpp | 4 +- libopenage/unit/unit_texture.cpp | 2 +- libopenage/unit/unit_type.cpp | 2 +- libopenage/unit/unit_type.h | 2 +- 66 files changed, 770 insertions(+), 622 deletions(-) create mode 100644 libopenage/gamestate/entity.cpp create mode 100644 libopenage/gamestate/entity.h create mode 100644 libopenage/gamestate/game.cpp create mode 100644 libopenage/gamestate/game.h create mode 100644 libopenage/gamestate/old/CMakeLists.txt rename libopenage/gamestate/{ => old}/civilisation.cpp (91%) rename libopenage/gamestate/{ => old}/civilisation.h (100%) rename libopenage/gamestate/{ => old}/cost.cpp (100%) rename libopenage/gamestate/{ => old}/cost.h (100%) rename libopenage/gamestate/{ => old}/game_main.cpp (95%) rename libopenage/gamestate/{ => old}/game_main.h (95%) rename libopenage/gamestate/{ => old}/game_save.cpp (94%) rename libopenage/gamestate/{ => old}/game_save.h (100%) rename libopenage/gamestate/{ => old}/game_spec.cpp (97%) rename libopenage/gamestate/{ => old}/game_spec.h (95%) rename libopenage/gamestate/{ => old}/generator.cpp (98%) rename libopenage/gamestate/{ => old}/generator.h (97%) rename libopenage/gamestate/{ => old}/market.cpp (100%) rename libopenage/gamestate/{ => old}/market.h (100%) create mode 100644 libopenage/gamestate/old/player.cpp create mode 100644 libopenage/gamestate/old/player.h rename libopenage/gamestate/{ => old}/population_tracker.cpp (100%) rename libopenage/gamestate/{ => old}/population_tracker.h (100%) rename libopenage/gamestate/{ => old}/resource.cpp (100%) rename libopenage/gamestate/{ => old}/resource.h (100%) rename libopenage/gamestate/{ => old}/score.cpp (98%) rename libopenage/gamestate/{ => old}/score.h (100%) rename libopenage/gamestate/{ => old}/team.cpp (100%) rename libopenage/gamestate/{ => old}/team.h (100%) create mode 100644 libopenage/gamestate/terrain.cpp create mode 100644 libopenage/gamestate/terrain.h create mode 100644 libopenage/gamestate/terrain_chunk.cpp create mode 100644 libopenage/gamestate/terrain_chunk.h create mode 100644 libopenage/gamestate/types.cpp create mode 100644 libopenage/gamestate/types.h create mode 100644 libopenage/gamestate/world.cpp create mode 100644 libopenage/gamestate/world.h diff --git a/libopenage/engine.cpp b/libopenage/engine.cpp index 220cd313ee..3017570c5b 100644 --- a/libopenage/engine.cpp +++ b/libopenage/engine.cpp @@ -13,8 +13,8 @@ #include "config.h" #include "error/error.h" #include "error/gl_debug.h" -#include "gamestate/game_main.h" -#include "gamestate/generator.h" +#include "gamestate/old/game_main.h" +#include "gamestate/old/generator.h" #include "gui/gui.h" #include "log/log.h" #include "renderer/font/font.h" diff --git a/libopenage/game_control.cpp b/libopenage/game_control.cpp index eb230b4dfb..f2ecdd73da 100644 --- a/libopenage/game_control.cpp +++ b/libopenage/game_control.cpp @@ -4,7 +4,7 @@ #include "engine.h" #include "error/error.h" -#include "gamestate/game_spec.h" +#include "gamestate/old/game_spec.h" #include "log/log.h" #include "renderer/color.h" #include "terrain/terrain_chunk.h" diff --git a/libopenage/game_control.h b/libopenage/game_control.h index b6e54f5063..61b8b021f5 100644 --- a/libopenage/game_control.h +++ b/libopenage/game_control.h @@ -9,7 +9,7 @@ #include "coord/pixel.h" #include "input/input_context.h" #include "rng/rng.h" -#include "gamestate/game_main.h" +#include "gamestate/old/game_main.h" #include "unit/command.h" #include "unit/selection.h" #include "unit/unit_type.h" diff --git a/libopenage/game_renderer.cpp b/libopenage/game_renderer.cpp index 0ec11be194..23684df06d 100644 --- a/libopenage/game_renderer.cpp +++ b/libopenage/game_renderer.cpp @@ -9,8 +9,8 @@ #include "console/console.h" #include "engine.h" #include "gamedata/color.gen.h" -#include "gamestate/game_main.h" -#include "gamestate/game_spec.h" +#include "gamestate/old/game_main.h" +#include "gamestate/old/game_spec.h" #include "input/input_manager.h" #include "log/log.h" #include "terrain/terrain.h" diff --git a/libopenage/gamestate/CMakeLists.txt b/libopenage/gamestate/CMakeLists.txt index 87dffe21d7..4f9f3ac922 100644 --- a/libopenage/gamestate/CMakeLists.txt +++ b/libopenage/gamestate/CMakeLists.txt @@ -1,14 +1,12 @@ add_sources(libopenage - civilisation.cpp - cost.cpp - game_main.cpp - game_save.cpp - game_spec.cpp - generator.cpp - market.cpp + entity.cpp + game.cpp player.cpp - population_tracker.cpp - score.cpp - team.cpp - resource.cpp + terrain.cpp + terrain_chunk.cpp + types.cpp + world.cpp ) + +# TODO: remove once migration is done. +add_subdirectory(old/) diff --git a/libopenage/gamestate/entity.cpp b/libopenage/gamestate/entity.cpp new file mode 100644 index 0000000000..56336fe335 --- /dev/null +++ b/libopenage/gamestate/entity.cpp @@ -0,0 +1,7 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#include "entity.h" + +namespace openage::gamestate { + +} // openage diff --git a/libopenage/gamestate/entity.h b/libopenage/gamestate/entity.h new file mode 100644 index 0000000000..ecdcc7b6ec --- /dev/null +++ b/libopenage/gamestate/entity.h @@ -0,0 +1,11 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#pragma once + +namespace openage::gamestate { + +class Entity { + +}; + +} // openage diff --git a/libopenage/gamestate/game.cpp b/libopenage/gamestate/game.cpp new file mode 100644 index 0000000000..bf55d0b962 --- /dev/null +++ b/libopenage/gamestate/game.cpp @@ -0,0 +1,8 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#include "game.h" + +namespace openage::gamestate { + + +} // openage::gamestate diff --git a/libopenage/gamestate/game.h b/libopenage/gamestate/game.h new file mode 100644 index 0000000000..dfc0088feb --- /dev/null +++ b/libopenage/gamestate/game.h @@ -0,0 +1,14 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#pragma once + +#include "world.h" + +namespace openage::gamestate { + +class Game { +public: + World world; +}; + +} // openage diff --git a/libopenage/gamestate/old/CMakeLists.txt b/libopenage/gamestate/old/CMakeLists.txt new file mode 100644 index 0000000000..87dffe21d7 --- /dev/null +++ b/libopenage/gamestate/old/CMakeLists.txt @@ -0,0 +1,14 @@ +add_sources(libopenage + civilisation.cpp + cost.cpp + game_main.cpp + game_save.cpp + game_spec.cpp + generator.cpp + market.cpp + player.cpp + population_tracker.cpp + score.cpp + team.cpp + resource.cpp +) diff --git a/libopenage/gamestate/civilisation.cpp b/libopenage/gamestate/old/civilisation.cpp similarity index 91% rename from libopenage/gamestate/civilisation.cpp rename to libopenage/gamestate/old/civilisation.cpp index 8c92b802ee..6139ed23fe 100644 --- a/libopenage/gamestate/civilisation.cpp +++ b/libopenage/gamestate/old/civilisation.cpp @@ -1,9 +1,9 @@ -// Copyright 2015-2017 the openage authors. See copying.md for legal info. +// Copyright 2015-2018 the openage authors. See copying.md for legal info. #include "civilisation.h" -#include "../log/log.h" -#include "../unit/unit_type.h" +#include "../../log/log.h" +#include "../../unit/unit_type.h" namespace openage { diff --git a/libopenage/gamestate/civilisation.h b/libopenage/gamestate/old/civilisation.h similarity index 100% rename from libopenage/gamestate/civilisation.h rename to libopenage/gamestate/old/civilisation.h diff --git a/libopenage/gamestate/cost.cpp b/libopenage/gamestate/old/cost.cpp similarity index 100% rename from libopenage/gamestate/cost.cpp rename to libopenage/gamestate/old/cost.cpp diff --git a/libopenage/gamestate/cost.h b/libopenage/gamestate/old/cost.h similarity index 100% rename from libopenage/gamestate/cost.h rename to libopenage/gamestate/old/cost.h diff --git a/libopenage/gamestate/game_main.cpp b/libopenage/gamestate/old/game_main.cpp similarity index 95% rename from libopenage/gamestate/game_main.cpp rename to libopenage/gamestate/old/game_main.cpp index fe275e60b3..045dd17a76 100644 --- a/libopenage/gamestate/game_main.cpp +++ b/libopenage/gamestate/old/game_main.cpp @@ -2,10 +2,10 @@ #include "game_main.h" -#include "../engine.h" -#include "../log/log.h" -#include "../terrain/terrain.h" -#include "../unit/unit_type.h" +#include "../../engine.h" +#include "../../log/log.h" +#include "../../terrain/terrain.h" +#include "../../unit/unit_type.h" #include "game_spec.h" #include "generator.h" diff --git a/libopenage/gamestate/game_main.h b/libopenage/gamestate/old/game_main.h similarity index 95% rename from libopenage/gamestate/game_main.h rename to libopenage/gamestate/old/game_main.h index 29580eaa03..56d803f52f 100644 --- a/libopenage/gamestate/game_main.h +++ b/libopenage/gamestate/old/game_main.h @@ -11,10 +11,10 @@ #include "market.h" #include "player.h" #include "team.h" -#include "../options.h" -#include "../terrain/terrain.h" -#include "../unit/unit_container.h" -#include "../util/timing.h" +#include "../../options.h" +#include "../../terrain/terrain.h" +#include "../../unit/unit_container.h" +#include "../../util/timing.h" namespace openage { diff --git a/libopenage/gamestate/game_save.cpp b/libopenage/gamestate/old/game_save.cpp similarity index 94% rename from libopenage/gamestate/game_save.cpp rename to libopenage/gamestate/old/game_save.cpp index 68d8b7015e..bf7acb6e83 100644 --- a/libopenage/gamestate/game_save.cpp +++ b/libopenage/gamestate/old/game_save.cpp @@ -5,12 +5,12 @@ #include #include -#include "../log/log.h" -#include "../terrain/terrain_chunk.h" -#include "../unit/producer.h" -#include "../unit/unit.h" -#include "../unit/unit_type.h" -#include "../versions/compiletime.h" +#include "../../log/log.h" +#include "../../terrain/terrain_chunk.h" +#include "../../unit/producer.h" +#include "../../unit/unit.h" +#include "../../unit/unit_type.h" +#include "../../versions/compiletime.h" #include "game_main.h" #include "game_save.h" #include "game_spec.h" diff --git a/libopenage/gamestate/game_save.h b/libopenage/gamestate/old/game_save.h similarity index 100% rename from libopenage/gamestate/game_save.h rename to libopenage/gamestate/old/game_save.h diff --git a/libopenage/gamestate/game_spec.cpp b/libopenage/gamestate/old/game_spec.cpp similarity index 97% rename from libopenage/gamestate/game_spec.cpp rename to libopenage/gamestate/old/game_spec.cpp index fd06086ab2..23817dd133 100644 --- a/libopenage/gamestate/game_spec.cpp +++ b/libopenage/gamestate/old/game_spec.cpp @@ -4,19 +4,19 @@ #include -#include "../assetmanager.h" -#include "../audio/error.h" -#include "../audio/resource_def.h" -#include "../engine.h" -#include "../gamedata/blending_mode.gen.h" -#include "../gamedata/string_resource.gen.h" -#include "../gamedata/terrain.gen.h" -#include "../log/log.h" -#include "../rng/global_rng.h" -#include "../unit/producer.h" -#include "../util/compiler.h" -#include "../util/strings.h" -#include "../util/timer.h" +#include "../../assetmanager.h" +#include "../../audio/error.h" +#include "../../audio/resource_def.h" +#include "../../engine.h" +#include "../../gamedata/blending_mode.gen.h" +#include "../../gamedata/string_resource.gen.h" +#include "../../gamedata/terrain.gen.h" +#include "../../log/log.h" +#include "../../rng/global_rng.h" +#include "../../unit/producer.h" +#include "../../util/compiler.h" +#include "../../util/strings.h" +#include "../../util/timer.h" #include "civilisation.h" diff --git a/libopenage/gamestate/game_spec.h b/libopenage/gamestate/old/game_spec.h similarity index 95% rename from libopenage/gamestate/game_spec.h rename to libopenage/gamestate/old/game_spec.h index 566c693fd8..1ddadd1c7b 100644 --- a/libopenage/gamestate/game_spec.h +++ b/libopenage/gamestate/old/game_spec.h @@ -1,13 +1,14 @@ -// Copyright 2015-2017 the openage authors. See copying.md for legal info. +// Copyright 2015-2018 the openage authors. See copying.md for legal info. #pragma once -#include "../job/job.h" -#include "../gamedata/gamedata.gen.h" -#include "../gamedata/graphic.gen.h" -#include "../terrain/terrain.h" -#include "../unit/unit_texture.h" -#include "../util/csv.h" +#include "../types.h" +#include "../../job/job.h" +#include "../../gamedata/gamedata.gen.h" +#include "../../gamedata/graphic.gen.h" +#include "../../terrain/terrain.h" +#include "../../unit/unit_texture.h" +#include "../../util/csv.h" #include #include @@ -23,12 +24,6 @@ class UnitTypeMeta; class Player; -/** - * the key type mapped to data objects - */ -using index_t = int; - - /** * could use unique ptr */ diff --git a/libopenage/gamestate/generator.cpp b/libopenage/gamestate/old/generator.cpp similarity index 98% rename from libopenage/gamestate/generator.cpp rename to libopenage/gamestate/old/generator.cpp index 084ee5604c..1ceabf571e 100644 --- a/libopenage/gamestate/generator.cpp +++ b/libopenage/gamestate/old/generator.cpp @@ -2,11 +2,11 @@ #include "generator.h" -#include "../log/log.h" -#include "../rng/rng.h" -#include "../terrain/terrain_chunk.h" -#include "../unit/unit.h" -#include "../util/math_constants.h" +#include "../../log/log.h" +#include "../../rng/rng.h" +#include "../../terrain/terrain_chunk.h" +#include "../../unit/unit.h" +#include "../../util/math_constants.h" #include "game_main.h" #include "game_save.h" #include "game_spec.h" diff --git a/libopenage/gamestate/generator.h b/libopenage/gamestate/old/generator.h similarity index 97% rename from libopenage/gamestate/generator.h rename to libopenage/gamestate/old/generator.h index f4cee94183..2f45509063 100644 --- a/libopenage/gamestate/generator.h +++ b/libopenage/gamestate/old/generator.h @@ -4,8 +4,8 @@ #include -#include "../coord/tile.h" -#include "../gui/guisys/public/gui_property_map.h" +#include "../../coord/tile.h" +#include "../../gui/guisys/public/gui_property_map.h" namespace qtsdl { class GuiItemLink; diff --git a/libopenage/gamestate/market.cpp b/libopenage/gamestate/old/market.cpp similarity index 100% rename from libopenage/gamestate/market.cpp rename to libopenage/gamestate/old/market.cpp diff --git a/libopenage/gamestate/market.h b/libopenage/gamestate/old/market.h similarity index 100% rename from libopenage/gamestate/market.h rename to libopenage/gamestate/old/market.h diff --git a/libopenage/gamestate/old/player.cpp b/libopenage/gamestate/old/player.cpp new file mode 100644 index 0000000000..ed6bd9e8b9 --- /dev/null +++ b/libopenage/gamestate/old/player.cpp @@ -0,0 +1,291 @@ +// Copyright 2015-2018 the openage authors. See copying.md for legal info. + +#include "player.h" + +#include + +#include "../../log/log.h" +#include "../../unit/unit.h" +#include "../../unit/unit_type.h" +#include "../../util/math_constants.h" +#include "team.h" + + +namespace openage { + +Player::Player(Civilisation *civ, unsigned int number, std::string name) + : + player_number{number}, + color{number}, + civ{civ}, + name{std::move(name)}, + team{nullptr}, + population{0, 200}, // TODO change, get population cap max from game options + score{this}, + age{1} { // TODO change, get starting age from game options + // starting resources + // TODO change, get starting resources from game options + this->resources.set_all(1000); + // TODO change, get starting resources capacity from game options or nyan + this->resources_capacity.set_all(math::DOUBLE_INF / 2); // half to avoid overflows + this->on_resources_change(); +} + +bool Player::operator ==(const Player &other) const { + return this->player_number == other.player_number; +} + +bool Player::is_enemy(const Player &other) const { + + return !this->is_ally(other); +} + +bool Player::is_ally(const Player &other) const { + + if (this->player_number == other.player_number) { + return true; // same player + } + + if (this->team && this->team->is_member(other)) { + return true; // same team + } + + // everyone else is enemy + return false; +} + +bool Player::owns(Unit &unit) const { + if (unit.has_attribute(attr_type::owner)) { + return this == &unit.get_attribute().player; + } + return false; +} + +void Player::receive(const ResourceBundle& amount) { + this->resources += amount; + this->on_resources_change(); +} + +void Player::receive(const game_resource resource, double amount) { + this->resources[resource] += amount; + this->on_resources_change(); +} + +bool Player::can_receive(const ResourceBundle& amount) const { + return this->resources_capacity.has(this->resources, amount); +} + +bool Player::can_receive(const game_resource resource, double amount) const { + return this->resources_capacity.get(resource) >= this->resources.get(resource) + amount; +} + +bool Player::deduct(const ResourceBundle& amount) { + if (this->resources.deduct(amount)) { + this->on_resources_change(); + return true; + } + return false; +} + +bool Player::deduct(const game_resource resource, double amount) { + if (this->resources[resource] >= amount) { + this->resources[resource] -= amount; + this->on_resources_change(); + return true; + } + return false; +} + +bool Player::can_deduct(const ResourceBundle& amount) const { + return this->resources.has(amount); +} + +bool Player::can_deduct(const game_resource resource, double amount) const { + return this->resources.get(resource) >= amount; +} + +double Player::amount(const game_resource resource) const { + return this->resources.get(resource); +} + +bool Player::can_make(const UnitType &type) const { + return this->can_deduct(type.cost.get(*this)) && + this->get_units_have(type.id()) + this->get_units_pending(type.id()) < type.have_limit && + this->get_units_had(type.id()) + this->get_units_pending(type.id()) < type.had_limit; +} + +size_t Player::type_count() { + return this->available_ids.size(); +} + +UnitType *Player::get_type(index_t type_id) const { + if (this->available_ids.count(type_id) == 0) { + if (type_id > 0) { + log::log(MSG(info) << " -> ignoring type_id: " << type_id); + } + return nullptr; + } + return this->available_ids.at(type_id); +} + +UnitType *Player::get_type_index(size_t type_index) const { + if (type_index < available_objects.size()) { + return available_objects.at(type_index).get(); + } + log::log(MSG(info) << " -> ignoring type_index: " << type_index); + return nullptr; +} + + +void Player::initialise_unit_types() { + log::log(MSG(info) << name << " has civilisation " << this->civ->civ_name); + for (auto &type : this->civ->object_meta()) { + auto shared_type = type->init(*this); + index_t id = shared_type->id(); + this->available_objects.emplace_back(shared_type); + this->available_ids[id] = shared_type.get(); + } +} + +void Player::active_unit_added(Unit *unit, bool from_pending) { + // check if unit is actually active + if (this->is_unit_pending(unit)) { + this->units_pending[unit->unit_type->id()] += 1; + return; + } + + if (from_pending) { + this->units_pending[unit->unit_type->id()] -= 1; + } + + this->units_have[unit->unit_type->id()] += 1; + this->units_had[unit->unit_type->id()] += 1; + // TODO handle here building dependencies + + // population + if (unit->has_attribute(attr_type::population)) { + auto popul = unit->get_attribute(); + if (popul.demand > 0) { + this->population.demand_population(popul.demand); + } + if (popul.capacity > 0) { + this->population.add_capacity(popul.capacity); + } + } + + // resources capacity + if (unit->has_attribute(attr_type::storage)) { + auto storage = unit->get_attribute(); + this->resources_capacity += storage.capacity; + this->on_resources_change(); + } + + // score + // TODO improve selectors + if (unit->unit_type->id() == 82 || unit->unit_type->id() == 276) { // Castle, Wonder + this->score.add_score(score_category::society, unit->unit_type->cost.get(*this).sum() * 0.2); + } else if (unit->has_attribute(attr_type::building) || unit->has_attribute(attr_type::population)) { // building, living + this->score.add_score(score_category::economy, unit->unit_type->cost.get(*this).sum() * 0.2); + } + + // TODO handle here on create unit triggers + // TODO check for unit based win conditions +} + +void Player::active_unit_removed(Unit *unit) { + // check if unit is actually active + if (this->is_unit_pending(unit)) { + this->units_pending[unit->unit_type->id()] -= 1; + return; + } + + this->units_have[unit->unit_type->id()] -= 1; + // TODO handle here building dependencies + + // population + if (unit->has_attribute(attr_type::population)) { + auto popul = unit->get_attribute(); + if (popul.demand > 0) { + this->population.free_population(popul.demand); + } + if (popul.capacity > 0) { + this->population.remove_capacity(popul.capacity); + } + } + + // resources capacity + if (unit->has_attribute(attr_type::storage)) { + auto storage = unit->get_attribute(); + this->resources_capacity -= storage.capacity; + this->on_resources_change(); + } + + // score + // TODO improve selectors + if (unit->unit_type->id() == 82 || unit->unit_type->id() == 276) { // Castle, Wonder + // nothing + } else if (unit->has_attribute(attr_type::building) || unit->has_attribute(attr_type::population)) { // building, living + this->score.remove_score(score_category::economy, unit->unit_type->cost.get(*this).sum() * 0.2); + } + + // TODO handle here on death unit triggers + // TODO check for unit based win conditions +} + +void Player::killed_unit(const Unit & unit) { + // score + this->score.add_score(score_category::military, unit.unit_type->cost.get(*this).sum() * 0.2); +} + +void Player::advance_age() { + this->age += 1; +} + +void Player::on_resources_change() { + + // capacity overflow + if (! (this->resources_capacity >= this->resources_capacity)) { + this->resources.limit(this->resources_capacity); + } + + // score + this->score.update_resources(this->resources); + + // TODO check for resource based win conditions +} + +int Player::get_units_have(int type_id) const { + if (this->units_have.count(type_id)) { + return this->units_have.at(type_id); + } + return 0; +} + +int Player::get_units_had(int type_id) const { + if (this->units_had.count(type_id)) { + return this->units_had.at(type_id); + } + return 0; +} + +int Player::get_units_pending(int type_id) const { + if (this->units_pending.count(type_id)) { + return this->units_pending.at(type_id); + } + return 0; +} + +bool Player::is_unit_pending(Unit *unit) const { + // TODO check aslo if unit is training + return unit->has_attribute(attr_type::building) && unit->get_attribute().completed < 1.0f; +} + +int Player::get_workforce_count() const { + // TODO get all units tagged as work force + return this->units_have.at(83) + this->units_have.at(293) + // villagers + this->units_have.at(13) + // fishing ship + this->units_have.at(128) + // trade cart + this->units_have.at(545); // transport ship +} + +} // openage diff --git a/libopenage/gamestate/old/player.h b/libopenage/gamestate/old/player.h new file mode 100644 index 0000000000..5267421b71 --- /dev/null +++ b/libopenage/gamestate/old/player.h @@ -0,0 +1,244 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + +#include "civilisation.h" +#include "population_tracker.h" +#include "resource.h" +#include "score.h" + + +namespace openage { + +class Unit; +class Team; + +class Player { +public: + Player(Civilisation *civ, unsigned int number, std::string name); + + /** + * values 0 .. player count - 1 + */ + const unsigned int player_number; + + /** + * values 1 .. player count + * would be better to have rgb color value + */ + const unsigned int color; + + /** + * civilisation and techs of this player + */ + const Civilisation *civ; + + /** + * visible name of this player + */ + const std::string name; + + /** + * the team of this player + * nullptr if member of no team + */ + Team *team; + + /** + * checks if two players are the same + */ + bool operator ==(const Player &other) const; + + /** + * the specified player is an enemy of this player + */ + bool is_enemy(const Player &) const; + + /** + * the specified player is an ally of this player + */ + bool is_ally(const Player &) const; + + /** + * this player owns the specified unit + */ + bool owns(Unit &) const; + + /** + * add to stockpile + */ + void receive(const ResourceBundle& amount); + void receive(const game_resource resource, double amount); + + /** + * Check if can add to stockpile + */ + bool can_receive(const ResourceBundle& amount) const; + bool can_receive(const game_resource resource, double amount) const; + + /** + * remove from stockpile if available + */ + bool deduct(const ResourceBundle& amount); + bool deduct(const game_resource resource, double amount); + + /** + * Check if the player has enough resources to deduct the given amount. + */ + bool can_deduct(const ResourceBundle& amount) const; + bool can_deduct(const game_resource resource, double amount) const; + + /** + * current stockpile amount + */ + double amount(const game_resource resource) const; + + /** + * Check if the player can make a new unit of the given type + */ + bool can_make(const UnitType &type) const; + + /** + * total number of unit types available + */ + size_t type_count(); + + /** + * unit types by aoe gamedata unit ids -- the unit type which corresponds to an aoe unit id + */ + UnitType *get_type(index_t type_id) const; + + /** + * unit types by list index -- a continuous array of all types + * probably not a useful function / can be removed + */ + UnitType *get_type_index(size_t type_index) const; + + /** + * initialise with the base tech level + */ + void initialise_unit_types(); + + /** + * Keeps track of the population information. + */ + PopulationTracker population; + + /** + * The score of the player. + */ + PlayerScore score; + + /** + * Called when a unit is created and active. + * + * If the unit was pending when create (constuction site, training) the method must be + * called again when the unit activates (with the from_penging param set to true) + */ + void active_unit_added(Unit *unit, bool from_pending=false); + + /** + * Called when a unit is destroyed. + */ + void active_unit_removed(Unit *unit); + + /** + * Called when a unit is killed by this player. + */ + void killed_unit(const Unit & unit); + + /** + * Advance to next age; + */ + void advance_age(); + + // Getters + + /** + * Get the number of units the player has for each unit type id. + */ + int get_units_have(int type_id) const; + + /** + * Get the number of units the player ever had for each unit type id. + */ + int get_units_had(int type_id) const; + + /** + * Get the number of units the player has being made for each unit type id. + */ + int get_units_pending(int type_id) const; + + /** + * Get the current age. + * The first age has the value 1. + */ + int get_age() const { return age; } + + /** + * The number of units considered part of the workforce. + */ + int get_workforce_count() const; + + +private: + + bool is_unit_pending(Unit *unit) const; + + /** + * The resources this player currently has + */ + ResourceBundle resources; + + /** + * The resources capacities this player currently has + */ + ResourceBundle resources_capacity; + + /** + * Called when the resources amounts change. + */ + void on_resources_change(); + + /** + * unit types which can be produced by this player. + * TODO revisit, can be simplified? + */ + unit_type_list available_objects; + + /** + * available objects mapped using type id + * unit ids -> unit type for that id + * TODO revisit, can be simplified? + */ + std::unordered_map available_ids; + + /** + * The number of units the player has for each unit type id. + * Used for and event triggers. + */ + std::unordered_map units_have; + + /** + * The number of units the player ever had for each unit type id. + * Used for unit dependencies (eg. Farm). + */ + std::unordered_map units_had; + + /* + * The number of units the player has being made for each unit type id. + * Used for unit limits (eg. Town Center). + */ + std::unordered_map units_pending; + + /** + * The current age. + */ + int age; + +}; + +} // openage diff --git a/libopenage/gamestate/population_tracker.cpp b/libopenage/gamestate/old/population_tracker.cpp similarity index 100% rename from libopenage/gamestate/population_tracker.cpp rename to libopenage/gamestate/old/population_tracker.cpp diff --git a/libopenage/gamestate/population_tracker.h b/libopenage/gamestate/old/population_tracker.h similarity index 100% rename from libopenage/gamestate/population_tracker.h rename to libopenage/gamestate/old/population_tracker.h diff --git a/libopenage/gamestate/resource.cpp b/libopenage/gamestate/old/resource.cpp similarity index 100% rename from libopenage/gamestate/resource.cpp rename to libopenage/gamestate/old/resource.cpp diff --git a/libopenage/gamestate/resource.h b/libopenage/gamestate/old/resource.h similarity index 100% rename from libopenage/gamestate/resource.h rename to libopenage/gamestate/old/resource.h diff --git a/libopenage/gamestate/score.cpp b/libopenage/gamestate/old/score.cpp similarity index 98% rename from libopenage/gamestate/score.cpp rename to libopenage/gamestate/old/score.cpp index d6c8ed15d9..d610debcda 100644 --- a/libopenage/gamestate/score.cpp +++ b/libopenage/gamestate/old/score.cpp @@ -5,7 +5,7 @@ #include "player.h" #include "score.h" #include "team.h" -#include "../log/log.h" +#include "../../log/log.h" namespace openage { diff --git a/libopenage/gamestate/score.h b/libopenage/gamestate/old/score.h similarity index 100% rename from libopenage/gamestate/score.h rename to libopenage/gamestate/old/score.h diff --git a/libopenage/gamestate/team.cpp b/libopenage/gamestate/old/team.cpp similarity index 100% rename from libopenage/gamestate/team.cpp rename to libopenage/gamestate/old/team.cpp diff --git a/libopenage/gamestate/team.h b/libopenage/gamestate/old/team.h similarity index 100% rename from libopenage/gamestate/team.h rename to libopenage/gamestate/old/team.h diff --git a/libopenage/gamestate/player.cpp b/libopenage/gamestate/player.cpp index acbf70a12f..c571596d20 100644 --- a/libopenage/gamestate/player.cpp +++ b/libopenage/gamestate/player.cpp @@ -1,291 +1,8 @@ -// Copyright 2015-2019 the openage authors. See copying.md for legal info. +// Copyright 2018-2019 the openage authors. See copying.md for legal info. #include "player.h" -#include +namespace openage::gamestate { -#include "../log/log.h" -#include "../unit/unit.h" -#include "../unit/unit_type.h" -#include "../util/math_constants.h" -#include "team.h" - -namespace openage { - -Player::Player(Civilisation *civ, unsigned int number, std::string name) - : - player_number{number}, - color{number}, - civ{civ}, - name{std::move(name)}, - team{nullptr}, - population{0, 200}, // TODO change, get population cap max from game options - score{this}, - age{1} { // TODO change, get starting age from game options - // starting resources - // TODO change, get starting resources from game options - this->resources.set_all(1000); - // TODO change, get starting resources capacity from game options or nyan - this->resources_capacity.set_all(math::DOUBLE_INF / 2); // half to avoid overflows - this->on_resources_change(); -} - -bool Player::operator ==(const Player &other) const { - return this->player_number == other.player_number; -} - -bool Player::is_enemy(const Player &other) const { - - return !this->is_ally(other); -} - -bool Player::is_ally(const Player &other) const { - - if (this->player_number == other.player_number) { - return true; // same player - } - - if (this->team && this->team->is_member(other)) { - return true; // same team - } - - // everyone else is enemy - return false; -} - -bool Player::owns(Unit &unit) const { - if (unit.has_attribute(attr_type::owner)) { - return this == &unit.get_attribute().player; - } - return false; -} - -void Player::receive(const ResourceBundle& amount) { - this->resources += amount; - this->on_resources_change(); -} - -void Player::receive(const game_resource resource, double amount) { - this->resources[resource] += amount; - this->on_resources_change(); -} - -bool Player::can_receive(const ResourceBundle& amount) const { - return this->resources_capacity.has(this->resources, amount); -} - -bool Player::can_receive(const game_resource resource, double amount) const { - return this->resources_capacity.get(resource) >= this->resources.get(resource) + amount; -} - -bool Player::deduct(const ResourceBundle& amount) { - if (this->resources.deduct(amount)) { - this->on_resources_change(); - return true; - } - return false; -} - -bool Player::deduct(const game_resource resource, double amount) { - if (this->resources[resource] >= amount) { - this->resources[resource] -= amount; - this->on_resources_change(); - return true; - } - return false; -} - -bool Player::can_deduct(const ResourceBundle& amount) const { - return this->resources.has(amount); -} - -bool Player::can_deduct(const game_resource resource, double amount) const { - return this->resources.get(resource) >= amount; -} - -double Player::amount(const game_resource resource) const { - return this->resources.get(resource); -} - -bool Player::can_make(const UnitType &type) const { - return this->can_deduct(type.cost.get(*this)) && - this->get_units_have(type.id()) + this->get_units_pending(type.id()) < type.have_limit && - this->get_units_had(type.id()) + this->get_units_pending(type.id()) < type.had_limit; -} - -size_t Player::type_count() { - return this->available_ids.size(); -} - -UnitType *Player::get_type(index_t type_id) const { - if (this->available_ids.count(type_id) == 0) { - if (type_id > 0) { - log::log(MSG(info) << " -> ignoring type_id: " << type_id); - } - return nullptr; - } - return this->available_ids.at(type_id); -} - -UnitType *Player::get_type_index(size_t type_index) const { - if (type_index < available_objects.size()) { - return available_objects.at(type_index).get(); - } - log::log(MSG(info) << " -> ignoring type_index: " << type_index); - return nullptr; -} - - -void Player::initialise_unit_types() { - log::log(MSG(info) << name << " has civilisation " << this->civ->civ_name); - for (auto &type : this->civ->object_meta()) { - auto shared_type = type->init(*this); - index_t id = shared_type->id(); - this->available_objects.emplace_back(shared_type); - this->available_ids[id] = shared_type.get(); - } -} - -void Player::active_unit_added(Unit *unit, bool from_pending) { - // check if unit is actually active - if (this->is_unit_pending(unit)) { - this->units_pending[unit->unit_type->id()] += 1; - return; - } - - if (from_pending) { - this->units_pending[unit->unit_type->id()] -= 1; - } - - this->units_have[unit->unit_type->id()] += 1; - this->units_had[unit->unit_type->id()] += 1; - // TODO handle here building dependencies - - // population - if (unit->has_attribute(attr_type::population)) { - auto popul = unit->get_attribute(); - if (popul.demand > 0) { - this->population.demand_population(popul.demand); - } - if (popul.capacity > 0) { - this->population.add_capacity(popul.capacity); - } - } - - // resources capacity - if (unit->has_attribute(attr_type::storage)) { - auto storage = unit->get_attribute(); - this->resources_capacity += storage.capacity; - this->on_resources_change(); - } - - // score - // TODO improve selectors - if (unit->unit_type->id() == 82 || unit->unit_type->id() == 276) { // Castle, Wonder - this->score.add_score(score_category::society, unit->unit_type->cost.get(*this).sum() * 0.2); - } else if (unit->has_attribute(attr_type::building) || unit->has_attribute(attr_type::population)) { // building, living - this->score.add_score(score_category::economy, unit->unit_type->cost.get(*this).sum() * 0.2); - } - - // TODO handle here on create unit triggers - // TODO check for unit based win conditions -} - -void Player::active_unit_removed(Unit *unit) { - // check if unit is actually active - if (this->is_unit_pending(unit)) { - this->units_pending[unit->unit_type->id()] -= 1; - return; - } - - this->units_have[unit->unit_type->id()] -= 1; - // TODO handle here building dependencies - - // population - if (unit->has_attribute(attr_type::population)) { - auto popul = unit->get_attribute(); - if (popul.demand > 0) { - this->population.free_population(popul.demand); - } - if (popul.capacity > 0) { - this->population.remove_capacity(popul.capacity); - } - } - - // resources capacity - if (unit->has_attribute(attr_type::storage)) { - auto storage = unit->get_attribute(); - this->resources_capacity -= storage.capacity; - this->on_resources_change(); - } - - // score - // TODO improve selectors - if (unit->unit_type->id() == 82 || unit->unit_type->id() == 276) { // Castle, Wonder - // nothing - } else if (unit->has_attribute(attr_type::building) || unit->has_attribute(attr_type::population)) { // building, living - this->score.remove_score(score_category::economy, unit->unit_type->cost.get(*this).sum() * 0.2); - } - - // TODO handle here on death unit triggers - // TODO check for unit based win conditions -} - -void Player::killed_unit(const Unit & unit) { - // score - this->score.add_score(score_category::military, unit.unit_type->cost.get(*this).sum() * 0.2); -} - -void Player::advance_age() { - this->age += 1; -} - -void Player::on_resources_change() { - - // capacity overflow - if (! (this->resources_capacity >= this->resources_capacity)) { - this->resources.limit(this->resources_capacity); - } - - // score - this->score.update_resources(this->resources); - - // TODO check for resource based win conditions -} - -int Player::get_units_have(int type_id) const { - if (this->units_have.count(type_id)) { - return this->units_have.at(type_id); - } - return 0; -} - -int Player::get_units_had(int type_id) const { - if (this->units_had.count(type_id)) { - return this->units_had.at(type_id); - } - return 0; -} - -int Player::get_units_pending(int type_id) const { - if (this->units_pending.count(type_id)) { - return this->units_pending.at(type_id); - } - return 0; -} - -bool Player::is_unit_pending(Unit *unit) const { - // TODO check aslo if unit is training - return unit->has_attribute(attr_type::building) && unit->get_attribute().completed < 1.0f; -} - -int Player::get_workforce_count() const { - // TODO get all units tagged as work force - return this->units_have.at(83) + this->units_have.at(293) + // villagers - this->units_have.at(13) + // fishing ship - this->units_have.at(128) + // trade cart - this->units_have.at(545); // transport ship -} - -} // openage +} // openage::gamestate diff --git a/libopenage/gamestate/player.h b/libopenage/gamestate/player.h index 5267421b71..5ff1e45bb2 100644 --- a/libopenage/gamestate/player.h +++ b/libopenage/gamestate/player.h @@ -1,243 +1,11 @@ -// Copyright 2015-2017 the openage authors. See copying.md for legal info. +// Copyright 2018-2018 the openage authors. See copying.md for legal info. #pragma once -#include -#include -#include "civilisation.h" -#include "population_tracker.h" -#include "resource.h" -#include "score.h" - - -namespace openage { - -class Unit; -class Team; +namespace openage::gamestate { class Player { -public: - Player(Civilisation *civ, unsigned int number, std::string name); - - /** - * values 0 .. player count - 1 - */ - const unsigned int player_number; - - /** - * values 1 .. player count - * would be better to have rgb color value - */ - const unsigned int color; - - /** - * civilisation and techs of this player - */ - const Civilisation *civ; - - /** - * visible name of this player - */ - const std::string name; - - /** - * the team of this player - * nullptr if member of no team - */ - Team *team; - - /** - * checks if two players are the same - */ - bool operator ==(const Player &other) const; - - /** - * the specified player is an enemy of this player - */ - bool is_enemy(const Player &) const; - - /** - * the specified player is an ally of this player - */ - bool is_ally(const Player &) const; - - /** - * this player owns the specified unit - */ - bool owns(Unit &) const; - - /** - * add to stockpile - */ - void receive(const ResourceBundle& amount); - void receive(const game_resource resource, double amount); - - /** - * Check if can add to stockpile - */ - bool can_receive(const ResourceBundle& amount) const; - bool can_receive(const game_resource resource, double amount) const; - - /** - * remove from stockpile if available - */ - bool deduct(const ResourceBundle& amount); - bool deduct(const game_resource resource, double amount); - - /** - * Check if the player has enough resources to deduct the given amount. - */ - bool can_deduct(const ResourceBundle& amount) const; - bool can_deduct(const game_resource resource, double amount) const; - - /** - * current stockpile amount - */ - double amount(const game_resource resource) const; - - /** - * Check if the player can make a new unit of the given type - */ - bool can_make(const UnitType &type) const; - - /** - * total number of unit types available - */ - size_t type_count(); - - /** - * unit types by aoe gamedata unit ids -- the unit type which corresponds to an aoe unit id - */ - UnitType *get_type(index_t type_id) const; - - /** - * unit types by list index -- a continuous array of all types - * probably not a useful function / can be removed - */ - UnitType *get_type_index(size_t type_index) const; - - /** - * initialise with the base tech level - */ - void initialise_unit_types(); - - /** - * Keeps track of the population information. - */ - PopulationTracker population; - - /** - * The score of the player. - */ - PlayerScore score; - - /** - * Called when a unit is created and active. - * - * If the unit was pending when create (constuction site, training) the method must be - * called again when the unit activates (with the from_penging param set to true) - */ - void active_unit_added(Unit *unit, bool from_pending=false); - - /** - * Called when a unit is destroyed. - */ - void active_unit_removed(Unit *unit); - - /** - * Called when a unit is killed by this player. - */ - void killed_unit(const Unit & unit); - - /** - * Advance to next age; - */ - void advance_age(); - - // Getters - - /** - * Get the number of units the player has for each unit type id. - */ - int get_units_have(int type_id) const; - - /** - * Get the number of units the player ever had for each unit type id. - */ - int get_units_had(int type_id) const; - - /** - * Get the number of units the player has being made for each unit type id. - */ - int get_units_pending(int type_id) const; - - /** - * Get the current age. - * The first age has the value 1. - */ - int get_age() const { return age; } - - /** - * The number of units considered part of the workforce. - */ - int get_workforce_count() const; - - -private: - - bool is_unit_pending(Unit *unit) const; - - /** - * The resources this player currently has - */ - ResourceBundle resources; - - /** - * The resources capacities this player currently has - */ - ResourceBundle resources_capacity; - - /** - * Called when the resources amounts change. - */ - void on_resources_change(); - - /** - * unit types which can be produced by this player. - * TODO revisit, can be simplified? - */ - unit_type_list available_objects; - - /** - * available objects mapped using type id - * unit ids -> unit type for that id - * TODO revisit, can be simplified? - */ - std::unordered_map available_ids; - - /** - * The number of units the player has for each unit type id. - * Used for and event triggers. - */ - std::unordered_map units_have; - - /** - * The number of units the player ever had for each unit type id. - * Used for unit dependencies (eg. Farm). - */ - std::unordered_map units_had; - - /* - * The number of units the player has being made for each unit type id. - * Used for unit limits (eg. Town Center). - */ - std::unordered_map units_pending; - - /** - * The current age. - */ - int age; }; diff --git a/libopenage/gamestate/terrain.cpp b/libopenage/gamestate/terrain.cpp new file mode 100644 index 0000000000..9a07542b39 --- /dev/null +++ b/libopenage/gamestate/terrain.cpp @@ -0,0 +1,8 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#include "terrain.h" + + +namespace openage::gamestate { + +} // openage::gamestate diff --git a/libopenage/gamestate/terrain.h b/libopenage/gamestate/terrain.h new file mode 100644 index 0000000000..266bb3ee05 --- /dev/null +++ b/libopenage/gamestate/terrain.h @@ -0,0 +1,12 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#pragma once + + +namespace openage::gamestate { + +class Terrain { + +}; + +} // openage::gamestate diff --git a/libopenage/gamestate/terrain_chunk.cpp b/libopenage/gamestate/terrain_chunk.cpp new file mode 100644 index 0000000000..d434a7a921 --- /dev/null +++ b/libopenage/gamestate/terrain_chunk.cpp @@ -0,0 +1,8 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#include "terrain_chunk.h" + + +namespace openage::gamestate { + +} // openage::gamestate diff --git a/libopenage/gamestate/terrain_chunk.h b/libopenage/gamestate/terrain_chunk.h new file mode 100644 index 0000000000..068bb1b3bf --- /dev/null +++ b/libopenage/gamestate/terrain_chunk.h @@ -0,0 +1,12 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#pragma once + + +namespace openage::gamestate { + +class TerrainChunk { + +}; + +} // openage::gamestate diff --git a/libopenage/gamestate/types.cpp b/libopenage/gamestate/types.cpp new file mode 100644 index 0000000000..92ffe234f7 --- /dev/null +++ b/libopenage/gamestate/types.cpp @@ -0,0 +1,8 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#include "types.h" + + +namespace openage { + +} // openage diff --git a/libopenage/gamestate/types.h b/libopenage/gamestate/types.h new file mode 100644 index 0000000000..0afeedb220 --- /dev/null +++ b/libopenage/gamestate/types.h @@ -0,0 +1,16 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#pragma once + + +namespace openage { + +/** + * The key type mapped to data objects. + * Used for graphics indices, sounds, ... + * TODO: get rid of this. + */ +using index_t = int; + + +} // openage diff --git a/libopenage/gamestate/world.cpp b/libopenage/gamestate/world.cpp new file mode 100644 index 0000000000..765229d629 --- /dev/null +++ b/libopenage/gamestate/world.cpp @@ -0,0 +1,8 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#include "world.h" + +namespace openage::gamestate { + + +} // openage::gamestate diff --git a/libopenage/gamestate/world.h b/libopenage/gamestate/world.h new file mode 100644 index 0000000000..eedaf725d2 --- /dev/null +++ b/libopenage/gamestate/world.h @@ -0,0 +1,11 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#pragma once + +namespace openage::gamestate { + +class World { + +}; + +} // openage diff --git a/libopenage/gui/category_contents_list_model.h b/libopenage/gui/category_contents_list_model.h index e644b5ac15..529f8f99d5 100644 --- a/libopenage/gui/category_contents_list_model.h +++ b/libopenage/gui/category_contents_list_model.h @@ -1,4 +1,4 @@ -// Copyright 2015-2016 the openage authors. See copying.md for legal info. +// Copyright 2015-2018 the openage authors. See copying.md for legal info. #pragma once @@ -7,7 +7,7 @@ #include #include -#include "../gamestate/game_spec.h" +#include "../gamestate/types.h" #include diff --git a/libopenage/gui/game_creator.cpp b/libopenage/gui/game_creator.cpp index d3b0a30c7f..38da360c68 100644 --- a/libopenage/gui/game_creator.cpp +++ b/libopenage/gui/game_creator.cpp @@ -4,9 +4,9 @@ #include -#include "../gamestate/game_main.h" -#include "../gamestate/game_spec.h" -#include "../gamestate/generator.h" +#include "../gamestate/old/game_main.h" +#include "../gamestate/old/game_spec.h" +#include "../gamestate/old/generator.h" #include "game_main_link.h" #include "game_spec_link.h" diff --git a/libopenage/gui/game_main_link.h b/libopenage/gui/game_main_link.h index 9836dc2b61..0277cab6b8 100644 --- a/libopenage/gui/game_main_link.h +++ b/libopenage/gui/game_main_link.h @@ -1,4 +1,4 @@ -// Copyright 2015-2016 the openage authors. See copying.md for legal info. +// Copyright 2015-2018 the openage authors. See copying.md for legal info. #pragma once @@ -6,7 +6,7 @@ #include "guisys/link/gui_item.h" -#include "../gamestate/game_main.h" +#include "../gamestate/old/game_main.h" namespace openage { namespace gui { diff --git a/libopenage/gui/game_saver.cpp b/libopenage/gui/game_saver.cpp index 1d71ee8aa1..08cc39dab7 100644 --- a/libopenage/gui/game_saver.cpp +++ b/libopenage/gui/game_saver.cpp @@ -4,9 +4,9 @@ #include -#include "../gamestate/game_save.h" -#include "../gamestate/game_main.h" -#include "../gamestate/generator.h" +#include "../gamestate/old/game_save.h" +#include "../gamestate/old/game_main.h" +#include "../gamestate/old/generator.h" #include "game_main_link.h" #include "generator_link.h" diff --git a/libopenage/gui/game_spec_link.h b/libopenage/gui/game_spec_link.h index a1bbe138ca..73284b8ec3 100644 --- a/libopenage/gui/game_spec_link.h +++ b/libopenage/gui/game_spec_link.h @@ -1,4 +1,4 @@ -// Copyright 2015-2016 the openage authors. See copying.md for legal info. +// Copyright 2015-2018 the openage authors. See copying.md for legal info. #pragma once @@ -9,7 +9,7 @@ #include "guisys/link/gui_item.h" -#include "../gamestate/game_spec.h" +#include "../gamestate/old/game_spec.h" namespace openage { diff --git a/libopenage/gui/generator_link.h b/libopenage/gui/generator_link.h index 2bb86e59d5..804f75e80e 100644 --- a/libopenage/gui/generator_link.h +++ b/libopenage/gui/generator_link.h @@ -1,8 +1,8 @@ -// Copyright 2015-2016 the openage authors. See copying.md for legal info. +// Copyright 2015-2018 the openage authors. See copying.md for legal info. #pragma once -#include "../gamestate/generator.h" +#include "../gamestate/old/generator.h" #include "guisys/link/gui_list_model.h" #include "guisys/link/gui_item_list_model.h" diff --git a/libopenage/gui/integration/private/gui_game_spec_image_provider_by_filename_impl.cpp b/libopenage/gui/integration/private/gui_game_spec_image_provider_by_filename_impl.cpp index 4effac65de..400557736c 100644 --- a/libopenage/gui/integration/private/gui_game_spec_image_provider_by_filename_impl.cpp +++ b/libopenage/gui/integration/private/gui_game_spec_image_provider_by_filename_impl.cpp @@ -4,7 +4,7 @@ #include "../../../error/error.h" -#include "../../../gamestate/game_spec.h" +#include "../../../gamestate/old/game_spec.h" namespace openage::gui { diff --git a/libopenage/gui/integration/private/gui_game_spec_image_provider_by_graphic_id_impl.cpp b/libopenage/gui/integration/private/gui_game_spec_image_provider_by_graphic_id_impl.cpp index dad94c0c69..3ac7499694 100644 --- a/libopenage/gui/integration/private/gui_game_spec_image_provider_by_graphic_id_impl.cpp +++ b/libopenage/gui/integration/private/gui_game_spec_image_provider_by_graphic_id_impl.cpp @@ -4,7 +4,7 @@ #include "../../../error/error.h" -#include "../../../gamestate/game_spec.h" +#include "../../../gamestate/old/game_spec.h" namespace openage::gui { diff --git a/libopenage/gui/integration/private/gui_game_spec_image_provider_by_id_impl.cpp b/libopenage/gui/integration/private/gui_game_spec_image_provider_by_id_impl.cpp index 1c17f6b9cb..ca9ddd5664 100644 --- a/libopenage/gui/integration/private/gui_game_spec_image_provider_by_id_impl.cpp +++ b/libopenage/gui/integration/private/gui_game_spec_image_provider_by_id_impl.cpp @@ -4,7 +4,7 @@ #include "../../../error/error.h" -#include "../../../gamestate/game_spec.h" +#include "../../../gamestate/old/game_spec.h" #include "gui_texture_factory.h" namespace openage::gui { diff --git a/libopenage/gui/integration/private/gui_game_spec_image_provider_by_terrain_id_impl.cpp b/libopenage/gui/integration/private/gui_game_spec_image_provider_by_terrain_id_impl.cpp index 740e874735..0c983cfb88 100644 --- a/libopenage/gui/integration/private/gui_game_spec_image_provider_by_terrain_id_impl.cpp +++ b/libopenage/gui/integration/private/gui_game_spec_image_provider_by_terrain_id_impl.cpp @@ -4,7 +4,7 @@ #include "../../../error/error.h" -#include "../../../gamestate/game_spec.h" +#include "../../../gamestate/old/game_spec.h" namespace openage::gui { diff --git a/libopenage/gui/integration/private/gui_game_spec_image_provider_impl.cpp b/libopenage/gui/integration/private/gui_game_spec_image_provider_impl.cpp index abf159bb9b..ce976f1909 100644 --- a/libopenage/gui/integration/private/gui_game_spec_image_provider_impl.cpp +++ b/libopenage/gui/integration/private/gui_game_spec_image_provider_impl.cpp @@ -8,7 +8,7 @@ #include "../../../error/error.h" -#include "../../../gamestate/game_spec.h" +#include "../../../gamestate/old/game_spec.h" #include "../../guisys/private/gui_event_queue_impl.h" #include "gui_texture_factory.h" diff --git a/libopenage/gui/resources_list_model.h b/libopenage/gui/resources_list_model.h index 8cfdc20832..574618fb78 100644 --- a/libopenage/gui/resources_list_model.h +++ b/libopenage/gui/resources_list_model.h @@ -1,4 +1,4 @@ -// Copyright 2016-2016 the openage authors. See copying.md for legal info. +// Copyright 2016-2018 the openage authors. See copying.md for legal info. #pragma once @@ -7,7 +7,7 @@ #include -#include "../gamestate/resource.h" +#include "../gamestate/old/resource.h" namespace openage { namespace gui { diff --git a/libopenage/main.cpp b/libopenage/main.cpp index 2e4e12b115..34538d68f6 100644 --- a/libopenage/main.cpp +++ b/libopenage/main.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2017 the openage authors. See copying.md for legal info. +// Copyright 2015-2018 the openage authors. See copying.md for legal info. #include "main.h" @@ -7,7 +7,6 @@ #include "game_control.h" #include "game_renderer.h" #include "gamedata/color.gen.h" -#include "gamestate/generator.h" #include "log/log.h" #include "shader/program.h" #include "shader/shader.h" diff --git a/libopenage/unit/ability.cpp b/libopenage/unit/ability.cpp index 907bf45a6e..e290b7c1dc 100644 --- a/libopenage/unit/ability.cpp +++ b/libopenage/unit/ability.cpp @@ -3,8 +3,8 @@ #include #include "../terrain/terrain_object.h" -#include "../gamestate/cost.h" -#include "../gamestate/player.h" +#include "../gamestate/old/cost.h" +#include "../gamestate/old/player.h" #include "ability.h" #include "action.h" #include "command.h" diff --git a/libopenage/unit/ability.h b/libopenage/unit/ability.h index fd879e1127..022b522caf 100644 --- a/libopenage/unit/ability.h +++ b/libopenage/unit/ability.h @@ -9,7 +9,6 @@ #include #include "../coord/phys.h" -#include "../gamestate/resource.h" namespace openage { diff --git a/libopenage/unit/action.h b/libopenage/unit/action.h index 727e151bdd..7809ae4bfd 100644 --- a/libopenage/unit/action.h +++ b/libopenage/unit/action.h @@ -6,7 +6,7 @@ #include #include "../pathfinding/path.h" -#include "../gamestate/resource.h" +#include "../gamestate/old/resource.h" #include "attribute.h" #include "research.h" #include "unit.h" diff --git a/libopenage/unit/attribute.h b/libopenage/unit/attribute.h index 6cd5c90cbf..9d335dfc7f 100644 --- a/libopenage/unit/attribute.h +++ b/libopenage/unit/attribute.h @@ -9,7 +9,7 @@ #include "../coord/tile.h" #include "../gamedata/unit.gen.h" #include "../terrain/terrain_object.h" -#include "../gamestate/resource.h" +#include "../gamestate/old/resource.h" #include "unit_container.h" namespace std { diff --git a/libopenage/unit/producer.h b/libopenage/unit/producer.h index 642ed74c4c..291aace616 100644 --- a/libopenage/unit/producer.h +++ b/libopenage/unit/producer.h @@ -9,7 +9,7 @@ #include "../coord/tile.h" #include "../gamedata/gamedata.gen.h" #include "../gamedata/graphic.gen.h" -#include "../gamestate/player.h" +#include "../gamestate/old/player.h" #include "unit.h" #include "unit_type.h" diff --git a/libopenage/unit/research.cpp b/libopenage/unit/research.cpp index eb74b138cc..7161565583 100644 --- a/libopenage/unit/research.cpp +++ b/libopenage/unit/research.cpp @@ -1,6 +1,6 @@ -// Copyright 2017-2017 the openage authors. See copying.md for legal info. +// Copyright 2017-2018 the openage authors. See copying.md for legal info. -#include "../gamestate/player.h" +#include "../gamestate/old/player.h" #include "research.h" diff --git a/libopenage/unit/unit_texture.cpp b/libopenage/unit/unit_texture.cpp index 3604b1c165..c155368d4f 100644 --- a/libopenage/unit/unit_texture.cpp +++ b/libopenage/unit/unit_texture.cpp @@ -7,7 +7,7 @@ #include "../coord/phys.h" #include "../coord/pixel.h" -#include "../gamestate/game_spec.h" +#include "../gamestate/old/game_spec.h" #include "../log/log.h" #include "../texture.h" #include "../util/math.h" diff --git a/libopenage/unit/unit_type.cpp b/libopenage/unit/unit_type.cpp index 76feeedaf2..01c1638e0b 100644 --- a/libopenage/unit/unit_type.cpp +++ b/libopenage/unit/unit_type.cpp @@ -1,6 +1,6 @@ // Copyright 2015-2019 the openage authors. See copying.md for legal info. -#include "../gamestate/player.h" +#include "../gamestate/old/player.h" #include "../terrain/terrain_object.h" #include "../util/math_constants.h" #include "action.h" diff --git a/libopenage/unit/unit_type.h b/libopenage/unit/unit_type.h index 53099f7aa4..3fa1d450e7 100644 --- a/libopenage/unit/unit_type.h +++ b/libopenage/unit/unit_type.h @@ -8,7 +8,7 @@ #include #include "../coord/phys.h" -#include "../gamestate/cost.h" +#include "../gamestate/old/cost.h" #include "attributes.h" namespace openage { From 60b2435922dd5ef239143a59743a5f84712a3ce8 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sat, 3 Nov 2018 02:18:59 +0100 Subject: [PATCH 02/28] main: duplicate renderer demo as first step --- libopenage/CMakeLists.txt | 10 +- libopenage/main/CMakeLists.txt | 7 + libopenage/main/tests.cpp | 255 ++++++++++++++++++++++++++++ libopenage/main/tests.h | 14 ++ libopenage/presenter/CMakeLists.txt | 0 openage/CMakeLists.txt | 3 +- openage/__main__.py | 6 + openage/main/CMakeLists.txt | 9 + openage/main/__init__.py | 5 + openage/main/main.py | 78 +++++++++ openage/main/main_cpp.pyx | 51 ++++++ openage/main/tests.pyx | 51 ++++++ openage/testing/testlist.py | 2 + 13 files changed, 486 insertions(+), 5 deletions(-) create mode 100644 libopenage/main/CMakeLists.txt create mode 100644 libopenage/main/tests.cpp create mode 100644 libopenage/main/tests.h create mode 100644 libopenage/presenter/CMakeLists.txt create mode 100644 openage/main/CMakeLists.txt create mode 100644 openage/main/__init__.py create mode 100644 openage/main/main.py create mode 100644 openage/main/main_cpp.pyx create mode 100644 openage/main/tests.pyx diff --git a/libopenage/CMakeLists.txt b/libopenage/CMakeLists.txt index c99c0506af..2bdd29177e 100644 --- a/libopenage/CMakeLists.txt +++ b/libopenage/CMakeLists.txt @@ -340,19 +340,21 @@ add_subdirectory("coord") add_subdirectory("curve") add_subdirectory("cvar") add_subdirectory("datastructure") -add_subdirectory("event") -add_subdirectory("gui") add_subdirectory("error") +add_subdirectory("event") add_subdirectory("gamestate") +add_subdirectory("gui") add_subdirectory("input") -add_subdirectory("log") add_subdirectory("job") +add_subdirectory("log") +add_subdirectory("main") add_subdirectory("pathfinding") +add_subdirectory("presenter") add_subdirectory("pyinterface") add_subdirectory("renderer") add_subdirectory("rng") -add_subdirectory("simulation") add_subdirectory("shader") +add_subdirectory("simulation") add_subdirectory("terrain") add_subdirectory("testing") add_subdirectory("unit") diff --git a/libopenage/main/CMakeLists.txt b/libopenage/main/CMakeLists.txt new file mode 100644 index 0000000000..50efd4266f --- /dev/null +++ b/libopenage/main/CMakeLists.txt @@ -0,0 +1,7 @@ +add_sources(libopenage + tests.cpp +) + +pxdgen( + tests.h +) diff --git a/libopenage/main/tests.cpp b/libopenage/main/tests.cpp new file mode 100644 index 0000000000..841fe0ea85 --- /dev/null +++ b/libopenage/main/tests.cpp @@ -0,0 +1,255 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#include +#include +#include +#include +#include +#include + +#include "../log/log.h" +#include "../error/error.h" +#include "../renderer/resources/shader_source.h" +#include "../renderer/resources/texture_data.h" +#include "../renderer/resources/mesh_data.h" +#include "../renderer/texture.h" +#include "../renderer/shader_program.h" +#include "../renderer/geometry.h" +#include "../renderer/opengl/window.h" + + +namespace openage::main::tests { + +void engine_demo_0(const util::Path& path) { + renderer::opengl::GlWindow window("openage engine test", 800, 600); + + auto renderer = window.make_renderer(); + + auto vshader_src = renderer::resources::ShaderSource( + renderer::resources::shader_lang_t::glsl, + renderer::resources::shader_stage_t::vertex, + R"s( +#version 330 + +layout(location=0) in vec2 position; +layout(location=1) in vec2 uv; +uniform mat4 mvp; +out vec2 v_uv; + +void main() { + gl_Position = mvp * vec4(position, 0.0, 1.0); + v_uv = vec2(uv.x, 1.0 - uv.y); +} +)s"); + + auto fshader_src = renderer::resources::ShaderSource( + renderer::resources::shader_lang_t::glsl, + renderer::resources::shader_stage_t::fragment, + R"s( +#version 330 + +in vec2 v_uv; +uniform sampler2D tex; +uniform uint u_id; + +layout(location=0) out vec4 col; +layout(location=1) out uint id; + +void main() { + vec4 tex_val = texture(tex, v_uv); + if (tex_val.a == 0) { + discard; + } + col = tex_val; + id = u_id + 1u; +} +)s"); + + auto vshader_display_src = renderer::resources::ShaderSource( + renderer::resources::shader_lang_t::glsl, + renderer::resources::shader_stage_t::vertex, + R"s( +#version 330 + +layout(location=0) in vec2 position; +layout(location=1) in vec2 uv; +uniform mat4 proj; +out vec2 v_uv; + +void main() { + gl_Position = proj * vec4(position, 0.0, 1.0); + v_uv = uv; +} +)s"); + + auto fshader_display_src = renderer::resources::ShaderSource( + renderer::resources::shader_lang_t::glsl, + renderer::resources::shader_stage_t::fragment, + R"s( +#version 330 + +uniform sampler2D color_texture; + +in vec2 v_uv; +out vec4 col; + +void main() { + col = texture(color_texture, v_uv); +} +)s"); + + auto shader = renderer->add_shader( { vshader_src, fshader_src } ); + auto shader_display = renderer->add_shader( { vshader_display_src, fshader_display_src } ); + + + auto transform1 = Eigen::Affine3f::Identity(); + transform1.prescale(Eigen::Vector3f(0.4f, 0.2f, 1.0f)); + transform1.prerotate(Eigen::AngleAxisf(30.0f * 3.14159f / 180.0f, Eigen::Vector3f::UnitX())); + transform1.pretranslate(Eigen::Vector3f(-0.4f, -0.6f, 0.0f)); + + auto unif_in1 = shader->new_uniform_input( + "mvp", transform1.matrix(), + "u_id", 1u + ); + + auto transform2 = Eigen::Affine3f::Identity(); + transform2.prescale(Eigen::Vector3f(0.3f, 0.1f, 1.0f)); + transform2.prerotate(Eigen::AngleAxisf(50.0f * 3.14159f / 180.0f, Eigen::Vector3f::UnitZ())); + + auto transform3 = transform2; + + transform2.pretranslate(Eigen::Vector3f(0.3f, 0.1f, 0.3f)); + + auto tex = renderer::resources::Texture2dData(path / "/assets/gaben.png"); + auto gltex = renderer->add_texture(tex); + auto unif_in2 = shader->new_uniform_input( + "mvp", transform2.matrix(), + "u_id", 2u, + "tex", gltex.get() + ); + + transform3.prerotate(Eigen::AngleAxisf(90.0f * 3.14159f / 180.0f, Eigen::Vector3f::UnitZ())); + transform3.pretranslate(Eigen::Vector3f(0.3f, 0.1f, 0.5f)); + + auto unif_in3 = shader->new_uniform_input( + "mvp", transform3.matrix(), + "u_id", 3u + ); + + auto quad = renderer->add_mesh_geometry(renderer::resources::MeshData::make_quad()); + renderer::Renderable obj1 { + unif_in1.get(), + quad.get(), + true, + true, + }; + + renderer::Renderable obj2{ + unif_in2.get(), + quad.get(), + true, + true, + }; + + renderer::Renderable obj3 { + unif_in3.get(), + quad.get(), + true, + true, + }; + + auto size = window.get_size(); + auto color_texture = renderer->add_texture(renderer::resources::Texture2dInfo(size.first, size.second, renderer::resources::pixel_format::rgba8)); + auto id_texture = renderer->add_texture(renderer::resources::Texture2dInfo(size.first, size.second, renderer::resources::pixel_format::r32ui)); + auto depth_texture = renderer->add_texture(renderer::resources::Texture2dInfo(size.first, size.second, renderer::resources::pixel_format::depth24)); + auto fbo = renderer->create_texture_target( { color_texture.get(), id_texture.get(), depth_texture.get() } ); + + auto color_texture_uniform = shader_display->new_uniform_input("color_texture", color_texture.get()); + renderer::Renderable display_obj { + color_texture_uniform.get(), + quad.get(), + false, + false, + }; + + renderer::RenderPass pass { + { obj1, obj2, obj3 }, + fbo.get() + }; + + renderer::RenderPass display_pass { + { display_obj }, + renderer->get_display_target(), + }; + + renderer::resources::Texture2dData id_texture_data = id_texture->into_data(); + bool texture_data_valid = false; + + glDepthFunc(GL_LEQUAL); + glDepthRange(0.0, 1.0); + // what is this + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + window.add_mouse_button_callback([&] (SDL_MouseButtonEvent const& ev) { + auto x = ev.x; + auto y = ev.y; + + log::log(INFO << "Clicked at location (" << x << ", " << y << ")"); + if (!texture_data_valid) { + id_texture_data = id_texture->into_data(); + texture_data_valid = true; + } + auto id = id_texture_data.read_pixel(x, y); + log::log(INFO << "Id-texture-value at location: " << id); + if (id == 0) { + //no renderable at given location + log::log(INFO << "Clicked at non existent object"); + return; + } + id--; //real id is id-1 + log::log(INFO << "Object number " << id << " clicked."); + } ); + + window.add_resize_callback([&] (size_t w, size_t h) { + // Calculate projection matrix + float aspectRatio = float(w)/float(h); + float xScale = 1.0/aspectRatio; + + Eigen::Matrix4f pmat; + pmat << xScale, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1; + + // resize fbo + color_texture = renderer->add_texture(renderer::resources::Texture2dInfo(w, h, renderer::resources::pixel_format::rgba8)); + id_texture = renderer->add_texture(renderer::resources::Texture2dInfo(w, h, renderer::resources::pixel_format::r32ui)); + depth_texture = renderer->add_texture(renderer::resources::Texture2dInfo(w, h, renderer::resources::pixel_format::depth24)); + fbo = renderer->create_texture_target( { color_texture.get(), id_texture.get(), depth_texture.get() } ); + texture_data_valid = false; + + shader_display->update_uniform_input(color_texture_uniform.get(), "color_texture", color_texture.get(), "proj", pmat); + pass.target = fbo.get(); + } ); + + while (!window.should_close()) { + renderer->render(pass); + renderer->render(display_pass); + window.update(); + renderer::opengl::GlContext::check_error(); + } +} + + +void engine_demo(int demo_id, const util::Path &path) { + switch (demo_id) { + case 0: + engine_demo_0(path); + break; + + default: + throw Error(ERR << "unknown engine demo " << demo_id << " requested."); + } +} + +} // namespace openage::main::tests diff --git a/libopenage/main/tests.h b/libopenage/main/tests.h new file mode 100644 index 0000000000..87e1bfb011 --- /dev/null +++ b/libopenage/main/tests.h @@ -0,0 +1,14 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#pragma once + +// pxd: from libopenage.util.path cimport Path +#include "../util/path.h" + + +namespace openage::main::tests { + +// pxd: void engine_demo(int demo_id, Path path) except + +void engine_demo(int demo_id, const util::Path &path); + +} // openage::main::tests diff --git a/libopenage/presenter/CMakeLists.txt b/libopenage/presenter/CMakeLists.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openage/CMakeLists.txt b/openage/CMakeLists.txt index f5d5f221c4..fb3937b34c 100644 --- a/openage/CMakeLists.txt +++ b/openage/CMakeLists.txt @@ -24,8 +24,9 @@ add_subdirectory(cvar) add_subdirectory(event) add_subdirectory(game) add_subdirectory(log) +add_subdirectory(main) add_subdirectory(nyan) -add_subdirectory(util) add_subdirectory(renderer) add_subdirectory(testing) +add_subdirectory(util) add_subdirectory(versions) diff --git a/openage/__main__.py b/openage/__main__.py index df1dfd5a0e..dae69d5f46 100644 --- a/openage/__main__.py +++ b/openage/__main__.py @@ -75,6 +75,12 @@ def main(argv=None): # enable reimports for "init_subparser" # pylint: disable=reimported + from .main.main import init_subparser + main_cli = subparsers.add_parser( + "main", + parents=[global_cli, cfg_cli]) + init_subparser(main_cli) + from .game.main import init_subparser game_cli = subparsers.add_parser( "game", diff --git a/openage/main/CMakeLists.txt b/openage/main/CMakeLists.txt new file mode 100644 index 0000000000..0e07f909d7 --- /dev/null +++ b/openage/main/CMakeLists.txt @@ -0,0 +1,9 @@ +add_cython_modules( + main_cpp.pyx + tests.pyx +) + +add_py_modules( + __init__.py + main.py +) diff --git a/openage/main/__init__.py b/openage/main/__init__.py new file mode 100644 index 0000000000..432a0d2d64 --- /dev/null +++ b/openage/main/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2018-2018 the openage authors. See copying.md for legal info. + +""" +Main engine entry point. +""" diff --git a/openage/main/main.py b/openage/main/main.py new file mode 100644 index 0000000000..513436623b --- /dev/null +++ b/openage/main/main.py @@ -0,0 +1,78 @@ +# Copyright 2015-2018 the openage authors. See copying.md for legal info. + +""" +Main engine entry point for openage. +""" + +from ..log import err, info + + +def init_subparser(cli): + """ Initializes the parser for game-specific args. """ + cli.set_defaults(entrypoint=main) + + cli.add_argument( + "--fps", type=int, + help="upper limit for fps. this limit is imposed on top of vsync") + + cli.add_argument( + "--gl-debug", action='store_true', + help="throw exceptions directly from the OpenGL calls") + + +def main(args, error): + """ + Makes sure that the assets have been converted, + and jumps into the C++ main method. + """ + del error # unused + + # we have to import stuff inside the function + # as it depends on generated/compiled code + from .main_cpp import run_game + from .. import config + from ..assets import get_asset_path + from ..convert.main import conversion_required, convert_assets + from ..cppinterface.setup import setup as cpp_interface_setup + from ..cvar.location import get_config_path + from ..util.fslike.union import Union + + # initialize libopenage + cpp_interface_setup(args) + + info("launching openage %s", config.VERSION) + info("compiled by %s", config.COMPILER) + + if config.DEVMODE: + info("running in DEVMODE") + + # create virtual file system for data paths + root = Union().root + + # mount the assets folder union at "assets/" + root["assets"].mount(get_asset_path(args.asset_dir)) + + # mount the config folder at "cfg/" + root["cfg"].mount(get_config_path(args.cfg_dir)) + + # ensure that the assets have been converted + if conversion_required(root["assets"], args): + # try to get previously used source dir + asset_location_path = root["cfg"] / "asset_location" + try: + with asset_location_path.open("rb") as file_obj: + prev_source_dir_path = file_obj.read().strip() + except FileNotFoundError: + prev_source_dir_path = None + used_asset_path = convert_assets(root["assets"], args, + prev_source_dir_path=prev_source_dir_path) + if used_asset_path: + # Remember the asset location + with asset_location_path.open("wb") as file_obj: + file_obj.write(used_asset_path) + else: + err("game asset conversion failed") + return 1 + + # start the game, continue in main_cpp.pyx! + return run_game(args, root) diff --git a/openage/main/main_cpp.pyx b/openage/main/main_cpp.pyx new file mode 100644 index 0000000000..24520be3ed --- /dev/null +++ b/openage/main/main_cpp.pyx @@ -0,0 +1,51 @@ +# Copyright 2015-2018 the openage authors. See copying.md for legal info. + +from cpython.ref cimport PyObject +from libcpp.memory cimport make_unique +from libcpp.string cimport string +from libcpp.vector cimport vector + +from libopenage.main cimport main_arguments, run_game as run_game_cpp +from libopenage.util.path cimport Path as Path_cpp +from libopenage.pyinterface.pyobject cimport PyObj +from libopenage.error.handlers cimport set_exit_ok + + +cdef extern from "Python.h": + void PyEval_InitThreads() + + +def run_game(args, root_path): + """ + Launches the game after arguments were translated. + """ + cdef int result + + # argument translation + cdef main_arguments args_cpp + + set_exit_ok(False) + try: + # root_path is a util.fslike.Path object from python + args_cpp.root_path = Path_cpp(PyObj(root_path.fsobj), + root_path.parts) + + # frame limiting + if args.fps is not None: + args_cpp.fps_limit = args.fps + else: + args_cpp.fps_limit = 0 + + # opengl debugging + args_cpp.gl_debug = args.gl_debug + + # create the gil, because now starts the multithread part! + PyEval_InitThreads() + + # run the game! + with nogil: + result = run_game_cpp(args_cpp) + + return result + finally: + set_exit_ok(True) diff --git a/openage/main/tests.pyx b/openage/main/tests.pyx new file mode 100644 index 0000000000..417bb67018 --- /dev/null +++ b/openage/main/tests.pyx @@ -0,0 +1,51 @@ +# Copyright 2018-2018 the openage authors. See copying.md for legal info. + +""" +tests for the engine itself. +""" + +import argparse + +from libopenage.util.path cimport Path as Path_cpp +from libopenage.pyinterface.pyobject cimport PyObj +from cpython.ref cimport PyObject +from libopenage.main.tests cimport engine_demo as engine_demo_c + + +def engine_demo(list argv): + """ + Invokes the available engine demos. + """ + + cmd = argparse.ArgumentParser( + prog='... engine_demo', + description='Demo of the game engine features') + cmd.add_argument("test_id", type=int, help="id of the demo to run.") + cmd.add_argument("--asset-dir", + help="Use this as an additional asset directory.") + cmd.add_argument("--cfg-dir", + help="Use this as an additional config directory.") + + args = cmd.parse_args(argv) + + + from ..cvar.location import get_config_path + from ..assets import get_asset_path + from ..util.fslike.union import Union + + # create virtual file system for data paths + root = Union().root + + # mount the assets folder union at "assets/" + root["assets"].mount(get_asset_path(args.asset_dir)) + + # mount the config folder at "cfg/" + root["cfg"].mount(get_config_path(args.cfg_dir)) + + cdef int engine_test_id = args.test_id + + cdef Path_cpp root_cpp = Path_cpp(PyObj(root.fsobj), + root.parts) + + with nogil: + engine_demo_c(engine_test_id, root_cpp) diff --git a/openage/testing/testlist.py b/openage/testing/testlist.py index bcec7b31bd..ea8e0b70f6 100644 --- a/openage/testing/testlist.py +++ b/openage/testing/testlist.py @@ -52,6 +52,8 @@ def demos_py(): "play pong on steroids through future prediction") yield ("openage.renderer.tests.renderer_demo", "showcases the new renderer") + yield ("openage.main.tests.engine_demo", + "showcases the engine features") def benchmark_py(): From 5ef672fdef1838572db5ca77bdb50cd5babb22dc Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sun, 23 Dec 2018 01:48:23 +0100 Subject: [PATCH 03/28] fslike: improve file-not-found message for c++ api --- openage/util/fslike/cpp.pyx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/openage/util/fslike/cpp.pyx b/openage/util/fslike/cpp.pyx index 21ca0b4f52..c6f18361ff 100644 --- a/openage/util/fslike/cpp.pyx +++ b/openage/util/fslike/cpp.pyx @@ -246,7 +246,7 @@ cdef bool fs_mkdirs(PyObject *fslike, const vector[string]& parts) except * with cdef File_cpp fs_open(object path, int mode) except *: if path is None: - raise FileNotFoundError("file could not be found") + raise Exception("fs_open can't open a path that is None") cdef PyObj ref @@ -277,28 +277,42 @@ cdef File_cpp fs_open(object path, int mode) except *: return File_cpp(ref) +cdef check_file_exists(object path, object fslike, const vector[string]& parts): + if path is None: + raise FileNotFoundError("file could not be found in filesystem %s " + "for path '%s'" % ( + fslike, + b"/".join(parts).decode(errors='ignore') + )) + + cdef File_cpp fs_open_r(PyObject *fslike, const vector[string]& parts) except * with gil: open_path = ( fslike).resolve_r(parts) + check_file_exists(open_path, fslike, parts) return fs_open(open_path, 0) cdef File_cpp fs_open_w(PyObject *fslike, const vector[string]& parts) except * with gil: open_path = ( fslike).resolve_w(parts) + check_file_exists(open_path, fslike, parts) return fs_open(open_path, 1) cdef File_cpp fs_open_rw(PyObject *fslike, const vector[string]& parts) except * with gil: open_path = ( fslike).resolve_w(parts) + check_file_exists(open_path, fslike, parts) return fs_open(open_path, 2) cdef File_cpp fs_open_a(PyObject *fslike, const vector[string]& parts) except * with gil: open_path = ( fslike).resolve_w(parts) + check_file_exists(open_path, fslike, parts) return fs_open(open_path, 3) cdef File_cpp fs_open_ar(PyObject *fslike, const vector[string]& parts) except * with gil: open_path = ( fslike).resolve_w(parts) + check_file_exists(open_path, fslike, parts) return fs_open(open_path, 4) From e37a80ebc95a0c345e23d998d79218dd6c17bdda Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sun, 27 Jan 2019 21:59:04 +0100 Subject: [PATCH 04/28] game: render funny red quad --- libopenage/main/CMakeLists.txt | 2 + libopenage/main/tests.cpp | 239 +------------------- libopenage/main/tests/CMakeLists.txt | 3 + libopenage/main/tests/pong.cpp | 138 +++++++++++ libopenage/main/tests/pong.h | 18 ++ libopenage/nyan/db.h | 12 + libopenage/presenter/CMakeLists.txt | 3 + libopenage/presenter/test.cpp | 12 + libopenage/renderer/CMakeLists.txt | 3 +- libopenage/renderer/renderer.h | 4 +- libopenage/renderer/resources/mesh_data.cpp | 113 ++++++++- libopenage/renderer/resources/mesh_data.h | 11 +- libopenage/renderer/tests.cpp | 7 +- libopenage/renderer/util.cpp | 22 ++ libopenage/renderer/util.h | 53 +++++ libopenage/simulation/gameentity.cpp | 0 libopenage/simulation/gameentity.h | 3 + openage/main/tests.pyx | 1 - 18 files changed, 393 insertions(+), 251 deletions(-) create mode 100644 libopenage/main/tests/CMakeLists.txt create mode 100644 libopenage/main/tests/pong.cpp create mode 100644 libopenage/main/tests/pong.h create mode 100644 libopenage/nyan/db.h create mode 100644 libopenage/presenter/test.cpp create mode 100644 libopenage/renderer/util.cpp create mode 100644 libopenage/renderer/util.h create mode 100644 libopenage/simulation/gameentity.cpp create mode 100644 libopenage/simulation/gameentity.h diff --git a/libopenage/main/CMakeLists.txt b/libopenage/main/CMakeLists.txt index 50efd4266f..0883a024ae 100644 --- a/libopenage/main/CMakeLists.txt +++ b/libopenage/main/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory("tests") + add_sources(libopenage tests.cpp ) diff --git a/libopenage/main/tests.cpp b/libopenage/main/tests.cpp index 841fe0ea85..bc04eef209 100644 --- a/libopenage/main/tests.cpp +++ b/libopenage/main/tests.cpp @@ -1,250 +1,15 @@ // Copyright 2018-2018 the openage authors. See copying.md for legal info. -#include -#include -#include -#include -#include -#include - -#include "../log/log.h" -#include "../error/error.h" -#include "../renderer/resources/shader_source.h" -#include "../renderer/resources/texture_data.h" -#include "../renderer/resources/mesh_data.h" -#include "../renderer/texture.h" -#include "../renderer/shader_program.h" -#include "../renderer/geometry.h" -#include "../renderer/opengl/window.h" +#include "tests/pong.h" namespace openage::main::tests { -void engine_demo_0(const util::Path& path) { - renderer::opengl::GlWindow window("openage engine test", 800, 600); - - auto renderer = window.make_renderer(); - - auto vshader_src = renderer::resources::ShaderSource( - renderer::resources::shader_lang_t::glsl, - renderer::resources::shader_stage_t::vertex, - R"s( -#version 330 - -layout(location=0) in vec2 position; -layout(location=1) in vec2 uv; -uniform mat4 mvp; -out vec2 v_uv; - -void main() { - gl_Position = mvp * vec4(position, 0.0, 1.0); - v_uv = vec2(uv.x, 1.0 - uv.y); -} -)s"); - - auto fshader_src = renderer::resources::ShaderSource( - renderer::resources::shader_lang_t::glsl, - renderer::resources::shader_stage_t::fragment, - R"s( -#version 330 - -in vec2 v_uv; -uniform sampler2D tex; -uniform uint u_id; - -layout(location=0) out vec4 col; -layout(location=1) out uint id; - -void main() { - vec4 tex_val = texture(tex, v_uv); - if (tex_val.a == 0) { - discard; - } - col = tex_val; - id = u_id + 1u; -} -)s"); - - auto vshader_display_src = renderer::resources::ShaderSource( - renderer::resources::shader_lang_t::glsl, - renderer::resources::shader_stage_t::vertex, - R"s( -#version 330 - -layout(location=0) in vec2 position; -layout(location=1) in vec2 uv; -uniform mat4 proj; -out vec2 v_uv; - -void main() { - gl_Position = proj * vec4(position, 0.0, 1.0); - v_uv = uv; -} -)s"); - - auto fshader_display_src = renderer::resources::ShaderSource( - renderer::resources::shader_lang_t::glsl, - renderer::resources::shader_stage_t::fragment, - R"s( -#version 330 - -uniform sampler2D color_texture; - -in vec2 v_uv; -out vec4 col; - -void main() { - col = texture(color_texture, v_uv); -} -)s"); - - auto shader = renderer->add_shader( { vshader_src, fshader_src } ); - auto shader_display = renderer->add_shader( { vshader_display_src, fshader_display_src } ); - - - auto transform1 = Eigen::Affine3f::Identity(); - transform1.prescale(Eigen::Vector3f(0.4f, 0.2f, 1.0f)); - transform1.prerotate(Eigen::AngleAxisf(30.0f * 3.14159f / 180.0f, Eigen::Vector3f::UnitX())); - transform1.pretranslate(Eigen::Vector3f(-0.4f, -0.6f, 0.0f)); - - auto unif_in1 = shader->new_uniform_input( - "mvp", transform1.matrix(), - "u_id", 1u - ); - - auto transform2 = Eigen::Affine3f::Identity(); - transform2.prescale(Eigen::Vector3f(0.3f, 0.1f, 1.0f)); - transform2.prerotate(Eigen::AngleAxisf(50.0f * 3.14159f / 180.0f, Eigen::Vector3f::UnitZ())); - - auto transform3 = transform2; - - transform2.pretranslate(Eigen::Vector3f(0.3f, 0.1f, 0.3f)); - - auto tex = renderer::resources::Texture2dData(path / "/assets/gaben.png"); - auto gltex = renderer->add_texture(tex); - auto unif_in2 = shader->new_uniform_input( - "mvp", transform2.matrix(), - "u_id", 2u, - "tex", gltex.get() - ); - - transform3.prerotate(Eigen::AngleAxisf(90.0f * 3.14159f / 180.0f, Eigen::Vector3f::UnitZ())); - transform3.pretranslate(Eigen::Vector3f(0.3f, 0.1f, 0.5f)); - - auto unif_in3 = shader->new_uniform_input( - "mvp", transform3.matrix(), - "u_id", 3u - ); - - auto quad = renderer->add_mesh_geometry(renderer::resources::MeshData::make_quad()); - renderer::Renderable obj1 { - unif_in1.get(), - quad.get(), - true, - true, - }; - - renderer::Renderable obj2{ - unif_in2.get(), - quad.get(), - true, - true, - }; - - renderer::Renderable obj3 { - unif_in3.get(), - quad.get(), - true, - true, - }; - - auto size = window.get_size(); - auto color_texture = renderer->add_texture(renderer::resources::Texture2dInfo(size.first, size.second, renderer::resources::pixel_format::rgba8)); - auto id_texture = renderer->add_texture(renderer::resources::Texture2dInfo(size.first, size.second, renderer::resources::pixel_format::r32ui)); - auto depth_texture = renderer->add_texture(renderer::resources::Texture2dInfo(size.first, size.second, renderer::resources::pixel_format::depth24)); - auto fbo = renderer->create_texture_target( { color_texture.get(), id_texture.get(), depth_texture.get() } ); - - auto color_texture_uniform = shader_display->new_uniform_input("color_texture", color_texture.get()); - renderer::Renderable display_obj { - color_texture_uniform.get(), - quad.get(), - false, - false, - }; - - renderer::RenderPass pass { - { obj1, obj2, obj3 }, - fbo.get() - }; - - renderer::RenderPass display_pass { - { display_obj }, - renderer->get_display_target(), - }; - - renderer::resources::Texture2dData id_texture_data = id_texture->into_data(); - bool texture_data_valid = false; - - glDepthFunc(GL_LEQUAL); - glDepthRange(0.0, 1.0); - // what is this - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - window.add_mouse_button_callback([&] (SDL_MouseButtonEvent const& ev) { - auto x = ev.x; - auto y = ev.y; - - log::log(INFO << "Clicked at location (" << x << ", " << y << ")"); - if (!texture_data_valid) { - id_texture_data = id_texture->into_data(); - texture_data_valid = true; - } - auto id = id_texture_data.read_pixel(x, y); - log::log(INFO << "Id-texture-value at location: " << id); - if (id == 0) { - //no renderable at given location - log::log(INFO << "Clicked at non existent object"); - return; - } - id--; //real id is id-1 - log::log(INFO << "Object number " << id << " clicked."); - } ); - - window.add_resize_callback([&] (size_t w, size_t h) { - // Calculate projection matrix - float aspectRatio = float(w)/float(h); - float xScale = 1.0/aspectRatio; - - Eigen::Matrix4f pmat; - pmat << xScale, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1; - - // resize fbo - color_texture = renderer->add_texture(renderer::resources::Texture2dInfo(w, h, renderer::resources::pixel_format::rgba8)); - id_texture = renderer->add_texture(renderer::resources::Texture2dInfo(w, h, renderer::resources::pixel_format::r32ui)); - depth_texture = renderer->add_texture(renderer::resources::Texture2dInfo(w, h, renderer::resources::pixel_format::depth24)); - fbo = renderer->create_texture_target( { color_texture.get(), id_texture.get(), depth_texture.get() } ); - texture_data_valid = false; - - shader_display->update_uniform_input(color_texture_uniform.get(), "color_texture", color_texture.get(), "proj", pmat); - pass.target = fbo.get(); - } ); - - while (!window.should_close()) { - renderer->render(pass); - renderer->render(display_pass); - window.update(); - renderer::opengl::GlContext::check_error(); - } -} - void engine_demo(int demo_id, const util::Path &path) { switch (demo_id) { case 0: - engine_demo_0(path); + pong::main(path); break; default: diff --git a/libopenage/main/tests/CMakeLists.txt b/libopenage/main/tests/CMakeLists.txt new file mode 100644 index 0000000000..e5cc7ddaa6 --- /dev/null +++ b/libopenage/main/tests/CMakeLists.txt @@ -0,0 +1,3 @@ +add_sources(libopenage + pong.cpp +) diff --git a/libopenage/main/tests/pong.cpp b/libopenage/main/tests/pong.cpp new file mode 100644 index 0000000000..20f0d5f7b0 --- /dev/null +++ b/libopenage/main/tests/pong.cpp @@ -0,0 +1,138 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#include +#include +#include +#include +#include +#include + +#include + +#include "../../util/math_constants.h" +#include "../../log/log.h" +#include "../../error/error.h" +#include "../../renderer/resources/shader_source.h" +#include "../../renderer/resources/texture_data.h" +#include "../../renderer/resources/mesh_data.h" +#include "../../renderer/texture.h" +#include "../../renderer/shader_program.h" +#include "../../renderer/util.h" +#include "../../renderer/geometry.h" +#include "../../renderer/opengl/window.h" + + +namespace openage::main::tests::pong { + + +void main(const util::Path& path) { + + std::shared_ptr db = nyan::Database::create(); + + db->load( + "test.nyan", + [&path] (const std::string &filename) { + // TODO: nyan should provide a file api that we can inherit from + // then we can natively interface to the openage file abstraction + // and do not need to read the whole file here. + // ideally, the returned file lazily accesses all data through openage::util::File. + return std::make_shared( + filename, + (path / ("assets/nyan/" + filename)).open_r().read() + ); + } + ); + + std::shared_ptr root = db->new_view(); + nyan::Object test = root->get("test.Test"); + log::log(INFO << "nyan read test: " << *test.get("member")); + + renderer::opengl::GlWindow window("openage engine test", 800, 600); + + auto renderer = window.make_renderer(); + + auto vshader_src = renderer::resources::ShaderSource( + renderer::resources::shader_lang_t::glsl, + renderer::resources::shader_stage_t::vertex, + R"s( +#version 330 + +layout(location=0) in vec2 position; +uniform mat4 pos; +uniform mat4 proj; + +void main() { + gl_Position = proj * pos * vec4(position, 0.0, 1.0); +} +)s"); + + auto fshader_src = renderer::resources::ShaderSource( + renderer::resources::shader_lang_t::glsl, + renderer::resources::shader_stage_t::fragment, + R"s( +#version 330 + +out vec4 col; + +void main() { + col = vec4(1.0, 0, 0, 1.0); +} +)s"); + + auto shader = renderer->add_shader( { vshader_src, fshader_src } ); + + auto pos1 = Eigen::Affine3f::Identity(); + pos1.prescale(Eigen::Vector3f(200.0f, 200.0f, 1.0f)); + //pos1.prerotate(Eigen::AngleAxisf(45.0f * math::PI / 180.0f, Eigen::Vector3f::UnitZ())); + pos1.pretranslate(Eigen::Vector3f(400.0f, 400.0f, 0.0f)); + + auto pos_in = shader->new_uniform_input( + "pos", pos1.matrix() + ); + + auto proj_in = shader->new_uniform_input( + "proj", Eigen::Affine3f::Identity().matrix() + ); + + auto quad = renderer->add_mesh_geometry(renderer::resources::MeshData::make_quad()); + renderer::Renderable obj1 { + pos_in.get(), + quad.get(), + true, + true, + }; + + renderer::Renderable projsetup { + proj_in.get(), + nullptr, + }; + + renderer::RenderPass pass { + { projsetup, obj1 }, + renderer->get_display_target(), + }; + + + glDepthFunc(GL_LEQUAL); + glDepthRange(0.0, 1.0); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + window.add_resize_callback( + [&] (size_t w, size_t h) { + Eigen::Matrix4f proj_matrix = renderer::util::ortho_matrix_f(0.0f, w, + 0.0f, h, + 0.0f, 1.0f); + + shader->update_uniform_input(proj_in.get(), "proj", proj_matrix); + } + ); + + while (!window.should_close()) { + renderer->render(pass); + window.update(); + renderer::opengl::GlContext::check_error(); + } +} + + +} // openage::main::tests::pong diff --git a/libopenage/main/tests/pong.h b/libopenage/main/tests/pong.h new file mode 100644 index 0000000000..54848eebf6 --- /dev/null +++ b/libopenage/main/tests/pong.h @@ -0,0 +1,18 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#pragma once + + +#include "../../util/path.h" + + +namespace openage::main::tests::pong { + + +/** + * Run pong, the non-terminal variant. + */ +void main(const util::Path& path); + + +} // openage::main::tests::pong diff --git a/libopenage/nyan/db.h b/libopenage/nyan/db.h new file mode 100644 index 0000000000..db6e13f013 --- /dev/null +++ b/libopenage/nyan/db.h @@ -0,0 +1,12 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "../log/log.h" +#include "../error/error.h" + +namespace openage::nyan { + +} diff --git a/libopenage/presenter/CMakeLists.txt b/libopenage/presenter/CMakeLists.txt index e69de29bb2..ddfe6943a6 100644 --- a/libopenage/presenter/CMakeLists.txt +++ b/libopenage/presenter/CMakeLists.txt @@ -0,0 +1,3 @@ +add_sources(libopenage + test.cpp +) diff --git a/libopenage/presenter/test.cpp b/libopenage/presenter/test.cpp new file mode 100644 index 0000000000..4c3bea12ab --- /dev/null +++ b/libopenage/presenter/test.cpp @@ -0,0 +1,12 @@ +// Copyright 2018-2018 the openage authors. See copying.md for legal info. + +#include "../log/log.h" + + +namespace openage::presenter::tests { + +void demo() { + log::log(INFO << "presenter demo launching..."); +} + +} // openage::presenter::tests diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index 037c70c85a..b18edc8077 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -2,13 +2,14 @@ add_sources(libopenage color.cpp geometry.cpp renderer.cpp - shader_program.cpp sdl_global.cpp + shader_program.cpp tests.cpp text.cpp texture.cpp texture_array.cpp uniform_input.cpp + util.cpp window.cpp ) diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index af5487e304..1aa85a4412 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -45,9 +45,9 @@ struct Renderable { /// Can be nullptr to only set uniforms but do not perform draw call. std::shared_ptr geometry; /// Whether to perform alpha-based color blending with whatever was in the render target before. - bool alpha_blending; + bool alpha_blending = true; /// Whether to perform depth testing and discard occluded fragments. - bool depth_test; + bool depth_test = true; }; /// A render pass is a series of draw calls represented by renderables that output into the given render target. diff --git a/libopenage/renderer/resources/mesh_data.cpp b/libopenage/renderer/resources/mesh_data.cpp index 265ab4e87b..358d2d4756 100644 --- a/libopenage/renderer/resources/mesh_data.cpp +++ b/libopenage/renderer/resources/mesh_data.cpp @@ -111,21 +111,122 @@ VertexInputInfo MeshData::get_info() const { /// Vertices of a quadrilateral filling the whole screen. /// Format: (pos, tex_coords) = (x, y, u, v) -static constexpr const std::array QUAD_DATA = { { +static constexpr const std::array QUAD_DATA_CENTERED = { + { -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f - } }; + } +}; + +/// Vertices of a quad from (0, 0) to (1, 1) +/// Format: (pos, tex_coords) = (x, y, u, v) +static constexpr const std::array QUAD_DATA_UNIT = { + { + 0.0f, 1.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 0.0f, 1.0f, 0.0f + } +}; + + +namespace { + +/** + * Generate triangle-strip meshdata for a given 4-vertex/uv-coord array. + */ +template +MeshData create_float_mesh(const std::array &src) { + auto const data_size = size * sizeof(float); -MeshData MeshData::make_quad() { - auto const data_size = QUAD_DATA.size() * sizeof(decltype(QUAD_DATA)::value_type); std::vector verts(data_size); - std::memcpy(verts.data(), reinterpret_cast(QUAD_DATA.data()), data_size); + std::memcpy(verts.data(), reinterpret_cast(src.data()), data_size); - VertexInputInfo info { { vertex_input_t::V2F32, vertex_input_t::V2F32 }, vertex_layout_t::AOS, vertex_primitive_t::TRIANGLE_STRIP }; + VertexInputInfo info { + { vertex_input_t::V2F32, vertex_input_t::V2F32 }, + vertex_layout_t::AOS, + vertex_primitive_t::TRIANGLE_STRIP + }; return MeshData(std::move(verts), info); } +} // anon namespace + + +MeshData MeshData::make_quad(bool centered) { + if (centered) { + return create_float_mesh(QUAD_DATA_CENTERED); + } + else { + return create_float_mesh(QUAD_DATA_UNIT); + } } + + +MeshData MeshData::make_quad(float sidelength, bool centered) { + + // 8 positions and 8 uv-coords. + // store pos and uv as: (x, y, uvx, uvy) + std::array positions; + + if (centered) { + float halfsidelength = sidelength/2; + positions = { + { + -halfsidelength, halfsidelength, 0.0f, 1.0f, + -halfsidelength, -halfsidelength, 0.0f, 0.0f, + halfsidelength, halfsidelength, 1.0f, 1.0f, + halfsidelength, -halfsidelength, 1.0f, 0.0f + } + }; + } + else { + positions = { + { + 0.0f, sidelength, 0.0f, 1.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + sidelength, sidelength, 1.0f, 1.0f, + sidelength, 0.0f, 1.0f, 0.0f + } + }; + } + + return create_float_mesh(positions); +} + + +MeshData MeshData::make_quad(float width, float height, bool centered) { + // 8 positions and 8 uv-coords. + // store pos and uv as: (x, y, uvx, uvy) + std::array positions; + + if (centered) { + float halfwidth = width/2; + float halfheight = height/2; + positions = { + { + -halfwidth, halfheight, 0.0f, 1.0f, + -halfwidth, -halfheight, 0.0f, 0.0f, + halfwidth, halfheight, 1.0f, 1.0f, + halfwidth, -halfheight, 1.0f, 0.0f + } + }; + } + else { + positions = { + { + 0.0f, height, 0.0f, 1.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + width, height, 1.0f, 1.0f, + width, 0.0f, 1.0f, 0.0f + } + }; + } + + return create_float_mesh(positions); +} + +} // namespace openage::renderer::resources diff --git a/libopenage/renderer/resources/mesh_data.h b/libopenage/renderer/resources/mesh_data.h index 3634b1eee8..5d1ce51f9e 100644 --- a/libopenage/renderer/resources/mesh_data.h +++ b/libopenage/renderer/resources/mesh_data.h @@ -133,7 +133,16 @@ class MeshData { /// Initializes the mesh data with a simple quadrilateral spanning the whole window space, /// with (vec2 pos, vec2 uv) vertex format. - static MeshData make_quad(); + /// (0, 0) of this quad is at the quad center and it stretches from (-1, -1) to (1, 1) + /// and uv=(0, 0) at bottom left (-> (-1, -1)) and uv=(1, 1) top right (at (1, 1)). + /// When centered = false, the quad stretches from (0, 0) to (1, 1). + static MeshData make_quad(bool centered=true); + + /// Initialize the mesh data with given sidelength. Optionally place (0, 0) bottom left. + static MeshData make_quad(float sidelength, bool centered=true); + + /// Initialize the mesh data with given width and height. Optionally place (0, 0) bottom left. + static MeshData make_quad(float width, float height, bool centered=true); private: /// The raw vertex data. The size is an integer multiple of the size of a single vertex. diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 8b958ab852..4d0d57fa17 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -11,6 +11,7 @@ #include "../log/log.h" #include "../error/error.h" +#include "../util/math_constants.h" #include "resources/shader_source.h" #include "resources/texture_data.h" #include "resources/mesh_data.h" @@ -118,7 +119,7 @@ void main() { auto transform1 = Eigen::Affine3f::Identity(); transform1.prescale(Eigen::Vector3f(0.4f, 0.2f, 1.0f)); - transform1.prerotate(Eigen::AngleAxisf(30.0f * 3.14159f / 180.0f, Eigen::Vector3f::UnitX())); + transform1.prerotate(Eigen::AngleAxisf(30.0f * math::PI / 180.0f, Eigen::Vector3f::UnitX())); transform1.pretranslate(Eigen::Vector3f(-0.4f, -0.6f, 0.0f)); /* Clickable objects on the screen consist of a transform (matrix which places each object @@ -131,7 +132,7 @@ void main() { auto transform2 = Eigen::Affine3f::Identity(); transform2.prescale(Eigen::Vector3f(0.3f, 0.1f, 1.0f)); - transform2.prerotate(Eigen::AngleAxisf(50.0f * 3.14159f / 180.0f, Eigen::Vector3f::UnitZ())); + transform2.prerotate(Eigen::AngleAxisf(50.0f * math::PI / 180.0f, Eigen::Vector3f::UnitZ())); auto transform3 = transform2; @@ -144,7 +145,7 @@ void main() { "tex", gltex ); - transform3.prerotate(Eigen::AngleAxisf(90.0f * 3.14159f / 180.0f, Eigen::Vector3f::UnitZ())); + transform3.prerotate(Eigen::AngleAxisf(90.0f * math::PI / 180.0f, Eigen::Vector3f::UnitZ())); transform3.pretranslate(Eigen::Vector3f(0.3f, 0.1f, 0.5f)); auto obj3_unifs = obj_shader->new_uniform_input( diff --git a/libopenage/renderer/util.cpp b/libopenage/renderer/util.cpp new file mode 100644 index 0000000000..d9c5fcfe1d --- /dev/null +++ b/libopenage/renderer/util.cpp @@ -0,0 +1,22 @@ +// Copyright 2018-2019 the openage authors. See copying.md for legal info. + + +#include "util.h" + + +namespace openage::renderer::util { + +Eigen::Matrix4d ortho_matrix_d(double left, double right, + double bottom, double top, + double near, double far) { + return ortho_matrix(left, right, bottom, top, near, far); +} + + +Eigen::Matrix4f ortho_matrix_f(float left, float right, + float bottom, float top, + float near, float far) { + return ortho_matrix(left, right, bottom, top, near, far); +} + +} // openage::renderer::util diff --git a/libopenage/renderer/util.h b/libopenage/renderer/util.h new file mode 100644 index 0000000000..6c7609a896 --- /dev/null +++ b/libopenage/renderer/util.h @@ -0,0 +1,53 @@ +// Copyright 2018-2019 the openage authors. See copying.md for legal info. + +#pragma once + + +#include + + +namespace openage::renderer::util { + +/** + * Creates a orthographic projection matrix. + * + * left, right: Left and right vertical clipping plane coordinates. + * bottom, top: Horizontal clipping plane coordinates. + * near, far: Distances to nearer and farther depth clipping planes. + * Specify as negative if the plane is behind the viewpoint. + */ +template +matrix_t ortho_matrix(number_t left, number_t right, + number_t bottom, number_t top, + number_t near, number_t far) { + + matrix_t ortho; + ortho << 2/(right-left), 0, 0, -(right + left)/(right - left), + 0, 2/(top - bottom), 0, -(top + bottom)/(top - bottom), + 0, 0, -2/(far-near), -(far + near)/(far - near), + 0, 0, 0, 1; + + return ortho; +} + + +/** + * Create a orthographic projection matrix. Double variant. + * + * @see ortho_matrix + */ +Eigen::Matrix4d ortho_matrix_d(double left, double right, + double bottom, double top, + double near, double far); + + +/** + * Create a orthographic projection matrix. Float variant. + * + * @see ortho_matrix + */ +Eigen::Matrix4f ortho_matrix_f(float left, float right, + float bottom, float top, + float near, float far); + +} // openage::renderer::util diff --git a/libopenage/simulation/gameentity.cpp b/libopenage/simulation/gameentity.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libopenage/simulation/gameentity.h b/libopenage/simulation/gameentity.h new file mode 100644 index 0000000000..7c38e28036 --- /dev/null +++ b/libopenage/simulation/gameentity.h @@ -0,0 +1,3 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#pragma once diff --git a/openage/main/tests.pyx b/openage/main/tests.pyx index 417bb67018..8432be1871 100644 --- a/openage/main/tests.pyx +++ b/openage/main/tests.pyx @@ -28,7 +28,6 @@ def engine_demo(list argv): args = cmd.parse_args(argv) - from ..cvar.location import get_config_path from ..assets import get_asset_path from ..util.fslike.union import Union From 54bcc30a61b8a19abafe008b6490506bfec301ba Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sat, 9 Feb 2019 15:13:48 +0100 Subject: [PATCH 05/28] etc: update valgrind suppressions --- etc/valgrind-python.supp | 50 ++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/etc/valgrind-python.supp b/etc/valgrind-python.supp index 392c1a9061..c723f91da4 100644 --- a/etc/valgrind-python.supp +++ b/etc/valgrind-python.supp @@ -1,39 +1,49 @@ # -# This is a valgrind suppression file that should be used -# when running Python with valgrind. +# This is a valgrind suppression file that should be used when using valgrind. +# +# It was taken from the cpython project and adapted for openage. +# Upstream URL is: https://raw.githubusercontent.com/python/cpython/master/Misc/valgrind-python.supp +# # # Here's an example of running valgrind: # -# cd python/dist/src -# valgrind --tool=memcheck --suppressions=Misc/valgrind-python.supp \ +# cd openage/bin/ +# PYTHONMALLOC=malloc valgrind --tool=memcheck --suppressions=../etc/valgrind-python.supp \ # python3 -m openage test -a # -# python can be compiled with --with-valgrind to enhance the experience +# Python can be compiled with --with-valgrind to enhance the valgrind experience: +# valgrind presence is detected and python memory is allocated more valgrind-friendly then. +# # Python 3.6 supports PYTHONMALLOC=malloc environment var to force malloc() - +# +# Disable the suppressions for _PyObject_Free and _PyObject_Realloc below by comment +# if valgrind shall complain about python memory leaks. +# +# See cpython/Misc/README.valgrind for more information +# # all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif { ADDRESS_IN_RANGE/Invalid read of size 4 Memcheck:Addr4 - fun:Py_ADDRESS_IN_RANGE + fun:address_in_range } { ADDRESS_IN_RANGE/Invalid read of size 4 Memcheck:Value4 - fun:Py_ADDRESS_IN_RANGE + fun:address_in_range } { ADDRESS_IN_RANGE/Invalid read of size 8 (x86_64 aka amd64) Memcheck:Value8 - fun:Py_ADDRESS_IN_RANGE + fun:address_in_range } { ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value Memcheck:Cond - fun:Py_ADDRESS_IN_RANGE + fun:address_in_range } # @@ -123,61 +133,61 @@ { ADDRESS_IN_RANGE/Invalid read of size 4 Memcheck:Addr4 - fun:PyObject_Free + fun:_PyObject_Free } { ADDRESS_IN_RANGE/Invalid read of size 4 Memcheck:Value4 - fun:PyObject_Free + fun:_PyObject_Free } { ADDRESS_IN_RANGE/Use of uninitialised value of size 8 Memcheck:Addr8 - fun:PyObject_Free + fun:_PyObject_Free } { ADDRESS_IN_RANGE/Use of uninitialised value of size 8 Memcheck:Value8 - fun:PyObject_Free + fun:_PyObject_Free } { ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value Memcheck:Cond - fun:PyObject_Free + fun:_PyObject_Free } { ADDRESS_IN_RANGE/Invalid read of size 4 Memcheck:Addr4 - fun:PyObject_Realloc + fun:_PyObject_Realloc } { ADDRESS_IN_RANGE/Invalid read of size 4 Memcheck:Value4 - fun:PyObject_Realloc + fun:_PyObject_Realloc } { ADDRESS_IN_RANGE/Use of uninitialised value of size 8 Memcheck:Addr8 - fun:PyObject_Realloc + fun:_PyObject_Realloc } { ADDRESS_IN_RANGE/Use of uninitialised value of size 8 Memcheck:Value8 - fun:PyObject_Realloc + fun:_PyObject_Realloc } { ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value Memcheck:Cond - fun:PyObject_Realloc + fun:_PyObject_Realloc } ### From 8edc6daaa2ac71ee5064abbe6e3d3a7972db6454 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sat, 9 Feb 2019 15:14:14 +0100 Subject: [PATCH 06/28] pxdgen: minor fixes --- buildsystem/pxdgen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildsystem/pxdgen.py b/buildsystem/pxdgen.py index 68dc981c67..0c95953479 100644 --- a/buildsystem/pxdgen.py +++ b/buildsystem/pxdgen.py @@ -334,7 +334,7 @@ def postprocess_annotation_line(self, annotation): Post-processes each individual annotation line, applying hacks and testing it, etc. - See openage/pyinterface/hacks.h for documentation on the individual + See libopenage/pyinterface/hacks.h for documentation on the individual hacks. """ annotation = annotation.rstrip() @@ -383,7 +383,7 @@ def generate(self, pxdfile, ignore_timestamps=False, print_warnings=True): outfile.write(result) if print_warnings and self.warnings: - print("\x1b[33;1mWARNING\x1b[m pxdgen[" + self.filename + "]:") + print("\x1b[33;1mWARNING\x1b[m pxdgen[%s]:" % self.filename) for warning in self.warnings: print(warning) From 68919c889d0105be47aa263162d8a0cac7485991 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sat, 9 Feb 2019 15:14:53 +0100 Subject: [PATCH 07/28] config: extract game version to prevent rebuilds on git commit --- libopenage/CMakeLists.txt | 3 +++ libopenage/engine.cpp | 2 ++ libopenage/gamestate/old/game_save.cpp | 1 + libopenage/version.cpp.in | 11 +++++++++++ libopenage/version.h.in | 11 +++++++++++ 5 files changed, 28 insertions(+) create mode 100644 libopenage/version.cpp.in create mode 100644 libopenage/version.h.in diff --git a/libopenage/CMakeLists.txt b/libopenage/CMakeLists.txt index 2bdd29177e..37277492d7 100644 --- a/libopenage/CMakeLists.txt +++ b/libopenage/CMakeLists.txt @@ -235,6 +235,8 @@ get_config_option_string() configure_file(config.h.in config.h) configure_file(config.cpp.in config.cpp) +configure_file(version.h.in version.h) +configure_file(version.cpp.in version.cpp) configure_file( "${CMAKE_SOURCE_DIR}/openage/config.py.in" @@ -325,6 +327,7 @@ add_sources(libopenage screenshot.cpp texture.cpp ${CMAKE_CURRENT_BINARY_DIR}/config.cpp + ${CMAKE_CURRENT_BINARY_DIR}/version.cpp ${CODEGEN_SCU_FILE} ) diff --git a/libopenage/engine.cpp b/libopenage/engine.cpp index 3017570c5b..3a52c98a74 100644 --- a/libopenage/engine.cpp +++ b/libopenage/engine.cpp @@ -26,8 +26,10 @@ #include "util/opengl.h" #include "util/strings.h" #include "util/timer.h" +#include "version.h" #include "versions/compiletime.h" + /** * Main openage namespace to store all things that make the have to do with the game. * diff --git a/libopenage/gamestate/old/game_save.cpp b/libopenage/gamestate/old/game_save.cpp index bf7acb6e83..0de9cbabaf 100644 --- a/libopenage/gamestate/old/game_save.cpp +++ b/libopenage/gamestate/old/game_save.cpp @@ -5,6 +5,7 @@ #include #include +#include "version.h" #include "../../log/log.h" #include "../../terrain/terrain_chunk.h" #include "../../unit/producer.h" diff --git a/libopenage/version.cpp.in b/libopenage/version.cpp.in new file mode 100644 index 0000000000..9f4139e1a1 --- /dev/null +++ b/libopenage/version.cpp.in @@ -0,0 +1,11 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +// ${AUTOGEN_WARNING} + +#include "version.h" + +namespace openage::version { + +const char *const version = "${VERSION_FULL_STRING}"; + +} // openage::version diff --git a/libopenage/version.h.in b/libopenage/version.h.in new file mode 100644 index 0000000000..44c6b57b3c --- /dev/null +++ b/libopenage/version.h.in @@ -0,0 +1,11 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +// ${AUTOGEN_WARNING} + +#pragma once + +namespace openage::version { + +extern const char *const version; + +} // openage::version From 7a5dd29a13f2d777b8c848aa46ca9a67b244be6f Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sat, 16 Feb 2019 01:21:00 +0100 Subject: [PATCH 08/28] towards graphical pong --- libopenage/curve/continuous.h | 22 +- libopenage/curve/discrete.h | 21 +- libopenage/curve/value_container.h | 20 +- libopenage/event/demo/gamestate.h | 23 ++ libopenage/event/event.cpp | 5 +- libopenage/event/event.h | 17 +- libopenage/event/eventqueue.cpp | 27 +- libopenage/event/eventqueue.h | 7 +- libopenage/event/eventtarget.cpp | 30 +- libopenage/event/eventtarget.h | 4 + libopenage/event/loop.cpp | 27 +- libopenage/event/loop.h | 16 +- libopenage/event/tests.cpp | 9 +- libopenage/main/tests.h | 3 +- libopenage/main/tests/CMakeLists.txt | 4 + libopenage/main/tests/aicontroller.cpp | 31 ++ libopenage/main/tests/aicontroller.h | 24 ++ libopenage/main/tests/gamestate.cpp | 104 ++++++ libopenage/main/tests/gamestate.h | 86 +++++ libopenage/main/tests/gui.cpp | 268 ++++++++++++++ libopenage/main/tests/gui.h | 48 +++ libopenage/main/tests/physics.cpp | 450 ++++++++++++++++++++++++ libopenage/main/tests/physics.h | 36 ++ libopenage/main/tests/pong.cpp | 220 ++++++++---- libopenage/pyinterface/exctranslate.cpp | 25 +- libopenage/renderer/renderer.h | 13 + libopenage/util/fixed_point.h | 11 +- 27 files changed, 1390 insertions(+), 161 deletions(-) create mode 100644 libopenage/main/tests/aicontroller.cpp create mode 100644 libopenage/main/tests/aicontroller.h create mode 100644 libopenage/main/tests/gamestate.cpp create mode 100644 libopenage/main/tests/gamestate.h create mode 100644 libopenage/main/tests/gui.cpp create mode 100644 libopenage/main/tests/gui.h create mode 100644 libopenage/main/tests/physics.cpp create mode 100644 libopenage/main/tests/physics.h diff --git a/libopenage/curve/continuous.h b/libopenage/curve/continuous.h index 77aa5dac59..b5d31f377e 100644 --- a/libopenage/curve/continuous.h +++ b/libopenage/curve/continuous.h @@ -1,8 +1,9 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #pragma once #include +#include #include "value_container.h" #include "../log/log.h" @@ -24,6 +25,9 @@ class Continuous : public ValueContainer { * will interpolate between the keyframes linearly based on the time. */ T get(const time_t &) const override; + + /** human readable identifier */ + std::string idstr() const override; }; @@ -60,4 +64,20 @@ T Continuous::get(const time_t &time) const { } } + +template +std::string Continuous::idstr() const { + std::stringstream ss; + ss << "ContinuousCurve["; + if (this->_idstr.size()) { + ss << this->_idstr; + } + else { + ss << this->id(); + } + ss << "]"; + return ss.str(); +} + + } // openage::curve diff --git a/libopenage/curve/discrete.h b/libopenage/curve/discrete.h index 63abf65638..a8b7d7297c 100644 --- a/libopenage/curve/discrete.h +++ b/libopenage/curve/discrete.h @@ -1,9 +1,10 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #pragma once #include #include +#include #include "value_container.h" @@ -29,6 +30,9 @@ class Discrete : public ValueContainer { */ T get(const time_t &t) const override; + /** human readable id string */ + std::string idstr() const override; + /** * Return the last time and keyframe with time <= t. */ @@ -49,6 +53,21 @@ T Discrete::get(const time_t &time) const { } +template +std::string Discrete::idstr() const { + std::stringstream ss; + ss << "DiscreteCurve["; + if (this->_idstr.size()) { + ss << this->_idstr; + } + else { + ss << this->id(); + } + ss << "]"; + return ss.str(); +} + + template std::pair Discrete::get_time(const time_t &time) const { auto e = this->container.last(time, this->last_element); diff --git a/libopenage/curve/value_container.h b/libopenage/curve/value_container.h index cd9b511bc6..281d638d74 100644 --- a/libopenage/curve/value_container.h +++ b/libopenage/curve/value_container.h @@ -1,4 +1,4 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #pragma once @@ -15,11 +15,13 @@ class ValueContainer : public event::EventTarget { public: ValueContainer(const std::shared_ptr &mgr, size_t id, + const std::string &idstr="", const EventTarget::single_change_notifier ¬ifier=nullptr) : EventTarget(mgr, notifier), container{mgr}, _id{id}, + _idstr{idstr}, last_element{this->container.begin()} {} virtual ~ValueContainer() = default; @@ -37,8 +39,15 @@ class ValueContainer : public event::EventTarget { virtual void set_last(const time_t &at, const T &value); virtual void set_insert(const time_t &at, const T &value); - virtual size_t id() const override { - return _id; + size_t id() const override { + return this->_id; + } + + std::string idstr() const override { + if (this->_idstr.size() == 0) { + return std::to_string(this->id()); + } + return this->_idstr; } protected: @@ -52,6 +61,11 @@ class ValueContainer : public event::EventTarget { */ const size_t _id; + /** + * Human-readable identifier for the container + */ + const std::string _idstr; + /** * Cache the iterator for quickly finding the end */ diff --git a/libopenage/event/demo/gamestate.h b/libopenage/event/demo/gamestate.h index c43b48d2ea..629099fa7d 100644 --- a/libopenage/event/demo/gamestate.h +++ b/libopenage/event/demo/gamestate.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include "config.h" #include "../state.h" @@ -10,8 +11,10 @@ #include "../eventtarget.h" #include "../../curve/continuous.h" #include "../../curve/discrete.h" +#include "../../util/strings.h" #include "../../util/vector.h" + namespace openage::event::demo { #if WITH_NCURSES @@ -44,22 +47,27 @@ class PongPlayer : public EventTarget { speed(std::make_shared>( mgr, (id << 4) + 1, + util::sformat("PongPlayer(%zd).speed", id), std::bind(&PongPlayer::child_changes, this, _1))), position(std::make_shared>( mgr, (id << 4) + 2, + util::sformat("PongPlayer(%zd).position", id), std::bind(&PongPlayer::child_changes, this, _1))), lives(std::make_shared>( mgr, (id << 4) + 3, + util::sformat("PongPlayer(%zd).lives", id), std::bind(&PongPlayer::child_changes, this, _1))), state(std::make_shared>( mgr, (id << 4) + 4, + util::sformat("PongPlayer(%zd).state", id), std::bind(&PongPlayer::child_changes, this, _1))), size(std::make_shared>( mgr, (id << 4) + 5, + util::sformat("PongPlayer(%zd).size", id), std::bind(&PongPlayer::child_changes, this, _1))), _id{id}, paddle_x{0} {} @@ -77,6 +85,12 @@ class PongPlayer : public EventTarget { return _id; } + std::string idstr() const override { + std::stringstream ss; + ss << "PongPlayer[" << this->id() << "]"; + return ss.str(); + } + private: void child_changes(const curve::time_t &time) { this->changes(time); @@ -92,10 +106,12 @@ class PongBall : public EventTarget { speed(std::make_shared>( mgr, (id << 2) + 1, + util::sformat("PongBall(%zd).speed", id), std::bind(&PongBall::child_changes, this, _1))), position(std::make_shared>( mgr, (id << 2) + 2, + util::sformat("PongBall(%zd).position", id), std::bind(&PongBall::child_changes, this, _1))), _id{id} {} @@ -105,6 +121,13 @@ class PongBall : public EventTarget { size_t id() const override { return _id; } + + std::string idstr() const override { + std::stringstream ss; + ss << "PongBall[" << this->id() << "]"; + return ss.str(); + } + private: void child_changes(const curve::time_t &time) { this->changes(time); diff --git a/libopenage/event/event.cpp b/libopenage/event/event.cpp index e7bb09de98..06b1831a0a 100644 --- a/libopenage/event/event.cpp +++ b/libopenage/event/event.cpp @@ -29,8 +29,9 @@ void Event::depend_on(const std::shared_ptr &dependency) { // TODO: do REPEAT and TRIGGER listen to changes (i.e. have dependents)? // if not, exclude them here and return early. - log::log(DBG << "Adding change listener for: " << this->get_eventclass()->id() - << " to dependency with id=" << dependency->id()); + log::log(DBG << "Registering dependency event from EventClass " + << this->get_eventclass()->id() + << " to EventTarget " << dependency->idstr()); dependency->add_dependent(this->shared_from_this()); } diff --git a/libopenage/event/event.h b/libopenage/event/event.h index 0df3771e43..75f183c38d 100644 --- a/libopenage/event/event.h +++ b/libopenage/event/event.h @@ -65,7 +65,19 @@ class Event : public std::enable_shared_from_this { */ bool operator <(const Event &other) const; - curve::time_t last_triggered = 0; + /** + * asdf what? + */ + const curve::time_t &get_last_triggered() const { + return this->last_triggered; + } + + /** + * asdf what? + */ + void set_last_triggered(const curve::time_t &t) { + this->last_triggered = t; + } private: /** @@ -82,6 +94,9 @@ class Event : public std::enable_shared_from_this { /** Time this event occurs/occured */ curve::time_t time; + /** Time this event was asdf what? */ + curve::time_t last_triggered = curve::time_t::min_value(); + /** Precalculated std::hash for the event */ size_t myhash; }; diff --git a/libopenage/event/eventqueue.cpp b/libopenage/event/eventqueue.cpp index 392bc86306..f5f47eaf94 100644 --- a/libopenage/event/eventqueue.cpp +++ b/libopenage/event/eventqueue.cpp @@ -83,15 +83,19 @@ EventQueue::EventQueue() void EventQueue::add_change(const std::shared_ptr &event, const curve::time_t &changed_at) { - auto it = this->changes->find(OnChangeElement(event, changed_at)); + const curve::time_t event_last_triggered = event->get_last_triggered(); // Has the event been triggered in this round? - if (event->last_triggered < changed_at) { + if (event_last_triggered < changed_at) { + auto it = this->changes->find(OnChangeElement(event, changed_at)); + // Is the change already in the queue? if (it != changes->end()) { // Is the new change dated _before_ the old one? - if (it->time > changed_at) { - log::log(DBG << "Queue: adjusting time in change queue"); + if (changed_at < it->time) { + log::log(DBG << "Queue: adjusting time in change queue: moving event of " + << event->get_eventclass()->id() + << " to earlier time"); // Save the element OnChangeElement e = *it; @@ -105,25 +109,26 @@ void EventQueue::add_change(const std::shared_ptr &event, // this change is to be ignored log::log(DBG << "Queue: skipping change for " << event->get_eventclass()->id() << " at " << changed_at - << " because there was already a better one at t=" << it->time); + << " because there was already an earlier one at t=" << it->time); } } else { // the change was not in the to be changed list this->changes->emplace(event, changed_at); - log::log(DBG << "Queue: inserting change for " << event->get_eventclass()->id() + log::log(DBG << "Queue: inserting change for event from " + << event->get_eventclass()->id() << " to be applied at t=" << changed_at); } } else { // the event has been triggered in this round already, so skip it this time this->future_changes->emplace(event, changed_at); - log::log(DBG << "Queue: putting change for " - << event->get_eventclass()->id() << " into the future, " - << "applied at t=" << changed_at); + log::log(DBG << "Queue: ignoring change at t=" << changed_at + << " for event of EventClass" << event->get_eventclass()->id() + << " because it has been triggered at t=" << event_last_triggered); } - event->last_triggered = changed_at; + event->set_last_triggered(changed_at); } @@ -135,7 +140,7 @@ void EventQueue::remove(const std::shared_ptr &evnt) { } -void EventQueue::enqueue_change(const std::shared_ptr &evnt) { +void EventQueue::enqueue(const std::shared_ptr &evnt) { if (this->event_queue.contains(evnt)) { this->event_queue.update(evnt); } diff --git a/libopenage/event/eventqueue.h b/libopenage/event/eventqueue.h index 990547760d..6c06acd5c1 100644 --- a/libopenage/event/eventqueue.h +++ b/libopenage/event/eventqueue.h @@ -61,8 +61,8 @@ class EventQueue final { * A target is every single unit in the game world - so best add these events * in the constructor of the game objects. * - * The `insertion_time` is the time used to calculate when - * the actual event time will happen! + * The `reference_time` is the time used to calculate when + * the actual event time will happen by calling `eventclass->predict_invoke_time()`! */ std::shared_ptr create_event(const std::shared_ptr &eventtarget, const std::shared_ptr &eventclass, @@ -81,7 +81,7 @@ class EventQueue final { * is newly created. This updates/inserts the given event * in the main queue. */ - void enqueue_change(const std::shared_ptr &event); + void enqueue(const std::shared_ptr &event); /** * The event was just removed, add it again. @@ -150,7 +150,6 @@ class EventQueue final { /** * The universe timeline processes through this queue. - * Type::ONCE is only inserted into the queue. */ EventStore event_queue; }; diff --git a/libopenage/event/eventtarget.cpp b/libopenage/event/eventtarget.cpp index 3555292c5e..4d071f8c38 100644 --- a/libopenage/event/eventtarget.cpp +++ b/libopenage/event/eventtarget.cpp @@ -1,7 +1,9 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #include "eventtarget.h" +#include + #include "event.h" #include "eventclass.h" #include "loop.h" @@ -11,11 +13,13 @@ namespace openage::event { + void EventTarget::changes(const curve::time_t &time) { // This target has some change, so we have to notify all dependents // that subscribed on this target. - log::log(DBG << "Target: change happens on " << this->id() << " at t=" << time); + log::log(DBG << "Target: processing change request at t=" << time + << " for EventTarget " << this->idstr() << "..."); if (this->parent_notifier != nullptr) { this->parent_notifier(time); } @@ -27,28 +31,29 @@ void EventTarget::changes(const curve::time_t &time) { switch (dependent->get_eventclass()->type) { case EventClass::trigger_type::DEPENDENCY_IMMEDIATELY: case EventClass::trigger_type::DEPENDENCY: - // Enqueue a change so that change events will be retriggered - // (that depend on this target) - // FIXME: Traverse the changed value - this->loop->create_change(dependent, time, 0); + // Enqueue a change so that change events, + // which depend on this target, will be retriggered + + log::log(DBG << "Target: change at t=" << time + << " for EventTarget " << this->idstr() << " registered"); + this->loop->create_change(dependent, time); ++it; break; case EventClass::trigger_type::ONCE: // If the dependent is a ONCE-event - // Has it been triggered? If so - forget it. - if (dependent->last_triggered > 0) { + // forget the change if the once event has been triggered already. + if (dependent->get_last_triggered() > curve::time_t::min_value()) { it = this->dependents.erase(it); } else { - // FIXME: Traverse the changed value - this->loop->create_change(dependent, time, 0); + this->loop->create_change(dependent, time); ++it; } break; case EventClass::trigger_type::TRIGGER: case EventClass::trigger_type::REPEAT: - // Ignore announced changes for execute/keyframe events + // Ignore announced changes for triggered or repeated events ++it; break; } @@ -74,8 +79,7 @@ void EventTarget::trigger(const curve::time_t &last_valid_time) { << dependent->get_eventclass()->id() << " at t=" << last_valid_time); - // FIXME: Traverse the changed value - loop->create_change(dependent, last_valid_time, 0); + loop->create_change(dependent, last_valid_time); } ++it; diff --git a/libopenage/event/eventtarget.h b/libopenage/event/eventtarget.h index e633427434..0125232f71 100644 --- a/libopenage/event/eventtarget.h +++ b/libopenage/event/eventtarget.h @@ -18,11 +18,15 @@ class EventClass; * Every Object in the gameworld that wants to be targeted by events or as * dependency for events, has to implement this class. */ +// asdf rename to EventSource ? because it is a source for event triggering? class EventTarget { public: /** Give a unique event system identifier for the entity */ virtual size_t id() const = 0; + /** Give a human-readable identifier for this target */ + virtual std::string idstr() const = 0; + using single_change_notifier = std::function; protected: diff --git a/libopenage/event/loop.cpp b/libopenage/event/loop.cpp index e02bb727b3..ba34d10fe5 100644 --- a/libopenage/event/loop.cpp +++ b/libopenage/event/loop.cpp @@ -61,10 +61,18 @@ void Loop::reach_time(const curve::time_t &max_time, // at least one processed event adds another event so // the queue never stops adding changes // simple "solution": abort after over 9000 attempts. + int max_attempts = 10; int cnt = 0; int attempts = 0; + do { + if (attempts > max_attempts) { + throw Error(ERR << "Loop: reached event settling threshold of " + << max_attempts << ", giving up."); + break; + } + log::log(DBG << "Loop: Attempt " << attempts << " to reach t=" << max_time); this->update_changes(state); cnt = this->execute_events(max_time, state); @@ -72,11 +80,6 @@ void Loop::reach_time(const curve::time_t &max_time, log::log(DBG << "Loop: to reach t=" << max_time << ", n=" << cnt << " events were executed"); - if (attempts > 9000) { - log::log(WARN << "Loop: reached event settling threshold, giving up."); - break; - } - attempts += 1; } while (cnt != 0); @@ -115,7 +118,7 @@ int Loop::execute_events(const curve::time_t &time_until, if (target) { log::log(DBG << "Loop: invoking event \"" << event->get_eventclass()->id() - << "\" on target \"" << target->id() + << "\" on target \"" << target->idstr() << "\" for time t=" << event->get_time()); this->active_event = event; @@ -155,6 +158,12 @@ int Loop::execute_events(const curve::time_t &time_until, } +void Loop::create_change(const std::shared_ptr &evnt, + const curve::time_t &changes_at) { + this->queue.add_change(evnt, changes_at); +} + + void Loop::update_changes(const std::shared_ptr &state) { log::log(DBG << "Loop: " << this->queue.get_changes().size() @@ -162,7 +171,7 @@ void Loop::update_changes(const std::shared_ptr &state) { size_t i = 0; - // reevaluate depending events beecause of the change + // reevaluate depending events because of the change for (const auto &change : this->queue.get_changes()) { auto evnt = change.evnt.lock(); if (evnt) { @@ -186,7 +195,7 @@ void Loop::update_changes(const std::shared_ptr &state) { evnt->set_time(new_time); - this->queue.enqueue_change(evnt); + this->queue.enqueue(evnt); } else { log::log(DBG << "Loop: due to a change, canceled execution of '" @@ -202,7 +211,7 @@ void Loop::update_changes(const std::shared_ptr &state) { case EventClass::trigger_type::TRIGGER: case EventClass::trigger_type::DEPENDENCY_IMMEDIATELY: evnt->set_time(change.time); - this->queue.enqueue_change(evnt); + this->queue.enqueue(evnt); break; case EventClass::trigger_type::REPEAT: diff --git a/libopenage/event/loop.h b/libopenage/event/loop.h index d0630f4ed4..71860cb1a4 100644 --- a/libopenage/event/loop.h +++ b/libopenage/event/loop.h @@ -84,10 +84,8 @@ class Loop { * This inserts the event into the changes queue * so it will be evaluated in the next loop iteration. */ - template void create_change(const std::shared_ptr &event, - const curve::time_t &changes_at, - const T &new_value); + const curve::time_t &changes_at); const EventQueue &get_queue() const { return this->queue; @@ -132,16 +130,4 @@ class Loop { std::unordered_map> curveindex; }; - -template -void Loop::create_change(const std::shared_ptr &evnt, - const curve::time_t &changes_at, - const T &new_value) { - - log::log(DBG << "Loop: registering change of " << evnt->get_eventclass()->id() - << " at t=" << changes_at << " to " << new_value); - this->queue.add_change(evnt, changes_at); -} - - } // openage::event diff --git a/libopenage/event/tests.cpp b/libopenage/event/tests.cpp index 99e483a4eb..d4d285913f 100644 --- a/libopenage/event/tests.cpp +++ b/libopenage/event/tests.cpp @@ -1,8 +1,9 @@ // Copyright 2017-2019 the openage authors. See copying.md for legal info. -#include // for strcmp +#include #include #include +#include #include "../testing/testing.h" #include "../log/log.h" @@ -36,6 +37,12 @@ class TestState : public State { return _id; } + std::string idstr() const override { + std::stringstream ss; + ss << "TestObject[" << this->id() << "]"; + return ss.str(); + } + void test_trigger(const curve::time_t &time) { this->trigger(time); } diff --git a/libopenage/main/tests.h b/libopenage/main/tests.h index 87e1bfb011..a03e74c727 100644 --- a/libopenage/main/tests.h +++ b/libopenage/main/tests.h @@ -8,7 +8,8 @@ namespace openage::main::tests { -// pxd: void engine_demo(int demo_id, Path path) except + +// pxd: void engine_demo(int demo_id, Path path) +// except + void engine_demo(int demo_id, const util::Path &path); } // openage::main::tests diff --git a/libopenage/main/tests/CMakeLists.txt b/libopenage/main/tests/CMakeLists.txt index e5cc7ddaa6..aeeaaf48d0 100644 --- a/libopenage/main/tests/CMakeLists.txt +++ b/libopenage/main/tests/CMakeLists.txt @@ -1,3 +1,7 @@ add_sources(libopenage + aicontroller.cpp + gamestate.cpp + gui.cpp + physics.cpp pong.cpp ) diff --git a/libopenage/main/tests/aicontroller.cpp b/libopenage/main/tests/aicontroller.cpp new file mode 100644 index 0000000000..9e2df26004 --- /dev/null +++ b/libopenage/main/tests/aicontroller.cpp @@ -0,0 +1,31 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#include "aicontroller.h" + + +namespace openage::main::tests::pong { + + +const std::vector &AIInput::get_inputs(const std::shared_ptr &player, + const std::shared_ptr &ball, + const curve::time_t &now) { + this->commands.clear(); + + auto position = player->position->get(now); + + // Yes i know, there is /3 used - instead of the logical /2 - this is to + // create a small safety boundary of 1/3 for enhanced fancyness + + // Ball is below position + if (ball->position->get(now)[1] > position + player->size->get(now) / 3) { + this->commands.push_back(PongEvent{player->id(), PongEvent::DOWN}); + } + // Ball is above position + else if (ball->position->get(now)[1] < position - player->size->get(now) / 3) { + this->commands.push_back(PongEvent{player->id(), PongEvent::UP}); + } + + return this->commands; +} + +} // openage::main::tests::pong diff --git a/libopenage/main/tests/aicontroller.h b/libopenage/main/tests/aicontroller.h new file mode 100644 index 0000000000..9d84528110 --- /dev/null +++ b/libopenage/main/tests/aicontroller.h @@ -0,0 +1,24 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#pragma once + +#include "gamestate.h" + +#include + + +namespace openage::main::tests::pong { + +class AIInput { +public: + const std::vector &get_inputs( + const std::shared_ptr &player, + const std::shared_ptr &ball, + const curve::time_t &now + ); + +private: + std::vector commands; +}; + +} // openage::main::tests::pong diff --git a/libopenage/main/tests/gamestate.cpp b/libopenage/main/tests/gamestate.cpp new file mode 100644 index 0000000000..7a38637abf --- /dev/null +++ b/libopenage/main/tests/gamestate.cpp @@ -0,0 +1,104 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#include "gamestate.h" + +#include + +#include "../../util/strings.h" + + +namespace openage::main::tests::pong { + + +using namespace std::placeholders; +PongPlayer::PongPlayer(const std::shared_ptr &mgr, size_t id) + : + EventTarget{mgr}, + speed(std::make_shared>( + mgr, + (id << 4) + 1, + util::sformat("PongPlayer(%zu).speed", id), + std::bind(&PongPlayer::child_changes, this, _1))), + position(std::make_shared>( + mgr, + (id << 4) + 2, + util::sformat("PongPlayer(%zu).position", id), + std::bind(&PongPlayer::child_changes, this, _1))), + lives(std::make_shared>( + mgr, + (id << 4) + 3, + util::sformat("PongPlayer(%zu).lives", id), + std::bind(&PongPlayer::child_changes, this, _1))), + state(std::make_shared>( + mgr, + (id << 4) + 4, + util::sformat("PongPlayer(%zu).state", id), + std::bind(&PongPlayer::child_changes, this, _1))), + size(std::make_shared>( + mgr, + (id << 4) + 5, + util::sformat("PongPlayer(%zu).size", id), + std::bind(&PongPlayer::child_changes, this, _1))), + _id{id}, + paddle_x{0} {} + + +size_t PongPlayer::id() const { + return _id; +} + +std::string PongPlayer::idstr() const { + std::stringstream ss; + ss << "PongPlayer(" << this->id() << ")"; + return ss.str(); +} + + +void PongPlayer::child_changes(const curve::time_t &time) { + this->changes(time); +} + + +PongBall::PongBall(const std::shared_ptr &mgr, size_t id) + : + EventTarget{mgr}, + speed(std::make_shared>( + mgr, + (id << 2) + 1, + util::sformat("PongBall(%zu).speed", id), + std::bind(&PongBall::child_changes, this, _1))), + position(std::make_shared>( + mgr, + (id << 2) + 2, + util::sformat("PongBall(%zu).position", id), + std::bind(&PongBall::child_changes, this, _1))), + _id{id} {} + + +size_t PongBall::id() const { + return _id; +} + + +std::string PongBall::idstr() const { + std::stringstream ss; + ss << "PongBall(" << this->id() << ")"; + return ss.str(); +} + + +void PongBall::child_changes(const curve::time_t &time) { + this->changes(time); +} + + +PongState::PongState(const std::shared_ptr &mgr, + const std::shared_ptr &gui) + : + State{mgr}, + p1(std::make_shared(mgr, 0)), + p2(std::make_shared(mgr, 1)), + ball(std::make_shared(mgr, 2)), + gui{gui} {} + +} // openage::main::tests::pong diff --git a/libopenage/main/tests/gamestate.h b/libopenage/main/tests/gamestate.h new file mode 100644 index 0000000000..8aec21ad40 --- /dev/null +++ b/libopenage/main/tests/gamestate.h @@ -0,0 +1,86 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "config.h" +#include "../../curve/continuous.h" +#include "../../curve/discrete.h" +#include "../../event/eventtarget.h" +#include "../../event/loop.h" +#include "../../event/state.h" +#include "../../util/vector.h" + + +namespace openage::main::tests::pong { + +class Gui; + + +class PongEvent { +public: + enum state_e { + UP, DOWN, START, IDLE, LOST + }; + + PongEvent(size_t id, state_e s) : player(id), state(s) {} + PongEvent() : player(0), state(IDLE) {} + + size_t player; + state_e state; +}; + + +class PongPlayer : public event::EventTarget { +public: + PongPlayer(const std::shared_ptr &mgr, size_t id); + + size_t id() const override; + std::string idstr() const override; + + std::shared_ptr> speed; + std::shared_ptr> position; + std::shared_ptr> lives; + std::shared_ptr> state; + std::shared_ptr> size; + + size_t _id; + float paddle_x; + +private: + void child_changes(const curve::time_t &time); +}; + + +class PongBall : public event::EventTarget { +public: + PongBall(const std::shared_ptr &mgr, size_t id); + + size_t id() const override; + std::string idstr() const override; + + std::shared_ptr> speed; + std::shared_ptr> position; + +private: + void child_changes(const curve::time_t &time); + size_t _id; +}; + + +class PongState : public event::State { +public: + PongState(const std::shared_ptr &mgr, + const std::shared_ptr &gui); + + std::shared_ptr p1; + std::shared_ptr p2; + std::shared_ptr ball; + util::Vector2d display_boundary; + + std::shared_ptr gui; +}; + + +} // openage::main::tests::pong diff --git a/libopenage/main/tests/gui.cpp b/libopenage/main/tests/gui.cpp new file mode 100644 index 0000000000..7fbf65018c --- /dev/null +++ b/libopenage/main/tests/gui.cpp @@ -0,0 +1,268 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#include "gui.h" + +#include +#include +#include + +#include "gamestate.h" +#include "../../log/log.h" +#include "../../renderer/opengl/shader.h" +#include "../../renderer/resources/shader_source.h" +#include "../../renderer/resources/texture_data.h" +#include "../../renderer/resources/mesh_data.h" +#include "../../renderer/texture.h" +#include "../../renderer/shader_program.h" +#include "../../renderer/util.h" +#include "../../renderer/geometry.h" +#include "../../renderer/opengl/window.h" + + +namespace openage::main::tests::pong { + + +const std::vector &Gui::get_inputs(const std::shared_ptr &player) { + this->input_cache.clear(); + + PongEvent evnt; + evnt.player = player->id(); + evnt.state = PongEvent::IDLE; + + std::vector inputs; + + /* + for (all inputs from SDL) { + add key to inputs vector; + } + */ + + for (auto input : inputs) { + switch (input) { + case 0: + evnt.state = PongEvent::DOWN; + break; + + case 1: + evnt.state = PongEvent::UP; + break; + + case 27: + exit(0); + break; + + case 'r': + case ' ': + evnt.state = PongEvent::START; + break; + + default: + evnt.state = PongEvent::IDLE; + break; + } + + this->input_cache.push_back(evnt); + } + + if (this->input_cache.empty()) { + // store 'idle' input to cancel movement + this->input_cache.push_back(evnt); + } + + return this->input_cache; +} + + +constexpr const int max_log_msgs = 10; + + +Gui::Gui() + : + window{"openage engine test", 800, 600}, + renderer{window.make_renderer()} { + + auto vshader_src = renderer::resources::ShaderSource( + renderer::resources::shader_lang_t::glsl, + renderer::resources::shader_stage_t::vertex, + R"s( +#version 330 + +layout(location=0) in vec2 position; +uniform mat4 pos; +uniform mat4 proj; + +void main() { + gl_Position = proj * pos * vec4(position, 0.0, 1.0); +} +)s"); + + auto fshader_src = renderer::resources::ShaderSource( + renderer::resources::shader_lang_t::glsl, + renderer::resources::shader_stage_t::fragment, + R"s( +#version 330 + +out vec4 col; + +void main() { + col = vec4(1.0, 0, 0, 1.0); +} +)s"); + + auto shader = renderer->add_shader( { vshader_src, fshader_src } ); + + auto pos1 = Eigen::Affine3f::Identity(); + pos1.prescale(Eigen::Vector3f(200.0f, 200.0f, 1.0f)); + //pos1.prerotate(Eigen::AngleAxisf(45.0f * math::PI / 180.0f, Eigen::Vector3f::UnitZ())); + pos1.pretranslate(Eigen::Vector3f(400.0f, 400.0f, 0.0f)); + + auto ball_props = shader->new_uniform_input( + "pos", pos1.matrix() + ); + + auto proj_in = shader->new_uniform_input( + "proj", Eigen::Affine3f::Identity().matrix() + ); + + auto quad = renderer->add_mesh_geometry(renderer::resources::MeshData::make_quad()); + + this->ball = renderer::Renderable{ + ball_props, + quad, + true, + true, + }; + + auto set_projmatrix = renderer::ShaderUpdate{proj_in}; + + this->pass = renderer::RenderPass{ + { std::move(set_projmatrix), this->ball }, + this->renderer->get_display_target(), + }; + + glDepthFunc(GL_LEQUAL); + glDepthRange(0.0, 1.0); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + this->window.add_resize_callback( + [=] (size_t w, size_t h) { + Eigen::Matrix4f proj_matrix = renderer::util::ortho_matrix_f( + 0.0f, w, 0.0f, h, 0.0f, 1.0f + ); + + shader->update_uniform_input(proj_in, "proj", proj_matrix); + } + ); +} + + +void Gui::get_display_size(const std::shared_ptr &state, + const curve::time_t &/*now*/) { + // TODO: make the display_boundary a curve as well. + + + // TODO asdf actually get graphical display size + + state->display_boundary[0] = 80; + state->display_boundary[1] = 60; +} + + +void Gui::draw(const std::shared_ptr &state, const curve::time_t &now) { + + // TODO draw score + //mvprintw(xpos, + // state->display_boundary[0] / 2 - 5, + // "P1 %i | P2 %i", + // state->p1->lives->get(now), + // state->p2->lives->get(now)); + + // TODO: draw middle line + + // TODO: draw debug info + /* + mvprintw(0, 1, "NOW: %f", now.to_double()); + mvprintw(1, 1, "SCR: %f | %f", state->display_boundary[0], state->display_boundary[1]); + mvprintw(2, 1, + "P1: %f, %f, %i", + state->p1->position->get(now), + state->p1->paddle_x, + state->p1->state->get(now).state); + mvprintw(3, 1, + "P2: %f, %f, %i", + state->p2->position->get(now), + state->p2->paddle_x, + state->p2->state->get(now).state); + */ + + // ball position predictions, 10s into the future + for (int i = 0; i < 10; i++) { + auto i_as_ctt = curve::time_t::from_int(i); + /* + mvprintw((5 + i), 1, + "BALL in %03f: %f | %f; SPEED: %f | %f | PLpos: %f, PRpos: %f", + i_as_ctt.to_double(), + state->ball->position->get(now + i_as_ctt)[0], + state->ball->position->get(now + i_as_ctt)[1], + state->ball->speed->get(now + i_as_ctt)[0], + state->ball->speed->get(now + i_as_ctt)[1], + state->p1->position->get(now + i_as_ctt), + state->p2->position->get(now + i_as_ctt) + ); + */ + } + + // show log messages + for (auto & msg : this->log_msgs) { + //mvprintw(ypos, state->display_boundary[0]/2 + 10, msg.c_str()); + } + + + // draw player 1 paddle + for (int i = -state->p1->size->get(now) / 2; i < state->p1->size->get(now) / 2; i++) { + //mvprintw(state->p1->position->get(now) + i, state->p1->paddle_x, "|"); + } + + // draw player 2 paddle + for (int i = -state->p2->size->get(now) / 2; i < state->p2->size->get(now) / 2; i++) { + //mvprintw(state->p2->position->get(now) + i, state->p2->paddle_x, "|"); + } + + // ball position prediction 10s into the future + for (int i = 1; i < 100; ++i) { + auto i_as_ctt = curve::time_t::from_double(i/10.0); + //this->draw_ball(state->ball->position->get(now + i_as_ctt), "x"); + } + + // draw the ball + //this->draw_ball(state->ball->position->get(now), "M"); // TODO: use "⚽" + + //auto ball_pos = state->ball->position->get(now); + //auto pos1 = Eigen::Affine3f::Identity(); + //pos1.prescale(Eigen::Vector3f(200.0f, 200.0f, 1.0f)); + //pos1.prerotate(Eigen::AngleAxisf(45.0f * math::PI / 180.0f, Eigen::Vector3f::UnitZ())); + //pos1.pretranslate(Eigen::Vector3f(ball_pos[0], ball_pos[1], 0.0f)); + //this->ball->unif_in->update_uniform_input("pos", ball_pos_matrix); + + this->renderer->render(this->pass); + this->window.update(); + renderer::opengl::GlContext::check_error(); + + this->exit_requested = window.should_close(); + if (this->exit_requested) { + log::log(INFO << "should exit"); + } +} + + +void Gui::log(const std::string &msg) { + + log::log(INFO << "Gui::log: " << msg); + + if (this->log_msgs.size() >= max_log_msgs) { + this->log_msgs.pop_back(); + } + this->log_msgs.push_front(msg); +} + +} // openage::main::tests::pong diff --git a/libopenage/main/tests/gui.h b/libopenage/main/tests/gui.h new file mode 100644 index 0000000000..6531749c62 --- /dev/null +++ b/libopenage/main/tests/gui.h @@ -0,0 +1,48 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#pragma once + + +#include +#include + +#include "../../curve/curve.h" +#include "../../renderer/renderer.h" +#include "../../renderer/opengl/window.h" + + +namespace openage::main::tests::pong { + +class PongEvent; +class PongState; +class PongPlayer; + + +class Gui { +public: + Gui(); + + const std::vector &get_inputs(const std::shared_ptr &player); + void get_display_size(const std::shared_ptr &state, + const curve::time_t &now); + + void draw(const std::shared_ptr &state, const curve::time_t &now); + + void log(const std::string &msg); + + bool exit_requested = false; + +private: + renderer::opengl::GlWindow window; + std::unique_ptr renderer; + + std::vector input_cache; + std::deque log_msgs; + + // rendering objects + renderer::Renderable ball; + + renderer::RenderPass pass; +}; + +} // openage::main::tests::pong diff --git a/libopenage/main/tests/physics.cpp b/libopenage/main/tests/physics.cpp new file mode 100644 index 0000000000..d17267a397 --- /dev/null +++ b/libopenage/main/tests/physics.cpp @@ -0,0 +1,450 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#include "physics.h" + +#include + +#include "gui.h" +#include "../../error/error.h" +#include "../../rng/global_rng.h" +#include "../../util/stringformatter.h" + + +namespace openage::main::tests::pong { + + +class BallReflectWall : public event::DependencyEventClass { +public: + BallReflectWall() : event::DependencyEventClass("demo.ball.reflect_wall") {} + + void setup_event(const std::shared_ptr &evnt, + const std::shared_ptr &gstate) override { + + auto state = std::dynamic_pointer_cast(gstate); + + // FIXME dependency to a full ball object so that any curve + // triggers a change + evnt->depend_on(state->ball->position); + evnt->depend_on(state->ball->speed); + // TODO add dependency to size of game area + + // FIXME: warn if it's not a dependency eventclass + } + + // FIXME we REALLY need dependencies to objects i.e. Ball : public EventTarget() + void invoke(event::Loop &, + const std::shared_ptr &target, + const std::shared_ptr &gstate, + const curve::time_t &now, + const event::EventClass::param_map &/*param*/) override { + + auto positioncurve = std::dynamic_pointer_cast>(target); + auto state = std::dynamic_pointer_cast(gstate); + auto speedcurve = state->ball->speed; + + // get speed and position to insert new movement keyframe + auto speed = speedcurve->get(now); + auto pos = positioncurve->get(now); + speed[1] *= -1.0; + state->ball->speed->set_last(now, speed); + state->ball->position->set_last(now, pos); + + if (speed[1] == 0) { + return; + } + + curve::time_t ty = 0; + if (speed[1] > 0) { + ty = curve::time_t::from_double( + (state->display_boundary[1] - pos[1]) / speed[1] + ); + } + else if (speed[1] < 0) { + ty = curve::time_t::from_double( + pos[1] / -speed[1] + ); + } + + state->ball->position->set_last(now + ty, pos + (speed * ty.to_double())); + } + + curve::time_t predict_invoke_time(const std::shared_ptr &target, + const std::shared_ptr &gstate, + const curve::time_t &now) override { + + auto positioncurve = std::dynamic_pointer_cast>(target); + auto state = std::dynamic_pointer_cast(gstate); + + auto speed = state->ball->speed->get(now); + auto pos = positioncurve->get(now); + + if (speed[1] == 0) { + return std::numeric_limits::max(); + } + + curve::time_t ty = 0; + if (speed[1] > 0) { + ty = curve::time_t::from_double((state->display_boundary[1] - pos[1]) / speed[1]); + } + else if (speed[1] < 0) { + ty = curve::time_t::from_double(pos[1] / -speed[1]); + } + + util::FString str; + str.fmt("WALL TY %f NOW %f, NOWTY %f ", + ty.to_double(), now.to_double(), (now + ty).to_double()); + state->gui->log(str); + + return now + ty; + } +}; + + +class BallReflectPanel : public event::DependencyEventClass { +public: + BallReflectPanel () + : + event::DependencyEventClass("demo.ball.reflect_panel") {} + + void setup_event(const std::shared_ptr &target, + const std::shared_ptr &gstate) override { + + auto state = std::dynamic_pointer_cast(gstate); + + // FIXME dependency to a full ball object + // then a change to any of the curves triggers the update. + target->depend_on(state->ball->position); + target->depend_on(state->ball->speed); + target->depend_on(state->p1->position); + target->depend_on(state->p2->position); + // TODO add dependency to size of game area + } + + // FIXME we REALLY need dependencies to objects + void invoke(event::Loop &mgr, + const std::shared_ptr &/*target*/, + const std::shared_ptr &gstate, + const curve::time_t &now, + const event::EventClass::param_map &/*param*/) override { + + auto state = std::dynamic_pointer_cast(gstate); + + auto pos = state->ball->position->get(now); + auto speed = state->ball->speed->get(now); + + static int cnt = 0; + util::FString str; + str.fmt("Panel hit [%i]", ++cnt); + state->gui->log(str); + + if (pos[0] <= 1 and + speed[0] < 0 and + (pos[1] < state->p1->position->get(now) - state->p1->size->get(now) / 2 or + pos[1] > state->p1->position->get(now) + state->p1->size->get(now) / 2)) { + + // ball missed the paddle of player 1 + auto l = state->p1->lives->get(now); + l--; + + state->p1->lives->set_last(now, l); + state->ball->position->set_last(now, pos); + + Physics::reset(state, mgr, now); + } + else if (pos[0] >= state->display_boundary[0] - 1 and + speed[0] > 0 and + (pos[1] < state->p2->position->get(now) - state->p2->size->get(now) / 2 or + pos[1] > state->p2->position->get(now) + state->p2->size->get(now) / 2)) { + + // ball missed the paddel of player 2 + auto l = state->p2->lives->get(now); + l--; + state->p2->lives->set_last(now, l); + state->ball->position->set_last(now, pos); + + Physics::reset(state, mgr, now); + } + else if (pos[0] >= state->display_boundary[0]- 1 || pos[0] <= 1) { + speed[0] *= -1; + state->ball->speed->set_last(now, speed); + state->ball->position->set_last(now, pos); + } + + curve::time_t ty = 0; + if (speed[0] > 0) { + ty = curve::time_t::from_double((state->display_boundary[0] - pos[0]) / speed[0]); + } + else if (speed[0] < 0) { + ty = curve::time_t::from_double(pos[0] / -speed[0]); + } + + auto hit_pos = pos + speed * ty.to_double(); + if (speed[0] > 0) { + hit_pos[0] = state->display_boundary[0]; + } + else { + hit_pos[0] = 0; + } + + state->ball->position->set_last(now + ty, hit_pos); + } + + curve::time_t predict_invoke_time(const std::shared_ptr &target, + const std::shared_ptr &gstate, + const curve::time_t &now) override { + + auto positioncurve = std::dynamic_pointer_cast>(target); + auto state = std::dynamic_pointer_cast(gstate); + + auto speed = state->ball->speed->get(now); + auto pos = positioncurve->get(now); + + if (speed[0] == 0) + return std::numeric_limits::max(); + + curve::time_t ty = 0; + + if (speed[0] > 0) { + ty = curve::time_t::from_double((state->display_boundary[0] - pos[0]) / speed[0]); + } + else if (speed[0] < 0) { + ty = curve::time_t::from_double(pos[0] / -speed[0]); + } + + { + util::FString str; + str.fmt("PANEL REFLECT AT %f NEXT %f", now.to_double(), (now + ty).to_double()); + state->gui->log(str); + } + + auto hit_pos = pos + speed * ty.to_double(); + if (speed[0] > 0) { + hit_pos[0] = state->display_boundary[0]; + } + else { + hit_pos[0] = 0; + } + + ENSURE(hit_pos[0] >= 0, "hit position x must be positive"); + + return now + ty; + } +}; + + +class ResetGame : public event::OnceEventClass { +public: + ResetGame () + : + event::OnceEventClass("demo.reset") {} + + void setup_event(const std::shared_ptr &/*target*/, + const std::shared_ptr &/*state*/) override {} + + void invoke(event::Loop &/*mgr*/, + const std::shared_ptr &/*target*/, + const std::shared_ptr &gstate, + const curve::time_t &now, + const event::EventClass::param_map &/*param*/) override { + + auto state = std::dynamic_pointer_cast(gstate); + + // Check if the condition still applies + { + auto pos = state->ball->position->get(now); + if (pos[0] > 0 && pos[0] < state->display_boundary[0]) { + // the gamestate is still valid - there is no need to reset + throw Error(ERR << "reset invoked even though unnecessary"); + } + } + + // move ball to the center, quickly + state->ball->position->set_last(now - curve::time_t::from_double(0.01), + state->ball->position->get(now)); + state->ball->position->set_last(now, state->display_boundary / 2); + + // move paddles to center + state->p1->position->set_last(now, state->display_boundary[1] / 2); + state->p2->position->set_last(now, state->display_boundary[1] / 2); + + float dirx = (rng::random() % 2) ? 1 : -1; + float diry = (rng::random() % 2) ? 1 : -1; + auto init_speed = util::Vector2d( + dirx * (10 + (rng::random() % 100) / 4.f), + diry * (0.3 + (rng::random() % 100) / 18.f) + ); + + state->ball->speed->set_last(now, init_speed); + auto pos = state->ball->position->get(now); + + { + static int cnt = 0; + + util::FString str; + str.fmt("Reset #%i. Speed %f | %f POS %f | %f", + ++cnt, + init_speed[0], + init_speed[1], + pos[0], + pos[1]); + state->gui->log(str); + } + + curve::time_t ty = 0; + + // calculate the wall-hit-times + if (init_speed[1] > 0) { + ty = curve::time_t::from_double((state->display_boundary[1] - pos[1]) / init_speed[1]); + } + else if (init_speed[1] < 0) { + ty = curve::time_t::from_double(pos[1] / -init_speed[1]); + } + else { + // currently never happens, but this would be a non-vertically-moving ball + // fallback to calculating panel-hit-times + if (init_speed[0] > 0) { + ty = curve::time_t::from_double((state->display_boundary[0] - pos[0]) / init_speed[0]); + } + else { + ty = curve::time_t::from_double(pos[0] / -init_speed[0]); + } + } + + state->ball->position->set_last(now + ty, pos + init_speed * ty.to_double()); + } + + curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, + const std::shared_ptr &/*state*/, + const curve::time_t &old_time) override { + return old_time; + } +}; + + +void Physics::init(const std::shared_ptr &gstate, + const std::shared_ptr &loop, + const curve::time_t &now) { + + auto state = std::dynamic_pointer_cast(gstate); + + loop->add_event_class(std::make_shared()); + loop->add_event_class(std::make_shared()); + loop->add_event_class(std::make_shared()); + + loop->create_event("demo.ball.reflect_wall", state->ball->position, state, now); + loop->create_event("demo.ball.reflect_panel", state->ball->position, state, now); + + // FIXME once "reset": deregister + //reset(state, mgr, now); +} + + +void Physics::reset(const std::shared_ptr &gstate, + event::Loop &mgr, + const curve::time_t &now) { + + auto state = std::dynamic_pointer_cast(gstate); + mgr.create_event("demo.reset", state->ball->position, state, now); +} + + +void Physics::process_input(const std::shared_ptr &state, + const std::shared_ptr &player, + const std::vector &events, + const std::shared_ptr &mgr, + const curve::time_t &now) { + + // seconds into the future + constexpr static auto predicted_movement_time = curve::time_t::from_double(5.0); + + // lines per second + constexpr static double movement_speed = 8.0; + + for (auto& evnt : events) { + + // Process only if the future has changed + if (player->state->get(now).state != evnt.state) { + + // log the new input in the state curve + player->state->set_last(now, evnt); + + // if the state is active longer than predicted, + // we have to extend the prediction! + bool extend_previous_prediction = false; + auto prev_state = player->state->get_previous(now); + if (prev_state and prev_state->second.state == evnt.state) { + extend_previous_prediction = true; + } + + switch(evnt.state) { + case PongEvent::UP: + case PongEvent::DOWN: { + float current_pos = player->position->get(now); + + if (not extend_previous_prediction) { + // create a keyframe for the new movement + player->position->set_last(now, current_pos); + // TODO: drop the intermediate keyframes + // for position and speed. + } + + switch (evnt.state) { + case PongEvent::UP: + player->speed->set_last(now, -movement_speed); + break; + case PongEvent::DOWN: + player->speed->set_last(now, movement_speed); + break; + default: + // never reached. + break; + } + + curve::time_t move_stop_guess = now + predicted_movement_time; + + // change the position by integrating the speed curve. + // TODO: add native integral to curves. + float new_pos = current_pos + + (((player->speed->get(move_stop_guess) + + player->speed->get(now)) / 2.0) * + predicted_movement_time.to_double()); + + // if paddle will get out-of-bounds, calculate the bound hit time + + // TODO: add native key/value finding in range to curves + if (new_pos < 0) { + move_stop_guess = now + (current_pos / movement_speed); + new_pos = 0; + } + + if (new_pos > state->display_boundary[1]) { + move_stop_guess = now + ((state->display_boundary[1] - current_pos) / movement_speed); + new_pos = state->display_boundary[1]; + } + + PongEvent set_idle{evnt.player, PongEvent::IDLE}; + player->state->set_last(move_stop_guess, set_idle); + player->speed->set_last(move_stop_guess, 0); + player->position->set_last(move_stop_guess, new_pos); + + // TODO: predict_reflect_panel(state, queue, now); + break; + } + + case PongEvent::IDLE: + player->position->set_last(now, player->position->get(now)); + player->speed->set_last(now, 0); + break; + + case PongEvent::START: + Physics::reset(state, *mgr, now); + break; + + default: + break; + } + } + } +} + +} // openage::main::tests::pong diff --git a/libopenage/main/tests/physics.h b/libopenage/main/tests/physics.h new file mode 100644 index 0000000000..b7420bbead --- /dev/null +++ b/libopenage/main/tests/physics.h @@ -0,0 +1,36 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#pragma once + +#include "gamestate.h" +#include "../../curve/curve.h" + +#include + + +namespace openage::event { + +class Loop; + +} // openage::event + +namespace openage::main::tests::pong { + +class Physics { +public: + static void init(const std::shared_ptr &, + const std::shared_ptr &mgr, + const curve::time_t &); + + void process_input(const std::shared_ptr &, + const std::shared_ptr &, + const std::vector &input, + const std::shared_ptr &mgr, + const curve::time_t &now); + + static void reset(const std::shared_ptr &, + event::Loop &mgr, + const curve::time_t &); +}; + +} // openage::main::tests::pong diff --git a/libopenage/main/tests/pong.cpp b/libopenage/main/tests/pong.cpp index 20f0d5f7b0..4325c88155 100644 --- a/libopenage/main/tests/pong.cpp +++ b/libopenage/main/tests/pong.cpp @@ -1,33 +1,46 @@ -// Copyright 2018-2018 the openage authors. See copying.md for legal info. +// Copyright 2018-2019 the openage authors. See copying.md for legal info. #include #include #include #include #include -#include #include -#include "../../util/math_constants.h" -#include "../../log/log.h" +#include "aicontroller.h" +#include "gui.h" +#include "physics.h" #include "../../error/error.h" -#include "../../renderer/resources/shader_source.h" -#include "../../renderer/resources/texture_data.h" -#include "../../renderer/resources/mesh_data.h" -#include "../../renderer/texture.h" -#include "../../renderer/shader_program.h" -#include "../../renderer/util.h" -#include "../../renderer/geometry.h" -#include "../../renderer/opengl/window.h" +#include "../../event/loop.h" +#include "../../log/log.h" +#include "../../util/math_constants.h" +#include "../../util/path.h" namespace openage::main::tests::pong { + +using Clock = std::chrono::high_resolution_clock; +using namespace std::literals::chrono_literals; + + +enum class timescale { + NOSLEEP, + REALTIME, + SLOW, + FAST, +}; + + void main(const util::Path& path) { + bool human_player = false; + + timescale speed = timescale::REALTIME; std::shared_ptr db = nyan::Database::create(); + std::shared_ptr gui = std::make_shared(); db->load( "test.nyan", @@ -47,90 +60,143 @@ void main(const util::Path& path) { nyan::Object test = root->get("test.Test"); log::log(INFO << "nyan read test: " << *test.get("member")); - renderer::opengl::GlWindow window("openage engine test", 800, 600); - auto renderer = window.make_renderer(); + bool running = true; - auto vshader_src = renderer::resources::ShaderSource( - renderer::resources::shader_lang_t::glsl, - renderer::resources::shader_stage_t::vertex, - R"s( -#version 330 + while (running) { + log::log(INFO << "initializing new pong game..."); -layout(location=0) in vec2 position; -uniform mat4 pos; -uniform mat4 proj; + auto loop = std::make_shared(); + curve::time_t now = 0; + Physics phys; + AIInput ai; -void main() { - gl_Position = proj * pos * vec4(position, 0.0, 1.0); -} -)s"); + auto state = std::make_shared(loop, gui); + Physics::init(state, loop, now); - auto fshader_src = renderer::resources::ShaderSource( - renderer::resources::shader_lang_t::glsl, - renderer::resources::shader_stage_t::fragment, - R"s( -#version 330 + state->p1->lives->set_last(now, 3); + state->p1->size->set_last(now, 4); -out vec4 col; + state->p2->lives->set_last(now, 3); + state->p2->size->set_last(now, 4); -void main() { - col = vec4(1.0, 0, 0, 1.0); -} -)s"); + gui->get_display_size(state, now); // update gui related parameters - auto shader = renderer->add_shader( { vshader_src, fshader_src } ); + // initialize the game enqueuing a physics reset should happen. + log::log(INFO << "initializing the physics state..."); + std::vector start_event{PongEvent{0, PongEvent::START}}; + phys.process_input(state, state->p1, start_event, loop, now); + loop->reach_time(now, state); - auto pos1 = Eigen::Affine3f::Identity(); - pos1.prescale(Eigen::Vector3f(200.0f, 200.0f, 1.0f)); - //pos1.prerotate(Eigen::AngleAxisf(45.0f * math::PI / 180.0f, Eigen::Vector3f::UnitZ())); - pos1.pretranslate(Eigen::Vector3f(400.0f, 400.0f, 0.0f)); + std::vector inputs; - auto pos_in = shader->new_uniform_input( - "pos", pos1.matrix() - ); + log::log(INFO << "starting game loop..."); - auto proj_in = shader->new_uniform_input( - "proj", Eigen::Affine3f::Identity().matrix() - ); + // this is the game loop, running while both players live! + while (running and + state->p1->lives->get(now) > 0 and + state->p2->lives->get(now) > 0) { - auto quad = renderer->add_mesh_geometry(renderer::resources::MeshData::make_quad()); - renderer::Renderable obj1 { - pos_in.get(), - quad.get(), - true, - true, - }; + log::log(DBG << "=================== LOOPING ===================="); - renderer::Renderable projsetup { - proj_in.get(), - nullptr, - }; + auto loop_start = Clock::now(); - renderer::RenderPass pass { - { projsetup, obj1 }, - renderer->get_display_target(), - }; + gui->get_display_size(state, now); + // process the input for both players + // player 1 can be AI or human. - glDepthFunc(GL_LEQUAL); - glDepthRange(0.0, 1.0); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (human_player) { + phys.process_input(state, state->p1, + inputs, loop, now); + } + else { + phys.process_input(state, state->p1, + ai.get_inputs(state->p1, state->ball, now), + loop, now); + } - window.add_resize_callback( - [&] (size_t w, size_t h) { - Eigen::Matrix4f proj_matrix = renderer::util::ortho_matrix_f(0.0f, w, - 0.0f, h, - 0.0f, 1.0f); + phys.process_input(state, state->p2, + ai.get_inputs(state->p2, state->ball, now), + loop, now); - shader->update_uniform_input(proj_in.get(), "proj", proj_matrix); - } - ); + // paddle x positions + state->p1->paddle_x = 0; + state->p2->paddle_x = state->display_boundary[0] - 1; + + // evaluate the event queue to reach the desired game time! + loop->reach_time(now, state); + + // draw the new state + gui->draw(state, now); - while (!window.should_close()) { - renderer->render(pass); - window.update(); - renderer::opengl::GlContext::check_error(); + /* + int pos = 1; + mvprintw(pos++, state->display_boundary[0]/2 + 10, "Enqueued events:"); + for (const auto &e : loop->get_queue().get_event_queue().get_sorted_events()) { + mvprintw(pos++, state->display_boundary[0]/2 + 10, + "%f: %s", + e->get_time().to_double(), + e->get_eventclass()->id().c_str()); + } + */ + + // get the input after the current frame, because the get_inputs + // implicitly updates the screen. if this is done too early, + // the screen will be updated to be empty -> flickering. + // TODO: take terminal input without ncurses, if on tty? + // e.g. 'q' to close it? + inputs = gui->get_inputs(state->p1); + + if (gui->exit_requested) { + running = false; + } + + // handle timing for screen refresh and simulation advancement + using dt_s_t = std::chrono::duration>; + using dt_ms_t = std::chrono::duration; + + // microseconds per frame + // 30fps = 1s/30 = 1000000us/30 per frame + constexpr static std::chrono::microseconds per_frame = 33333us; + constexpr static curve::time_t per_frame_s = std::chrono::duration_cast(per_frame).count(); + + if (speed == timescale::NOSLEEP) { + // increase the simulation loop time a bit + SDL_Delay(5); + } + + dt_ms_t dt_us = Clock::now() - loop_start; + + if (speed != timescale::NOSLEEP) { + dt_ms_t wait_time = per_frame - dt_us; + + if (wait_time > dt_ms_t::zero()) { + SDL_Delay(wait_time.count()); + } + } + + switch (speed) { + case timescale::NOSLEEP: + // advance the frame calculation time only, + // because we didn't sleep + now += std::chrono::duration_cast(dt_us).count(); + break; + + case timescale::REALTIME: + // advance the game time in seconds + now += per_frame_s; + break; + + case timescale::SLOW: + now += per_frame_s / 10; + break; + + case timescale::FAST: + now += per_frame_s * 4; + break; + } + } } } diff --git a/libopenage/pyinterface/exctranslate.cpp b/libopenage/pyinterface/exctranslate.cpp index 9e46889020..53fe792dc9 100644 --- a/libopenage/pyinterface/exctranslate.cpp +++ b/libopenage/pyinterface/exctranslate.cpp @@ -49,7 +49,9 @@ void translate_exc_cpp_to_py() { // when we reach this, cython caught an error. // and we're now in the handler. - // to continue, first rethrow the exception so we can analyze it: + // to continue, first rethrow the exception so we can analyze it + // and to restore its context in case it's an exception that doesn't + // store useful information. throw; } catch (PyException &exc) { @@ -77,28 +79,9 @@ void translate_exc_cpp_to_py() { // = insert it in pythons backtrace raise_cpp_error(&exc); } - // we also need to catch std::exception so that rethrown causes are handled - // properly. otherwise they just crash everything. not good. - catch (const std::exception &exc) { - // the std::exception doesn't contain a stack trace... - // as we're at a special place we must not generate the current - // stack trace either. and we _must not_ store the causing - // exception, as the whole point of this code is to get rid - // of std::exceptions. - Error error{ - ERR << "std::exception: " - << util::demangle(typeid(exc).name()) - << ": " - << exc.what(), - false, - false - }; - - raise_cpp_error(&error); - } /* - * all other exceptions are more than unexpected; + * all other exceptions are more than unexpected; even std::exception. * they don't contain any useful stack trace information, * so the safest course of action is not to catch them. * That way, terminate() is called, where we can analyze the stack diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index 1aa85a4412..cd1caedfe6 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -21,6 +21,7 @@ class Geometry; class Texture2d; class UniformInput; + /// The abstract base for a render target. class RenderTarget : public std::enable_shared_from_this { protected: @@ -50,6 +51,17 @@ struct Renderable { bool depth_test = true; }; + +/// Simplified form of Renderable, which is just an update for a shader. +struct ShaderUpdate : Renderable { + ShaderUpdate(std::shared_ptr const& unif_in) + : Renderable{unif_in, nullptr} {} + + ShaderUpdate(std::shared_ptr && unif_in) + : Renderable{std::move(unif_in), nullptr} {} +}; + + /// A render pass is a series of draw calls represented by renderables that output into the given render target. class RenderPass { protected: @@ -66,6 +78,7 @@ class RenderPass { std::shared_ptr target; }; + /// The renderer. This class is used for performing all graphics operations. It is abstract and has implementations /// for various low-level graphics APIs like OpenGL. class Renderer { diff --git a/libopenage/util/fixed_point.h b/libopenage/util/fixed_point.h index 5c9b2b4880..0658c20ea6 100644 --- a/libopenage/util/fixed_point.h +++ b/libopenage/util/fixed_point.h @@ -1,4 +1,4 @@ -// Copyright 2015-2018 the openage authors. See copying.md for legal info. +// Copyright 2015-2019 the openage authors. See copying.md for legal info. #pragma once @@ -10,6 +10,7 @@ #include #include +#include "compiler.h" #include "misc.h" namespace openage { @@ -381,6 +382,14 @@ class FixedPoint { // I/O operators friend std::ostream &operator <<(std::ostream &os, const FixedPoint &n) { os << std::fixed << std::setprecision(FixedPoint::approx_decimal_places) << double(n); + + if (unlikely(n == FixedPoint::max_value())) { + os << "[MAX]"; + } + else if (unlikely(n != 0 and n == FixedPoint::min_value())) { + os << "[MIN]"; + } + return os; } From 50e3344eb564ca22c7e34127aa0e1d606e1da674 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sat, 16 Feb 2019 03:06:40 +0100 Subject: [PATCH 09/28] pong: render moving ball --- libopenage/main/tests/gui.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libopenage/main/tests/gui.cpp b/libopenage/main/tests/gui.cpp index 7fbf65018c..4548aafecc 100644 --- a/libopenage/main/tests/gui.cpp +++ b/libopenage/main/tests/gui.cpp @@ -237,12 +237,12 @@ void Gui::draw(const std::shared_ptr &state, const curve::time_t &now // draw the ball //this->draw_ball(state->ball->position->get(now), "M"); // TODO: use "⚽" - //auto ball_pos = state->ball->position->get(now); - //auto pos1 = Eigen::Affine3f::Identity(); - //pos1.prescale(Eigen::Vector3f(200.0f, 200.0f, 1.0f)); - //pos1.prerotate(Eigen::AngleAxisf(45.0f * math::PI / 180.0f, Eigen::Vector3f::UnitZ())); - //pos1.pretranslate(Eigen::Vector3f(ball_pos[0], ball_pos[1], 0.0f)); - //this->ball->unif_in->update_uniform_input("pos", ball_pos_matrix); + auto ball_pos = state->ball->position->get(now); + auto ball_pos_matrix = Eigen::Affine3f::Identity(); + ball_pos_matrix.prescale(Eigen::Vector3f(200.0f, 200.0f, 1.0f)); + //ball_pos_matrix.prerotate(Eigen::AngleAxisf(45.0f * math::PI / 180.0f, Eigen::Vector3f::UnitZ())); + ball_pos_matrix.pretranslate(Eigen::Vector3f(ball_pos[0], ball_pos[1], 0.0f)); + this->ball.unif_in->update("pos", ball_pos_matrix.matrix()); this->renderer->render(this->pass); this->window.update(); From e69e5c27754e2e9e1a2038571cf21b9503089fb0 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sun, 17 Feb 2019 23:42:15 +0100 Subject: [PATCH 10/28] event/loop: pretty-print event target name --- libopenage/event/loop.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libopenage/event/loop.cpp b/libopenage/event/loop.cpp index ba34d10fe5..514095b90e 100644 --- a/libopenage/event/loop.cpp +++ b/libopenage/event/loop.cpp @@ -142,7 +142,7 @@ int Loop::execute_events(const curve::time_t &time_until, event->set_time(new_time); log::log(DBG << "Loop: repeating event \"" << event->get_eventclass()->id() - << "\" on target \"" << target->id() + << "\" on target \"" << target->idstr() << "\" will be reenqueued for time t=" << event->get_time()); this->queue.reenqueue(event); @@ -189,7 +189,7 @@ void Loop::update_changes(const std::shared_ptr &state) { if (new_time != std::numeric_limits::min()) { log::log(DBG << "Loop: due to a change, rescheduling event of '" << evnt->get_eventclass()->id() - << "' on target '" << target->id() + << "' on target '" << target->idstr() << "' at time t=" << change.time << " to NEW TIME t=" << new_time); @@ -200,7 +200,7 @@ void Loop::update_changes(const std::shared_ptr &state) { else { log::log(DBG << "Loop: due to a change, canceled execution of '" << evnt->get_eventclass()->id() - << "' on target '" << target->id() + << "' on target '" << target->idstr() << "' at time t=" << change.time); this->queue.remove(evnt); From 919cd25d8f2147d01b5b6170e568d82c582a9228 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Mon, 18 Feb 2019 01:05:59 +0100 Subject: [PATCH 11/28] pong: show paddles --- libopenage/main/tests/gamestate.cpp | 22 ++++-- libopenage/main/tests/gamestate.h | 3 +- libopenage/main/tests/gui.cpp | 101 +++++++++++++++++++--------- libopenage/main/tests/gui.h | 11 ++- libopenage/main/tests/physics.cpp | 56 ++++++++------- libopenage/main/tests/pong.cpp | 30 +++++---- 6 files changed, 146 insertions(+), 77 deletions(-) diff --git a/libopenage/main/tests/gamestate.cpp b/libopenage/main/tests/gamestate.cpp index 7a38637abf..ddfce82a76 100644 --- a/libopenage/main/tests/gamestate.cpp +++ b/libopenage/main/tests/gamestate.cpp @@ -4,9 +4,10 @@ #include +#include "gui.h" +#include "../../log/log.h" #include "../../util/strings.h" - namespace openage::main::tests::pong { @@ -39,8 +40,7 @@ PongPlayer::PongPlayer(const std::shared_ptr &mgr, size_t id) (id << 4) + 5, util::sformat("PongPlayer(%zu).size", id), std::bind(&PongPlayer::child_changes, this, _1))), - _id{id}, - paddle_x{0} {} + _id{id} {} size_t PongPlayer::id() const { @@ -99,6 +99,20 @@ PongState::PongState(const std::shared_ptr &mgr, p1(std::make_shared(mgr, 0)), p2(std::make_shared(mgr, 1)), ball(std::make_shared(mgr, 2)), - gui{gui} {} + area_size( + std::make_shared>( + mgr, + 1001, + "area_size")), + gui{gui} { + + auto size = gui->get_window_size(); + + log::log(INFO << "initializing pong state with area_size=" + << size << "..."); + + // initialize display size with real window size + this->area_size->set_last(0, size); + } } // openage::main::tests::pong diff --git a/libopenage/main/tests/gamestate.h b/libopenage/main/tests/gamestate.h index 8aec21ad40..b221c832eb 100644 --- a/libopenage/main/tests/gamestate.h +++ b/libopenage/main/tests/gamestate.h @@ -46,7 +46,6 @@ class PongPlayer : public event::EventTarget { std::shared_ptr> size; size_t _id; - float paddle_x; private: void child_changes(const curve::time_t &time); @@ -77,7 +76,7 @@ class PongState : public event::State { std::shared_ptr p1; std::shared_ptr p2; std::shared_ptr ball; - util::Vector2d display_boundary; + std::shared_ptr> area_size; std::shared_ptr gui; }; diff --git a/libopenage/main/tests/gui.cpp b/libopenage/main/tests/gui.cpp index 4548aafecc..f4e6ca6634 100644 --- a/libopenage/main/tests/gui.cpp +++ b/libopenage/main/tests/gui.cpp @@ -111,15 +111,6 @@ void main() { auto shader = renderer->add_shader( { vshader_src, fshader_src } ); - auto pos1 = Eigen::Affine3f::Identity(); - pos1.prescale(Eigen::Vector3f(200.0f, 200.0f, 1.0f)); - //pos1.prerotate(Eigen::AngleAxisf(45.0f * math::PI / 180.0f, Eigen::Vector3f::UnitZ())); - pos1.pretranslate(Eigen::Vector3f(400.0f, 400.0f, 0.0f)); - - auto ball_props = shader->new_uniform_input( - "pos", pos1.matrix() - ); - auto proj_in = shader->new_uniform_input( "proj", Eigen::Affine3f::Identity().matrix() ); @@ -127,7 +118,27 @@ void main() { auto quad = renderer->add_mesh_geometry(renderer::resources::MeshData::make_quad()); this->ball = renderer::Renderable{ - ball_props, + shader->new_uniform_input( + "pos", Eigen::Affine3f::Identity().matrix() + ), + quad, + true, + true, + }; + + this->p1paddle = renderer::Renderable{ + shader->new_uniform_input( + "pos", Eigen::Affine3f::Identity().matrix() + ), + quad, + true, + true, + }; + + this->p2paddle = renderer::Renderable{ + shader->new_uniform_input( + "pos", Eigen::Affine3f::Identity().matrix() + ), quad, true, true, @@ -136,7 +147,7 @@ void main() { auto set_projmatrix = renderer::ShaderUpdate{proj_in}; this->pass = renderer::RenderPass{ - { std::move(set_projmatrix), this->ball }, + { std::move(set_projmatrix), this->ball, this->p1paddle, this->p2paddle }, this->renderer->get_display_target(), }; @@ -150,25 +161,22 @@ void main() { 0.0f, w, 0.0f, h, 0.0f, 1.0f ); - shader->update_uniform_input(proj_in, "proj", proj_matrix); + proj_in->update("proj", proj_matrix); + + for (auto &cb : this->resize_callbacks) { + cb(w, h); + } } ); } -void Gui::get_display_size(const std::shared_ptr &state, - const curve::time_t &/*now*/) { - // TODO: make the display_boundary a curve as well. - - - // TODO asdf actually get graphical display size - - state->display_boundary[0] = 80; - state->display_boundary[1] = 60; -} +void Gui::draw(const std::shared_ptr &state, const curve::time_t &now) { + constexpr float ball_size = 50.0f; + constexpr float paddle_width = 20.0f; -void Gui::draw(const std::shared_ptr &state, const curve::time_t &now) { + auto screen_size = state->area_size->get(now); // TODO draw score //mvprintw(xpos, @@ -186,12 +194,12 @@ void Gui::draw(const std::shared_ptr &state, const curve::time_t &now mvprintw(2, 1, "P1: %f, %f, %i", state->p1->position->get(now), - state->p1->paddle_x, + state->display_size->get(now)[0], state->p1->state->get(now).state); mvprintw(3, 1, "P2: %f, %f, %i", state->p2->position->get(now), - state->p2->paddle_x, + state->display_size->get(now)[0], state->p2->state->get(now).state); */ @@ -220,41 +228,59 @@ void Gui::draw(const std::shared_ptr &state, const curve::time_t &now // draw player 1 paddle for (int i = -state->p1->size->get(now) / 2; i < state->p1->size->get(now) / 2; i++) { - //mvprintw(state->p1->position->get(now) + i, state->p1->paddle_x, "|"); + //mvprintw(state->p1->position->get(now) + i, state->display_size->get(now)[0], "|"); } // draw player 2 paddle for (int i = -state->p2->size->get(now) / 2; i < state->p2->size->get(now) / 2; i++) { - //mvprintw(state->p2->position->get(now) + i, state->p2->paddle_x, "|"); + //mvprintw(state->p2->position->get(now) + i, state->display_size->get(now)[0], "|"); } - // ball position prediction 10s into the future + // ball position prediction display 10s into the future for (int i = 1; i < 100; ++i) { auto i_as_ctt = curve::time_t::from_double(i/10.0); //this->draw_ball(state->ball->position->get(now + i_as_ctt), "x"); } // draw the ball - //this->draw_ball(state->ball->position->get(now), "M"); // TODO: use "⚽" - + // TODO: use "⚽" auto ball_pos = state->ball->position->get(now); auto ball_pos_matrix = Eigen::Affine3f::Identity(); - ball_pos_matrix.prescale(Eigen::Vector3f(200.0f, 200.0f, 1.0f)); + ball_pos_matrix.prescale(Eigen::Vector3f(ball_size, ball_size, 1.0f)); //ball_pos_matrix.prerotate(Eigen::AngleAxisf(45.0f * math::PI / 180.0f, Eigen::Vector3f::UnitZ())); ball_pos_matrix.pretranslate(Eigen::Vector3f(ball_pos[0], ball_pos[1], 0.0f)); this->ball.unif_in->update("pos", ball_pos_matrix.matrix()); + auto p1_pos = state->p1->position->get(now); + auto p1_size = state->p1->size->get(now); + auto p1_pos_matrix = Eigen::Affine3f::Identity(); + p1_pos_matrix.prescale(Eigen::Vector3f(paddle_width, p1_size, 1.0f)); + p1_pos_matrix.pretranslate(Eigen::Vector3f(0, p1_pos, 0.0f)); + this->p1paddle.unif_in->update("pos", p1_pos_matrix.matrix()); + + auto p2_pos = state->p2->position->get(now); + auto p2_size = state->p2->size->get(now); + auto p2_pos_matrix = Eigen::Affine3f::Identity(); + p2_pos_matrix.prescale(Eigen::Vector3f(paddle_width, p2_size, 1.0f)); + p2_pos_matrix.pretranslate(Eigen::Vector3f(screen_size[0], p2_pos, 0.0f)); + this->p2paddle.unif_in->update("pos", p2_pos_matrix.matrix()); + this->renderer->render(this->pass); this->window.update(); renderer::opengl::GlContext::check_error(); this->exit_requested = window.should_close(); if (this->exit_requested) { - log::log(INFO << "should exit"); + log::log(INFO << "game exit requested due to window close"); } } +const util::Vector2s &Gui::get_window_size() const { + return this->window.get_size(); +} + + void Gui::log(const std::string &msg) { log::log(INFO << "Gui::log: " << msg); @@ -265,4 +291,15 @@ void Gui::log(const std::string &msg) { this->log_msgs.push_front(msg); } + +void Gui::add_resize_callback(const renderer::Window::resize_cb_t& cb) { + this->resize_callbacks.push_back(cb); +} + + +void Gui::clear_resize_callbacks() { + this->resize_callbacks.clear(); +} + + } // openage::main::tests::pong diff --git a/libopenage/main/tests/gui.h b/libopenage/main/tests/gui.h index 6531749c62..80fbb3c934 100644 --- a/libopenage/main/tests/gui.h +++ b/libopenage/main/tests/gui.h @@ -23,13 +23,16 @@ class Gui { Gui(); const std::vector &get_inputs(const std::shared_ptr &player); - void get_display_size(const std::shared_ptr &state, - const curve::time_t &now); void draw(const std::shared_ptr &state, const curve::time_t &now); + const util::Vector2s &get_window_size() const; + void log(const std::string &msg); + void add_resize_callback(const renderer::Window::resize_cb_t&); + void clear_resize_callbacks(); + bool exit_requested = false; private: @@ -41,8 +44,12 @@ class Gui { // rendering objects renderer::Renderable ball; + renderer::Renderable p1paddle; + renderer::Renderable p2paddle; renderer::RenderPass pass; + + std::vector resize_callbacks; }; } // openage::main::tests::pong diff --git a/libopenage/main/tests/physics.cpp b/libopenage/main/tests/physics.cpp index d17267a397..5022400d81 100644 --- a/libopenage/main/tests/physics.cpp +++ b/libopenage/main/tests/physics.cpp @@ -45,6 +45,8 @@ class BallReflectWall : public event::DependencyEventClass { // get speed and position to insert new movement keyframe auto speed = speedcurve->get(now); auto pos = positioncurve->get(now); + auto screen_size = state->area_size->get(now); + speed[1] *= -1.0; state->ball->speed->set_last(now, speed); state->ball->position->set_last(now, pos); @@ -56,7 +58,7 @@ class BallReflectWall : public event::DependencyEventClass { curve::time_t ty = 0; if (speed[1] > 0) { ty = curve::time_t::from_double( - (state->display_boundary[1] - pos[1]) / speed[1] + (screen_size[1] - pos[1]) / speed[1] ); } else if (speed[1] < 0) { @@ -77,6 +79,7 @@ class BallReflectWall : public event::DependencyEventClass { auto speed = state->ball->speed->get(now); auto pos = positioncurve->get(now); + auto screen_size = state->area_size->get(now); if (speed[1] == 0) { return std::numeric_limits::max(); @@ -84,7 +87,7 @@ class BallReflectWall : public event::DependencyEventClass { curve::time_t ty = 0; if (speed[1] > 0) { - ty = curve::time_t::from_double((state->display_boundary[1] - pos[1]) / speed[1]); + ty = curve::time_t::from_double((screen_size[1] - pos[1]) / speed[1]); } else if (speed[1] < 0) { ty = curve::time_t::from_double(pos[1] / -speed[1]); @@ -131,6 +134,7 @@ class BallReflectPanel : public event::DependencyEventClass { auto pos = state->ball->position->get(now); auto speed = state->ball->speed->get(now); + auto screen_size = state->area_size->get(now); static int cnt = 0; util::FString str; @@ -151,7 +155,7 @@ class BallReflectPanel : public event::DependencyEventClass { Physics::reset(state, mgr, now); } - else if (pos[0] >= state->display_boundary[0] - 1 and + else if (pos[0] >= screen_size[0] - 1 and speed[0] > 0 and (pos[1] < state->p2->position->get(now) - state->p2->size->get(now) / 2 or pos[1] > state->p2->position->get(now) + state->p2->size->get(now) / 2)) { @@ -164,7 +168,7 @@ class BallReflectPanel : public event::DependencyEventClass { Physics::reset(state, mgr, now); } - else if (pos[0] >= state->display_boundary[0]- 1 || pos[0] <= 1) { + else if (pos[0] >= screen_size[0]- 1 || pos[0] <= 1) { speed[0] *= -1; state->ball->speed->set_last(now, speed); state->ball->position->set_last(now, pos); @@ -172,7 +176,7 @@ class BallReflectPanel : public event::DependencyEventClass { curve::time_t ty = 0; if (speed[0] > 0) { - ty = curve::time_t::from_double((state->display_boundary[0] - pos[0]) / speed[0]); + ty = curve::time_t::from_double((screen_size[0] - pos[0]) / speed[0]); } else if (speed[0] < 0) { ty = curve::time_t::from_double(pos[0] / -speed[0]); @@ -180,7 +184,7 @@ class BallReflectPanel : public event::DependencyEventClass { auto hit_pos = pos + speed * ty.to_double(); if (speed[0] > 0) { - hit_pos[0] = state->display_boundary[0]; + hit_pos[0] = screen_size[0]; } else { hit_pos[0] = 0; @@ -198,6 +202,7 @@ class BallReflectPanel : public event::DependencyEventClass { auto speed = state->ball->speed->get(now); auto pos = positioncurve->get(now); + auto screen_size = state->area_size->get(now); if (speed[0] == 0) return std::numeric_limits::max(); @@ -205,7 +210,7 @@ class BallReflectPanel : public event::DependencyEventClass { curve::time_t ty = 0; if (speed[0] > 0) { - ty = curve::time_t::from_double((state->display_boundary[0] - pos[0]) / speed[0]); + ty = curve::time_t::from_double((screen_size[0] - pos[0]) / speed[0]); } else if (speed[0] < 0) { ty = curve::time_t::from_double(pos[0] / -speed[0]); @@ -219,7 +224,7 @@ class BallReflectPanel : public event::DependencyEventClass { auto hit_pos = pos + speed * ty.to_double(); if (speed[0] > 0) { - hit_pos[0] = state->display_boundary[0]; + hit_pos[0] = screen_size[0]; } else { hit_pos[0] = 0; @@ -249,10 +254,12 @@ class ResetGame : public event::OnceEventClass { auto state = std::dynamic_pointer_cast(gstate); + auto screen_size = state->area_size->get(now); + // Check if the condition still applies { auto pos = state->ball->position->get(now); - if (pos[0] > 0 && pos[0] < state->display_boundary[0]) { + if (pos[0] > 0 && pos[0] < screen_size[0]) { // the gamestate is still valid - there is no need to reset throw Error(ERR << "reset invoked even though unnecessary"); } @@ -261,17 +268,18 @@ class ResetGame : public event::OnceEventClass { // move ball to the center, quickly state->ball->position->set_last(now - curve::time_t::from_double(0.01), state->ball->position->get(now)); - state->ball->position->set_last(now, state->display_boundary / 2); + state->ball->position->set_last(now, screen_size.casted() / 2); // move paddles to center - state->p1->position->set_last(now, state->display_boundary[1] / 2); - state->p2->position->set_last(now, state->display_boundary[1] / 2); + state->p1->position->set_last(now, screen_size[1] / 2); + state->p2->position->set_last(now, screen_size[1] / 2); + // initial speed of the ball: float dirx = (rng::random() % 2) ? 1 : -1; float diry = (rng::random() % 2) ? 1 : -1; auto init_speed = util::Vector2d( - dirx * (10 + (rng::random() % 100) / 4.f), - diry * (0.3 + (rng::random() % 100) / 18.f) + dirx * (screen_size[0] / 15.0 + (rng::random() % (screen_size[0]/10))), + diry * (screen_size[1] / 20.0 + (rng::random() % (screen_size[1]/20))) ); state->ball->speed->set_last(now, init_speed); @@ -294,7 +302,7 @@ class ResetGame : public event::OnceEventClass { // calculate the wall-hit-times if (init_speed[1] > 0) { - ty = curve::time_t::from_double((state->display_boundary[1] - pos[1]) / init_speed[1]); + ty = curve::time_t::from_double((screen_size[1] - pos[1]) / init_speed[1]); } else if (init_speed[1] < 0) { ty = curve::time_t::from_double(pos[1] / -init_speed[1]); @@ -303,7 +311,7 @@ class ResetGame : public event::OnceEventClass { // currently never happens, but this would be a non-vertically-moving ball // fallback to calculating panel-hit-times if (init_speed[0] > 0) { - ty = curve::time_t::from_double((state->display_boundary[0] - pos[0]) / init_speed[0]); + ty = curve::time_t::from_double((screen_size[0] - pos[0]) / init_speed[0]); } else { ty = curve::time_t::from_double(pos[0] / -init_speed[0]); @@ -321,12 +329,10 @@ class ResetGame : public event::OnceEventClass { }; -void Physics::init(const std::shared_ptr &gstate, +void Physics::init(const std::shared_ptr &state, const std::shared_ptr &loop, const curve::time_t &now) { - auto state = std::dynamic_pointer_cast(gstate); - loop->add_event_class(std::make_shared()); loop->add_event_class(std::make_shared()); loop->add_event_class(std::make_shared()); @@ -357,8 +363,8 @@ void Physics::process_input(const std::shared_ptr &state, // seconds into the future constexpr static auto predicted_movement_time = curve::time_t::from_double(5.0); - // lines per second - constexpr static double movement_speed = 8.0; + // pixels per second for paddle movement + constexpr static double movement_speed = 250.0; for (auto& evnt : events) { @@ -368,6 +374,8 @@ void Physics::process_input(const std::shared_ptr &state, // log the new input in the state curve player->state->set_last(now, evnt); + auto screen_size = state->area_size->get(now); + // if the state is active longer than predicted, // we have to extend the prediction! bool extend_previous_prediction = false; @@ -417,9 +425,9 @@ void Physics::process_input(const std::shared_ptr &state, new_pos = 0; } - if (new_pos > state->display_boundary[1]) { - move_stop_guess = now + ((state->display_boundary[1] - current_pos) / movement_speed); - new_pos = state->display_boundary[1]; + if (new_pos > screen_size[1]) { + move_stop_guess = now + ((screen_size[1] - current_pos) / movement_speed); + new_pos = screen_size[1]; } PongEvent set_idle{evnt.player, PongEvent::IDLE}; diff --git a/libopenage/main/tests/pong.cpp b/libopenage/main/tests/pong.cpp index 4325c88155..d83e2933a2 100644 --- a/libopenage/main/tests/pong.cpp +++ b/libopenage/main/tests/pong.cpp @@ -40,7 +40,6 @@ void main(const util::Path& path) { timescale speed = timescale::REALTIME; std::shared_ptr db = nyan::Database::create(); - std::shared_ptr gui = std::make_shared(); db->load( "test.nyan", @@ -60,6 +59,7 @@ void main(const util::Path& path) { nyan::Object test = root->get("test.Test"); log::log(INFO << "nyan read test: " << *test.get("member")); + std::shared_ptr gui = std::make_shared(); bool running = true; @@ -72,15 +72,25 @@ void main(const util::Path& path) { AIInput ai; auto state = std::make_shared(loop, gui); + + gui->clear_resize_callbacks(); + gui->add_resize_callback( + [&] (size_t w, size_t h) { + log::log(INFO << "update pong area size=(" + << w << "," << h << ")..."); + + state->area_size->set_last(now, {w, h}); + } + ); + Physics::init(state, loop, now); + // initialize player properties state->p1->lives->set_last(now, 3); - state->p1->size->set_last(now, 4); + state->p1->size->set_last(now, 200); state->p2->lives->set_last(now, 3); - state->p2->size->set_last(now, 4); - - gui->get_display_size(state, now); // update gui related parameters + state->p2->size->set_last(now, 200); // initialize the game enqueuing a physics reset should happen. log::log(INFO << "initializing the physics state..."); @@ -101,8 +111,6 @@ void main(const util::Path& path) { auto loop_start = Clock::now(); - gui->get_display_size(state, now); - // process the input for both players // player 1 can be AI or human. @@ -120,10 +128,6 @@ void main(const util::Path& path) { ai.get_inputs(state->p2, state->ball, now), loop, now); - // paddle x positions - state->p1->paddle_x = 0; - state->p2->paddle_x = state->display_boundary[0] - 1; - // evaluate the event queue to reach the desired game time! loop->reach_time(now, state); @@ -132,9 +136,9 @@ void main(const util::Path& path) { /* int pos = 1; - mvprintw(pos++, state->display_boundary[0]/2 + 10, "Enqueued events:"); + mvprintw(pos++, state->area_size->get(now)[0]/2 + 10, "Enqueued events:"); for (const auto &e : loop->get_queue().get_event_queue().get_sorted_events()) { - mvprintw(pos++, state->display_boundary[0]/2 + 10, + mvprintw(pos++, state->area_size->get(now)[0]/2 + 10, "%f: %s", e->get_time().to_double(), e->get_eventclass()->id().c_str()); From 8a5aa036311bb90d10813f7092493770cead8491 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Wed, 27 Feb 2019 00:38:39 +0100 Subject: [PATCH 12/28] event: aicontroller must emit idle event when doing nothing --- libopenage/event/demo/aicontroller.cpp | 17 ++++++++++------- libopenage/event/demo/aicontroller.h | 18 +++++------------- libopenage/event/demo/main.cpp | 5 ++--- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/libopenage/event/demo/aicontroller.cpp b/libopenage/event/demo/aicontroller.cpp index cb052d01be..dbfcda1a14 100644 --- a/libopenage/event/demo/aicontroller.cpp +++ b/libopenage/event/demo/aicontroller.cpp @@ -4,10 +4,10 @@ namespace openage::event::demo { -const std::vector &AIInput::get_inputs(const std::shared_ptr &player, - const std::shared_ptr &ball, - const curve::time_t &now) { - this->commands.clear(); +std::vector get_ai_inputs(const std::shared_ptr &player, + const std::shared_ptr &ball, + const curve::time_t &now) { + std::vector ret; auto position = player->position->get(now); @@ -16,14 +16,17 @@ const std::vector &AIInput::get_inputs(const std::shared_ptrposition->get(now)[1] > position + player->size->get(now) / 3) { - this->commands.emplace_back(player->id(), PongEvent::DOWN); + ret.emplace_back(player->id(), PongEvent::DOWN); } // Ball is above position else if (ball->position->get(now)[1] < position - player->size->get(now) / 3) { - this->commands.emplace_back(player->id(), PongEvent::UP); + ret.emplace_back(player->id(), PongEvent::UP); + } + else { + ret.emplace_back(player->id(), PongEvent::IDLE); } - return this->commands; + return ret; } } // openage::event::demo diff --git a/libopenage/event/demo/aicontroller.h b/libopenage/event/demo/aicontroller.h index d42e9b4462..892cef3610 100644 --- a/libopenage/event/demo/aicontroller.h +++ b/libopenage/event/demo/aicontroller.h @@ -1,4 +1,4 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #pragma once @@ -8,16 +8,8 @@ namespace openage::event::demo { -class AIInput { -public: - const std::vector &get_inputs( - const std::shared_ptr &player, - const std::shared_ptr &ball, - const curve::time_t &now - ); +std::vector get_ai_inputs(const std::shared_ptr &player, + const std::shared_ptr &ball, + const curve::time_t &now); -private: - std::vector commands; -}; - -} // openage::event::demo +} // openage::event::demo diff --git a/libopenage/event/demo/main.cpp b/libopenage/event/demo/main.cpp index 74df060402..11f0b37ca1 100644 --- a/libopenage/event/demo/main.cpp +++ b/libopenage/event/demo/main.cpp @@ -61,7 +61,6 @@ void curvepong(bool disable_gui, bool no_human) { auto loop = std::make_shared(); curve::time_t now = 1; Physics phys; - AIInput ai; auto state = std::make_shared(loop, enable_gui #if WITH_NCURSES @@ -118,12 +117,12 @@ void curvepong(bool disable_gui, bool no_human) { else { phys.process_input(state, state->p1, - ai.get_inputs(state->p1, state->ball, now), + get_ai_inputs(state->p1, state->ball, now), loop, now); } phys.process_input(state, state->p2, - ai.get_inputs(state->p2, state->ball, now), + get_ai_inputs(state->p2, state->ball, now), loop, now); // paddle x positions From 9d39759e8b2cd1fe510d7caa38a77b7c9a09c717 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Wed, 27 Feb 2019 00:43:34 +0100 Subject: [PATCH 13/28] advanced pong ai that predicts hit points --- libopenage/main/tests/aicontroller.cpp | 90 ++++++++++++++++++++++---- libopenage/main/tests/aicontroller.h | 20 ++---- libopenage/main/tests/physics.cpp | 6 +- libopenage/main/tests/pong.cpp | 7 +- 4 files changed, 90 insertions(+), 33 deletions(-) diff --git a/libopenage/main/tests/aicontroller.cpp b/libopenage/main/tests/aicontroller.cpp index 9e2df26004..046669e478 100644 --- a/libopenage/main/tests/aicontroller.cpp +++ b/libopenage/main/tests/aicontroller.cpp @@ -6,26 +6,88 @@ namespace openage::main::tests::pong { -const std::vector &AIInput::get_inputs(const std::shared_ptr &player, - const std::shared_ptr &ball, - const curve::time_t &now) { - this->commands.clear(); +std::vector get_ai_inputs(const std::shared_ptr &player, + const std::shared_ptr &ball, + const std::shared_ptr> &area_size_curve, + const curve::time_t &now, + bool right_player) { + std::vector ret; - auto position = player->position->get(now); + auto player_pos = player->position->get(now); + auto player_size = player->size->get(now); + auto ball_pos = ball->position->get(now); + auto speed = ball->speed->get(now); - // Yes i know, there is /3 used - instead of the logical /2 - this is to - // create a small safety boundary of 1/3 for enhanced fancyness + auto area_size = area_size_curve->get(now); + double area_width = area_size[0]; + double area_height = area_size[1]; - // Ball is below position - if (ball->position->get(now)[1] > position + player->size->get(now) / 3) { - this->commands.push_back(PongEvent{player->id(), PongEvent::DOWN}); + curve::time_t hit_time; + util::Vector2d hit_pos; + + // calculate ball trajectory + // extrapolate trajectory to player's panel wall + // follow reflections at walls + // move panel to predicted hit position of panel wall + + while (true) { + curve::time_t ty_hit = 0, tx_hit = 0; + + if (speed[0] == 0) { + tx_hit = std::numeric_limits::max(); + } else if (speed[0] > 0) { + tx_hit = curve::time_t::from_double((area_width - ball_pos[0]) / speed[0]); + } else if (speed[0] < 0) { + tx_hit = curve::time_t::from_double(ball_pos[0] / -speed[0]); + } + + if (speed[1] == 0) { + ty_hit = std::numeric_limits::max(); + } else if (speed[1] > 0) { + ty_hit = curve::time_t::from_double((area_height - ball_pos[1]) / speed[1]); + } else if (speed[1] < 0) { + ty_hit = curve::time_t::from_double(ball_pos[1] / -speed[1]); + } + + // actual hit has lowest time: + hit_time = std::min(ty_hit, tx_hit); + hit_pos = ball_pos + (speed * hit_time.to_double()); + + // continue calculating until panel hit + if (ty_hit < tx_hit) { + speed[1] *= -1; + ball_pos = hit_pos; + continue; + } + else { + // stop the iteration if the ball is moving towards us + // i.e. it is not hitting the panel on the oposite wall. + if (right_player and speed[0] > 0) { + break; + } + else if (not right_player and speed[0] < 0) { + break; + } + + speed[0] *= -1; + ball_pos = hit_pos; + continue; + } + } + + // move the panel towards the ball hit pos, but only + // if the ball does not hit the middle piece of the 3-divided panel + if (hit_pos[1] > player_pos + (player_size / 3)) { + ret.push_back(PongEvent{player->id(), PongEvent::DOWN}); + } + else if (hit_pos[1] < player_pos - (player_size / 3)) { + ret.push_back(PongEvent{player->id(), PongEvent::UP}); } - // Ball is above position - else if (ball->position->get(now)[1] < position - player->size->get(now) / 3) { - this->commands.push_back(PongEvent{player->id(), PongEvent::UP}); + else { + ret.push_back(PongEvent{player->id(), PongEvent::IDLE}); } - return this->commands; + return ret; } } // openage::main::tests::pong diff --git a/libopenage/main/tests/aicontroller.h b/libopenage/main/tests/aicontroller.h index 9d84528110..ea8376fd2e 100644 --- a/libopenage/main/tests/aicontroller.h +++ b/libopenage/main/tests/aicontroller.h @@ -4,21 +4,15 @@ #include "gamestate.h" -#include - namespace openage::main::tests::pong { -class AIInput { -public: - const std::vector &get_inputs( - const std::shared_ptr &player, - const std::shared_ptr &ball, - const curve::time_t &now - ); - -private: - std::vector commands; -}; +std::vector get_ai_inputs( + const std::shared_ptr &player, + const std::shared_ptr &ball, + const std::shared_ptr> &area_size, + const curve::time_t &now, + bool right_player +); } // openage::main::tests::pong diff --git a/libopenage/main/tests/physics.cpp b/libopenage/main/tests/physics.cpp index 5022400d81..87b13f563b 100644 --- a/libopenage/main/tests/physics.cpp +++ b/libopenage/main/tests/physics.cpp @@ -278,8 +278,8 @@ class ResetGame : public event::OnceEventClass { float dirx = (rng::random() % 2) ? 1 : -1; float diry = (rng::random() % 2) ? 1 : -1; auto init_speed = util::Vector2d( - dirx * (screen_size[0] / 15.0 + (rng::random() % (screen_size[0]/10))), - diry * (screen_size[1] / 20.0 + (rng::random() % (screen_size[1]/20))) + dirx * (screen_size[0] / 2.0 + (rng::random() % (screen_size[0]/4))), + diry * (screen_size[1] / 3.0 + (rng::random() % (screen_size[1]/5))) ); state->ball->speed->set_last(now, init_speed); @@ -364,7 +364,7 @@ void Physics::process_input(const std::shared_ptr &state, constexpr static auto predicted_movement_time = curve::time_t::from_double(5.0); // pixels per second for paddle movement - constexpr static double movement_speed = 250.0; + constexpr static double movement_speed = 350.0; for (auto& evnt : events) { diff --git a/libopenage/main/tests/pong.cpp b/libopenage/main/tests/pong.cpp index d83e2933a2..31a8909c94 100644 --- a/libopenage/main/tests/pong.cpp +++ b/libopenage/main/tests/pong.cpp @@ -69,7 +69,6 @@ void main(const util::Path& path) { auto loop = std::make_shared(); curve::time_t now = 0; Physics phys; - AIInput ai; auto state = std::make_shared(loop, gui); @@ -120,12 +119,14 @@ void main(const util::Path& path) { } else { phys.process_input(state, state->p1, - ai.get_inputs(state->p1, state->ball, now), + get_ai_inputs(state->p1, state->ball, + state->area_size, now, false), loop, now); } phys.process_input(state, state->p2, - ai.get_inputs(state->p2, state->ball, now), + get_ai_inputs(state->p2, state->ball, + state->area_size, now, true), loop, now); // evaluate the event queue to reach the desired game time! From b0e7e85da04375d132f099a88c2d4c7ba525021f Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Wed, 27 Feb 2019 10:48:42 +0100 Subject: [PATCH 14/28] event: remove unused eventfilter class --- libopenage/event/CMakeLists.txt | 1 - libopenage/event/eventfilter.cpp | 19 ------------------- libopenage/event/eventfilter.h | 27 --------------------------- libopenage/event/eventtarget.h | 6 +++--- libopenage/event/loop.h | 20 +------------------- 5 files changed, 4 insertions(+), 69 deletions(-) delete mode 100644 libopenage/event/eventfilter.cpp delete mode 100644 libopenage/event/eventfilter.h diff --git a/libopenage/event/CMakeLists.txt b/libopenage/event/CMakeLists.txt index b4231385ff..8e6169c19f 100644 --- a/libopenage/event/CMakeLists.txt +++ b/libopenage/event/CMakeLists.txt @@ -1,7 +1,6 @@ add_sources(libopenage event.cpp eventclass.cpp - eventfilter.cpp eventqueue.cpp eventstore.cpp eventtarget.cpp diff --git a/libopenage/event/eventfilter.cpp b/libopenage/event/eventfilter.cpp deleted file mode 100644 index 9b4727f79d..0000000000 --- a/libopenage/event/eventfilter.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2018-2019 the openage authors. See copying.md for legal info. - - -#include "eventfilter.h" - -#include - -namespace openage::event { - -EventFilter::EventFilter(std::function &)> filter) - : - filter{std::move(filter)} {} - - -bool EventFilter::apply(const std::shared_ptr &target) const { - return this->filter(target); -} - -} // openage::event diff --git a/libopenage/event/eventfilter.h b/libopenage/event/eventfilter.h deleted file mode 100644 index 4cfaf6f658..0000000000 --- a/libopenage/event/eventfilter.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2017-2019 the openage authors. See copying.md for legal info. - -#pragma once - -#include -#include - -#include "eventtarget.h" - - -namespace openage::event { - -/** - * Store a filter function that can then be applied on an event target. - */ -class EventFilter { -public: - EventFilter(std::function &)> filter); - - bool apply(const std::shared_ptr &target) const; - -private: - std::function&)> filter; -}; - - -} // namespace openage::event diff --git a/libopenage/event/eventtarget.h b/libopenage/event/eventtarget.h index 0125232f71..5b260a080b 100644 --- a/libopenage/event/eventtarget.h +++ b/libopenage/event/eventtarget.h @@ -1,4 +1,4 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #pragma once @@ -63,9 +63,9 @@ class EventTarget { void changes(const curve::time_t &change_time); /** - * Call this when a keyframe in the underlying container was passed by in time. + * Call this when depending TriggerEventClasses should be invoked. */ - void trigger(const curve::time_t &last_valid_time); + void trigger(const curve::time_t &invoke_time); private: /** Event loop this target is registered to */ diff --git a/libopenage/event/loop.h b/libopenage/event/loop.h index 71860cb1a4..87dc52af09 100644 --- a/libopenage/event/loop.h +++ b/libopenage/event/loop.h @@ -1,4 +1,4 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #pragma once @@ -9,7 +9,6 @@ #include "../curve/curve.h" #include "eventqueue.h" -#include "eventfilter.h" #include "event.h" #include "../log/log.h" @@ -62,15 +61,6 @@ class Loop { const curve::time_t &reference_time, const EventClass::param_map ¶ms=EventClass::param_map({})); - void onfilter(const std::shared_ptr &eventclass, const EventFilter &); - - template - void onfilter(const EventFilter &filter) { - this->onfilter(std::make_shared(), filter); - } - - void register_object(const std::shared_ptr &); - /** * Execute all events that are registered until a certain point in time. */ @@ -110,12 +100,6 @@ class Loop { */ std::unordered_map> classstore; - /** - * Here we store all running filters that shall be applied whenever a new - * obejct is added to our objectstore - */ - std::list filters; - /** * All events are enqueued here. */ @@ -126,8 +110,6 @@ class Loop { * This is useful for event cancelations (so one can't cancel itself). */ std::shared_ptr active_event; - - std::unordered_map> curveindex; }; } // openage::event From 5d3f32104018971da9c77057a4d5138646508e60 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Thu, 28 Feb 2019 20:01:43 +0100 Subject: [PATCH 15/28] event: rename EventTarget->EventEntity and EventClass->EventHandler --- libopenage/curve/value_container.h | 8 +- libopenage/event/CMakeLists.txt | 4 +- libopenage/event/demo/gamestate.h | 10 +-- libopenage/event/demo/main.cpp | 2 +- libopenage/event/demo/physics.cpp | 34 +++---- libopenage/event/event.cpp | 28 +++--- libopenage/event/event.h | 64 ++++++++------ libopenage/event/eventclass.cpp | 50 ----------- .../{eventtarget.cpp => evententity.cpp} | 53 ++++++----- .../event/{eventtarget.h => evententity.h} | 18 ++-- libopenage/event/eventhandler.cpp | 48 ++++++++++ .../event/{eventclass.h => eventhandler.h} | 48 +++++----- libopenage/event/eventqueue.cpp | 88 ++++++++++--------- libopenage/event/eventqueue.h | 32 +++---- libopenage/event/loop.cpp | 78 ++++++++-------- libopenage/event/loop.h | 26 +++--- libopenage/event/tests.cpp | 86 +++++++++--------- libopenage/input/event.h | 2 +- libopenage/main/tests/gamestate.cpp | 4 +- libopenage/main/tests/gamestate.h | 6 +- libopenage/main/tests/physics.cpp | 34 +++---- libopenage/main/tests/pong.cpp | 2 +- 22 files changed, 377 insertions(+), 348 deletions(-) delete mode 100644 libopenage/event/eventclass.cpp rename libopenage/event/{eventtarget.cpp => evententity.cpp} (56%) rename libopenage/event/{eventtarget.h => evententity.h} (77%) create mode 100644 libopenage/event/eventhandler.cpp rename libopenage/event/{eventclass.h => eventhandler.h} (79%) diff --git a/libopenage/curve/value_container.h b/libopenage/curve/value_container.h index 281d638d74..e7e68fa5c6 100644 --- a/libopenage/curve/value_container.h +++ b/libopenage/curve/value_container.h @@ -2,7 +2,7 @@ #pragma once -#include "../event/eventtarget.h" +#include "../event/evententity.h" #include "keyframe_container.h" #include @@ -11,14 +11,14 @@ namespace openage::curve { template -class ValueContainer : public event::EventTarget { +class ValueContainer : public event::EventEntity { public: ValueContainer(const std::shared_ptr &mgr, size_t id, const std::string &idstr="", - const EventTarget::single_change_notifier ¬ifier=nullptr) + const EventEntity::single_change_notifier ¬ifier=nullptr) : - EventTarget(mgr, notifier), + EventEntity(mgr, notifier), container{mgr}, _id{id}, _idstr{idstr}, diff --git a/libopenage/event/CMakeLists.txt b/libopenage/event/CMakeLists.txt index 8e6169c19f..114d5bab1b 100644 --- a/libopenage/event/CMakeLists.txt +++ b/libopenage/event/CMakeLists.txt @@ -1,9 +1,9 @@ add_sources(libopenage event.cpp - eventclass.cpp + evententity.cpp + eventhandler.cpp eventqueue.cpp eventstore.cpp - eventtarget.cpp loop.cpp state.cpp tests.cpp diff --git a/libopenage/event/demo/gamestate.h b/libopenage/event/demo/gamestate.h index 629099fa7d..0ef606da9a 100644 --- a/libopenage/event/demo/gamestate.h +++ b/libopenage/event/demo/gamestate.h @@ -8,7 +8,7 @@ #include "config.h" #include "../state.h" #include "../loop.h" -#include "../eventtarget.h" +#include "../evententity.h" #include "../../curve/continuous.h" #include "../../curve/discrete.h" #include "../../util/strings.h" @@ -39,11 +39,11 @@ class PongEvent { using namespace std::placeholders; -class PongPlayer : public EventTarget { +class PongPlayer : public EventEntity { public: PongPlayer(const std::shared_ptr &mgr, size_t id) : - EventTarget{mgr}, + EventEntity{mgr}, speed(std::make_shared>( mgr, (id << 4) + 1, @@ -98,11 +98,11 @@ class PongPlayer : public EventTarget { }; -class PongBall : public EventTarget { +class PongBall : public EventEntity { public: PongBall(const std::shared_ptr &mgr, size_t id) : - EventTarget{mgr}, + EventEntity{mgr}, speed(std::make_shared>( mgr, (id << 2) + 1, diff --git a/libopenage/event/demo/main.cpp b/libopenage/event/demo/main.cpp index 11f0b37ca1..d1350a126b 100644 --- a/libopenage/event/demo/main.cpp +++ b/libopenage/event/demo/main.cpp @@ -142,7 +142,7 @@ void curvepong(bool disable_gui, bool no_human) { mvprintw(pos++, state->display_boundary[0]/2 + 10, "%f: %s", e->get_time().to_double(), - e->get_eventclass()->id().c_str()); + e->get_eventhandler()->id().c_str()); } gui->update_screen(); diff --git a/libopenage/event/demo/physics.cpp b/libopenage/event/demo/physics.cpp index 51c314026f..e230b2601a 100644 --- a/libopenage/event/demo/physics.cpp +++ b/libopenage/event/demo/physics.cpp @@ -24,9 +24,9 @@ namespace openage::event::demo { -class BallReflectWall : public DependencyEventClass { +class BallReflectWall : public DependencyEventHandler { public: - BallReflectWall() : DependencyEventClass("demo.ball.reflect_wall") {} + BallReflectWall() : DependencyEventHandler("demo.ball.reflect_wall") {} void setup_event(const std::shared_ptr &evnt, const std::shared_ptr &gstate) override { @@ -39,15 +39,15 @@ class BallReflectWall : public DependencyEventClass { evnt->depend_on(state->ball->speed); // TODO add dependency to size of game area - // FIXME: warn if it's not a dependency eventclass + // FIXME: warn if it's not a dependency eventhandler } - // FIXME we REALLY need dependencies to objects i.e. Ball : public EventTarget() + // FIXME we REALLY need dependencies to objects i.e. Ball : public EventEntity() void invoke(Loop &, - const std::shared_ptr &target, + const std::shared_ptr &target, const std::shared_ptr &gstate, const curve::time_t &now, - const EventClass::param_map &/*param*/) override { + const EventHandler::param_map &/*param*/) override { auto positioncurve = std::dynamic_pointer_cast>(target); auto state = std::dynamic_pointer_cast(gstate); @@ -79,7 +79,7 @@ class BallReflectWall : public DependencyEventClass { state->ball->position->set_last(now + ty, pos + (speed * ty.to_double())); } - curve::time_t predict_invoke_time(const std::shared_ptr &target, + curve::time_t predict_invoke_time(const std::shared_ptr &target, const std::shared_ptr &gstate, const curve::time_t &now) override { @@ -114,11 +114,11 @@ class BallReflectWall : public DependencyEventClass { }; -class BallReflectPanel : public DependencyEventClass { +class BallReflectPanel : public DependencyEventHandler { public: BallReflectPanel () : - DependencyEventClass("demo.ball.reflect_panel") {} + DependencyEventHandler("demo.ball.reflect_panel") {} void setup_event(const std::shared_ptr &target, const std::shared_ptr &gstate) override { @@ -136,10 +136,10 @@ class BallReflectPanel : public DependencyEventClass { // FIXME we REALLY need dependencies to objects void invoke(Loop &mgr, - const std::shared_ptr &/*target*/, + const std::shared_ptr &/*target*/, const std::shared_ptr &gstate, const curve::time_t &now, - const EventClass::param_map &/*param*/) override { + const EventHandler::param_map &/*param*/) override { auto state = std::dynamic_pointer_cast(gstate); @@ -212,7 +212,7 @@ class BallReflectPanel : public DependencyEventClass { state->ball->position->set_last(now + ty, hit_pos); } - curve::time_t predict_invoke_time(const std::shared_ptr &target, + curve::time_t predict_invoke_time(const std::shared_ptr &target, const std::shared_ptr &gstate, const curve::time_t &now) override { @@ -263,20 +263,20 @@ class BallReflectPanel : public DependencyEventClass { }; -class ResetGame : public OnceEventClass { +class ResetGame : public OnceEventHandler { public: ResetGame () : - OnceEventClass("demo.reset") {} + OnceEventHandler("demo.reset") {} void setup_event(const std::shared_ptr &/*target*/, const std::shared_ptr &/*state*/) override {} void invoke(Loop &/*mgr*/, - const std::shared_ptr &/*target*/, + const std::shared_ptr &/*target*/, const std::shared_ptr &gstate, const curve::time_t &now, - const EventClass::param_map &/*param*/) override { + const EventHandler::param_map &/*param*/) override { auto state = std::dynamic_pointer_cast(gstate); @@ -351,7 +351,7 @@ class ResetGame : public OnceEventClass { state->ball->position->set_last(now + ty, pos + init_speed * ty.to_double()); } - curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, + curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, const std::shared_ptr &/*state*/, const curve::time_t &old_time) override { return old_time; diff --git a/libopenage/event/event.cpp b/libopenage/event/event.cpp index 06b1831a0a..8d3a1d3178 100644 --- a/libopenage/event/event.cpp +++ b/libopenage/event/event.cpp @@ -4,34 +4,34 @@ #include -#include "eventtarget.h" -#include "eventclass.h" +#include "evententity.h" +#include "eventhandler.h" #include "../log/log.h" #include "../util/hash.h" namespace openage::event { -Event::Event(const std::shared_ptr &target, - const std::shared_ptr &eventclass, - EventClass::param_map params) +Event::Event(const std::shared_ptr &entity, + const std::shared_ptr &eventhandler, + const EventHandler::param_map ¶ms) : - params(std::move(params)), - target{target}, - eventclass{eventclass}, + params(params), + entity{entity}, + eventhandler{eventhandler}, myhash{ - util::hash_combine(std::hash()(target->id()), - std::hash()(eventclass->id())) + util::hash_combine(std::hash()(entity->id()), + std::hash()(eventhandler->id())) } {} -void Event::depend_on(const std::shared_ptr &dependency) { +void Event::depend_on(const std::shared_ptr &dependency) { // TODO: do REPEAT and TRIGGER listen to changes (i.e. have dependents)? // if not, exclude them here and return early. - log::log(DBG << "Registering dependency event from EventClass " - << this->get_eventclass()->id() - << " to EventTarget " << dependency->idstr()); + log::log(DBG << "Registering dependency event from EventHandler " + << this->get_eventhandler()->id() + << " to EventEntity " << dependency->idstr()); dependency->add_dependent(this->shared_from_this()); } diff --git a/libopenage/event/event.h b/libopenage/event/event.h index 75f183c38d..d3e2b90888 100644 --- a/libopenage/event/event.h +++ b/libopenage/event/event.h @@ -4,13 +4,13 @@ #include -#include "eventclass.h" +#include "eventhandler.h" #include "../curve/curve.h" namespace openage::event { class EventQueue; -class EventTarget; +class EventEntity; /** * The actual one event that may be called - it is used to manage the event itself. @@ -18,16 +18,16 @@ class EventTarget; */ class Event : public std::enable_shared_from_this { public: - Event(const std::shared_ptr &trgt, - const std::shared_ptr &eventclass, - EventClass::param_map params); + Event(const std::shared_ptr &trgt, + const std::shared_ptr &eventhandler, + const EventHandler::param_map ¶ms); - const std::weak_ptr &get_target() const { - return this->target; + const std::weak_ptr &get_entity() const { + return this->entity; } - const std::shared_ptr &get_eventclass() const { - return this->eventclass; + const std::shared_ptr &get_eventhandler() const { + return this->eventhandler; } /** @@ -48,17 +48,17 @@ class Event : public std::enable_shared_from_this { this->time = t; } - const EventClass::param_map &get_params() const { + const EventHandler::param_map &get_params() const { return this->params; } /** - * Let this event depend on another an event target. - * When this target is changes, the event is reevaluated. + * Let this event depend on another an event entity. + * When this entity is changes, the event is reevaluated. * - * To be called in the EventClass::setup function. + * To be called in the EventHandler::setup function. */ - void depend_on(const std::shared_ptr &dependency); + void depend_on(const std::shared_ptr &dependency); /** * For sorting events by their trigger time. @@ -66,36 +66,44 @@ class Event : public std::enable_shared_from_this { bool operator <(const Event &other) const; /** - * asdf what? + * When a change happens on an EventEntity (this->entity), + * it needs to be processed and all depending events need reevaluation as well. + * This registers the time so we know the point in time that we + * need to go back to and handle the change. + * When changes happen after `last_change_time` in the same time-reaching-round, + * they can be ignored since the earlies point in time determines all implications. */ - const curve::time_t &get_last_triggered() const { - return this->last_triggered; + void set_last_changed(const curve::time_t &t) { + this->last_change_time = t; } /** - * asdf what? + * Get the time the event was changed the last time. */ - void set_last_triggered(const curve::time_t &t) { - this->last_triggered = t; + const curve::time_t &get_last_changed() const { + return this->last_change_time; } private: /** - * Parameters for the event (determined by its EventClass) + * Parameters for the event (determined by its EventHandler) */ - EventClass::param_map params; + EventHandler::param_map params; - /** The actor that has the event */ - std::weak_ptr target; + /** The actor that this event refers to. */ + std::weak_ptr entity; /** Type of this event. */ - std::shared_ptr eventclass; + std::shared_ptr eventhandler; - /** Time this event occurs/occured */ + /** + * Time this event occurs/occured. + * It establishes the order of events in the EventQueue. + */ curve::time_t time; - /** Time this event was asdf what? */ - curve::time_t last_triggered = curve::time_t::min_value(); + /** Time this event was registered to be changed last. */ + curve::time_t last_change_time = curve::time_t::min_value(); /** Precalculated std::hash for the event */ size_t myhash; diff --git a/libopenage/event/eventclass.cpp b/libopenage/event/eventclass.cpp deleted file mode 100644 index fcc48cb8dd..0000000000 --- a/libopenage/event/eventclass.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2017-2019 the openage authors. See copying.md for legal info. - -#include "eventclass.h" - -#include - -#include "event.h" -#include "eventtarget.h" - -#include "../log/log.h" - -namespace openage::event { - - -EventClass::EventClass(std::string name, const EventClass::trigger_type &type) : - type{type}, - _id{std::move(name)} {} - - -const std::string &EventClass::id() { - return _id; -} - - -DependencyEventClass::DependencyEventClass(const std::string &name) - : - EventClass(name, EventClass::trigger_type::DEPENDENCY) {} - - -DependencyImmediatelyEventClass::DependencyImmediatelyEventClass(const std::string &name) - : - EventClass(name, EventClass::trigger_type::DEPENDENCY_IMMEDIATELY) {} - - -TriggerEventClass::TriggerEventClass(const std::string &name) - : - EventClass(name, EventClass::trigger_type::TRIGGER) {} - - -RepeatEventClass::RepeatEventClass(const std::string &name) - : - EventClass(name, EventClass::trigger_type::REPEAT) {} - - -OnceEventClass::OnceEventClass(const std::string &name) - : - EventClass(name, EventClass::trigger_type::ONCE) {} - - -} // openage::event diff --git a/libopenage/event/eventtarget.cpp b/libopenage/event/evententity.cpp similarity index 56% rename from libopenage/event/eventtarget.cpp rename to libopenage/event/evententity.cpp index 4d071f8c38..0c2b821897 100644 --- a/libopenage/event/eventtarget.cpp +++ b/libopenage/event/evententity.cpp @@ -1,11 +1,11 @@ // Copyright 2017-2019 the openage authors. See copying.md for legal info. -#include "eventtarget.h" +#include "evententity.h" #include #include "event.h" -#include "eventclass.h" +#include "eventhandler.h" #include "loop.h" #include "../log/log.h" @@ -14,12 +14,12 @@ namespace openage::event { -void EventTarget::changes(const curve::time_t &time) { +void EventEntity::changes(const curve::time_t &time) { // This target has some change, so we have to notify all dependents - // that subscribed on this target. + // that subscribed on this entity. log::log(DBG << "Target: processing change request at t=" << time - << " for EventTarget " << this->idstr() << "..."); + << " for EventEntity " << this->idstr() << "..."); if (this->parent_notifier != nullptr) { this->parent_notifier(time); } @@ -28,22 +28,22 @@ void EventTarget::changes(const curve::time_t &time) { for (auto it = this->dependents.begin(); it != this->dependents.end(); ) { auto dependent = it->lock(); if (dependent) { - switch (dependent->get_eventclass()->type) { - case EventClass::trigger_type::DEPENDENCY_IMMEDIATELY: - case EventClass::trigger_type::DEPENDENCY: + switch (dependent->get_eventhandler()->type) { + case EventHandler::trigger_type::DEPENDENCY_IMMEDIATELY: + case EventHandler::trigger_type::DEPENDENCY: // Enqueue a change so that change events, // which depend on this target, will be retriggered log::log(DBG << "Target: change at t=" << time - << " for EventTarget " << this->idstr() << " registered"); + << " for EventEntity " << this->idstr() << " registered"); this->loop->create_change(dependent, time); ++it; break; - case EventClass::trigger_type::ONCE: + case EventHandler::trigger_type::ONCE: // If the dependent is a ONCE-event - // forget the change if the once event has been triggered already. - if (dependent->get_last_triggered() > curve::time_t::min_value()) { + // forget the change if the once event has been notified already. + if (dependent->get_last_changed() > curve::time_t::min_value()) { it = this->dependents.erase(it); } else { this->loop->create_change(dependent, time); @@ -51,9 +51,10 @@ void EventTarget::changes(const curve::time_t &time) { } break; - case EventClass::trigger_type::TRIGGER: - case EventClass::trigger_type::REPEAT: + case EventHandler::trigger_type::TRIGGER: + case EventHandler::trigger_type::REPEAT: // Ignore announced changes for triggered or repeated events + // for that there is the 'DEPENDENCY' events. ++it; break; } @@ -66,7 +67,7 @@ void EventTarget::changes(const curve::time_t &time) { } -void EventTarget::trigger(const curve::time_t &last_valid_time) { +void EventEntity::trigger(const curve::time_t &last_valid_time) { // notify all dependent events that are triggered `on_keyframe` // that the this target changed. // the only events that is "notified" by are TRIGGER. @@ -74,9 +75,9 @@ void EventTarget::trigger(const curve::time_t &last_valid_time) { for (auto it = this->dependents.begin(); it != this->dependents.end(); ) { auto dependent = it->lock(); if (dependent) { - if (dependent->get_eventclass()->type == EventClass::trigger_type::TRIGGER) { + if (dependent->get_eventhandler()->type == EventHandler::trigger_type::TRIGGER) { log::log(DBG << "Target: trigger creates a change for " - << dependent->get_eventclass()->id() + << dependent->get_eventhandler()->id() << " at t=" << last_valid_time); loop->create_change(dependent, last_valid_time); @@ -91,16 +92,26 @@ void EventTarget::trigger(const curve::time_t &last_valid_time) { } -void EventTarget::add_dependent(const std::weak_ptr &event) { - this->dependents.emplace_back(event); +void EventEntity::add_dependent(const std::shared_ptr &event) { + switch (event->get_eventhandler()->type) { + case EventHandler::trigger_type::TRIGGER: + case EventHandler::trigger_type::REPEAT: + throw Error(ERR << "Can't add a REPEAT or TRIGGER event '" + << event->get_eventhandler()->id() + << "' as dependent for EventEntity " << this->idstr()); + break; + default: + this->dependents.emplace_back(event); + break; + } } -void EventTarget::show_dependents() const { +void EventEntity::show_dependents() const { log::log(DBG << "Dependent list:"); for (auto &dep : this->dependents) { auto dependent = dep.lock(); if (dependent) { - log::log(DBG << " - " << dependent->get_eventclass()->id()); + log::log(DBG << " - " << dependent->get_eventhandler()->id()); } else { log::log(DBG << " - ** outdated old reference **"); diff --git a/libopenage/event/eventtarget.h b/libopenage/event/evententity.h similarity index 77% rename from libopenage/event/eventtarget.h rename to libopenage/event/evententity.h index 5b260a080b..68182f7bee 100644 --- a/libopenage/event/eventtarget.h +++ b/libopenage/event/evententity.h @@ -12,14 +12,13 @@ namespace openage::event { class Event; class Loop; -class EventClass; +class EventHandler; /** * Every Object in the gameworld that wants to be targeted by events or as * dependency for events, has to implement this class. */ -// asdf rename to EventSource ? because it is a source for event triggering? -class EventTarget { +class EventEntity { public: /** Give a unique event system identifier for the entity */ virtual size_t id() const = 0; @@ -37,21 +36,22 @@ class EventTarget { * change up in the tree, this is necessary to make containers with event * targets inside and listen to any changes on the full. */ - EventTarget(const std::shared_ptr &loop, + EventEntity(const std::shared_ptr &loop, single_change_notifier parent_notifier=nullptr) : loop{loop}, parent_notifier{parent_notifier} {} public: - virtual ~EventTarget() = default; + virtual ~EventEntity() = default; /** - * Add a dependent class, that should be notified when dependency is called + * Add a dependent event that is notified whenever this entity changes. + * Does not support TRIGGER and REPEAT event types. */ - void add_dependent(const std::weak_ptr &event); + void add_dependent(const std::shared_ptr &event); /** - * For debugging: print the dependent eventclass ids as log messages. + * For debugging: print the dependent eventhandler ids as log messages. */ void show_dependents() const; @@ -63,7 +63,7 @@ class EventTarget { void changes(const curve::time_t &change_time); /** - * Call this when depending TriggerEventClasses should be invoked. + * Call this when depending TriggerEventHandleres should be invoked. */ void trigger(const curve::time_t &invoke_time); diff --git a/libopenage/event/eventhandler.cpp b/libopenage/event/eventhandler.cpp new file mode 100644 index 0000000000..d648cd255b --- /dev/null +++ b/libopenage/event/eventhandler.cpp @@ -0,0 +1,48 @@ +// Copyright 2017-2018 the openage authors. See copying.md for legal info. + +#include "eventhandler.h" + +#include "event.h" +#include "evententity.h" + +#include "../log/log.h" + +namespace openage::event { + + +EventHandler::EventHandler(const std::string &name, const EventHandler::trigger_type &type) : + type{type}, + _id{name} {} + + +const std::string &EventHandler::id() { + return _id; +} + + +DependencyEventHandler::DependencyEventHandler(const std::string &name) + : + EventHandler(name, EventHandler::trigger_type::DEPENDENCY) {} + + +DependencyImmediatelyEventHandler::DependencyImmediatelyEventHandler(const std::string &name) + : + EventHandler(name, EventHandler::trigger_type::DEPENDENCY_IMMEDIATELY) {} + + +TriggerEventHandler::TriggerEventHandler(const std::string &name) + : + EventHandler(name, EventHandler::trigger_type::TRIGGER) {} + + +RepeatEventHandler::RepeatEventHandler(const std::string &name) + : + EventHandler(name, EventHandler::trigger_type::REPEAT) {} + + +OnceEventHandler::OnceEventHandler(const std::string &name) + : + EventHandler(name, EventHandler::trigger_type::ONCE) {} + + +} // openage::event diff --git a/libopenage/event/eventclass.h b/libopenage/event/eventhandler.h similarity index 79% rename from libopenage/event/eventclass.h rename to libopenage/event/eventhandler.h index ffcfe469f6..cebbd84454 100644 --- a/libopenage/event/eventclass.h +++ b/libopenage/event/eventhandler.h @@ -15,19 +15,19 @@ namespace openage::event { class Event; class Loop; -class EventTarget; +class EventEntity; class State; /** - * A eventclass has to be implemented for every type of event that exists. + * A eventhandler.has to be implemented for every type of event that exists. * It determines what the event means and how it is handled. */ -class EventClass { +class EventHandler { public: /** - * Available types for the event class: - * These decide when an event of this event class will be executed. + * Available types for the event handler: + * These decide when an event of this event handler will be executed. */ enum class trigger_type { /** @@ -67,7 +67,7 @@ class EventClass { }; /** - * Storage for parameters for an event class. + * Storage for parameters for an event handler. */ class param_map { public: @@ -125,12 +125,12 @@ class EventClass { /** * Constructor to be constructed with the unique identifier */ - EventClass(std::string name, const trigger_type &type); + EventHandler(const std::string &name, const trigger_type &type); - virtual ~EventClass() = default; + virtual ~EventHandler() = default; /** - * The event type this event class represents. + * The event type this event handler represents. */ const trigger_type type; @@ -140,7 +140,7 @@ class EventClass { const std::string &id(); /** - * Called for each event that is created for this EventClass. + * Called for each event that is created for this EventHandler. * The job of the setup function is to add all dependencies with other event * targets found in state. */ @@ -154,13 +154,13 @@ class EventClass { * Called from the Loop. */ virtual void invoke(Loop &loop, - const std::shared_ptr &target, + const std::shared_ptr &target, const std::shared_ptr &state, const curve::time_t &time, const param_map ¶ms) = 0; /** - * Is called to calculate the execution time for an event of this eventclass. + * Is called to calculate the execution time for an event of this eventhandler. * This is called whenever one of the set up dependencies was changed, * or when a REPEAT event was executed. * @@ -175,13 +175,13 @@ class EventClass { * then dependencies may not be resolved perfectly anymore * (if other events have already been calculated before that). */ - virtual curve::time_t predict_invoke_time(const std::shared_ptr &target, + virtual curve::time_t predict_invoke_time(const std::shared_ptr &target, const std::shared_ptr &state, const curve::time_t &at) = 0; private: /** - * String identifier for this event class. + * String identifier for this event handler. */ std::string _id; }; @@ -189,29 +189,29 @@ class EventClass { // helper classes -class DependencyEventClass : public EventClass { +class DependencyEventHandler : public EventHandler { public: - DependencyEventClass(const std::string &name); + DependencyEventHandler(const std::string &name); }; -class DependencyImmediatelyEventClass : public EventClass { +class DependencyImmediatelyEventHandler : public EventHandler { public: - DependencyImmediatelyEventClass(const std::string &name); + DependencyImmediatelyEventHandler(const std::string &name); }; -class TriggerEventClass : public EventClass { +class TriggerEventHandler : public EventHandler { public: - TriggerEventClass(const std::string &name); + TriggerEventHandler(const std::string &name); }; -class RepeatEventClass : public EventClass { +class RepeatEventHandler : public EventHandler { public: - RepeatEventClass(const std::string &name); + RepeatEventHandler(const std::string &name); }; -class OnceEventClass : public EventClass { +class OnceEventHandler : public EventHandler { public: - OnceEventClass(const std::string &name); + OnceEventHandler(const std::string &name); }; diff --git a/libopenage/event/eventqueue.cpp b/libopenage/event/eventqueue.cpp index f5f47eaf94..67437b2b70 100644 --- a/libopenage/event/eventqueue.cpp +++ b/libopenage/event/eventqueue.cpp @@ -6,66 +6,66 @@ #include #include "event.h" -#include "eventclass.h" -#include "eventtarget.h" +#include "eventhandler.h" +#include "evententity.h" #include "../log/log.h" #include "../util/compiler.h" namespace openage::event { -std::shared_ptr EventQueue::create_event(const std::shared_ptr &trgt, - const std::shared_ptr &cls, +std::shared_ptr EventQueue::create_event(const std::shared_ptr &trgt, + const std::shared_ptr &cls, const std::shared_ptr &state, const curve::time_t &reference_time, - const EventClass::param_map ¶ms) { + const EventHandler::param_map ¶ms) { auto event = std::make_shared(trgt, cls, params); cls->setup_event(event, state); switch(cls->type) { - case EventClass::trigger_type::DEPENDENCY: - case EventClass::trigger_type::REPEAT: - case EventClass::trigger_type::ONCE: - event->set_time(event->get_eventclass() + case EventHandler::trigger_type::DEPENDENCY: + case EventHandler::trigger_type::REPEAT: + case EventHandler::trigger_type::ONCE: + event->set_time(event->get_eventhandler() ->predict_invoke_time(trgt, state, reference_time)); if (event->get_time() == std::numeric_limits::min()) { log::log(DBG << "Queue: ignoring insertion of event " - << event->get_eventclass()->id() << + << event->get_eventhandler()->id() << " because no execution was scheduled."); return {}; } break; - case EventClass::trigger_type::DEPENDENCY_IMMEDIATELY: - case EventClass::trigger_type::TRIGGER: + case EventHandler::trigger_type::DEPENDENCY_IMMEDIATELY: + case EventHandler::trigger_type::TRIGGER: event->set_time(reference_time); break; } - log::log(DBG << "Queue: inserting event " << event->get_eventclass()->id() << + log::log(DBG << "Queue: inserting event " << event->get_eventhandler()->id() << " into queue to be executed at t=" << event->get_time()); // store the event // or enqueue it for execution - switch(event->get_eventclass()->type) { - case EventClass::trigger_type::DEPENDENCY: + switch(event->get_eventhandler()->type) { + case EventHandler::trigger_type::DEPENDENCY: this->dependency_events.insert(event); break; - case EventClass::trigger_type::DEPENDENCY_IMMEDIATELY: + case EventHandler::trigger_type::DEPENDENCY_IMMEDIATELY: this->dependency_immediately_events.insert(event); break; - case EventClass::trigger_type::TRIGGER: + case EventHandler::trigger_type::TRIGGER: this->trigger_events.insert(event); break; - case EventClass::trigger_type::REPEAT: - case EventClass::trigger_type::ONCE: + case EventHandler::trigger_type::REPEAT: + case EventHandler::trigger_type::ONCE: default: this->event_queue.push(event); } @@ -83,31 +83,32 @@ EventQueue::EventQueue() void EventQueue::add_change(const std::shared_ptr &event, const curve::time_t &changed_at) { - const curve::time_t event_last_triggered = event->get_last_triggered(); + const curve::time_t event_previous_changed = event->get_last_changed(); - // Has the event been triggered in this round? - if (event_last_triggered < changed_at) { - auto it = this->changes->find(OnChangeElement(event, changed_at)); + // Has the event already been fired in this round? + if (event_previous_changed < changed_at) { + auto it = this->changes->find(Change{event, changed_at}); // Is the change already in the queue? if (it != changes->end()) { // Is the new change dated _before_ the old one? if (changed_at < it->time) { log::log(DBG << "Queue: adjusting time in change queue: moving event of " - << event->get_eventclass()->id() + << event->get_eventhandler()->id() << " to earlier time"); // Save the element - OnChangeElement e = *it; - e.time = changed_at; + Change change = *it; + change.time = changed_at; // delete it from the container and readd it + // with the updated time it = this->changes->erase(it); - it = this->changes->insert(it, e); + it = this->changes->insert(it, change); } else { // this change is to be ignored - log::log(DBG << "Queue: skipping change for " << event->get_eventclass()->id() + log::log(DBG << "Queue: skipping change for " << event->get_eventhandler()->id() << " at " << changed_at << " because there was already an earlier one at t=" << it->time); } @@ -116,7 +117,7 @@ void EventQueue::add_change(const std::shared_ptr &event, // the change was not in the to be changed list this->changes->emplace(event, changed_at); log::log(DBG << "Queue: inserting change for event from " - << event->get_eventclass()->id() + << event->get_eventhandler()->id() << " to be applied at t=" << changed_at); } } @@ -124,11 +125,11 @@ void EventQueue::add_change(const std::shared_ptr &event, // the event has been triggered in this round already, so skip it this time this->future_changes->emplace(event, changed_at); log::log(DBG << "Queue: ignoring change at t=" << changed_at - << " for event of EventClass" << event->get_eventclass()->id() - << " because it has been triggered at t=" << event_last_triggered); + << " for event for handler " << event->get_eventhandler()->id() + << " because it's already processed as change at t=" << event_previous_changed); } - event->set_last_triggered(changed_at); + event->set_last_changed(changed_at); } @@ -196,31 +197,36 @@ void EventQueue::swap_changesets() { } -EventQueue::OnChangeElement::OnChangeElement(const std::shared_ptr &evnt, - curve::time_t time) +EventQueue::Change::Change(const std::shared_ptr &evnt, + curve::time_t time) : time{std::move(time)}, evnt{evnt}, hash{evnt->hash()} {} -size_t EventQueue::OnChangeElement::Equal::operator()(const OnChangeElement& left, - const OnChangeElement& right) const { +size_t EventQueue::Change::Equal::operator()(const Change& left, + const Change& right) const { auto left_evnt = left.evnt.lock(); auto right_evnt = right.evnt.lock(); if (left_evnt && right_evnt) { - if (left_evnt->get_eventclass()->id() == right_evnt->get_eventclass()->id()) { + if (left_evnt->get_eventhandler()->id() == right_evnt->get_eventhandler()->id()) { return true; } } else { return false; } - auto left_trgt = left_evnt->get_target().lock(); - auto right_trgt = right_evnt->get_target().lock(); + auto left_entity = left_evnt->get_entity().lock(); + auto right_entity = right_evnt->get_entity().lock(); - return left_trgt && right_trgt && - left_trgt->id() == right_trgt->id(); + if (left_entity && right_entity) { + if (left_entity->id() == right_entity->id()) { + return true; + } + } + + return false; } } // namespace openage::event diff --git a/libopenage/event/eventqueue.h b/libopenage/event/eventqueue.h index 6c06acd5c1..f9d99cf674 100644 --- a/libopenage/event/eventqueue.h +++ b/libopenage/event/eventqueue.h @@ -5,7 +5,7 @@ #include #include -#include "eventclass.h" +#include "eventhandler.h" #include "eventstore.h" #include "../curve/curve.h" @@ -14,18 +14,18 @@ namespace openage::event { class Event; class Loop; -class EventTarget; +class EventEntity; /** - * The core event class for execution and execution dependencies. + * The core event handler for execution and execution dependencies. */ class EventQueue final { public: - class OnChangeElement { + class Change { public: - OnChangeElement(const std::shared_ptr &evnt, - curve::time_t time); + Change(const std::shared_ptr &evnt, + curve::time_t time); curve::time_t time; std::weak_ptr evnt; @@ -33,24 +33,24 @@ class EventQueue final { class Hasher { public: - size_t operator ()(const OnChangeElement& e) const { + size_t operator ()(const Change& e) const { return e.hash; } }; class Equal { public: - size_t operator ()(const OnChangeElement& left, - const OnChangeElement& right) const; + size_t operator ()(const Change& left, + const Change& right) const; }; }; /** * Type for the set to store changes to track. */ - using change_set = std::unordered_set; + using change_set = std::unordered_set; EventQueue(); @@ -62,13 +62,13 @@ class EventQueue final { * in the constructor of the game objects. * * The `reference_time` is the time used to calculate when - * the actual event time will happen by calling `eventclass->predict_invoke_time()`! + * the actual event time will happen by calling `eventhandler->predict_invoke_time()`! */ - std::shared_ptr create_event(const std::shared_ptr &eventtarget, - const std::shared_ptr &eventclass, + std::shared_ptr create_event(const std::shared_ptr &evententity, + const std::shared_ptr &eventhandler, const std::shared_ptr &state, const curve::time_t &reference_time, - const EventClass::param_map ¶ms); + const EventHandler::param_map ¶ms); /** * Remove the given event from the queue. diff --git a/libopenage/event/loop.cpp b/libopenage/event/loop.cpp index 514095b90e..08879bd973 100644 --- a/libopenage/event/loop.cpp +++ b/libopenage/event/loop.cpp @@ -3,9 +3,9 @@ #include "loop.h" #include "event.h" -#include "eventclass.h" +#include "eventhandler.h" #include "eventqueue.h" -#include "eventtarget.h" +#include "evententity.h" #include "../log/log.h" @@ -13,19 +13,19 @@ namespace openage::event { -void Loop::add_event_class(const std::shared_ptr &cls) { +void Loop::add_event_class(const std::shared_ptr &cls) { classstore.insert(std::make_pair(cls->id(), cls)); } std::weak_ptr Loop::create_event(const std::string &name, - const std::shared_ptr &target, + const std::shared_ptr &target, const std::shared_ptr &state, const curve::time_t &reference_time, - const EventClass::param_map ¶ms) { + const EventHandler::param_map ¶ms) { auto it = classstore.find(name); if (it == classstore.end()) { - throw Error{MSG(err) << "Trying to subscribe to eventclass " + throw Error{MSG(err) << "Trying to subscribe to eventhandler " << name << ", which does not exist."}; } @@ -33,19 +33,19 @@ std::weak_ptr Loop::create_event(const std::string &name, } -std::weak_ptr Loop::create_event(const std::shared_ptr &eventclass, - const std::shared_ptr &target, +std::weak_ptr Loop::create_event(const std::shared_ptr &eventhandler, + const std::shared_ptr &target, const std::shared_ptr &state, const curve::time_t &reference_time, - const EventClass::param_map ¶ms) { + const EventHandler::param_map ¶ms) { - auto it = this->classstore.find(eventclass->id()); + auto it = this->classstore.find(eventhandler->id()); if (it == this->classstore.end()) { - auto res = this->classstore.insert(std::make_pair(eventclass->id(), eventclass)); + auto res = this->classstore.insert(std::make_pair(eventhandler->id(), eventhandler)); if (res.second) { it = res.first; } else { - throw Error{ERR << "could not insert eventclass into class store"}; + throw Error{ERR << "could not insert eventhandler into class store"}; } } @@ -100,7 +100,7 @@ int Loop::execute_events(const curve::time_t &time_until, size_t i = 0; for (const auto &e : this->queue.get_event_queue().get_sorted_events()) { log::log(DBG << " event " - << i << ": t=" << e->get_time() << ": " << e->get_eventclass()->id()); + << i << ": t=" << e->get_time() << ": " << e->get_eventhandler()->id()); i++; } } @@ -114,17 +114,17 @@ int Loop::execute_events(const curve::time_t &time_until, break; } - auto target = event->get_target().lock(); + auto target = event->get_entity().lock(); if (target) { - log::log(DBG << "Loop: invoking event \"" << event->get_eventclass()->id() + log::log(DBG << "Loop: invoking event \"" << event->get_eventhandler()->id() << "\" on target \"" << target->idstr() << "\" for time t=" << event->get_time()); this->active_event = event; // apply the event effects - event->get_eventclass()->invoke( + event->get_eventhandler()->invoke( *this, target, state, event->get_time(), event->get_params() ); @@ -133,15 +133,15 @@ int Loop::execute_events(const curve::time_t &time_until, cnt += 1; // if the event is REPEAT, readd the event. - if (event->get_eventclass()->type == EventClass::trigger_type::REPEAT) { - curve::time_t new_time = event->get_eventclass()->predict_invoke_time( + if (event->get_eventhandler()->type == EventHandler::trigger_type::REPEAT) { + curve::time_t new_time = event->get_eventhandler()->predict_invoke_time( target, state, event->get_time() ); if (new_time != std::numeric_limits::min()) { event->set_time(new_time); - log::log(DBG << "Loop: repeating event \"" << event->get_eventclass()->id() + log::log(DBG << "Loop: repeating event \"" << event->get_eventhandler()->id() << "\" on target \"" << target->idstr() << "\" will be reenqueued for time t=" << event->get_time()); @@ -171,25 +171,26 @@ void Loop::update_changes(const std::shared_ptr &state) { size_t i = 0; - // reevaluate depending events because of the change + // Some EventEntity has changed, so all depending events were + // added to the EventQueue as changes. + // These changes need to be reevaluated. for (const auto &change : this->queue.get_changes()) { auto evnt = change.evnt.lock(); if (evnt) { - log::log(DBG << " change " << i++ << ": " << evnt->get_eventclass()->id()); - switch(evnt->get_eventclass()->type) { - case EventClass::trigger_type::ONCE: - case EventClass::trigger_type::DEPENDENCY: { - auto target = evnt->get_target().lock(); - // TODO what happens when the target is degraded? + log::log(DBG << " change " << i++ << ": " << evnt->get_eventhandler()->id()); + switch(evnt->get_eventhandler()->type) { + case EventHandler::trigger_type::ONCE: + case EventHandler::trigger_type::DEPENDENCY: { + auto entity = evnt->get_entity().lock(); - if (target) { - curve::time_t new_time = evnt->get_eventclass() - ->predict_invoke_time(target, state, change.time); + if (entity) { + curve::time_t new_time = evnt->get_eventhandler() + ->predict_invoke_time(entity, state, change.time); if (new_time != std::numeric_limits::min()) { log::log(DBG << "Loop: due to a change, rescheduling event of '" - << evnt->get_eventclass()->id() - << "' on target '" << target->idstr() + << evnt->get_eventhandler()->id() + << "' on entity '" << entity->idstr() << "' at time t=" << change.time << " to NEW TIME t=" << new_time); @@ -199,22 +200,27 @@ void Loop::update_changes(const std::shared_ptr &state) { } else { log::log(DBG << "Loop: due to a change, canceled execution of '" - << evnt->get_eventclass()->id() - << "' on target '" << target->idstr() + << evnt->get_eventhandler()->id() + << "' on entity '" << entity->idstr() << "' at time t=" << change.time); this->queue.remove(evnt); } } + else { + // the event is for a no-longer-existing entity, + // so we can remove it from the queue. + this->queue.remove(evnt); + } } break; - case EventClass::trigger_type::TRIGGER: - case EventClass::trigger_type::DEPENDENCY_IMMEDIATELY: + case EventHandler::trigger_type::TRIGGER: + case EventHandler::trigger_type::DEPENDENCY_IMMEDIATELY: evnt->set_time(change.time); this->queue.enqueue(evnt); break; - case EventClass::trigger_type::REPEAT: + case EventHandler::trigger_type::REPEAT: break; } } diff --git a/libopenage/event/loop.h b/libopenage/event/loop.h index 87dc52af09..a3085b3814 100644 --- a/libopenage/event/loop.h +++ b/libopenage/event/loop.h @@ -20,13 +20,13 @@ int curvepong(); } class Event; -class EventTarget; +class EventEntity; class State; /** - * The core class to manage event class and targets. + * The core class to manage event handler and targets. */ class Loop { @@ -34,8 +34,8 @@ class Loop { friend int demo::curvepong(); public: - /** register a new event class */ - void add_event_class(const std::shared_ptr &cls); + /** register a new event handler */ + void add_event_class(const std::shared_ptr &cls); /** * Add a new Event to the queue. @@ -45,21 +45,21 @@ class Loop { * The `reference_time` is used to calculate the actual event time. */ std::weak_ptr create_event(const std::string &name, - const std::shared_ptr &target, + const std::shared_ptr &target, const std::shared_ptr &state, const curve::time_t &reference_time, - const EventClass::param_map ¶ms=EventClass::param_map({})); + const EventHandler::param_map ¶ms=EventHandler::param_map({})); /** - * This will generate a new randomly named eventclass for this specific element + * This will generate a new randomly named eventhandler for this specific element * * The `reference_time` is used to determine the actual event trigger time. */ - std::weak_ptr create_event(const std::shared_ptr &eventclass, - const std::shared_ptr &target, + std::weak_ptr create_event(const std::shared_ptr &eventhandler, + const std::shared_ptr &target, const std::shared_ptr &state, const curve::time_t &reference_time, - const EventClass::param_map ¶ms=EventClass::param_map({})); + const EventHandler::param_map ¶ms=EventHandler::param_map({})); /** * Execute all events that are registered until a certain point in time. @@ -69,7 +69,7 @@ class Loop { /** * Register that a given event must be reevaluated at a time, - * this usually happens because this event depended on an eventtarget + * this usually happens because this event depended on an evententity * that got changed at this time. * This inserts the event into the changes queue * so it will be evaluated in the next loop iteration. @@ -96,9 +96,9 @@ class Loop { void update_changes(const std::shared_ptr &state); /** - * Here we do the bookkeeping of registered event classes. + * Here we do the bookkeeping of registered event handleres. */ - std::unordered_map> classstore; + std::unordered_map> classstore; /** * All events are enqueued here. diff --git a/libopenage/event/tests.cpp b/libopenage/event/tests.cpp index d4d285913f..0682cf69c1 100644 --- a/libopenage/event/tests.cpp +++ b/libopenage/event/tests.cpp @@ -10,7 +10,7 @@ #include "loop.h" #include "event.h" -#include "eventtarget.h" +#include "evententity.h" #include "state.h" @@ -19,12 +19,12 @@ namespace openage::event::tests { // We have to create a temporary State due to the magic of C++ class TestState : public State { public: - class TestObject : public EventTarget { + class TestObject : public EventEntity { const int _id; public: TestObject(const std::shared_ptr &loop, int id) : - EventTarget(loop), + EventEntity(loop), _id{id}, number(0) {} @@ -80,12 +80,12 @@ class TestState : public State { }; -class TestEventClass : public EventClass { +class TestEventHandler : public EventHandler { int idx; public: - TestEventClass(const std::string &name, int idx) + TestEventHandler(const std::string &name, int idx) : - EventClass(name, EventClass::trigger_type::DEPENDENCY), + EventHandler(name, EventHandler::trigger_type::DEPENDENCY), idx{idx} {} void setup_event(const std::shared_ptr &event, @@ -106,10 +106,10 @@ class TestEventClass : public EventClass { } void invoke(Loop &/*loop*/, - const std::shared_ptr &target, + const std::shared_ptr &target, const std::shared_ptr &gstate, const curve::time_t &time, - const EventClass::param_map &/*param*/) override { + const EventHandler::param_map &/*param*/) override { auto state = std::dynamic_pointer_cast(gstate); @@ -130,7 +130,7 @@ class TestEventClass : public EventClass { } } - curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, + curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, const std::shared_ptr &/*state*/, const curve::time_t &at) override { return at + curve::time_t::from_double(2); @@ -138,11 +138,11 @@ class TestEventClass : public EventClass { }; -class TestEventClassTwo : public EventClass { +class TestEventHandlerTwo : public EventHandler { public: - explicit TestEventClassTwo(const std::string &name) + explicit TestEventHandlerTwo(const std::string &name) : - EventClass(name, EventClass::trigger_type::DEPENDENCY) {} + EventHandler(name, EventHandler::trigger_type::DEPENDENCY) {} void setup_event(const std::shared_ptr &target, const std::shared_ptr &gstate) override { @@ -152,20 +152,20 @@ class TestEventClassTwo : public EventClass { } void invoke(Loop &/*loop*/, - const std::shared_ptr >arget, + const std::shared_ptr >arget, const std::shared_ptr &gstate, const curve::time_t &time, - const EventClass::param_map &/*param*/) override { + const EventHandler::param_map &/*param*/) override { auto state = std::dynamic_pointer_cast(gstate); auto target = std::dynamic_pointer_cast(gtarget); state->objectB->set_number(target->number + 1, time); - log::log(DBG << "I am EventClassTwo. Setting B.number to " << state->objectB->number); + log::log(DBG << "I am EventHandlerTwo. Setting B.number to " << state->objectB->number); state->trace.emplace_back("B", time); } - curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, + curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, const std::shared_ptr &/*state*/, const curve::time_t &at) override { // TODO recalculate a hit time @@ -174,11 +174,11 @@ class TestEventClassTwo : public EventClass { }; -class EventTypeTestClass : public EventClass { +class EventTypeTestClass : public EventHandler { public: - EventTypeTestClass(const std::string &name, EventClass::trigger_type type) + EventTypeTestClass(const std::string &name, EventHandler::trigger_type type) : - EventClass(name, type) {} + EventHandler(name, type) {} void setup_event(const std::shared_ptr &event, const std::shared_ptr &gstate) override { @@ -191,10 +191,10 @@ class EventTypeTestClass : public EventClass { } void invoke(Loop &/*loop*/, - const std::shared_ptr &target, + const std::shared_ptr &target, const std::shared_ptr &gstate, const curve::time_t &time, - const EventClass::param_map &/*param*/) override { + const EventHandler::param_map &/*param*/) override { auto state = std::dynamic_pointer_cast(gstate); @@ -205,27 +205,27 @@ class EventTypeTestClass : public EventClass { state->trace.emplace_back(this->id(), time); } - curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, + curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, const std::shared_ptr &/*state*/, const curve::time_t &at) override { switch(this->type) { - case EventClass::trigger_type::DEPENDENCY: + case EventHandler::trigger_type::DEPENDENCY: // Execute 1 after the change (usually it is neccessary to recalculate a collision return at + curve::time_t::from_double(1); - case EventClass::trigger_type::DEPENDENCY_IMMEDIATELY: + case EventHandler::trigger_type::DEPENDENCY_IMMEDIATELY: TESTFAILMSG("DEPENDENCY_IMMEDIATELY does not recalculate time!"); return 0; - case EventClass::trigger_type::TRIGGER: + case EventHandler::trigger_type::TRIGGER: TESTFAILMSG("TRIGGER does not recalculate time!"); return 0; - case EventClass::trigger_type::REPEAT: + case EventHandler::trigger_type::REPEAT: // This will force the execution every 5ms return at + curve::time_t::from_double(5); - case EventClass::trigger_type::ONCE: + case EventHandler::trigger_type::ONCE: return 10; // even if data changed it will happen at the given time! } return at; @@ -240,11 +240,11 @@ void eventtrigger() { std::weak_ptr destruction_test_state; { - // Test with one event class + // Test with one event handler auto loop = std::make_shared(); - loop->add_event_class(std::make_shared("test_on_A", 0)); - loop->add_event_class(std::make_shared("test_on_B", 1)); + loop->add_event_class(std::make_shared("test_on_A", 0)); + loop->add_event_class(std::make_shared("test_on_B", 1)); auto state = std::make_shared(loop); auto gstate = std::static_pointer_cast(state); @@ -299,11 +299,11 @@ void eventtrigger() { log::log(DBG << "------------- [ Starting Test: Two Event Ping Pong ] ------------"); { - // Test with two event classes to check interplay + // Test with two event handleres to check interplay auto loop = std::make_shared(); - loop->add_event_class(std::make_shared("test_on_A", 0)); - loop->add_event_class(std::make_shared("test_on_B")); + loop->add_event_class(std::make_shared("test_on_A", 0)); + loop->add_event_class(std::make_shared("test_on_B")); auto state = std::make_shared(loop); auto gstate = std::static_pointer_cast(state); @@ -357,19 +357,19 @@ void eventtrigger() { loop->add_event_class(std::make_shared( "object_modify", - EventClass::trigger_type::DEPENDENCY)); + EventHandler::trigger_type::DEPENDENCY)); loop->add_event_class(std::make_shared( "object_modify_immediately", - EventClass::trigger_type::DEPENDENCY_IMMEDIATELY)); + EventHandler::trigger_type::DEPENDENCY_IMMEDIATELY)); loop->add_event_class(std::make_shared( "object_trigger", - EventClass::trigger_type::TRIGGER)); + EventHandler::trigger_type::TRIGGER)); loop->add_event_class(std::make_shared( "repeat_exec", - EventClass::trigger_type::REPEAT)); + EventHandler::trigger_type::REPEAT)); loop->add_event_class(std::make_shared( "once", - EventClass::trigger_type::ONCE)); + EventHandler::trigger_type::ONCE)); auto state = std::make_shared(loop); auto gstate = std::static_pointer_cast(state); @@ -496,20 +496,20 @@ void eventtrigger() { log::log(DBG << "------------- [ Starting Test: Event parameter Mapping ] ------------"); { - class EventParameterMapTestClass : public EventClass { + class EventParameterMapTestClass : public EventHandler { public: EventParameterMapTestClass() : - EventClass("EventParameterMap", EventClass::trigger_type::ONCE) {} + EventHandler("EventParameterMap", EventHandler::trigger_type::ONCE) {} void setup_event(const std::shared_ptr &/*target*/, const std::shared_ptr &/*state*/) override {} void invoke(Loop &/*loop*/, - const std::shared_ptr &/*target*/, + const std::shared_ptr &/*target*/, const std::shared_ptr &/*state*/, const curve::time_t &/*time*/, - const EventClass::param_map ¶m) override { + const EventHandler::param_map ¶m) override { log::log(DBG << "Testing unknown parameter"); TESTEQUALS(param.contains("tomato"), false); @@ -537,7 +537,7 @@ void eventtrigger() { TESTEQUALS(param.get("testStdString"), "stdstring"); } - curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, + curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, const std::shared_ptr &/*state*/, const curve::time_t &at) override { return at; diff --git a/libopenage/input/event.h b/libopenage/input/event.h index f062a103ee..91dbf20a3a 100644 --- a/libopenage/input/event.h +++ b/libopenage/input/event.h @@ -80,7 +80,7 @@ using modset_t = std::unordered_set; /** - * base event type containing event class and event code + * base event type containing event handler and event code */ class ClassCode { public: diff --git a/libopenage/main/tests/gamestate.cpp b/libopenage/main/tests/gamestate.cpp index ddfce82a76..a3ea24994c 100644 --- a/libopenage/main/tests/gamestate.cpp +++ b/libopenage/main/tests/gamestate.cpp @@ -14,7 +14,7 @@ namespace openage::main::tests::pong { using namespace std::placeholders; PongPlayer::PongPlayer(const std::shared_ptr &mgr, size_t id) : - EventTarget{mgr}, + EventEntity{mgr}, speed(std::make_shared>( mgr, (id << 4) + 1, @@ -61,7 +61,7 @@ void PongPlayer::child_changes(const curve::time_t &time) { PongBall::PongBall(const std::shared_ptr &mgr, size_t id) : - EventTarget{mgr}, + EventEntity{mgr}, speed(std::make_shared>( mgr, (id << 2) + 1, diff --git a/libopenage/main/tests/gamestate.h b/libopenage/main/tests/gamestate.h index b221c832eb..c2e5f4240d 100644 --- a/libopenage/main/tests/gamestate.h +++ b/libopenage/main/tests/gamestate.h @@ -7,7 +7,7 @@ #include "config.h" #include "../../curve/continuous.h" #include "../../curve/discrete.h" -#include "../../event/eventtarget.h" +#include "../../event/evententity.h" #include "../../event/loop.h" #include "../../event/state.h" #include "../../util/vector.h" @@ -32,7 +32,7 @@ class PongEvent { }; -class PongPlayer : public event::EventTarget { +class PongPlayer : public event::EventEntity { public: PongPlayer(const std::shared_ptr &mgr, size_t id); @@ -52,7 +52,7 @@ class PongPlayer : public event::EventTarget { }; -class PongBall : public event::EventTarget { +class PongBall : public event::EventEntity { public: PongBall(const std::shared_ptr &mgr, size_t id); diff --git a/libopenage/main/tests/physics.cpp b/libopenage/main/tests/physics.cpp index 87b13f563b..41b97d49d7 100644 --- a/libopenage/main/tests/physics.cpp +++ b/libopenage/main/tests/physics.cpp @@ -13,9 +13,9 @@ namespace openage::main::tests::pong { -class BallReflectWall : public event::DependencyEventClass { +class BallReflectWall : public event::DependencyEventHandler { public: - BallReflectWall() : event::DependencyEventClass("demo.ball.reflect_wall") {} + BallReflectWall() : event::DependencyEventHandler("demo.ball.reflect_wall") {} void setup_event(const std::shared_ptr &evnt, const std::shared_ptr &gstate) override { @@ -28,15 +28,15 @@ class BallReflectWall : public event::DependencyEventClass { evnt->depend_on(state->ball->speed); // TODO add dependency to size of game area - // FIXME: warn if it's not a dependency eventclass + // FIXME: warn if it's not a dependency eventhandler } - // FIXME we REALLY need dependencies to objects i.e. Ball : public EventTarget() + // FIXME we REALLY need dependencies to objects i.e. Ball : public EventEntity() void invoke(event::Loop &, - const std::shared_ptr &target, + const std::shared_ptr &target, const std::shared_ptr &gstate, const curve::time_t &now, - const event::EventClass::param_map &/*param*/) override { + const event::EventHandler::param_map &/*param*/) override { auto positioncurve = std::dynamic_pointer_cast>(target); auto state = std::dynamic_pointer_cast(gstate); @@ -70,7 +70,7 @@ class BallReflectWall : public event::DependencyEventClass { state->ball->position->set_last(now + ty, pos + (speed * ty.to_double())); } - curve::time_t predict_invoke_time(const std::shared_ptr &target, + curve::time_t predict_invoke_time(const std::shared_ptr &target, const std::shared_ptr &gstate, const curve::time_t &now) override { @@ -103,11 +103,11 @@ class BallReflectWall : public event::DependencyEventClass { }; -class BallReflectPanel : public event::DependencyEventClass { +class BallReflectPanel : public event::DependencyEventHandler { public: BallReflectPanel () : - event::DependencyEventClass("demo.ball.reflect_panel") {} + event::DependencyEventHandler("demo.ball.reflect_panel") {} void setup_event(const std::shared_ptr &target, const std::shared_ptr &gstate) override { @@ -125,10 +125,10 @@ class BallReflectPanel : public event::DependencyEventClass { // FIXME we REALLY need dependencies to objects void invoke(event::Loop &mgr, - const std::shared_ptr &/*target*/, + const std::shared_ptr &/*target*/, const std::shared_ptr &gstate, const curve::time_t &now, - const event::EventClass::param_map &/*param*/) override { + const event::EventHandler::param_map &/*param*/) override { auto state = std::dynamic_pointer_cast(gstate); @@ -193,7 +193,7 @@ class BallReflectPanel : public event::DependencyEventClass { state->ball->position->set_last(now + ty, hit_pos); } - curve::time_t predict_invoke_time(const std::shared_ptr &target, + curve::time_t predict_invoke_time(const std::shared_ptr &target, const std::shared_ptr &gstate, const curve::time_t &now) override { @@ -237,20 +237,20 @@ class BallReflectPanel : public event::DependencyEventClass { }; -class ResetGame : public event::OnceEventClass { +class ResetGame : public event::OnceEventHandler { public: ResetGame () : - event::OnceEventClass("demo.reset") {} + event::OnceEventHandler("demo.reset") {} void setup_event(const std::shared_ptr &/*target*/, const std::shared_ptr &/*state*/) override {} void invoke(event::Loop &/*mgr*/, - const std::shared_ptr &/*target*/, + const std::shared_ptr &/*target*/, const std::shared_ptr &gstate, const curve::time_t &now, - const event::EventClass::param_map &/*param*/) override { + const event::EventHandler::param_map &/*param*/) override { auto state = std::dynamic_pointer_cast(gstate); @@ -321,7 +321,7 @@ class ResetGame : public event::OnceEventClass { state->ball->position->set_last(now + ty, pos + init_speed * ty.to_double()); } - curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, + curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, const std::shared_ptr &/*state*/, const curve::time_t &old_time) override { return old_time; diff --git a/libopenage/main/tests/pong.cpp b/libopenage/main/tests/pong.cpp index 31a8909c94..422f79fae7 100644 --- a/libopenage/main/tests/pong.cpp +++ b/libopenage/main/tests/pong.cpp @@ -142,7 +142,7 @@ void main(const util::Path& path) { mvprintw(pos++, state->area_size->get(now)[0]/2 + 10, "%f: %s", e->get_time().to_double(), - e->get_eventclass()->id().c_str()); + e->get_eventhandler()->id().c_str()); } */ From 4e4cca5bf6af475a80c2a0070deb30cbaf13124f Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Thu, 28 Feb 2019 20:15:22 +0100 Subject: [PATCH 16/28] gitignore: add ccls-cache for c++ completion --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index fe6e067a08..287c2b9919 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ __pycache__ /.bin /build /deps +/.ccls-cache # root dir run script /run From 6305848f6a03b05af61ed0fcb97d9fdb6e61cbdb Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sat, 2 Mar 2019 23:33:30 +0100 Subject: [PATCH 17/28] pong: fix initial fetch of window size --- libopenage/main/tests/gui.cpp | 3 +++ libopenage/main/tests/gui.h | 2 ++ libopenage/main/tests/physics.cpp | 4 ++-- libopenage/main/tests/pong.cpp | 6 ++++++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/libopenage/main/tests/gui.cpp b/libopenage/main/tests/gui.cpp index f4e6ca6634..6fa7f74397 100644 --- a/libopenage/main/tests/gui.cpp +++ b/libopenage/main/tests/gui.cpp @@ -301,5 +301,8 @@ void Gui::clear_resize_callbacks() { this->resize_callbacks.clear(); } +void Gui::update() { + this->window.update(); +} } // openage::main::tests::pong diff --git a/libopenage/main/tests/gui.h b/libopenage/main/tests/gui.h index 80fbb3c934..c8b993f6a0 100644 --- a/libopenage/main/tests/gui.h +++ b/libopenage/main/tests/gui.h @@ -33,6 +33,8 @@ class Gui { void add_resize_callback(const renderer::Window::resize_cb_t&); void clear_resize_callbacks(); + void update(); + bool exit_requested = false; private: diff --git a/libopenage/main/tests/physics.cpp b/libopenage/main/tests/physics.cpp index 41b97d49d7..14a1ae7055 100644 --- a/libopenage/main/tests/physics.cpp +++ b/libopenage/main/tests/physics.cpp @@ -278,8 +278,8 @@ class ResetGame : public event::OnceEventHandler { float dirx = (rng::random() % 2) ? 1 : -1; float diry = (rng::random() % 2) ? 1 : -1; auto init_speed = util::Vector2d( - dirx * (screen_size[0] / 2.0 + (rng::random() % (screen_size[0]/4))), - diry * (screen_size[1] / 3.0 + (rng::random() % (screen_size[1]/5))) + dirx * (screen_size[0] / 8.0 + (rng::random() % (screen_size[0]/10))), + diry * (screen_size[1] / 10.0 + (rng::random() % (screen_size[1]/10))) ); state->ball->speed->set_last(now, init_speed); diff --git a/libopenage/main/tests/pong.cpp b/libopenage/main/tests/pong.cpp index 422f79fae7..4ed593ebbd 100644 --- a/libopenage/main/tests/pong.cpp +++ b/libopenage/main/tests/pong.cpp @@ -70,6 +70,9 @@ void main(const util::Path& path) { curve::time_t now = 0; Physics phys; + // the window size is fetched in here already, + // so the resize callback doesn't need to be called for + // a new state. auto state = std::make_shared(loop, gui); gui->clear_resize_callbacks(); @@ -82,6 +85,9 @@ void main(const util::Path& path) { } ); + // process one round of window events, mainly to get the initial screen size. + gui->update(); + Physics::init(state, loop, now); // initialize player properties From bf09ae9e66ae9fa6667bab54de0ef0f70450d45a Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sat, 2 Mar 2019 23:38:23 +0100 Subject: [PATCH 18/28] pong: let reflections depend on area size --- libopenage/main/tests/physics.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libopenage/main/tests/physics.cpp b/libopenage/main/tests/physics.cpp index 14a1ae7055..d3ea615adc 100644 --- a/libopenage/main/tests/physics.cpp +++ b/libopenage/main/tests/physics.cpp @@ -26,7 +26,7 @@ class BallReflectWall : public event::DependencyEventHandler { // triggers a change evnt->depend_on(state->ball->position); evnt->depend_on(state->ball->speed); - // TODO add dependency to size of game area + evnt->depend_on(state->area_size); // FIXME: warn if it's not a dependency eventhandler } @@ -109,18 +109,18 @@ class BallReflectPanel : public event::DependencyEventHandler { : event::DependencyEventHandler("demo.ball.reflect_panel") {} - void setup_event(const std::shared_ptr &target, + void setup_event(const std::shared_ptr &evnt, const std::shared_ptr &gstate) override { auto state = std::dynamic_pointer_cast(gstate); // FIXME dependency to a full ball object // then a change to any of the curves triggers the update. - target->depend_on(state->ball->position); - target->depend_on(state->ball->speed); - target->depend_on(state->p1->position); - target->depend_on(state->p2->position); - // TODO add dependency to size of game area + evnt->depend_on(state->ball->position); + evnt->depend_on(state->ball->speed); + evnt->depend_on(state->p1->position); + evnt->depend_on(state->p2->position); + evnt->depend_on(state->area_size); } // FIXME we REALLY need dependencies to objects From e7d9f80d2b96382c37205013546049684a9bbfd0 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sat, 2 Mar 2019 23:54:03 +0100 Subject: [PATCH 19/28] update some copyright years --- libopenage/event/demo/gamestate.h | 2 +- libopenage/event/eventhandler.cpp | 2 +- libopenage/event/loop.cpp | 2 +- libopenage/gamestate/old/civilisation.h | 2 +- libopenage/gamestate/old/cost.h | 2 +- libopenage/gamestate/old/generator.h | 2 +- libopenage/gamestate/old/market.cpp | 2 +- libopenage/gamestate/old/market.h | 2 +- libopenage/gamestate/old/player.h | 2 +- libopenage/gamestate/old/population_tracker.cpp | 2 +- libopenage/gamestate/old/population_tracker.h | 2 +- libopenage/gamestate/old/team.h | 2 +- libopenage/main/tests.cpp | 2 +- libopenage/main/tests.h | 2 +- libopenage/main/tests/pong.h | 2 +- libopenage/nyan/db.h | 2 +- libopenage/presenter/test.cpp | 2 +- libopenage/pyinterface/exctranslate.cpp | 2 +- openage/main/tests.pyx | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/libopenage/event/demo/gamestate.h b/libopenage/event/demo/gamestate.h index 0ef606da9a..f61a17887a 100644 --- a/libopenage/event/demo/gamestate.h +++ b/libopenage/event/demo/gamestate.h @@ -1,4 +1,4 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #pragma once diff --git a/libopenage/event/eventhandler.cpp b/libopenage/event/eventhandler.cpp index d648cd255b..1e7575e3b1 100644 --- a/libopenage/event/eventhandler.cpp +++ b/libopenage/event/eventhandler.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #include "eventhandler.h" diff --git a/libopenage/event/loop.cpp b/libopenage/event/loop.cpp index 08879bd973..ea3f49e0db 100644 --- a/libopenage/event/loop.cpp +++ b/libopenage/event/loop.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #include "loop.h" diff --git a/libopenage/gamestate/old/civilisation.h b/libopenage/gamestate/old/civilisation.h index 2e88a1a8fd..97045031ea 100644 --- a/libopenage/gamestate/old/civilisation.h +++ b/libopenage/gamestate/old/civilisation.h @@ -1,4 +1,4 @@ -// Copyright 2015-2017 the openage authors. See copying.md for legal info. +// Copyright 2015-2019 the openage authors. See copying.md for legal info. #pragma once diff --git a/libopenage/gamestate/old/cost.h b/libopenage/gamestate/old/cost.h index 648dffbead..749aa50986 100644 --- a/libopenage/gamestate/old/cost.h +++ b/libopenage/gamestate/old/cost.h @@ -1,4 +1,4 @@ -// Copyright 2017-2017 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #pragma once diff --git a/libopenage/gamestate/old/generator.h b/libopenage/gamestate/old/generator.h index 2f45509063..a2d4bf58b7 100644 --- a/libopenage/gamestate/old/generator.h +++ b/libopenage/gamestate/old/generator.h @@ -1,4 +1,4 @@ -// Copyright 2015-2016 the openage authors. See copying.md for legal info. +// Copyright 2015-2019 the openage authors. See copying.md for legal info. #pragma once diff --git a/libopenage/gamestate/old/market.cpp b/libopenage/gamestate/old/market.cpp index 4d580bcd88..4b1d50bc24 100644 --- a/libopenage/gamestate/old/market.cpp +++ b/libopenage/gamestate/old/market.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2017 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #include "player.h" #include "market.h" diff --git a/libopenage/gamestate/old/market.h b/libopenage/gamestate/old/market.h index 93b997eaa3..643668fe59 100644 --- a/libopenage/gamestate/old/market.h +++ b/libopenage/gamestate/old/market.h @@ -1,4 +1,4 @@ -// Copyright 2017-2017 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #pragma once diff --git a/libopenage/gamestate/old/player.h b/libopenage/gamestate/old/player.h index 5267421b71..469a6c1f98 100644 --- a/libopenage/gamestate/old/player.h +++ b/libopenage/gamestate/old/player.h @@ -1,4 +1,4 @@ -// Copyright 2015-2017 the openage authors. See copying.md for legal info. +// Copyright 2015-2019 the openage authors. See copying.md for legal info. #pragma once diff --git a/libopenage/gamestate/old/population_tracker.cpp b/libopenage/gamestate/old/population_tracker.cpp index cb7a708362..fdb79590f6 100644 --- a/libopenage/gamestate/old/population_tracker.cpp +++ b/libopenage/gamestate/old/population_tracker.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2017 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #include "population_tracker.h" diff --git a/libopenage/gamestate/old/population_tracker.h b/libopenage/gamestate/old/population_tracker.h index 59371aaaa1..431c0f28ea 100644 --- a/libopenage/gamestate/old/population_tracker.h +++ b/libopenage/gamestate/old/population_tracker.h @@ -1,4 +1,4 @@ -// Copyright 2017-2017 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #pragma once diff --git a/libopenage/gamestate/old/team.h b/libopenage/gamestate/old/team.h index c29d5b6542..657909df9e 100644 --- a/libopenage/gamestate/old/team.h +++ b/libopenage/gamestate/old/team.h @@ -1,4 +1,4 @@ -// Copyright 2016-2017 the openage authors. See copying.md for legal info. +// Copyright 2016-2019 the openage authors. See copying.md for legal info. #pragma once diff --git a/libopenage/main/tests.cpp b/libopenage/main/tests.cpp index bc04eef209..27236e3635 100644 --- a/libopenage/main/tests.cpp +++ b/libopenage/main/tests.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2018 the openage authors. See copying.md for legal info. +// Copyright 2018-2019 the openage authors. See copying.md for legal info. #include "tests/pong.h" diff --git a/libopenage/main/tests.h b/libopenage/main/tests.h index a03e74c727..ca027593e4 100644 --- a/libopenage/main/tests.h +++ b/libopenage/main/tests.h @@ -1,4 +1,4 @@ -// Copyright 2018-2018 the openage authors. See copying.md for legal info. +// Copyright 2018-2019 the openage authors. See copying.md for legal info. #pragma once diff --git a/libopenage/main/tests/pong.h b/libopenage/main/tests/pong.h index 54848eebf6..38d234ddee 100644 --- a/libopenage/main/tests/pong.h +++ b/libopenage/main/tests/pong.h @@ -1,4 +1,4 @@ -// Copyright 2018-2018 the openage authors. See copying.md for legal info. +// Copyright 2018-2019 the openage authors. See copying.md for legal info. #pragma once diff --git a/libopenage/nyan/db.h b/libopenage/nyan/db.h index db6e13f013..23a39980a6 100644 --- a/libopenage/nyan/db.h +++ b/libopenage/nyan/db.h @@ -1,4 +1,4 @@ -// Copyright 2018-2018 the openage authors. See copying.md for legal info. +// Copyright 2018-2019 the openage authors. See copying.md for legal info. #pragma once diff --git a/libopenage/presenter/test.cpp b/libopenage/presenter/test.cpp index 4c3bea12ab..ed61c52a14 100644 --- a/libopenage/presenter/test.cpp +++ b/libopenage/presenter/test.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2018 the openage authors. See copying.md for legal info. +// Copyright 2018-2019 the openage authors. See copying.md for legal info. #include "../log/log.h" diff --git a/libopenage/pyinterface/exctranslate.cpp b/libopenage/pyinterface/exctranslate.cpp index 53fe792dc9..04e1a12d68 100644 --- a/libopenage/pyinterface/exctranslate.cpp +++ b/libopenage/pyinterface/exctranslate.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2018 the openage authors. See copying.md for legal info. +// Copyright 2015-2019 the openage authors. See copying.md for legal info. #include "exctranslate.h" diff --git a/openage/main/tests.pyx b/openage/main/tests.pyx index 8432be1871..2809db80cd 100644 --- a/openage/main/tests.pyx +++ b/openage/main/tests.pyx @@ -1,4 +1,4 @@ -# Copyright 2018-2018 the openage authors. See copying.md for legal info. +# Copyright 2018-2019 the openage authors. See copying.md for legal info. """ tests for the engine itself. From 7d772b220bc419f4e464eb7db08563eff3f32d72 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Wed, 27 Mar 2019 10:36:25 +0100 Subject: [PATCH 20/28] buildsystem: clarify missing header guard message --- buildsystem/codecompliance/headerguards.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildsystem/codecompliance/headerguards.py b/buildsystem/codecompliance/headerguards.py index c35090ed83..8bccf11570 100644 --- a/buildsystem/codecompliance/headerguards.py +++ b/buildsystem/codecompliance/headerguards.py @@ -45,7 +45,7 @@ def find_issues(dirname): match = GUARD_RE.match(data) if not match: - raise HeaderIssue("No valid header guard found") + raise HeaderIssue("No valid header guard found (e.g. #pragma once)") except HeaderIssue as exc: yield (f"header guard issue in {fname}", exc.args[0], None) From a15e6e4daa7e189c9f4c6d62f771bd8dd2603bd6 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sat, 30 Mar 2019 18:12:48 +0100 Subject: [PATCH 21/28] curve: add segmented curve --- libopenage/curve/CMakeLists.txt | 5 +- libopenage/curve/base_curve.cpp | 7 + libopenage/curve/base_curve.h | 193 +++++++++++++ libopenage/curve/continuous.h | 63 +++-- libopenage/curve/discrete.h | 6 +- libopenage/curve/interpolated.cpp | 3 + libopenage/curve/interpolated.h | 77 +++++ libopenage/curve/keyframe.cpp | 3 + libopenage/curve/keyframe.h | 40 +++ libopenage/curve/keyframe_container.h | 377 +++++++++++++++++-------- libopenage/curve/segmented.cpp | 9 + libopenage/curve/segmented.h | 93 ++++++ libopenage/curve/tests/curve_types.cpp | 206 ++++++++++++-- libopenage/curve/value_container.cpp | 7 - libopenage/curve/value_container.h | 118 -------- 15 files changed, 919 insertions(+), 288 deletions(-) create mode 100644 libopenage/curve/base_curve.cpp create mode 100644 libopenage/curve/base_curve.h create mode 100644 libopenage/curve/interpolated.cpp create mode 100644 libopenage/curve/interpolated.h create mode 100644 libopenage/curve/keyframe.cpp create mode 100644 libopenage/curve/keyframe.h create mode 100644 libopenage/curve/segmented.cpp create mode 100644 libopenage/curve/segmented.h delete mode 100644 libopenage/curve/value_container.cpp delete mode 100644 libopenage/curve/value_container.h diff --git a/libopenage/curve/CMakeLists.txt b/libopenage/curve/CMakeLists.txt index fd797510ab..5a74be80e7 100644 --- a/libopenage/curve/CMakeLists.txt +++ b/libopenage/curve/CMakeLists.txt @@ -1,14 +1,17 @@ add_sources(libopenage + base_curve.cpp continuous.cpp curve.cpp discrete.cpp + interpolated.cpp iterator.cpp + keyframe.cpp keyframe_container.cpp map.cpp map_filter_iterator.cpp queue.cpp queue_filter_iterator.cpp - value_container.cpp + segmented.cpp ) add_subdirectory("tests") diff --git a/libopenage/curve/base_curve.cpp b/libopenage/curve/base_curve.cpp new file mode 100644 index 0000000000..4fd47917cc --- /dev/null +++ b/libopenage/curve/base_curve.cpp @@ -0,0 +1,7 @@ +// Copyright 2017-2019 the openage authors. See copying.md for legal info. + +#include "base_curve.h" + +namespace openage::curve { + +} // openage::curve diff --git a/libopenage/curve/base_curve.h b/libopenage/curve/base_curve.h new file mode 100644 index 0000000000..e1d7b0d289 --- /dev/null +++ b/libopenage/curve/base_curve.h @@ -0,0 +1,193 @@ +// Copyright 2017-2019 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "keyframe_container.h" +#include "../event/evententity.h" +#include "../event/loop.h" + + +namespace openage::curve { + +template +class BaseCurve : public event::EventEntity { +public: + BaseCurve(const std::shared_ptr &loop, + size_t id, + const std::string &idstr="", + const EventEntity::single_change_notifier ¬ifier=nullptr) + : + EventEntity(loop, notifier), + _id{id}, + _idstr{idstr}, + loop{loop}, + last_element{this->container.begin()} {} + + virtual ~BaseCurve() = default; + + virtual T get(const time_t &t) const = 0; + + virtual T operator ()(const time_t &now) { + return get(now); + } + + virtual std::pair frame(const time_t &) const; + virtual std::pair next_frame(const time_t &) const; + + /** + * Insert/overwrite given value at given time and erase all elements + * that follow at a later time. + * If multiple elements exist at the given time, + * overwrite the last one. + */ + virtual void set_last(const time_t &at, const T &value); + + /** + * Insert a value at the given time. + * If there already is a value at this time, + * the value is inserted directly after the existing one. + */ + virtual void set_insert(const time_t &at, const T &value); + + /** + * Insert a value at the given time. + * If there already is a value at this time, + * the given value will replace the first value with the same time. + */ + virtual void set_replace(const time_t &at, const T &value); + + /** + * Remove all values that have the given time. + */ + virtual void erase(const time_t &at); + + /** + * Integrity check, for debugging/testing reasons only. + */ + void check_integrity() const; + + size_t id() const override { + return this->_id; + } + + std::string idstr() const override { + if (this->_idstr.size() == 0) { + return std::to_string(this->id()); + } + return this->_idstr; + } + + /** + * Get a string representation of the curve. + */ + std::string str() const; + +protected: + /** + * Stores all the keyframes + */ + KeyframeContainer container; + + /** + * Identifier for the container + */ + const size_t _id; + + /** + * Human-readable identifier for the container + */ + const std::string _idstr; + + /** + * The eventloop this curve was registered to + */ + const std::shared_ptr loop; + + /** + * Cache the iterator for quickly finding the end + */ + mutable typename KeyframeContainer::iterator last_element; +}; + + +template +void BaseCurve::set_last(const time_t &at, const T &value) { + auto hint = this->container.last(at, this->last_element); + + // erase max one same-time value + if (hint->time == at) { + hint--; + } + + hint = this->container.erase_after(hint); + + this->container.insert_before(at, value, hint); + this->last_element = hint; + + this->changes(at); +} + + +template +void BaseCurve::set_insert(const time_t &at, const T &value) { + this->container.insert_after(at, value, this->last_element); + // TODO: check if this is now the last element, then remember it + this->changes(at); +} + + +template +void BaseCurve::set_replace(const time_t &at, const T &value) { + this->container.insert_overwrite(at, value, this->last_element); + this->changes(at); +} + + +template +void BaseCurve::erase(const time_t &at) { + this->last_element = this->container.erase(at, this->last_element); + this->changes(at); +} + + +template +std::pair BaseCurve::frame(const time_t &time) const { + auto e = this->container.last(time, this->container.end()); + return std::make_pair(e->time, e->value); +} + + +template +std::pair BaseCurve::next_frame(const time_t &time) const { + auto e = this->container.last(time, this->container.end()); + e++; + return std::make_pair(e->time, e->value); +} + +template +std::string BaseCurve::str() const { + std::stringstream ss; + ss << "Curve[" << this->idstr() << "]{" << std::endl; + for (const auto &keyframe : this->container) { + ss << " " << keyframe.time << ": " << keyframe.value << "," << std::endl; + } + ss << "}"; + + return ss.str(); +} + +template +void BaseCurve::check_integrity() const { + time_t last_time = std::numeric_limits::min(); + for (const auto &keyframe : this->container) { + if (keyframe.time < last_time) { + throw Error{ERR << "curve is broken after t=" << last_time << ": " << this->str()}; + } + last_time = keyframe.time; + } +} + + +} // openage::curve diff --git a/libopenage/curve/continuous.h b/libopenage/curve/continuous.h index b5d31f377e..0d32f14629 100644 --- a/libopenage/curve/continuous.h +++ b/libopenage/curve/continuous.h @@ -5,63 +5,64 @@ #include #include -#include "value_container.h" +#include "interpolated.h" #include "../log/log.h" namespace openage::curve { /** - * Continuous Datatype. - * Stores a value container with continuous access. + * Continuous linear curve. + * + * At one point in time, there can be only one value, + * thus, there can't be jumps. All values are connected through linear + * interpolation. + * * The bound template type T has to implement `operator+(T)` and * `operator*(time_t)`. */ template -class Continuous : public ValueContainer { +class Continuous : public Interpolated { public: - using ValueContainer::ValueContainer; + using Interpolated::Interpolated; + /** - * will interpolate between the keyframes linearly based on the time. + * Insert/overwrite given value at given time and erase all elements + * that follow at a later time. + * If multiple elements exist at the given time, + * overwrite all of them. */ - T get(const time_t &) const override; + void set_last(const time_t &t, const T &value) override; + + /** This just calls set_replace in order to guarantee the continuity. */ + void set_insert(const time_t &t, const T &value) override; /** human readable identifier */ std::string idstr() const override; }; - template -T Continuous::get(const time_t &time) const { - const auto &e = this->container.last(time, this->last_element); - this->last_element = e; +void Continuous::set_last(const time_t &at, const T &value) { + auto hint = this->container.last(at, this->last_element); - auto nxt = e; - ++nxt; + // erase all same-time entries + while (hint->time == at) { + hint--; + } - time_t diff_time = 0; + hint = this->container.erase_after(hint); - auto offset = time - e->time; + this->container.insert_before(at, value, hint); + this->last_element = hint; - // If we do not have a next (buffer underrun!!) we assign values - if (nxt == this->container.end()) { - // log::log(WARN << "Continuous buffer underrun. This might be bad! Assuming constant."); - } else { - diff_time = nxt->time - e->time; - } - - if (nxt == this->container.end() // use the last curve value - || offset == 0 // values equal -> don't need to interpolate - || diff_time == 0) { // values at the same time -> division-by-zero-error + this->changes(at); +} - return e->value; - } else { - // Interpolation between time(now) and time(next) that has elapsed - double elapsed_frac = offset.to_double() / diff_time.to_double(); - return e->value + (nxt->value - e->value) * elapsed_frac; - } +template +void Continuous::set_insert(const time_t &t, const T &value) { + this->set_replace(t, value); } diff --git a/libopenage/curve/discrete.h b/libopenage/curve/discrete.h index a8b7d7297c..2d95a17b78 100644 --- a/libopenage/curve/discrete.h +++ b/libopenage/curve/discrete.h @@ -6,7 +6,7 @@ #include #include -#include "value_container.h" +#include "base_curve.h" namespace openage::curve { @@ -16,13 +16,13 @@ namespace openage::curve { * implement `operator=` and copy ctor. */ template -class Discrete : public ValueContainer { +class Discrete : public BaseCurve { static_assert(std::is_copy_assignable::value, "Template type is not copy assignable"); static_assert(std::is_copy_constructible::value, "Template type is not copy constructible"); public: - using ValueContainer::ValueContainer; + using BaseCurve::BaseCurve; /** * Does not interpolate anything, diff --git a/libopenage/curve/interpolated.cpp b/libopenage/curve/interpolated.cpp new file mode 100644 index 0000000000..e1dc1fccbc --- /dev/null +++ b/libopenage/curve/interpolated.cpp @@ -0,0 +1,3 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#include "interpolated.h" diff --git a/libopenage/curve/interpolated.h b/libopenage/curve/interpolated.h new file mode 100644 index 0000000000..38cbe3106c --- /dev/null +++ b/libopenage/curve/interpolated.h @@ -0,0 +1,77 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "base_curve.h" +#include "error.h" +#include "../log/log.h" + +namespace openage::curve { + +/** + * Interpolation base class. + * + * Extends the value container to support interpolation between values. + * + * The bound template type T has to implement `operator +(T)` and + * `operator *(time_t)`. + */ +template +class Interpolated : public BaseCurve { +public: + using BaseCurve::BaseCurve; + + /** + * Will interpolate between the keyframes linearly based on the time. + * Picks two adjacent keyframes for interpolation. + * Uses the leftmost element of the right keyframe group with same times + * and the rightmost element of the left keyframe group with same times. + * A curve of [0:0, 1:5, 1:10, 2:20] evaluated at t=0.5 is 2.5, t=1 is 10, + * at t=1.5 is 15. + * + * example for a <= t <= b: + * val([a:x, b:y], t) = x + (y - x)/(b - a) * (t - a) + */ + + T get(const time_t &) const override; +}; + + + +template +T Interpolated::get(const time_t &time) const { + const auto &e = this->container.last(time, this->last_element); + this->last_element = e; + + auto nxt = e; + ++nxt; + + time_t interval = 0; + + auto offset = time - e->time; + + if (nxt != this->container.end()) { + interval = nxt->time - e->time; + } + + // here, offset > interval will never hold. + // otherwise the underlying storage is broken. + + // If the next element is at the same time, just return the value of this one. + if (nxt == this->container.end() // use the last curve value + || offset == 0 // values equal -> don't need to interpolate + || interval == 0) { // values at the same time -> division-by-zero-error + + return e->value; + } + else { + // Interpolation between time(now) and time(next) that has elapsed + double elapsed_frac = offset.to_double() / interval.to_double(); + return e->value + (nxt->value - e->value) * elapsed_frac; + } +} + + +} // openage::curve diff --git a/libopenage/curve/keyframe.cpp b/libopenage/curve/keyframe.cpp new file mode 100644 index 0000000000..c497e7d869 --- /dev/null +++ b/libopenage/curve/keyframe.cpp @@ -0,0 +1,3 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#include "keyframe.h" diff --git a/libopenage/curve/keyframe.h b/libopenage/curve/keyframe.h new file mode 100644 index 0000000000..a0fc39d1e6 --- /dev/null +++ b/libopenage/curve/keyframe.h @@ -0,0 +1,40 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "curve.h" + +namespace openage::curve { + +/** + * A element of the curvecontainer. This is especially used to keep track of + * the value-timing. + */ +template +class Keyframe { +public: + /** + * New default object at numericlimits