forked from tsuki/openroller
Initial public source release
Split reusable rendering and format support into vectorail-core and vectorail-gc.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#include <SDL3/SDL.h>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class AudioManager {
|
||||
public:
|
||||
enum class GameplaySound : size_t {
|
||||
Adlib = 0,
|
||||
Tap1 = 1,
|
||||
Tap2 = 2,
|
||||
};
|
||||
|
||||
AudioManager();
|
||||
~AudioManager();
|
||||
|
||||
bool loadMusic(const std::string& path, float gain = 1.0f);
|
||||
bool loadMusicPair(const std::string& bgmPath, const std::string& shotPath,
|
||||
float bgmGain, float shotGain);
|
||||
bool loadGameplaySounds(const std::array<std::string, 3>& paths,
|
||||
const std::array<float, 3>& gains);
|
||||
void playGameplaySound(GameplaySound sound);
|
||||
void play();
|
||||
void pause();
|
||||
void resume();
|
||||
void setShotMuted(bool muted);
|
||||
|
||||
double getTime() const;
|
||||
double getDuration() const; // Длина песни в секундах
|
||||
|
||||
bool isPlaying() const { return playing; }
|
||||
|
||||
private:
|
||||
struct GameplaySoundSlot {
|
||||
std::vector<Uint8> data;
|
||||
std::array<SDL_AudioStream*, 2> voices{nullptr, nullptr};
|
||||
size_t nextVoice = 0;
|
||||
};
|
||||
|
||||
void clear();
|
||||
void clearGameplaySounds();
|
||||
bool openStreams(const SDL_AudioSpec& bgmSpec, const Uint8* bgmData, Uint32 bgmLen,
|
||||
float bgmGain, const SDL_AudioSpec* shotSpec = nullptr,
|
||||
const Uint8* shotData = nullptr, Uint32 shotLen = 0,
|
||||
float shotGain = 1.0f);
|
||||
|
||||
SDL_AudioDeviceID device;
|
||||
SDL_AudioStream* bgmStream;
|
||||
SDL_AudioStream* shotStream;
|
||||
std::array<GameplaySoundSlot, 3> gameplaySounds;
|
||||
double duration; // Предрассчитанная длительность
|
||||
float shotBaseGain;
|
||||
bool shotMuted;
|
||||
bool playing;
|
||||
Uint64 startTime;
|
||||
Uint64 accumulatedTicks;
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
enum class CabinetInput : std::size_t {
|
||||
Test,
|
||||
Service,
|
||||
Coin,
|
||||
Select,
|
||||
Enter,
|
||||
LeftUp,
|
||||
LeftDown,
|
||||
LeftLeft,
|
||||
LeftRight,
|
||||
LeftButton,
|
||||
RightUp,
|
||||
RightDown,
|
||||
RightLeft,
|
||||
RightRight,
|
||||
RightButton,
|
||||
Count,
|
||||
};
|
||||
|
||||
struct CabinetRgb {
|
||||
std::uint8_t r = 0;
|
||||
std::uint8_t g = 0;
|
||||
std::uint8_t b = 0;
|
||||
};
|
||||
|
||||
enum class CardReaderStatus {
|
||||
Ready,
|
||||
Disconnected,
|
||||
Unformatted,
|
||||
ReadError,
|
||||
Timeout,
|
||||
};
|
||||
|
||||
struct CardReaderState {
|
||||
CardReaderStatus status = CardReaderStatus::Disconnected;
|
||||
std::string cardId;
|
||||
};
|
||||
|
||||
// Hardware boundary shared by gameplay and test mode. The software backend
|
||||
// is deliberately useful on its own; an ACM implementation can replace it
|
||||
// without teaching the test-mode forms about packets or device paths.
|
||||
class CabinetBackend {
|
||||
public:
|
||||
virtual ~CabinetBackend() = default;
|
||||
|
||||
virtual void poll() = 0;
|
||||
virtual bool input(CabinetInput input) const = 0;
|
||||
|
||||
virtual void setLed(std::size_t logicalIndex, CabinetRgb color) = 0;
|
||||
virtual void clearLeds() = 0;
|
||||
virtual void commitOutputs() = 0;
|
||||
virtual const std::array<CabinetRgb, 118>& leds() const = 0;
|
||||
|
||||
virtual int headphoneVolume() const { return 0; }
|
||||
virtual bool headphoneConnected() const { return false; }
|
||||
virtual bool grooveStageConnected() const { return false; }
|
||||
virtual CardReaderState cardReader() const { return {}; }
|
||||
};
|
||||
|
||||
// Keyboard + in-memory outputs. This keeps service mode fully testable before
|
||||
// the cabinet ACM and RFID transports are attached.
|
||||
CabinetBackend& defaultCabinetBackend();
|
||||
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <cstdint>
|
||||
#include <glm/glm.hpp>
|
||||
#include "gc/StagePattern.hpp"
|
||||
|
||||
struct TrackPoint {
|
||||
glm::vec3 position;
|
||||
int type; // 0=Smooth, 1=Straight
|
||||
bool visible = true; // NEW: Render rail or not
|
||||
float timeMs = 0.0f;
|
||||
};
|
||||
|
||||
struct Keyframe {
|
||||
float t;
|
||||
std::string param;
|
||||
float value;
|
||||
};
|
||||
|
||||
struct LevelNote {
|
||||
float distance = 0.0f;
|
||||
float timeMs = 0.0f;
|
||||
float appearTimeMs = 0.0f;
|
||||
float endTimeMs = 0.0f;
|
||||
float endDistance = 0.0f;
|
||||
unsigned int rawType = 0;
|
||||
unsigned int effectiveType = 0;
|
||||
// Wire +5 becomes runtime marker +8 in FUN_005ea800. Any non-zero value
|
||||
// makes FUN_005e9410 select the ALB slot of the active gameplay SE set.
|
||||
bool adlib = false;
|
||||
// First signed 16-bit field at wire +6. DrawMark passes this value minus
|
||||
// one as the UV-list selector for game effect 3.
|
||||
int markEffectId = -1;
|
||||
// game471 wire +55. The stage authors use this packed RRGGBBAA value for
|
||||
// the path/body colour of duration targets.
|
||||
uint32_t packedColor = 0xffffffffu;
|
||||
uint32_t merryCount = 0;
|
||||
// BuildTimingDataSub converts wire +25 (length) and +29/+33 (HPB angles)
|
||||
// to this world-space vector. GameScene::buildGameData later projects it
|
||||
// through the camera active at timeMs to obtain a fixed screen angle.
|
||||
glm::vec3 directionVector{0.0f};
|
||||
// Runtime +0x20 and +0xc4 in game471's marker object.
|
||||
float beatDurationMs = 500.0f;
|
||||
float earlyTimingMs = 250.0f;
|
||||
float lateTimingMs = 250.0f;
|
||||
float missTimingMs = 250.0f;
|
||||
float muteTimingMs = 0.0f;
|
||||
float markerFadeEndTimeMs = 0.0f;
|
||||
bool gcTiming = false;
|
||||
};
|
||||
|
||||
// Exact 59-byte stage camera record after decoding from big endian. The
|
||||
// original game expands this to a 0x44-byte runtime key by adding rotationA.z
|
||||
// = 0; keeping the wire fields here lets the player reproduce aMode/fMode
|
||||
// instead of flattening them into the legacy editor timeline.
|
||||
struct GcCameraKey {
|
||||
uint32_t timeMs = 0;
|
||||
uint8_t aMode = 0;
|
||||
uint8_t fMode = 0;
|
||||
float dist = 0.0f;
|
||||
glm::vec3 rotationA{0.0f};
|
||||
glm::vec3 originOff{0.0f};
|
||||
uint8_t projType = 0;
|
||||
glm::vec3 fieldFar{0.0f};
|
||||
glm::vec3 fieldNear{0.0f};
|
||||
float rotationB = 0.0f;
|
||||
};
|
||||
|
||||
struct LevelData {
|
||||
std::string title;
|
||||
std::string author;
|
||||
std::string audioPath;
|
||||
std::string audioShotPath;
|
||||
float audioBgmGain = 1.0f;
|
||||
float audioShotGain = 1.0f;
|
||||
// game471 SE set slots: ALB, TP1 and TP2. The arcade runtime allocates
|
||||
// two playback channels for each slot so repeated taps can overlap.
|
||||
std::array<std::string, 3> gameplaySoundPaths;
|
||||
std::array<float, 3> gameplaySoundGains{1.0f, 1.0f, 1.0f};
|
||||
std::string backgroundPath;
|
||||
std::vector<TrackPoint> trackPoints;
|
||||
std::vector<LevelNote> notes;
|
||||
std::vector<GcCameraKey> gcCameraKeys;
|
||||
// Optional arcade *_clip.dat table. It is object-major and contains one
|
||||
// byte per 60 Hz frame; a zero byte suppresses that stage object for the
|
||||
// frame. game471 consumes this before evaluating authored visibility.
|
||||
uint32_t gcObjectClipFrameCount = 0;
|
||||
std::vector<uint8_t> gcObjectClipVisibility;
|
||||
// Complete decoded stage background: particle/visualizer keys, exact
|
||||
// gradient fade flags and the animated 3D object scene.
|
||||
std::optional<gc::ParsedStagePattern> gcStage;
|
||||
std::vector<Keyframe> timeline;
|
||||
std::map<std::string, float> config;
|
||||
};
|
||||
|
||||
class LevelLoader {
|
||||
public:
|
||||
static LevelData load(const std::string& path);
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
class CabinetBackend;
|
||||
|
||||
// Runs the cabinet operator/test mode until Test/Escape returns to the caller.
|
||||
// The existing OpenGL context remains owned by the caller.
|
||||
void runServiceMenu(SDL_Window* window, CabinetBackend& cabinet,
|
||||
const std::filesystem::path& contentPath = {});
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
// Runs the Groove Coaster-style song and difficulty selector. Returns false
|
||||
// when the window is closed/cancelled, otherwise writes the chosen stage .dat.
|
||||
bool runSongSelect(SDL_Window* window,
|
||||
const std::filesystem::path& gcRoot,
|
||||
std::string* selectedStagePath);
|
||||
Reference in New Issue
Block a user