375 lines
13 KiB
C++
375 lines
13 KiB
C++
/**
|
|
* @file
|
|
*/
|
|
#include "TestTraze.h"
|
|
#include "core/SharedPtr.h"
|
|
#include "command/Command.h"
|
|
#include "testcore/TestAppMain.h"
|
|
#include "voxel/MaterialColor.h"
|
|
#include "voxel/Region.h"
|
|
#include "voxel/RawVolumeWrapper.h"
|
|
#define GLM_ENABLE_EXPERIMENTAL
|
|
#include <glm/gtx/transform.hpp>
|
|
|
|
namespace {
|
|
const int PlayFieldVolume = 0;
|
|
const int FloorVolume = 1;
|
|
const int FontSize = 48;
|
|
}
|
|
|
|
TestTraze::TestTraze(const metric::MetricPtr& metric, const io::FilesystemPtr& filesystem, const core::EventBusPtr& eventBus, const core::TimeProviderPtr& timeProvider) :
|
|
Super(metric, filesystem, eventBus, timeProvider), _protocol(eventBus), _voxelFontRender(FontSize, 4, voxel::VoxelFont::OriginUpperLeft), _soundMgr(filesystem) {
|
|
init(ORGANISATION, "testtraze");
|
|
setRenderAxis(false);
|
|
setRelativeMouseMode(false);
|
|
_allowRelativeMouseMode = false;
|
|
_eventBus->subscribe<traze::NewGridEvent>(*this);
|
|
_eventBus->subscribe<traze::NewGamesEvent>(*this);
|
|
_eventBus->subscribe<traze::PlayerListEvent>(*this);
|
|
_eventBus->subscribe<traze::TickerEvent>(*this);
|
|
_eventBus->subscribe<traze::SpawnEvent>(*this);
|
|
_eventBus->subscribe<traze::BikeEvent>(*this);
|
|
_eventBus->subscribe<traze::ScoreEvent>(*this);
|
|
}
|
|
|
|
const core::String& TestTraze::playerName(traze::PlayerId playerId) const {
|
|
return player(playerId).name;
|
|
}
|
|
|
|
const traze::Player& TestTraze::player(traze::PlayerId playerId) const {
|
|
for (auto& p : _players) {
|
|
if (p.id == playerId) {
|
|
return p;
|
|
}
|
|
}
|
|
static traze::Player player;
|
|
return player;
|
|
}
|
|
|
|
app::AppState TestTraze::onConstruct() {
|
|
app::AppState state = Super::onConstruct();
|
|
_framesPerSecondsCap->setVal(60.0f);
|
|
core::Var::get("mosquitto_host", "traze.iteratec.de");
|
|
core::Var::get("mosquitto_port", "1883");
|
|
_name = core::Var::get("name", "noname_testtraze");
|
|
command::Command::registerCommand("join", [&] (const command::CmdArgs& args) { _protocol.join(_name->strVal()); });
|
|
command::Command::registerCommand("bail", [&] (const command::CmdArgs& args) { _protocol.bail(); });
|
|
command::Command::registerCommand("left", [&] (const command::CmdArgs& args) { _protocol.steer(traze::BikeDirection::W); });
|
|
command::Command::registerCommand("right", [&] (const command::CmdArgs& args) { _protocol.steer(traze::BikeDirection::E); });
|
|
command::Command::registerCommand("forward", [&] (const command::CmdArgs& args) { _protocol.steer(traze::BikeDirection::N); });
|
|
command::Command::registerCommand("backward", [&] (const command::CmdArgs& args) { _protocol.steer(traze::BikeDirection::S); });
|
|
command::Command::registerCommand("players", [&] (const command::CmdArgs& args) {
|
|
for (const auto& p : _players) {
|
|
Log::info("%s", p.name.c_str());
|
|
}
|
|
});
|
|
core::Var::get(cfg::VoxelMeshSize, "16", core::CV_READONLY);
|
|
_rawVolumeRenderer.construct();
|
|
_messageQueue.construct();
|
|
_soundMgr.construct();
|
|
return state;
|
|
}
|
|
|
|
void TestTraze::sound(const char *soundId) {
|
|
if (_soundMgr.play(-1, soundId, glm::ivec3(0), false) < 0) {
|
|
Log::warn("Failed to play sound %s", soundId);
|
|
}
|
|
}
|
|
|
|
app::AppState TestTraze::onInit() {
|
|
app::AppState state = Super::onInit();
|
|
if (state != app::AppState::Running) {
|
|
return state;
|
|
}
|
|
if (!voxel::initDefaultMaterialColors()) {
|
|
Log::error("Failed to initialize the palette data");
|
|
return app::AppState::InitFailure;
|
|
}
|
|
if (!_protocol.init()) {
|
|
Log::error("Failed to init protocol");
|
|
return app::AppState::InitFailure;
|
|
}
|
|
if (!_rawVolumeRenderer.init()) {
|
|
Log::error("Failed to initialize the raw volume renderer");
|
|
return app::AppState::InitFailure;
|
|
}
|
|
if (!_messageQueue.init()) {
|
|
Log::error("Failed to init message queue");
|
|
return app::AppState::InitFailure;
|
|
}
|
|
if (!_voxelFontRender.init()) {
|
|
Log::error("Failed to init voxel font");
|
|
return app::AppState::InitFailure;
|
|
}
|
|
if (!_soundMgr.init()) {
|
|
Log::error("Failed to init sound manager");
|
|
return app::AppState::InitFailure;
|
|
}
|
|
|
|
camera().setWorldPosition(glm::vec3(0.0f, 50.0f, 84.0f));
|
|
_logLevelVar->setVal(core::string::toString(SDL_LOG_PRIORITY_INFO));
|
|
Log::init();
|
|
|
|
_textCamera.setMode(video::CameraMode::Orthogonal);
|
|
_textCamera.setNearPlane(-10.0f);
|
|
_textCamera.setFarPlane(10.0f);
|
|
_textCamera.setSize(windowDimension());
|
|
_textCamera.update(0.0);
|
|
|
|
_voxelFontRender.setViewProjectionMatrix(_textCamera.viewProjectionMatrix());
|
|
|
|
return state;
|
|
}
|
|
|
|
void TestTraze::onEvent(const traze::NewGamesEvent& event) {
|
|
_games = event.get();
|
|
Log::debug("Got %i games", (int)_games.size());
|
|
// there are some points were we assume a limited amount of games...
|
|
if (_games.size() >= UCHAR_MAX) {
|
|
Log::warn("Too many games found - reducing them");
|
|
_games.resize(UCHAR_MAX - 1);
|
|
}
|
|
// TODO: this doesn't work if the instanceName changed (new game added, old game removed...)
|
|
if (_games.empty() || _currentGameIndex > (int8_t)_games.size()) {
|
|
_protocol.unsubscribe();
|
|
_currentGameIndex = -1;
|
|
} else if (!_games.empty() && _currentGameIndex == -1) {
|
|
Log::info("Select first game");
|
|
_currentGameIndex = 0;
|
|
}
|
|
}
|
|
|
|
void TestTraze::onEvent(const traze::BikeEvent& event) {
|
|
const traze::Bike& bike = event.get();
|
|
Log::debug("Received bike event for player %u (%i:%i)",
|
|
bike.playerId, bike.currentLocation.x, bike.currentLocation.y);
|
|
}
|
|
|
|
void TestTraze::onEvent(const traze::TickerEvent& event) {
|
|
const traze::Ticker& ticker = event.get();
|
|
const core::String& fraggerName = playerName(ticker.fragger);
|
|
const core::String& casualtyName = playerName(ticker.casualty);
|
|
switch (ticker.type) {
|
|
case traze::TickerType::Frag:
|
|
if (ticker.fragger == _protocol.playerId()) {
|
|
sound("you_win");
|
|
_messageQueue.message("You fragged %s", casualtyName.c_str());
|
|
} else if (ticker.casualty == (int)_protocol.playerId()) {
|
|
sound("you_lose");
|
|
_messageQueue.message("You were fragged by %s", fraggerName.c_str());
|
|
} else {
|
|
_messageQueue.message("%s fragged %s", fraggerName.c_str(), casualtyName.c_str());
|
|
}
|
|
break;
|
|
case traze::TickerType::Suicide:
|
|
if (ticker.casualty == (int)_protocol.playerId()) {
|
|
sound("you_lose");
|
|
} else {
|
|
sound("suicide");
|
|
}
|
|
_messageQueue.message("%s committed suicide", fraggerName.c_str());
|
|
break;
|
|
case traze::TickerType::Collision:
|
|
if (ticker.casualty == (int)_protocol.playerId()) {
|
|
sound("you_lose");
|
|
} else if (ticker.fragger == _protocol.playerId()) {
|
|
sound("you_win");
|
|
} else {
|
|
sound("collision");
|
|
}
|
|
_messageQueue.message("%s - collision with another player", fraggerName.c_str());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TestTraze::onEvent(const traze::ScoreEvent& event) {
|
|
const traze::Score& score = event.get();
|
|
Log::debug("Received score event with %i entries", (int)score.size());
|
|
}
|
|
|
|
void TestTraze::onEvent(const traze::SpawnEvent& event) {
|
|
const traze::Spawn& spawn = event.get();
|
|
Log::debug("Spawn at position %i:%i", spawn.position.x, spawn.position.y);
|
|
if (spawn.own) {
|
|
_spawnPosition = spawn.position;
|
|
_spawnTime = _nowSeconds;
|
|
sound("join");
|
|
}
|
|
}
|
|
|
|
void TestTraze::onEvent(const traze::NewGridEvent& event) {
|
|
core::SharedPtr<voxel::RawVolume> v = event.get();
|
|
if (_spawnTime > 0.0 && _nowSeconds - _spawnTime < 4.0) {
|
|
const voxel::Voxel voxel = voxel::createRandomColorVoxel(voxel::VoxelType::Generic);
|
|
v->setVoxel(glm::ivec3(_spawnPosition.y, 0, _spawnPosition.x), voxel);
|
|
v->setVoxel(glm::ivec3(_spawnPosition.y, 1, _spawnPosition.x), voxel);
|
|
}
|
|
voxel::RawVolume* volume = _rawVolumeRenderer.volume(PlayFieldVolume);
|
|
voxel::Region dirtyRegion;
|
|
if (volume == nullptr || volume->region() != v->region()) {
|
|
volume = new voxel::RawVolume(v.get());
|
|
delete _rawVolumeRenderer.setVolume(PlayFieldVolume, volume);
|
|
dirtyRegion = v->region();
|
|
|
|
voxel::RawVolume* floor = new voxel::RawVolume(dirtyRegion);
|
|
const voxel::Region& region = floor->region();
|
|
const voxel::Voxel voxel = voxel::createColorVoxel(voxel::VoxelType::Dirt, 0);
|
|
// ground
|
|
for (int32_t z = region.getLowerZ(); z <= region.getUpperZ(); ++z) {
|
|
for (int32_t x = region.getLowerX(); x <= region.getUpperX(); ++x) {
|
|
floor->setVoxel(x, region.getLowerY(), z, voxel);
|
|
}
|
|
}
|
|
// walls
|
|
for (int32_t z = region.getLowerZ(); z <= region.getUpperZ(); ++z) {
|
|
floor->setVoxel(region.getLowerX(), region.getLowerY() + 1, z, voxel);
|
|
floor->setVoxel(region.getUpperX(), region.getLowerY() + 1, z, voxel);
|
|
floor->setVoxel(region.getLowerX(), region.getLowerY() + 2, z, voxel);
|
|
floor->setVoxel(region.getUpperX(), region.getLowerY() + 2, z, voxel);
|
|
}
|
|
for (int32_t x = region.getLowerX(); x <= region.getUpperX(); ++x) {
|
|
floor->setVoxel(x, region.getLowerY() + 1, region.getLowerZ(), voxel);
|
|
floor->setVoxel(x, region.getLowerY() + 1, region.getUpperZ(), voxel);
|
|
floor->setVoxel(x, region.getLowerY() + 2, region.getLowerZ(), voxel);
|
|
floor->setVoxel(x, region.getLowerY() + 2, region.getUpperZ(), voxel);
|
|
}
|
|
delete _rawVolumeRenderer.setVolume(FloorVolume, floor);
|
|
if (!_rawVolumeRenderer.extractRegion(FloorVolume, region)) {
|
|
Log::error("Failed to extract the volume");
|
|
}
|
|
} else {
|
|
voxel::RawVolumeWrapper wrapper(volume);
|
|
const voxel::Region& region = volume->region();
|
|
const glm::ivec3& mins = region.getLowerCorner();
|
|
const glm::ivec3& maxs = region.getUpperCorner();
|
|
glm::ivec3 p;
|
|
for (int x = mins.x; x <= maxs.x; ++x) {
|
|
p.x = x;
|
|
for (int y = mins.y; y <= maxs.y; ++y) {
|
|
p.y = y;
|
|
for (int z = mins.z; z <= maxs.z; ++z) {
|
|
p.z = z;
|
|
wrapper.setVoxel(p, v->voxel(p));
|
|
}
|
|
}
|
|
}
|
|
dirtyRegion = wrapper.dirtyRegion();
|
|
}
|
|
const glm::mat4& translate = glm::translate(-volume->region().getCenter());
|
|
const glm::mat4& rotateY = glm::rotate(glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f));
|
|
const glm::mat4& rotateX = glm::rotate(glm::radians(25.0f), glm::vec3(1.0f, 0.0f, 0.0f));
|
|
const glm::mat4 model = rotateX * rotateY * translate;
|
|
_rawVolumeRenderer.setModelMatrix(PlayFieldVolume, model);
|
|
_rawVolumeRenderer.setModelMatrix(FloorVolume, model);
|
|
if (!_rawVolumeRenderer.extractRegion(PlayFieldVolume, dirtyRegion)) {
|
|
Log::error("Failed to extract the volume");
|
|
}
|
|
}
|
|
|
|
void TestTraze::onEvent(const traze::PlayerListEvent& event) {
|
|
_players = event.get();
|
|
_maxLength = 200;
|
|
for (const traze::Player& p : _players) {
|
|
_maxLength = core_max(_maxLength, _voxelFontRender.stringWidth(p.name.c_str(), p.name.size()) + 60);
|
|
}
|
|
}
|
|
|
|
app::AppState TestTraze::onRunning() {
|
|
_rawVolumeRenderer.update();
|
|
app::AppState state = Super::onRunning();
|
|
if (!_protocol.connected()) {
|
|
if (_nextConnectTime <= 0.0) {
|
|
_nextConnectTime = 3.0;
|
|
_protocol.connect();
|
|
} else {
|
|
_nextConnectTime -= deltaFrameSeconds();
|
|
}
|
|
} else if (_currentGameIndex != -1) {
|
|
_protocol.subscribe(_games[_currentGameIndex]);
|
|
}
|
|
_messageQueue.update(_deltaFrameSeconds);
|
|
_soundMgr.setListenerPosition(camera().worldPosition());
|
|
_soundMgr.update();
|
|
return state;
|
|
}
|
|
|
|
app::AppState TestTraze::onCleanup() {
|
|
_voxelFontRender.shutdown();
|
|
_soundMgr.shutdown();
|
|
const core::DynamicArray<voxel::RawVolume*>& old = _rawVolumeRenderer.shutdown();
|
|
for (voxel::RawVolume* v : old) {
|
|
delete v;
|
|
}
|
|
_protocol.shutdown();
|
|
_messageQueue.shutdown();
|
|
return Super::onCleanup();
|
|
}
|
|
|
|
void TestTraze::onRenderUI() {
|
|
if (ImGui::BeginCombo("GameInfo", _currentGameIndex == -1 ? "" : _games[_currentGameIndex].name.c_str(), 0)) {
|
|
for (size_t i = 0u; i < (size_t)_games.size(); ++i) {
|
|
const traze::GameInfo& game = _games[i];
|
|
const bool selected = _currentGameIndex == (int)i;
|
|
if (ImGui::Selectable(game.name.c_str(), selected)) {
|
|
_currentGameIndex = i;
|
|
}
|
|
if (selected) {
|
|
ImGui::SetItemDefaultFocus();
|
|
}
|
|
}
|
|
ImGui::EndCombo();
|
|
}
|
|
ImGui::InputVarString("Name", _name);
|
|
if (!_protocol.joined() && ImGui::Button("Join")) {
|
|
_protocol.join(_name->strVal());
|
|
}
|
|
if (_protocol.joined() && ImGui::Button("Leave")) {
|
|
_protocol.bail();
|
|
}
|
|
ImGui::Checkbox("Render board", &_renderBoard);
|
|
ImGui::Checkbox("Render player names", &_renderPlayerNames);
|
|
Super::onRenderUI();
|
|
}
|
|
|
|
void TestTraze::doRender() {
|
|
if (_renderBoard) {
|
|
_rawVolumeRenderer.render(camera());
|
|
}
|
|
|
|
const glm::ivec2& dim = frameBufferDimension();
|
|
_voxelFontRender.setModelMatrix(glm::translate(glm::vec3(dim.x / 3, 0.0f, 0.0f)));
|
|
int messageOffset = 0;
|
|
_messageQueue.visitMessages([&] (int64_t /*remainingMillis*/, const core::String& msg) {
|
|
_voxelFontRender.text(glm::ivec3(0.0f, (float)messageOffset, 0.0f), core::Color::White, "%s", msg.c_str());
|
|
messageOffset += _voxelFontRender.lineHeight();
|
|
});
|
|
_voxelFontRender.swapBuffers();
|
|
_voxelFontRender.render();
|
|
|
|
if (!_protocol.connected()) {
|
|
const char* connecting = "Connecting";
|
|
const int w = _voxelFontRender.stringWidth(connecting, SDL_strlen(connecting));
|
|
_voxelFontRender.setModelMatrix(glm::translate(glm::vec3(dim.x / 2 - w / 2, dim.y / 2 - _voxelFontRender.lineHeight() / 2, 0.0f)));
|
|
const glm::ivec3 pos(0, 0, 0);
|
|
_voxelFontRender.text(pos, core::Color::Red, "%s", connecting);
|
|
_voxelFontRender.text(glm::ivec3(pos.x, pos.y + _voxelFontRender.lineHeight(), pos.z), core::Color::Red, ".");
|
|
} else if (_renderPlayerNames) {
|
|
_voxelFontRender.setModelMatrix(glm::translate(glm::vec3(20.0f, 20.0f, 0.0f)));
|
|
int yOffset = 0;
|
|
_voxelFontRender.text(glm::ivec3(0, yOffset, 0), core::Color::White, "%i Players", (int)_players.size());
|
|
yOffset += _voxelFontRender.lineHeight();
|
|
for (const traze::Player& p : _players) {
|
|
_voxelFontRender.text(glm::ivec3(0, yOffset, 0), p.color, "* %s", p.name.c_str());
|
|
_voxelFontRender.text(glm::ivec3(_maxLength, yOffset, 0), p.color, "%i/%i", p.frags, p.owned);
|
|
yOffset += _voxelFontRender.lineHeight();
|
|
}
|
|
}
|
|
|
|
_voxelFontRender.swapBuffers();
|
|
_voxelFontRender.render();
|
|
}
|
|
|
|
TEST_APP(TestTraze)
|