Add project files
This commit is contained in:
commit
c92f6a1865
15 files changed
+1672
No files matched your search
@@ -0,0 +1,251 @@
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <cassert>
|
||||
#include <acrux/coroutines/Coroutine.h>
|
||||
#include <acrux/coroutines/CoroutineRunner.h>
|
||||
#include <acrux/coroutines/standard/WaitConditions.h>
|
||||
using namespace acrux::coroutines;
|
||||
using namespace acrux::coroutines::standard;
|
||||
|
||||
#define yield co_yield
|
||||
#define exit co_return
|
||||
|
||||
struct AlphaController
|
||||
{
|
||||
float alpha = 1;
|
||||
|
||||
Coroutine fadeIn(float delta)
|
||||
{
|
||||
std::cout << "alpha: " << alpha << '\n';
|
||||
while(alpha < 1)
|
||||
{
|
||||
alpha += delta;
|
||||
if(alpha > 1) alpha = 1;
|
||||
std::cout << "alpha: " << alpha << '\n';
|
||||
yield waitForSeconds(0.15);
|
||||
}
|
||||
}
|
||||
|
||||
Coroutine fadeOut(float delta)
|
||||
{
|
||||
std::cout << "alpha: " << alpha << '\n';
|
||||
while(alpha > 0)
|
||||
{
|
||||
alpha -= delta;
|
||||
if(alpha < 0) alpha = 0;
|
||||
std::cout << "alpha: " << alpha << '\n';
|
||||
yield waitForSeconds(0.15);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Coroutine blink(AlphaController& controller, float delta, float waitSeconds = 1)
|
||||
{
|
||||
yield controller.fadeOut(delta);
|
||||
yield waitForSeconds(waitSeconds);
|
||||
yield controller.fadeIn(delta);
|
||||
}
|
||||
|
||||
Coroutine typewrite(const std::string& text, float delay = 0.03)
|
||||
{
|
||||
for(char c : text)
|
||||
{
|
||||
std::cout << c;
|
||||
std::cout.flush();
|
||||
yield waitForSeconds(delay);
|
||||
}
|
||||
}
|
||||
|
||||
Coroutine writeAndWait(const std::string& text)
|
||||
{
|
||||
yield typewrite(text);
|
||||
std::cin.get();
|
||||
}
|
||||
|
||||
Coroutine rudeBuster()
|
||||
{
|
||||
yield waitForSeconds(2);
|
||||
std::cout << "(RudeBuster Hit)\n";
|
||||
}
|
||||
|
||||
Coroutine attack()
|
||||
{
|
||||
std::cout << "(Attacking)\n";
|
||||
yield waitForSeconds(2);
|
||||
|
||||
std::srand(std::time(nullptr));
|
||||
bool cancel = (std::rand() % 100) < 50;
|
||||
if(cancel)
|
||||
{
|
||||
std::cout << "(Miss)\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
std::cout << "(Hit)\n";
|
||||
}
|
||||
|
||||
Coroutine myCutscene()
|
||||
{
|
||||
yield writeAndWait("* Susie used RUDE BUSTER!");
|
||||
//yield rudeBuster();
|
||||
//yield waitForSeconds(1);
|
||||
CoroutineHandle buster = startCoroutine(rudeBuster());
|
||||
yield writeAndWait("* Ralsei cast HEAL PLAYER.");
|
||||
//yield waitForSeconds(1);
|
||||
buster.stop();
|
||||
yield attack();
|
||||
//stopCoroutine(buster);
|
||||
//yield buster;
|
||||
yield writeAndWait("* You won!\n* Got 0 EXP and 249 D$.");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Demonstrates deriving from CoroutineRunner to attach extra data to each
|
||||
// coroutine it starts, keyed by CoroutineHandle -- e.g. "which entity does
|
||||
// this coroutine belong to?" in a game engine. CoroutineRunner itself knows
|
||||
// nothing about this; it only provides the extension points
|
||||
// (onCoroutineStarted/onCoroutineFinished) and a hashable CoroutineHandle.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
using EntityId = int;
|
||||
|
||||
class GameCoroutineRunner : public CoroutineRunner
|
||||
{
|
||||
public:
|
||||
// Without this, declaring our own startCoroutine(task, owner) below would
|
||||
// hide CoroutineRunner::startCoroutine(task) entirely -- C++ overload
|
||||
// resolution doesn't look at base class overloads once a derived class
|
||||
// declares any overload with the same name.
|
||||
using CoroutineRunner::startCoroutine;
|
||||
|
||||
// A new overload alongside the base class's plain startCoroutine(task),
|
||||
// which remains available and untouched.
|
||||
CoroutineHandle startCoroutine(Coroutine task, EntityId owner)
|
||||
{
|
||||
CoroutineHandle handle = CoroutineRunner::startCoroutine(std::move(task));
|
||||
if (handle) m_owners[handle] = owner;
|
||||
return handle;
|
||||
}
|
||||
|
||||
EntityId ownerOf(CoroutineHandle handle) const
|
||||
{
|
||||
auto it = m_owners.find(handle);
|
||||
return it != m_owners.end() ? it->second : -1;
|
||||
}
|
||||
|
||||
size_t trackedCount() const { return m_owners.size(); }
|
||||
|
||||
protected:
|
||||
// If this coroutine was started by another one we're tracking an owner
|
||||
// for -- whether via `co_yield childTask()` or a direct startCoroutine()
|
||||
// call made from inside that coroutine's body -- inherit that owner
|
||||
// automatically. The explicit-owner startCoroutine(task, owner) overload
|
||||
// above still runs its own m_owners[handle] = owner afterward, so an
|
||||
// explicit owner always wins over an inherited one.
|
||||
void onCoroutineStarted(CoroutineHandle handle, CoroutineHandle parent) override
|
||||
{
|
||||
auto it = m_owners.find(parent);
|
||||
if (it != m_owners.end())
|
||||
{
|
||||
m_owners[handle] = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
void onCoroutineFinished(CoroutineHandle handle) override
|
||||
{
|
||||
m_owners.erase(handle);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<CoroutineHandle, EntityId> m_owners;
|
||||
};
|
||||
|
||||
Coroutine countToThree()
|
||||
{
|
||||
co_yield nullptr;
|
||||
co_yield nullptr;
|
||||
co_yield nullptr;
|
||||
}
|
||||
|
||||
// Starts a child coroutine two different ways -- co_yield, and a direct
|
||||
// startCoroutine() call -- both of which should hand the child the same
|
||||
// owner as this coroutine, with no explicit owner passed at either call site.
|
||||
CoroutineHandle g_directChildHandle;
|
||||
|
||||
Coroutine parentTask()
|
||||
{
|
||||
co_yield countToThree(); // child #1: via co_yield, waits for it to finish
|
||||
g_directChildHandle = startCoroutine(countToThree()); // child #2: fire-and-forget
|
||||
co_yield nullptr;
|
||||
}
|
||||
|
||||
void testExtensionMechanism()
|
||||
{
|
||||
GameCoroutineRunner runner;
|
||||
|
||||
CoroutineHandle a = runner.startCoroutine(countToThree(), /* owner */ 42);
|
||||
CoroutineHandle b = runner.startCoroutine(countToThree()); // base overload, no owner
|
||||
|
||||
assert(runner.ownerOf(a) == 42);
|
||||
assert(runner.ownerOf(b) == -1); // never registered
|
||||
assert(runner.trackedCount() == 1);
|
||||
|
||||
while (a.isRunning() || b.isRunning())
|
||||
{
|
||||
runner.update();
|
||||
}
|
||||
|
||||
// Both coroutines have finished; onCoroutineFinished should have
|
||||
// cleaned up the side table automatically, with no leftover entries.
|
||||
assert(runner.trackedCount() == 0);
|
||||
|
||||
// A coroutine started with an owner. Its children -- one started via
|
||||
// co_yield, one via a direct startCoroutine() call from inside its
|
||||
// body -- should both inherit that same owner automatically, without
|
||||
// either call site ever passing it explicitly.
|
||||
CoroutineHandle parent = runner.startCoroutine(parentTask(), /* owner */ 7);
|
||||
assert(runner.trackedCount() == 1); // just `parent` so far
|
||||
|
||||
runner.update(); // runs parentTask up to `co_yield countToThree()`,
|
||||
// which starts child #1 on this same tick
|
||||
|
||||
// The only way trackedCount can have grown is if onCoroutineStarted
|
||||
// found `parent` in the owner table and propagated it to child #1.
|
||||
assert(runner.trackedCount() == 2);
|
||||
|
||||
while (parent.isRunning())
|
||||
{
|
||||
runner.update();
|
||||
}
|
||||
|
||||
// child #2 (started via a direct startCoroutine() call in parentTask's
|
||||
// body, after child #1 finished) should have inherited the same owner.
|
||||
// parentTask doesn't wait on child #2, so it's still running/tracked here.
|
||||
assert(runner.ownerOf(g_directChildHandle) == 7);
|
||||
|
||||
// Drain everything so the next test starts from a clean runner.
|
||||
while (g_directChildHandle.isRunning())
|
||||
{
|
||||
runner.update();
|
||||
}
|
||||
assert(runner.trackedCount() == 0);
|
||||
|
||||
std::cout << "[PASS] testExtensionMechanism\n";
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
testExtensionMechanism();
|
||||
|
||||
CoroutineRunner engine;
|
||||
AlphaController alphaController;
|
||||
|
||||
engine.startCoroutine(myCutscene());
|
||||
|
||||
while(engine.getActiveCount() > 0)
|
||||
{
|
||||
engine.update();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user