diff --git a/doc/code/game_simulation/images/activity_graph.svg b/doc/code/game_simulation/images/activity_graph.svg
index bf87f779fa..4044797dc2 100644
--- a/doc/code/game_simulation/images/activity_graph.svg
+++ b/doc/code/game_simulation/images/activity_graph.svg
@@ -1,4 +1,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/libopenage/curve/base_curve.h b/libopenage/curve/base_curve.h
index 8c56586637..bd5b6003ae 100644
--- a/libopenage/curve/base_curve.h
+++ b/libopenage/curve/base_curve.h
@@ -115,7 +115,7 @@ class BaseCurve : public event::EventEntity {
* the keyframes of \p other.
*/
void sync(const BaseCurve &other,
- const time::time_t &start = std::numeric_limits::min());
+ const time::time_t &start = time::TIME_MIN);
/**
* Copy keyframes from another curve (with a different element type) to this curve.
@@ -134,7 +134,7 @@ class BaseCurve : public event::EventEntity {
template
void sync(const BaseCurve &other,
const std::function &converter,
- const time::time_t &start = std::numeric_limits::min());
+ const time::time_t &start = time::TIME_MIN);
/**
* Get the identifier of this curve.
@@ -270,7 +270,7 @@ std::string BaseCurve::str() const {
template
void BaseCurve::check_integrity() const {
- time::time_t last_time = std::numeric_limits::min();
+ time::time_t last_time = time::TIME_MIN;
for (const auto &keyframe : this->container) {
if (keyframe.time < last_time) {
throw Error{MSG(err) << "curve is broken after t=" << last_time << ": " << this->str()};
diff --git a/libopenage/curve/iterator.h b/libopenage/curve/iterator.h
index 2500e083cd..1062ddcc1e 100644
--- a/libopenage/curve/iterator.h
+++ b/libopenage/curve/iterator.h
@@ -33,8 +33,8 @@ class CurveIterator {
explicit CurveIterator(const container_t *c) :
base{},
container{c},
- from{-std::numeric_limits::max()},
- to{+std::numeric_limits::max()} {}
+ from{-time::TIME_MAX},
+ to{+time::TIME_MAX} {}
protected:
/**
diff --git a/libopenage/curve/keyframe.h b/libopenage/curve/keyframe.h
index d6b90af4b5..82e36ed147 100644
--- a/libopenage/curve/keyframe.h
+++ b/libopenage/curve/keyframe.h
@@ -33,7 +33,7 @@ class Keyframe {
time{time},
value{value} {}
- const time::time_t time = std::numeric_limits::min();
+ const time::time_t time = time::TIME_MIN;
T value = T{};
};
diff --git a/libopenage/curve/keyframe_container.h b/libopenage/curve/keyframe_container.h
index 1d7079498b..13b50bd04f 100644
--- a/libopenage/curve/keyframe_container.h
+++ b/libopenage/curve/keyframe_container.h
@@ -281,7 +281,7 @@ class KeyframeContainer {
* the keyframes of \p other.
*/
iterator sync(const KeyframeContainer &other,
- const time::time_t &start = std::numeric_limits::min());
+ const time::time_t &start = time::TIME_MIN);
/**
* Copy keyframes from another container (with a different element type) to this container.
@@ -298,7 +298,7 @@ class KeyframeContainer {
template
iterator sync(const KeyframeContainer &other,
const std::function &converter,
- const time::time_t &start = std::numeric_limits::min());
+ const time::time_t &start = time::TIME_MIN);
/**
* Debugging method to be used from gdb to understand bugs better.
@@ -328,7 +328,7 @@ template
KeyframeContainer::KeyframeContainer() {
// Create a default element at -Inf, that can always be dereferenced - so
// there will by definition never be a element that cannot be dereferenced
- this->container.push_back(keyframe_t(std::numeric_limits::min(), T()));
+ this->container.push_back(keyframe_t(time::TIME_MIN, T()));
}
@@ -336,7 +336,7 @@ template
KeyframeContainer::KeyframeContainer(const T &defaultval) {
// Create a default element at -Inf, that can always be dereferenced - so
// there will by definition never be a element that cannot be dereferenced
- this->container.push_back(keyframe_t(std::numeric_limits::min(), defaultval));
+ this->container.push_back(keyframe_t(time::TIME_MIN, defaultval));
}
diff --git a/libopenage/curve/map.h b/libopenage/curve/map.h
index 3a8374a128..9712c89470 100644
--- a/libopenage/curve/map.h
+++ b/libopenage/curve/map.h
@@ -48,10 +48,10 @@ class UnorderedMap {
at(const time::time_t &, const key_t &) const;
MapFilterIterator
- begin(const time::time_t &e = std::numeric_limits::max()) const;
+ begin(const time::time_t &e = time::TIME_MAX) const;
MapFilterIterator
- end(const time::time_t &e = std::numeric_limits::max()) const;
+ end(const time::time_t &e = time::TIME_MAX) const;
MapFilterIterator
insert(const time::time_t &birth, const key_t &, const val_t &);
@@ -100,7 +100,7 @@ UnorderedMap::at(const time::time_t &time,
e,
this,
time,
- std::numeric_limits::max());
+ time::TIME_MAX);
}
else {
return {};
@@ -114,7 +114,7 @@ UnorderedMap::begin(const time::time_t &time) const {
this->container.begin(),
this,
time,
- std::numeric_limits::max());
+ time::TIME_MAX);
}
template
@@ -123,7 +123,7 @@ UnorderedMap::end(const time::time_t &time) const {
return MapFilterIterator>(
this->container.end(),
this,
- -std::numeric_limits::max(),
+ -time::TIME_MAX,
time);
}
@@ -149,7 +149,7 @@ UnorderedMap::insert(const time::time_t &alive,
const val_t &value) {
return this->insert(
alive,
- std::numeric_limits::max(),
+ time::TIME_MAX,
key,
value);
}
diff --git a/libopenage/curve/queue.h b/libopenage/curve/queue.h
index 3ac8d20d9a..32675a72d3 100644
--- a/libopenage/curve/queue.h
+++ b/libopenage/curve/queue.h
@@ -53,7 +53,7 @@ class Queue : public event::EventEntity {
EventEntity{loop},
_id{id},
_idstr{idstr},
- last_front{this->container.begin()} {}
+ last_pop{time::TIME_ZERO} {}
// prevent accidental copy of queue
Queue(const Queue &) = delete;
@@ -69,12 +69,13 @@ class Queue : public event::EventEntity {
const T &front(const time::time_t &time) const;
/**
- * Get the first element in the queue at the given time.
+ * Get the first element in the queue at the given time and remove it from
+ * the queue.
*
* @param time The time to get the element at.
* @param value Queue element.
*/
- const T &pop_front(const time::time_t &time);
+ const T pop_front(const time::time_t &time);
/**
* Check if the queue is empty at a given time.
@@ -92,7 +93,7 @@ class Queue : public event::EventEntity {
* @return Iterator to the first element.
*/
QueueFilterIterator> begin(
- const time::time_t &t = -std::numeric_limits::max()) const;
+ const time::time_t &t = -time::TIME_MAX) const;
/**
* Get an iterator to the last element in the queue at the given time.
@@ -101,7 +102,7 @@ class Queue : public event::EventEntity {
* @return Iterator to the last element.
*/
QueueFilterIterator> end(
- const time::time_t &t = std::numeric_limits::max()) const;
+ const time::time_t &t = time::TIME_MAX) const;
/**
* Get an iterator to elements that are in the queue between two time frames.
@@ -111,8 +112,8 @@ class Queue : public event::EventEntity {
* @return Iterator to the first element in the time frame.
*/
QueueFilterIterator> between(
- const time::time_t &begin = std::numeric_limits::max(),
- const time::time_t &end = std::numeric_limits::max()) const;
+ const time::time_t &begin = time::TIME_MAX,
+ const time::time_t &end = time::TIME_MAX) const;
/**
* Erase an element from the queue.
@@ -183,24 +184,72 @@ class Queue : public event::EventEntity {
*/
container_t container;
- iterator last_front;
+ /**
+ * The time of the last access to the queue.
+ */
+ time::time_t last_pop;
};
template
-const T &Queue::front(const time::time_t &t) const {
- return this->begin(t).value();
+const T &Queue::front(const time::time_t &time) const {
+ if (this->empty(time)) [[unlikely]] {
+ throw Error{MSG(err) << "Tried accessing front at "
+ << time << " but queue is empty."};
+ }
+
+ // search for the last element before the given time
+ auto it = this->container.end();
+ --it;
+ while (it->time() > time and it != this->container.begin()) {
+ --it;
+ }
+
+ return it->value;
}
template
-bool Queue::empty(const time::time_t &time) const {
- return this->last_front == this->begin(time).get_base();
+const T Queue::pop_front(const time::time_t &time) {
+ if (this->empty(time)) [[unlikely]] {
+ throw Error{MSG(err) << "Tried accessing front at "
+ << time << " but queue is empty."};
+ }
+
+ // search for the last element before the given time
+ auto it = this->container.end();
+ --it;
+ while (it->time() > time and it != this->container.begin()) {
+ --it;
+ }
+
+ // get the last element inserted before the given time
+ auto val = std::move(it->value);
+
+ // get the time span between current time and the next element
+ auto to = (++it)->time();
+ --it;
+ auto from = time;
+
+ // erase the element
+ // TODO: We should be able to reinsert elements
+ auto filter_iterator = QueueFilterIterator>(it, this, to, from);
+ this->erase(filter_iterator);
+
+ this->last_pop = time;
+
+ return val;
}
template
-inline const T &Queue::pop_front(const time::time_t &time) {
- this->last_front = this->begin(time).get_base();
- return this->front(time);
+bool Queue::empty(const time::time_t &time) const {
+ if (this->container.empty()) {
+ return true;
+ }
+
+ // search for the first element that is after the given time
+ auto begin = this->begin(time).get_base();
+
+ return begin == this->container.begin() and begin->time() > time;
}
template
@@ -211,7 +260,7 @@ QueueFilterIterator> Queue::begin(const time::time_t &t) const {
it,
this,
t,
- std::numeric_limits::max());
+ time::TIME_MAX);
}
}
@@ -225,20 +274,19 @@ QueueFilterIterator> Queue::end(const time::time_t &t) const {
container.end(),
this,
t,
- std::numeric_limits::max());
+ time::TIME_MAX);
}
template
-QueueFilterIterator> Queue::between(
- const time::time_t &begin,
- const time::time_t &end) const {
+QueueFilterIterator> Queue::between(const time::time_t &begin,
+ const time::time_t &end) const {
auto it = QueueFilterIterator>(
container.begin(),
this,
begin,
end);
- if (!container.empty() && !it.valid()) {
+ if (not it.valid()) {
++it;
}
return it;
@@ -253,9 +301,8 @@ void Queue::erase(const CurveIterator> &it) {
template
-QueueFilterIterator> Queue::insert(
- const time::time_t &time,
- const T &e) {
+QueueFilterIterator> Queue::insert(const time::time_t &time,
+ const T &e) {
const_iterator insertion_point = this->container.end();
for (auto it = this->container.begin(); it != this->container.end(); ++it) {
if (time < it->time()) {
@@ -272,7 +319,7 @@ QueueFilterIterator> Queue::insert(
insertion_point,
this,
time,
- std::numeric_limits::max());
+ time::TIME_MAX);
if (!ct.valid()) {
++ct;
diff --git a/libopenage/curve/tests/container.cpp b/libopenage/curve/tests/container.cpp
index a5efbf2887..9787243db3 100644
--- a/libopenage/curve/tests/container.cpp
+++ b/libopenage/curve/tests/container.cpp
@@ -149,11 +149,25 @@ void test_queue() {
auto loop = std::make_shared();
Queue q{loop, 0};
- q.insert(0, 1);
+
+ TESTEQUALS(q.empty(0), true);
+ TESTEQUALS(q.empty(1), true);
+ TESTEQUALS(q.empty(100001), true);
+
q.insert(2, 2);
q.insert(4, 3);
q.insert(10, 4);
q.insert(100001, 5);
+ q.insert(100001, 6);
+
+ TESTEQUALS(q.empty(0), true);
+ TESTEQUALS(q.empty(1), true);
+ TESTEQUALS(q.empty(2), false);
+ TESTEQUALS(q.empty(100001), false);
+ TESTEQUALS(q.empty(100002), false);
+
+ q.insert(0, 1);
+
TESTEQUALS(*q.begin(0), 1);
TESTEQUALS(*q.begin(1), 2);
TESTEQUALS(*q.begin(2), 2);
@@ -164,6 +178,17 @@ void test_queue() {
TESTEQUALS(*q.begin(12), 5);
TESTEQUALS(*q.begin(100000), 5);
+ TESTEQUALS(q.front(0), 1);
+ TESTEQUALS(q.front(1), 1);
+ TESTEQUALS(q.front(2), 2);
+ TESTEQUALS(q.front(3), 2);
+ TESTEQUALS(q.front(4), 3);
+ TESTEQUALS(q.front(5), 3);
+ TESTEQUALS(q.front(10), 4);
+ TESTEQUALS(q.front(12), 4);
+ TESTEQUALS(q.front(100000), 4);
+ TESTEQUALS(q.front(100001), 6);
+
{
std::unordered_set reference = {1, 2, 3};
for (auto it = q.between(0, 6); it != q.end(); ++it) {
@@ -204,6 +229,19 @@ void test_queue() {
}
TESTEQUALS(reference.empty(), true);
}
+
+
+ TESTEQUALS(q.pop_front(0), 1);
+ TESTEQUALS(q.empty(0), true);
+
+ TESTEQUALS(q.pop_front(12), 4);
+ TESTEQUALS(q.empty(12), false);
+
+ TESTEQUALS(q.pop_front(12), 3);
+ TESTEQUALS(q.empty(12), false);
+
+ TESTEQUALS(q.pop_front(12), 2);
+ TESTEQUALS(q.empty(12), true);
}
diff --git a/libopenage/curve/tests/curve_types.cpp b/libopenage/curve/tests/curve_types.cpp
index cab8473bc3..40f20395ee 100644
--- a/libopenage/curve/tests/curve_types.cpp
+++ b/libopenage/curve/tests/curve_types.cpp
@@ -37,7 +37,7 @@ void curve_types() {
{
auto it = c.begin();
TESTEQUALS(it->value, 0);
- TESTEQUALS(it->time, std::numeric_limits::min());
+ TESTEQUALS(it->time, time::TIME_MIN);
TESTEQUALS((++it)->time, 0);
TESTEQUALS(it->value, 0);
TESTEQUALS((++it)->time, 1);
@@ -140,7 +140,7 @@ void curve_types() {
{
auto it = c.begin();
- TESTEQUALS(it->time, std::numeric_limits::min());
+ TESTEQUALS(it->time, time::TIME_MIN);
TESTEQUALS(it->value, 0);
TESTEQUALS((++it)->time, 0);
diff --git a/libopenage/event/demo/physics.cpp b/libopenage/event/demo/physics.cpp
index d43365f042..e27a51d962 100644
--- a/libopenage/event/demo/physics.cpp
+++ b/libopenage/event/demo/physics.cpp
@@ -104,7 +104,7 @@ class BallReflectWall : public DependencyEventHandler {
auto pos = positioncurve->get(now);
if (speed[1] == 0) {
- return std::numeric_limits::max();
+ return time::TIME_MAX;
}
time::time_t ty = 0;
@@ -227,7 +227,7 @@ class BallReflectPanel : public DependencyEventHandler {
auto pos = positioncurve->get(now);
if (speed[0] == 0)
- return std::numeric_limits::max();
+ return time::TIME_MAX;
time::time_t ty = 0;
diff --git a/libopenage/event/event.h b/libopenage/event/event.h
index 72c943d807..34fcbc0cee 100644
--- a/libopenage/event/event.h
+++ b/libopenage/event/event.h
@@ -12,6 +12,8 @@
namespace openage::event {
class EventEntity;
+using event_hash_t = size_t;
+
/**
* The actual one event that may be called - it is used to manage the event itself.
* It does not need to be stored.
@@ -36,7 +38,7 @@ class Event : public std::enable_shared_from_this {
*/
void reschedule(const time::time_t reference_time);
- size_t hash() const {
+ event_hash_t hash() const {
return this->myhash;
}
@@ -111,7 +113,7 @@ class Event : public std::enable_shared_from_this {
time::time_t last_change_time = time::time_t::min_value();
/** Precalculated std::hash for the event */
- size_t myhash;
+ event_hash_t myhash;
};
diff --git a/libopenage/event/event_loop.cpp b/libopenage/event/event_loop.cpp
index eb9e8ca6f1..26bf176978 100644
--- a/libopenage/event/event_loop.cpp
+++ b/libopenage/event/event_loop.cpp
@@ -151,7 +151,7 @@ int EventLoop::execute_events(const time::time_t &time_until,
time::time_t new_time = event->get_eventhandler()->predict_invoke_time(
target, state, event->get_time());
- if (new_time != std::numeric_limits::min()) {
+ if (new_time != time::TIME_MIN) {
event->set_time(new_time);
log::log(DBG << "Loop: repeating event \"" << event->get_eventhandler()->id()
@@ -204,7 +204,7 @@ void EventLoop::update_changes(const std::shared_ptr &state) {
time::time_t new_time = evnt->get_eventhandler()
->predict_invoke_time(entity, state, change.time);
- if (new_time != std::numeric_limits::min()) {
+ if (new_time != time::TIME_MIN) {
log::log(DBG << "Loop: due to a change, rescheduling event of '"
<< evnt->get_eventhandler()->id()
<< "' on entity '" << entity->idstr()
diff --git a/libopenage/event/eventqueue.cpp b/libopenage/event/eventqueue.cpp
index 83a96e6e16..51a0c2fdaa 100644
--- a/libopenage/event/eventqueue.cpp
+++ b/libopenage/event/eventqueue.cpp
@@ -35,7 +35,7 @@ std::shared_ptr EventQueue::create_event(const std::shared_ptrset_time(event->get_eventhandler()
->predict_invoke_time(trgt, state, reference_time));
- if (event->get_time() == std::numeric_limits::min()) {
+ if (event->get_time() == time::TIME_MIN) {
log::log(DBG << "Queue: ignoring insertion of event "
<< event->get_eventhandler()->id() << " because no execution was scheduled.");
diff --git a/libopenage/gamestate/activity/CMakeLists.txt b/libopenage/gamestate/activity/CMakeLists.txt
index 27b43c1776..78a78e7ab0 100644
--- a/libopenage/gamestate/activity/CMakeLists.txt
+++ b/libopenage/gamestate/activity/CMakeLists.txt
@@ -1,12 +1,15 @@
add_sources(libopenage
activity.cpp
end_node.cpp
- event_node.cpp
node.cpp
start_node.cpp
task_node.cpp
task_system_node.cpp
tests.cpp
types.cpp
- xor_node.cpp
+ xor_event_gate.cpp
+ xor_gate.cpp
)
+
+add_subdirectory("event")
+add_subdirectory("condition")
diff --git a/libopenage/gamestate/activity/activity.cpp b/libopenage/gamestate/activity/activity.cpp
index 7ee478eda8..9af770b8fd 100644
--- a/libopenage/gamestate/activity/activity.cpp
+++ b/libopenage/gamestate/activity/activity.cpp
@@ -6,8 +6,8 @@
namespace openage::gamestate::activity {
Activity::Activity(activity_id id,
- activity_label label,
- const std::shared_ptr &start) :
+ const std::shared_ptr &start,
+ activity_label label) :
id{id},
label{label},
start{start} {
diff --git a/libopenage/gamestate/activity/activity.h b/libopenage/gamestate/activity/activity.h
index f5be9d254a..d0832068ad 100644
--- a/libopenage/gamestate/activity/activity.h
+++ b/libopenage/gamestate/activity/activity.h
@@ -18,19 +18,52 @@ using activity_label = std::string;
*/
class Activity {
public:
+ /**
+ * Create a new activity.
+ *
+ * @param id Unique ID.
+ * @param start Start node in the graph.
+ * @param label Human-readable label (optional).
+ */
Activity(activity_id id,
- activity_label label = "",
- const std::shared_ptr &start = {});
+ const std::shared_ptr &start,
+ activity_label label = "");
+ /**
+ * Get the unique ID of this activity.
+ *
+ * @return Unique ID.
+ */
activity_id get_id() const;
+ /**
+ * Get the human-readable label of this activity.
+ *
+ * @return Human-readable label.
+ */
const activity_label get_label() const;
+ /**
+ * Get the start node of this activity.
+ *
+ * @return Start node.
+ */
const std::shared_ptr &get_start() const;
private:
+ /**
+ * Unique ID.
+ */
const activity_id id;
+
+ /**
+ * Human-readable label.
+ */
const activity_label label;
+
+ /**
+ * Start node.
+ */
std::shared_ptr start;
};
diff --git a/libopenage/gamestate/activity/condition/CMakeLists.txt b/libopenage/gamestate/activity/condition/CMakeLists.txt
new file mode 100644
index 0000000000..aabd159b0c
--- /dev/null
+++ b/libopenage/gamestate/activity/condition/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_sources(libopenage
+ command_in_queue.cpp
+ next_command.cpp
+)
diff --git a/libopenage/gamestate/activity/condition/command_in_queue.cpp b/libopenage/gamestate/activity/condition/command_in_queue.cpp
new file mode 100644
index 0000000000..7e300701f1
--- /dev/null
+++ b/libopenage/gamestate/activity/condition/command_in_queue.cpp
@@ -0,0 +1,19 @@
+// Copyright 2023-2023 the openage authors. See copying.md for legal info.
+
+#include "next_command.h"
+
+#include "gamestate/component/internal/command_queue.h"
+#include "gamestate/game_entity.h"
+
+
+namespace openage::gamestate::activity {
+
+bool command_in_queue(const time::time_t &time,
+ const std::shared_ptr &entity) {
+ auto command_queue = std::dynamic_pointer_cast(
+ entity->get_component(component::component_t::COMMANDQUEUE));
+
+ return not command_queue->get_queue().empty(time);
+}
+
+} // namespace openage::gamestate::activity
diff --git a/libopenage/gamestate/activity/condition/command_in_queue.h b/libopenage/gamestate/activity/condition/command_in_queue.h
new file mode 100644
index 0000000000..67c7a794cc
--- /dev/null
+++ b/libopenage/gamestate/activity/condition/command_in_queue.h
@@ -0,0 +1,28 @@
+// Copyright 2023-2023 the openage authors. See copying.md for legal info.
+
+#pragma once
+
+#include
+#include
+
+#include "time/time.h"
+
+
+namespace openage::gamestate {
+class GameEntity;
+
+namespace activity {
+
+/**
+ * Condition for command in queue check in the activity system.
+ *
+ * @param time Time when the condition is checked.
+ * @param entity Game entity.
+ *
+ * @return true if there is at least one command in the entity's command queue, false otherwise.
+ */
+bool command_in_queue(const time::time_t &time,
+ const std::shared_ptr &entity);
+
+} // namespace activity
+} // namespace openage::gamestate
diff --git a/libopenage/gamestate/activity/condition/next_command.cpp b/libopenage/gamestate/activity/condition/next_command.cpp
new file mode 100644
index 0000000000..c0b619a783
--- /dev/null
+++ b/libopenage/gamestate/activity/condition/next_command.cpp
@@ -0,0 +1,37 @@
+// Copyright 2023-2023 the openage authors. See copying.md for legal info.
+
+#include "next_command.h"
+
+#include "gamestate/component/internal/command_queue.h"
+#include "gamestate/game_entity.h"
+
+
+namespace openage::gamestate::activity {
+
+bool next_command_idle(const time::time_t &time,
+ const std::shared_ptr &entity) {
+ auto command_queue = std::dynamic_pointer_cast(
+ entity->get_component(component::component_t::COMMANDQUEUE));
+
+ if (command_queue->get_queue().empty(time)) {
+ return false;
+ }
+
+ auto command = command_queue->get_queue().front(time);
+ return command->get_type() == component::command::command_t::MOVE;
+}
+
+bool next_command_move(const time::time_t &time,
+ const std::shared_ptr &entity) {
+ auto command_queue = std::dynamic_pointer_cast(
+ entity->get_component(component::component_t::COMMANDQUEUE));
+
+ if (command_queue->get_queue().empty(time)) {
+ return false;
+ }
+
+ auto command = command_queue->get_queue().front(time);
+ return command->get_type() == component::command::command_t::MOVE;
+}
+
+} // namespace openage::gamestate::activity
diff --git a/libopenage/gamestate/activity/condition/next_command.h b/libopenage/gamestate/activity/condition/next_command.h
new file mode 100644
index 0000000000..046a18cec6
--- /dev/null
+++ b/libopenage/gamestate/activity/condition/next_command.h
@@ -0,0 +1,39 @@
+// Copyright 2023-2023 the openage authors. See copying.md for legal info.
+
+#pragma once
+
+#include
+#include
+
+#include "time/time.h"
+
+
+namespace openage::gamestate {
+class GameEntity;
+
+namespace activity {
+
+/**
+ * Condition for next command check in the activity system.
+ *
+ * @param time Time when the condition is checked.
+ * @param entity Game entity.
+ *
+ * @return true if the entity has a idle command next in the queue, false otherwise.
+ */
+bool next_command_idle(const time::time_t &time,
+ const std::shared_ptr &entity);
+
+/**
+ * Condition for next command check in the activity system.
+ *
+ * @param time Time when the condition is checked.
+ * @param entity Game entity.
+ *
+ * @return true if the entity has a move command next in the queue, false otherwise.
+ */
+bool next_command_move(const time::time_t &time,
+ const std::shared_ptr &entity);
+
+} // namespace activity
+} // namespace openage::gamestate
diff --git a/libopenage/gamestate/activity/end_node.cpp b/libopenage/gamestate/activity/end_node.cpp
index 01ecfebade..f933a08172 100644
--- a/libopenage/gamestate/activity/end_node.cpp
+++ b/libopenage/gamestate/activity/end_node.cpp
@@ -8,13 +8,9 @@
namespace openage::gamestate::activity {
-EndNode::EndNode(node_id id,
- node_label label) :
+EndNode::EndNode(node_id_t id,
+ node_label_t label) :
Node{id, label, {}} {
}
-void EndNode::add_output(const std::shared_ptr & /* output */) {
- throw Error{ERR << "End node cannot have outputs"};
-}
-
} // namespace openage::gamestate::activity
diff --git a/libopenage/gamestate/activity/end_node.h b/libopenage/gamestate/activity/end_node.h
index c068f39475..e20323d04a 100644
--- a/libopenage/gamestate/activity/end_node.h
+++ b/libopenage/gamestate/activity/end_node.h
@@ -26,22 +26,13 @@ class EndNode : public Node {
* @param id Unique identifier for this node.
* @param label Human-readable label (optional).
*/
- EndNode(node_id id,
- node_label label = "End");
+ EndNode(node_id_t id,
+ node_label_t label = "End");
virtual ~EndNode() = default;
inline node_t get_type() const override {
return node_t::END;
}
-
- /**
- * Throws an error since end nodes are not supposed to have outputs
- *
- * @param output Output node.
- *
- * @throws openage::Error
- */
- [[noreturn]] void add_output(const std::shared_ptr &output) override;
};
} // namespace openage::gamestate::activity
diff --git a/libopenage/gamestate/activity/event/CMakeLists.txt b/libopenage/gamestate/activity/event/CMakeLists.txt
new file mode 100644
index 0000000000..863fa6d28a
--- /dev/null
+++ b/libopenage/gamestate/activity/event/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_sources(libopenage
+ command_in_queue.cpp
+ wait.cpp
+)
diff --git a/libopenage/gamestate/activity/event/command_in_queue.cpp b/libopenage/gamestate/activity/event/command_in_queue.cpp
new file mode 100644
index 0000000000..af57692078
--- /dev/null
+++ b/libopenage/gamestate/activity/event/command_in_queue.cpp
@@ -0,0 +1,35 @@
+// Copyright 2023-2023 the openage authors. See copying.md for legal info.
+
+#include "command_in_queue.h"
+
+#include "event/event_loop.h"
+#include "event/evententity.h"
+#include "gamestate/component/internal/command_queue.h"
+#include "gamestate/game_entity.h"
+#include "gamestate/game_state.h"
+#include "gamestate/manager.h"
+
+
+namespace openage::gamestate::activity {
+
+std::shared_ptr primer_command_in_queue(const time::time_t &,
+ const std::shared_ptr &entity,
+ const std::shared_ptr &loop,
+ const std::shared_ptr &state,
+ size_t next_id) {
+ openage::event::EventHandler::param_map::map_t params{{"next", next_id}}; // move->get_id();
+ auto ev = loop->create_event("game.process_command",
+ entity->get_manager(),
+ state,
+ // event is not executed until a command is available
+ time::TIME_MAX,
+ params);
+ auto entity_queue = std::dynamic_pointer_cast(
+ entity->get_component(component::component_t::COMMANDQUEUE));
+ auto &queue = entity_queue->get_queue();
+ queue.add_dependent(ev);
+
+ return ev;
+};
+
+} // namespace openage::gamestate::activity
diff --git a/libopenage/gamestate/activity/event/command_in_queue.h b/libopenage/gamestate/activity/event/command_in_queue.h
new file mode 100644
index 0000000000..ebb71c166d
--- /dev/null
+++ b/libopenage/gamestate/activity/event/command_in_queue.h
@@ -0,0 +1,42 @@
+// Copyright 2023-2023 the openage authors. See copying.md for legal info.
+
+#pragma once
+
+#include
+#include
+
+#include "time/time.h"
+
+
+namespace openage {
+namespace event {
+class Event;
+class EventLoop;
+} // namespace event
+
+namespace gamestate {
+class GameEntity;
+class GameState;
+
+namespace activity {
+
+/**
+ * Primer for command in queue events in the activity system.
+ *
+ * @param time Current simulation time.
+ * @param entity Game entity.
+ * @param loop Event loop that the event is registered on.
+ * @param state Game state.
+ * @param next_id ID of the next node in the activity graph.
+ *
+ * @return Scheduled event.
+ */
+std::shared_ptr primer_command_in_queue(const time::time_t &,
+ const std::shared_ptr &entity,
+ const std::shared_ptr &loop,
+ const std::shared_ptr &state,
+ size_t next_id);
+
+} // namespace activity
+} // namespace gamestate
+} // namespace openage
diff --git a/libopenage/gamestate/activity/event/wait.cpp b/libopenage/gamestate/activity/event/wait.cpp
new file mode 100644
index 0000000000..6bfd03f632
--- /dev/null
+++ b/libopenage/gamestate/activity/event/wait.cpp
@@ -0,0 +1,29 @@
+// Copyright 2023-2023 the openage authors. See copying.md for legal info.
+
+#include "wait.h"
+
+#include "event/event_loop.h"
+#include "event/evententity.h"
+#include "gamestate/game_entity.h"
+#include "gamestate/game_state.h"
+#include "gamestate/manager.h"
+
+
+namespace openage::gamestate::activity {
+
+std::shared_ptr primer_wait(const time::time_t &time,
+ const std::shared_ptr &entity,
+ const std::shared_ptr &loop,
+ const std::shared_ptr &state,
+ size_t next_id) {
+ openage::event::EventHandler::param_map::map_t params{{"next", next_id}};
+ auto ev = loop->create_event("game.wait",
+ entity->get_manager(),
+ state,
+ time,
+ params);
+
+ return ev;
+};
+
+} // namespace openage::gamestate::activity
diff --git a/libopenage/gamestate/activity/event/wait.h b/libopenage/gamestate/activity/event/wait.h
new file mode 100644
index 0000000000..c33be2a5be
--- /dev/null
+++ b/libopenage/gamestate/activity/event/wait.h
@@ -0,0 +1,44 @@
+// Copyright 2023-2023 the openage authors. See copying.md for legal info.
+
+#pragma once
+
+#include
+#include
+
+#include "time/time.h"
+
+
+namespace openage {
+namespace event {
+class Event;
+class EventLoop;
+} // namespace event
+
+namespace gamestate {
+class GameEntity;
+class GameState;
+
+namespace activity {
+
+
+/**
+ * Primer for wait events in the activity system.
+ *
+ * @param time Wait until this time. If the time is in the past, the event is executed immediately.
+ * @param entity Game entity.
+ * @param loop Event loop that the event is registered on.
+ * @param state Game state.
+ * @param next_id ID of the next node in the activity graph.
+ *
+ * @return Scheduled event.
+ */
+std::shared_ptr primer_wait(const time::time_t &time,
+ const std::shared_ptr &entity,
+ const std::shared_ptr &loop,
+ const std::shared_ptr &state,
+ size_t next_id);
+
+
+} // namespace activity
+} // namespace gamestate
+} // namespace openage
diff --git a/libopenage/gamestate/activity/event_node.cpp b/libopenage/gamestate/activity/event_node.cpp
deleted file mode 100644
index 3d28e007a3..0000000000
--- a/libopenage/gamestate/activity/event_node.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2023-2023 the openage authors. See copying.md for legal info.
-
-#include "event_node.h"
-
-#include
-
-
-namespace openage::gamestate::activity {
-
-XorEventGate::XorEventGate(node_id id,
- node_label label,
- const std::vector> &outputs,
- event_primer_func_t primer_func,
- event_next_func_t next_func) :
- Node{id, label, outputs},
- primer_func{primer_func},
- next_func{next_func} {
-}
-
-void XorEventGate::add_output(const std::shared_ptr &output) {
- this->outputs.emplace(output->get_id(), output);
-}
-
-void XorEventGate::set_primer_func(event_primer_func_t primer_func) {
- this->primer_func = primer_func;
-}
-
-void XorEventGate::set_next_func(event_next_func_t next_func) {
- this->next_func = next_func;
-}
-
-event_primer_func_t XorEventGate::get_primer_func() const {
- return this->primer_func;
-}
-
-event_next_func_t XorEventGate::get_next_func() const {
- return this->next_func;
-}
-
-} // namespace openage::gamestate::activity
diff --git a/libopenage/gamestate/activity/event_node.h b/libopenage/gamestate/activity/event_node.h
deleted file mode 100644
index a753682b68..0000000000
--- a/libopenage/gamestate/activity/event_node.h
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright 2023-2023 the openage authors. See copying.md for legal info.
-
-#pragma once
-
-#include
-#include
-#include
-
-#include "error/error.h"
-#include "log/message.h"
-
-#include "gamestate/activity/node.h"
-#include "gamestate/activity/types.h"
-#include "time/time.h"
-
-
-namespace openage {
-namespace event {
-class Event;
-class EventLoop;
-} // namespace event
-
-namespace gamestate {
-class GameEntity;
-class GameState;
-
-namespace activity {
-
-using event_store_t = std::vector>;
-
-/* */
-/**
- * Create and register an event on the event loop
- *
- * @param time Time at which the primer function is executed.
- * @param entity Game entity that the node is associated with.
- * @param loop Event loop that events are registered on.
- * @param state Game state.
- *
- * @return List of events registered on the event loop.
- */
-using event_primer_func_t = std::function &,
- const std::shared_ptr &,
- const std::shared_ptr &)>;
-
-/**
- * Decide which node to visit after the event is handled.
- *
- * @param time Time at which the next function is executed.
- * @param entity Game entity that the node is associated with.
- * @param loop Event loop that events are registered on.
- * @param state Game state.
- *
- * @return ID of the next node to visit.
- */
-using event_next_func_t = std::function &,
- const std::shared_ptr &,
- const std::shared_ptr &)>;
-
-
-static const event_primer_func_t no_event = [](const time::time_t &,
- const std::shared_ptr &,
- const std::shared_ptr &,
- const std::shared_ptr &) {
- throw Error{ERR << "No event primer function registered."};
- return event_store_t{};
-};
-
-static const event_next_func_t no_next = [](const time::time_t &,
- const std::shared_ptr &,
- const std::shared_ptr &,
- const std::shared_ptr &) {
- throw Error{ERR << "No event next function registered."};
- return 0;
-};
-
-
-/**
- * Waits for an event to be executed before continuing the control flow.
- */
-class XorEventGate : public Node {
-public:
- /**
- * Create a new exclusive event gateway.
- *
- * @param id Unique identifier for this node.
- * @param label Human-readable label (optional).
- * @param outputs Output nodes (can be set later).
- * @param primer_func Function to create and register the event.
- * @param next_func Function to decide which node to visit after the event is handled.
- */
- XorEventGate(node_id id,
- node_label label = "Event",
- const std::vector> &outputs = {},
- event_primer_func_t primer_func = no_event,
- event_next_func_t next_func = no_next);
- virtual ~XorEventGate() = default;
-
- inline node_t get_type() const override {
- return node_t::XOR_EVENT_GATE;
- }
-
- /**
- * Add an output node.
- *
- * @param output Output node.
- */
- void add_output(const std::shared_ptr &output) override;
-
- /**
- * Set the function to create the event.
- *
- * @param primer_func Event creation function.
- */
- void set_primer_func(event_primer_func_t primer_func);
-
- /**
- * Set the function to decide which node to visit after the event is handled.
- *
- * @param next_func Next node function.
- */
- void set_next_func(event_next_func_t next_func);
-
- /**
- * Get the function to create the event.
- *
- * @return Event creation function.
- */
- event_primer_func_t get_primer_func() const;
-
- /**
- * Get the function to decide which node to visit after the event is handled.
- *
- * @return Next node function.
- */
- event_next_func_t get_next_func() const;
-
-private:
- /**
- * Creates the event when the node is visited.
- */
- event_primer_func_t primer_func;
-
- /**
- * Decide which node to visit after the event is handled.
- */
- event_next_func_t next_func;
-};
-
-} // namespace activity
-} // namespace gamestate
-} // namespace openage
diff --git a/libopenage/gamestate/activity/node.cpp b/libopenage/gamestate/activity/node.cpp
index 5c7f007d74..833b4031eb 100644
--- a/libopenage/gamestate/activity/node.cpp
+++ b/libopenage/gamestate/activity/node.cpp
@@ -10,8 +10,8 @@
namespace openage::gamestate::activity {
-Node::Node(node_id id,
- node_label label,
+Node::Node(node_id_t id,
+ node_label_t label,
const std::vector> &outputs) :
outputs{},
id{id},
@@ -22,11 +22,11 @@ Node::Node(node_id id,
}
}
-node_id Node::get_id() const {
+node_id_t Node::get_id() const {
return this->id;
}
-const node_label Node::get_label() const {
+const node_label_t Node::get_label() const {
return this->label;
}
@@ -42,7 +42,7 @@ std::string Node::str() const {
return ret.str();
}
-const std::shared_ptr &Node::next(node_id id) const {
+const std::shared_ptr &Node::next(node_id_t id) const {
if (not this->outputs.contains(id)) [[unlikely]] {
throw Error{MSG(err) << "Node " << this->str() << " has no output with id " << id};
}
diff --git a/libopenage/gamestate/activity/node.h b/libopenage/gamestate/activity/node.h
index 9a8edfcf9f..ed56277930 100644
--- a/libopenage/gamestate/activity/node.h
+++ b/libopenage/gamestate/activity/node.h
@@ -13,8 +13,8 @@
namespace openage::gamestate::activity {
-using node_id = size_t;
-using node_label = std::string;
+using node_id_t = size_t;
+using node_label_t = std::string;
/**
* Node in the flow graph describing the activity.
@@ -28,8 +28,8 @@ class Node {
* @param label Human-readable label (optional).
* @param outputs Output nodes.
*/
- Node(node_id id,
- node_label label = "",
+ Node(node_id_t id,
+ node_label_t label = "",
const std::vector> &outputs = {});
virtual ~Node() = default;
@@ -45,14 +45,14 @@ class Node {
*
* @return The unique identifier.
*/
- node_id get_id() const;
+ node_id_t get_id() const;
/**
* Get the human-readable label for this node.
*
* @return Human-readable label.
*/
- const node_label get_label() const;
+ const node_label_t get_label() const;
/**
* Get a human-readable string representation of this node.
@@ -67,31 +67,24 @@ class Node {
* @param id Unique identifier of the output node.
* @return Output node.
*/
- const std::shared_ptr &next(node_id id) const;
-
- /**
- * Add an output node.
- *
- * @param output Output node.
- */
- virtual void add_output(const std::shared_ptr &output) = 0;
+ const std::shared_ptr &next(node_id_t id) const;
protected:
/**
* Output nodes.
*/
- std::unordered_map> outputs;
+ std::unordered_map> outputs;
private:
/**
* Unique identifier for this node.
*/
- const node_id id;
+ const node_id_t id;
/**
* Human-readable label.
*/
- const node_label label;
+ const node_label_t label;
};
} // namespace openage::gamestate::activity
diff --git a/libopenage/gamestate/activity/start_node.cpp b/libopenage/gamestate/activity/start_node.cpp
index 8686fc2f4e..a6b400fb1e 100644
--- a/libopenage/gamestate/activity/start_node.cpp
+++ b/libopenage/gamestate/activity/start_node.cpp
@@ -8,8 +8,8 @@
namespace openage::gamestate::activity {
-StartNode::StartNode(node_id id,
- node_label label,
+StartNode::StartNode(node_id_t id,
+ node_label_t label,
const std::shared_ptr &output) :
Node{id, label} {
if (output) {
@@ -22,7 +22,7 @@ void StartNode::add_output(const std::shared_ptr &output) {
this->outputs.emplace(output->get_id(), output);
}
-node_id StartNode::get_next() const {
+node_id_t StartNode::get_next() const {
return (*this->outputs.begin()).first;
}
diff --git a/libopenage/gamestate/activity/start_node.h b/libopenage/gamestate/activity/start_node.h
index bec65e6ea7..6a9406b381 100644
--- a/libopenage/gamestate/activity/start_node.h
+++ b/libopenage/gamestate/activity/start_node.h
@@ -25,8 +25,8 @@ class StartNode : public Node {
* @param label Human-readable label (optional).
* @param output Next node to visit (can be set later).
*/
- StartNode(node_id id,
- node_label label = "Start",
+ StartNode(node_id_t id,
+ node_label_t label = "Start",
const std::shared_ptr &output = nullptr);
virtual ~StartNode() = default;
@@ -41,7 +41,7 @@ class StartNode : public Node {
*
* @param output Output node.
*/
- void add_output(const std::shared_ptr &output) override;
+ void add_output(const std::shared_ptr &output);
/**
* Get the next node to visit.
@@ -49,7 +49,7 @@ class StartNode : public Node {
* @param time Current time.
* @return Next node to visit.
*/
- node_id get_next() const;
+ node_id_t get_next() const;
};
} // namespace openage::gamestate::activity
diff --git a/libopenage/gamestate/activity/task_node.cpp b/libopenage/gamestate/activity/task_node.cpp
index 36b5de6663..59186eb451 100644
--- a/libopenage/gamestate/activity/task_node.cpp
+++ b/libopenage/gamestate/activity/task_node.cpp
@@ -8,8 +8,8 @@
namespace openage::gamestate::activity {
-TaskCustom::TaskCustom(node_id id,
- node_label label,
+TaskCustom::TaskCustom(node_id_t id,
+ node_label_t label,
const std::shared_ptr &output,
task_func_t task_func) :
Node{id, label},
@@ -32,7 +32,7 @@ task_func_t TaskCustom::get_task_func() const {
return this->task_func;
}
-node_id TaskCustom::get_next() const {
+node_id_t TaskCustom::get_next() const {
return (*this->outputs.begin()).first;
}
diff --git a/libopenage/gamestate/activity/task_node.h b/libopenage/gamestate/activity/task_node.h
index 0efd3f784b..a5eddf59c2 100644
--- a/libopenage/gamestate/activity/task_node.h
+++ b/libopenage/gamestate/activity/task_node.h
@@ -39,8 +39,8 @@ class TaskCustom : public Node {
* @param task_func Action to perform when visiting this node (can be set later).
* @param output Next node to visit (optional).
*/
- TaskCustom(node_id id,
- node_label label = "TaskCustom",
+ TaskCustom(node_id_t id,
+ node_label_t label = "TaskCustom",
const std::shared_ptr &output = nullptr,
task_func_t task_func = no_task);
virtual ~TaskCustom() = default;
@@ -54,7 +54,7 @@ class TaskCustom : public Node {
*
* @param output Output node.
*/
- void add_output(const std::shared_ptr &output) override;
+ void add_output(const std::shared_ptr &output);
/**
* Set the task function.
@@ -76,7 +76,7 @@ class TaskCustom : public Node {
* @param time Current time.
* @return Next node to visit.
*/
- node_id get_next() const;
+ node_id_t get_next() const;
private:
/**
diff --git a/libopenage/gamestate/activity/task_system_node.cpp b/libopenage/gamestate/activity/task_system_node.cpp
index 158bcc41b2..c73e00475e 100644
--- a/libopenage/gamestate/activity/task_system_node.cpp
+++ b/libopenage/gamestate/activity/task_system_node.cpp
@@ -8,8 +8,8 @@
namespace openage::gamestate::activity {
-TaskSystemNode::TaskSystemNode(node_id id,
- node_label label,
+TaskSystemNode::TaskSystemNode(node_id_t id,
+ node_label_t label,
const std::shared_ptr &output,
system::system_id_t system_id) :
Node{id, label},
@@ -32,7 +32,7 @@ system::system_id_t TaskSystemNode::get_system_id() const {
return this->system_id;
}
-node_id TaskSystemNode::get_next() const {
+node_id_t TaskSystemNode::get_next() const {
return (*this->outputs.begin()).first;
}
diff --git a/libopenage/gamestate/activity/task_system_node.h b/libopenage/gamestate/activity/task_system_node.h
index 8f9e6b1163..74513cbd64 100644
--- a/libopenage/gamestate/activity/task_system_node.h
+++ b/libopenage/gamestate/activity/task_system_node.h
@@ -25,8 +25,8 @@ class TaskSystemNode : public Node {
* @param output Next node to visit (optional).
* @param system_id System to run when visiting this node (can be set later).
*/
- TaskSystemNode(node_id id,
- node_label label = "TaskSystem",
+ TaskSystemNode(node_id_t id,
+ node_label_t label = "TaskSystem",
const std::shared_ptr &output = nullptr,
system::system_id_t system_id = system::system_id_t::NONE);
virtual ~TaskSystemNode() = default;
@@ -40,7 +40,7 @@ class TaskSystemNode : public Node {
*
* @param output Output node.
*/
- void add_output(const std::shared_ptr &output) override;
+ void add_output(const std::shared_ptr &output);
/**
* Set the system id.
@@ -62,7 +62,7 @@ class TaskSystemNode : public Node {
* @param time Current time.
* @return Next node to visit.
*/
- node_id get_next() const;
+ node_id_t get_next() const;
private:
/**
diff --git a/libopenage/gamestate/activity/tests.cpp b/libopenage/gamestate/activity/tests.cpp
index 7588e5a9b0..1ddad0e7e8 100644
--- a/libopenage/gamestate/activity/tests.cpp
+++ b/libopenage/gamestate/activity/tests.cpp
@@ -15,12 +15,12 @@
#include "log/message.h"
#include "gamestate/activity/end_node.h"
-#include "gamestate/activity/event_node.h"
#include "gamestate/activity/node.h"
#include "gamestate/activity/start_node.h"
#include "gamestate/activity/task_node.h"
#include "gamestate/activity/types.h"
-#include "gamestate/activity/xor_node.h"
+#include "gamestate/activity/xor_event_gate.h"
+#include "gamestate/activity/xor_gate.h"
#include "time/time.h"
@@ -33,7 +33,8 @@ namespace openage::gamestate::tests {
* @param current_node Node where the control flow starts from.
* @return Node where the control flow should continue.
*/
-const std::shared_ptr activity_flow(const std::shared_ptr ¤t_node);
+const std::shared_ptr activity_flow(const std::shared_ptr ¤t_node,
+ const std::optional ev_params = std::nullopt);
/**
@@ -57,11 +58,11 @@ class TestActivityManager : public event::EventEntity {
return "TestActivityManager";
}
- void run() {
+ void run(const std::optional ev_params = std::nullopt) {
if (not current_node) {
throw Error{ERR << "No current node given"};
}
- this->current_node = activity_flow(this->current_node);
+ this->current_node = activity_flow(this->current_node, ev_params);
}
std::shared_ptr current_node;
@@ -93,9 +94,9 @@ class TestActivityHandler : public event::OnceEventHandler {
const std::shared_ptr &target,
const std::shared_ptr & /* state */,
const time::time_t & /* time */,
- const param_map & /* params */) override {
+ const param_map ¶ms) override {
auto mgr_target = std::dynamic_pointer_cast(target);
- mgr_target->run();
+ mgr_target->run(params);
}
time::time_t predict_invoke_time(const std::shared_ptr & /* target */,
@@ -106,14 +107,27 @@ class TestActivityHandler : public event::OnceEventHandler {
};
-const std::shared_ptr activity_flow(const std::shared_ptr ¤t_node) {
+const std::shared_ptr activity_flow(const std::shared_ptr ¤t_node,
+ const std::optional ev_params) {
+ // events that are currently being listened for
+ // in the gamestate these are stored in the activity component
+ static std::vector> events;
+
auto current = current_node;
if (current->get_type() == activity::node_t::XOR_EVENT_GATE) {
- auto node = std::static_pointer_cast(current);
- auto event_next = node->get_next_func();
- auto next_id = event_next(0, nullptr, nullptr, nullptr);
- current = node->next(next_id);
+ log::log(INFO << "Continuing from event node");
+ if (not ev_params.has_value()) {
+ throw Error{ERR << "XorEventGate: No event parameters given on continue"};
+ }
+
+ auto next_id = ev_params.value().get("next");
+ current = current->next(next_id);
+
+ // cancel all other events that the manager may have been waiting for
+ for (auto &event : events) {
+ event->cancel(0);
+ }
}
while (current->get_type() != activity::node_t::END) {
@@ -138,16 +152,30 @@ const std::shared_ptr activity_flow(const std::shared_ptr(current);
- auto condition = node->get_condition_func();
- auto next_id = condition(0, nullptr);
+ auto next_id = node->get_default()->get_id();
+ for (auto &condition : node->get_conditions()) {
+ auto condition_func = condition.second;
+ if (condition_func(0, nullptr)) {
+ next_id = condition.first;
+ break;
+ }
+ }
current = node->next(next_id);
} break;
case activity::node_t::XOR_EVENT_GATE: {
auto node = std::static_pointer_cast(current);
- auto event_primer = node->get_primer_func();
- event_primer(0, nullptr, nullptr, nullptr);
+ auto event_primers = node->get_primers();
+ for (auto &primer : event_primers) {
+ auto ev = primer.second(0,
+ nullptr,
+ nullptr,
+ nullptr,
+ primer.first);
+ events.push_back(ev);
+ }
// wait for event
+ log::log(INFO << "Waiting for event");
return current;
} break;
default:
@@ -157,7 +185,7 @@ const std::shared_ptr activity_flow(const std::shared_ptrstr());
}
- log::log(INFO << "Reached end note: " << current->str());
+ log::log(INFO << "Reached end node: " << current->str());
return current;
}
@@ -203,44 +231,44 @@ void activity_demo() {
});
// Conditional branch
- size_t counter = 0;
- xor_node->add_output(task1);
- xor_node->add_output(event_node);
- xor_node->set_condition_func([&](const time::time_t & /* time */,
- const std::shared_ptr & /* entity */) {
+ static size_t counter = 0;
+ activity::condition_t branch_task1 = [&](const time::time_t & /* time */,
+ const std::shared_ptr & /* entity */) {
log::log(INFO << "Checking condition (counter < 4): counter=" << counter);
if (counter < 4) {
log::log(INFO << "Selecting path 1 (back to task node " << task1->get_id() << ")");
counter++;
- return task1->get_id();
+ return true;
}
-
+ return false;
+ };
+ xor_node->add_output(task1, branch_task1);
+ activity::condition_t branch_event = [&](const time::time_t & /* time */,
+ const std::shared_ptr & /* entity */) {
+ // No check needed here, the event node is always selected
log::log(INFO << "Selecting path 2 (to event node " << event_node->get_id() << ")");
-
- return event_node->get_id();
- });
+ return true;
+ };
+ xor_node->add_output(event_node, branch_event);
+ xor_node->set_default(event_node);
// event node
- event_node->add_output(task2);
- event_node->set_primer_func([&](const time::time_t & /* time */,
- const std::shared_ptr & /* entity */,
- const std::shared_ptr & /* loop */,
- const std::shared_ptr & /* state */) {
+ activity::event_primer_t primer = [&](const time::time_t & /* time */,
+ const std::shared_ptr & /* entity */,
+ const std::shared_ptr & /* loop */,
+ const std::shared_ptr & /* state */,
+ size_t next_id) {
log::log(INFO << "Setting up event");
+ event::EventHandler::param_map::map_t params{{"next", next_id}};
auto ev = loop->create_event("test.activity",
mgr,
state,
- 0);
- return activity::event_store_t{ev};
- });
- event_node->set_next_func([&task2](const time::time_t & /* time */,
- const std::shared_ptr & /* entity */,
- const std::shared_ptr & /* loop */,
- const std::shared_ptr & /* state */) {
- log::log(INFO << "Selecting next node (task node " << task2->get_id() << ")");
- return task2->get_id();
- });
+ 0,
+ params);
+ return ev;
+ };
+ event_node->add_output(task2, primer);
// task 2
task2->add_output(end);
diff --git a/libopenage/gamestate/activity/xor_event_gate.cpp b/libopenage/gamestate/activity/xor_event_gate.cpp
new file mode 100644
index 0000000000..f018cfd96a
--- /dev/null
+++ b/libopenage/gamestate/activity/xor_event_gate.cpp
@@ -0,0 +1,42 @@
+// Copyright 2023-2023 the openage authors. See copying.md for legal info.
+
+#include "xor_event_gate.h"
+
+#include
+
+
+namespace openage::gamestate::activity {
+
+XorEventGate::XorEventGate(node_id_t id,
+ node_label_t label) :
+ Node{id, label},
+ primers{} {
+}
+
+XorEventGate::XorEventGate(node_id_t id,
+ node_label_t label,
+ const std::vector> &outputs,
+ const std::map &primers) :
+ Node{id, label, outputs},
+ primers{} {
+ if (primers.size() != outputs.size()) {
+ throw Error{MSG(err) << "XorEventGate " << this->str() << " has " << outputs.size()
+ << " outputs but " << primers.size() << " primers"};
+ }
+
+ for (const auto &[id, primer] : primers) {
+ this->primers.emplace(id, primer);
+ }
+}
+
+void XorEventGate::add_output(const std::shared_ptr &output,
+ const event_primer_t &primer) {
+ this->outputs.emplace(output->get_id(), output);
+ this->primers.emplace(output->get_id(), primer);
+}
+
+const std::map &XorEventGate::get_primers() const {
+ return this->primers;
+}
+
+} // namespace openage::gamestate::activity
diff --git a/libopenage/gamestate/activity/xor_event_gate.h b/libopenage/gamestate/activity/xor_event_gate.h
new file mode 100644
index 0000000000..6c4912d714
--- /dev/null
+++ b/libopenage/gamestate/activity/xor_event_gate.h
@@ -0,0 +1,112 @@
+// Copyright 2023-2023 the openage authors. See copying.md for legal info.
+
+#pragma once
+
+#include
+#include