Code works more or less good

This commit is contained in:
Vladislav Khorev committed 2026-04-25 21:11:07 +03:00
1 parent 3495d28edf
commit 348aa632dd
17 files changed
+4180 -56

No files matched your search

+72
View File
@@ -22,6 +22,78 @@
"description": "A mysterious essence from the Ghost realm", "description": "A mysterious essence from the Ghost realm",
"icon": "resources/w/red.png" "icon": "resources/w/red.png"
} }
},
{
"id": "ghost_01x",
"name": "Опасный Призрак",
"texturePath": "resources/w/ghost_skin002.png",
"animationIdlePath": "resources/w/default_float001.anim",
"animationWalkPath": "resources/w/default_float001.anim",
"animationActionIdlePath": "resources/w/float_attack003_cut.anim",
"animationActionAttackPath": "resources/w/float_attack003.anim",
"animationStandToActionPath": "resources/w/default_float001_cut.anim",
"animationActionToStandPath": "resources/w/default_float001_cut.anim",
"animationActionToDeathPath": "resources/w/default_float001_cut.anim",
"animationDeathIdlePath": "resources/w/default_float001_cut.anim",
"positionX": 0.0,
"positionY": 0.0,
"positionZ": -20.0,
"walkSpeed": 1.5,
"rotationSpeed": 8.0,
"modelScale": 0.01,
"modelCorrectionRotX": 0.0,
"modelCorrectionRotY": 180.0,
"modelCorrectionRotZ": 0.0,
"hp": 35,
"canAttack": true
},
{
"id": "ghost_02x",
"name": "Злой призрак",
"texturePath": "resources/w/ghost_skin002.png",
"animationIdlePath": "resources/w/default_float001.anim",
"animationWalkPath": "resources/w/default_float001.anim",
"animationActionIdlePath": "resources/w/float_attack003_cut.anim",
"animationActionAttackPath": "resources/w/float_attack003.anim",
"animationStandToActionPath": "resources/w/default_float001_cut.anim",
"animationActionToStandPath": "resources/w/default_float001_cut.anim",
"animationActionToDeathPath": "resources/w/default_float001_cut.anim",
"animationDeathIdlePath": "resources/w/default_float001_cut.anim",
"positionX": -5.91548,
"positionY": 0.0,
"positionZ": -25.1861,
"walkSpeed": 1.5,
"rotationSpeed": 8.0,
"modelScale": 0.01,
"modelCorrectionRotX": 0.0,
"modelCorrectionRotY": 180.0,
"modelCorrectionRotZ": 0.0,
"hp": 25,
"canAttack": true
},
{
"id": "ghost_02x",
"name": "Злой дух",
"texturePath": "resources/w/ghost_skin002.png",
"animationIdlePath": "resources/w/default_float001.anim",
"animationWalkPath": "resources/w/default_float001.anim",
"animationActionIdlePath": "resources/w/float_attack003_cut.anim",
"animationActionAttackPath": "resources/w/float_attack003.anim",
"animationStandToActionPath": "resources/w/default_float001_cut.anim",
"animationActionToStandPath": "resources/w/default_float001_cut.anim",
"animationActionToDeathPath": "resources/w/default_float001_cut.anim",
"animationDeathIdlePath": "resources/w/default_float001_cut.anim",
"positionX": 6.64604,
"positionY": 0.0,
"positionZ": -16.0176,
"walkSpeed": 1.5,
"rotationSpeed": 8.0,
"modelScale": 0.01,
"modelCorrectionRotX": 0.0,
"modelCorrectionRotY": 180.0,
"modelCorrectionRotZ": 0.0,
"hp": 15,
"canAttack": true
} }
] ]
} }
+1 -1
View File
@@ -168,7 +168,7 @@
"id": "choice_1", "id": "choice_1",
"type": "Choice", "type": "Choice",
"speaker": "Hero", "speaker": "Hero",
"portrait": "resources/hero.png", "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
"text": "", "text": "",
"choices": [ "choices": [
{ {
Binary file not shown.
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -99,7 +99,7 @@ public:
std::string npcId; std::string npcId;
std::string npcName; std::string npcName;
bool giftReceived = false; bool giftReceived = false;
float hp = 100.f; float hp = 200.f;
// Captured lazily from `hp` on the first update() tick if left at zero; // Captured lazily from `hp` on the first update() tick if left at zero;
// set explicitly if you need a different reference for the health bar. // set explicitly if you need a different reference for the health bar.
float initialHp = 0.f; float initialHp = 0.f;
+3 -3
View File
@@ -15,12 +15,12 @@ namespace ZL {
using std::max; using std::max;
#endif #endif
constexpr float DEFAULT_ZOOM = 36.f; constexpr float DEFAULT_ZOOM = 24.f;
class Environment { class Environment {
public: public:
static constexpr int CONST_DEFAULT_WIDTH = 1280; static constexpr int CONST_DEFAULT_WIDTH = 1280;
static constexpr int CONST_DEFAULT_HEIGHT = 720; static constexpr int CONST_DEFAULT_HEIGHT = 720;
static int windowHeaderHeight; static int windowHeaderHeight;
static int width; static int width;
static int height; static int height;
+3 -4
View File
@@ -160,7 +160,7 @@ namespace ZL
std::cout << "Load resurces step 4" << std::endl; std::cout << "Load resurces step 4" << std::endl;
LocationSetup params1; LocationSetup params1;
params1.roomMeshPath = "resources/w/room001.txt"; params1.roomMeshPath = "resources/w/room003.txt";
params1.roomTexturePath = "resources/w/room005.png"; params1.roomTexturePath = "resources/w/room005.png";
params1.gameObjectsJsonPath = "resources/config2/gameobjects.json"; params1.gameObjectsJsonPath = "resources/config2/gameobjects.json";
params1.npcsJsonPath = "resources/config2/npcs.json"; params1.npcsJsonPath = "resources/config2/npcs.json";
@@ -168,7 +168,6 @@ namespace ZL
params1.navigationJsonPath = "resources/config2/navigation.json"; params1.navigationJsonPath = "resources/config2/navigation.json";
params1.scriptPath = "resources/start.lua"; params1.scriptPath = "resources/start.lua";
params1.playerPosition = Eigen::Vector3f::Zero(); params1.playerPosition = Eigen::Vector3f::Zero();
params1.ghostPosition = Eigen::Vector3f(0.f, 0.f, -20.f);
params1.teleportPosition = Eigen::Vector3f(-2.03001f, 0.f, -4.95618f); params1.teleportPosition = Eigen::Vector3f(-2.03001f, 0.f, -4.95618f);
params1.teleportRadius = 1.5f; params1.teleportRadius = 1.5f;
@@ -185,7 +184,6 @@ namespace ZL
params2.playerPosition = Eigen::Vector3f(7.f, 0.f, 27.f); params2.playerPosition = Eigen::Vector3f(7.f, 0.f, 27.f);
params2.npcsJsonPath = "resources/config2/npcs2.json"; params2.npcsJsonPath = "resources/config2/npcs2.json";
params2.ghostPosition = Eigen::Vector3f(700.f, 0.f, 700.f);
params2.teleportPosition = Eigen::Vector3f(-6.45, 0.0, 7.82); params2.teleportPosition = Eigen::Vector3f(-6.45, 0.0, 7.82);
params2.teleportRadius = 1.5f; params2.teleportRadius = 1.5f;
location2 = std::make_shared<Location>(renderer, inventory); location2 = std::make_shared<Location>(renderer, inventory);
@@ -270,6 +268,7 @@ namespace ZL
this->menuManager.uiManager.setText("inventory_items_text", itemText); this->menuManager.uiManager.setText("inventory_items_text", itemText);
}); });
menuManager.uiManager.setTextButtonCallback("close_inventory_button", [this](const std::string& name) { menuManager.uiManager.setTextButtonCallback("close_inventory_button", [this](const std::string& name) {
std::cout << "[UI] Close button clicked" << std::endl; std::cout << "[UI] Close button clicked" << std::endl;
menuManager.uiManager.setNodeVisible("inventory_items_panel", false); menuManager.uiManager.setNodeVisible("inventory_items_panel", false);
@@ -531,7 +530,7 @@ namespace ZL
case SDLK_o: case SDLK_o:
//y = y + 0.002; //y = y + 0.002;
currentLocation->player->hp = 5; currentLocation->player->hp = 200;
break; break;
case SDLK_k: case SDLK_k:
+12 -35
View File
@@ -103,43 +103,19 @@ namespace ZL
player->setupHitSparks(sparkTexture); player->setupHitSparks(sparkTexture);
std::cout << "Load resurces step 9" << std::endl; std::cout << "Load resurces step 9" << std::endl;
// Load NPCs from JSON // Load NPCs from JSON. Aggressive (canAttack) NPCs need a player target
// and hit sparks; peaceful NPCs don't take damage from the player so
// they don't need either.
npcs = GameObjectLoader::loadAndCreate_Npcs(params.npcsJsonPath, CONST_ZIP_FILE); npcs = GameObjectLoader::loadAndCreate_Npcs(params.npcsJsonPath, CONST_ZIP_FILE);
for (auto& npc : npcs) { for (auto& npc : npcs) {
if (npc) npc->setupHitSparks(sparkTexture); if (!npc) continue;
if (npc->canAttack) {
npc->setupHitSparks(sparkTexture);
npc->attackTarget = player.get();
}
} }
auto ghostTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/ghost_skin001.png", CONST_ZIP_FILE)); auto sparkTexture2 = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/star.png", CONST_ZIP_FILE));
std::cout << "Load resurces step 11" << std::endl;
auto npc02 = std::make_unique<Character>();
npc02->loadBinaryAnimation(AnimationState::STAND, "resources/w/default_float001.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::WALK, "resources/w/default_float001.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::ACTION_IDLE, "resources/w/float_attack003_cut.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::ACTION_ATTACK, "resources/w/float_attack003.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::STAND_TO_ACTION, "resources/w/default_float001_cut.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::ACTION_TO_STAND, "resources/w/default_float001_cut.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::ACTION_TO_DEATH, "resources/w/default_float001_cut.anim", CONST_ZIP_FILE);
npc02->loadBinaryAnimation(AnimationState::DEATH_IDLE, "resources/w/default_float001_cut.anim", CONST_ZIP_FILE);
npc02->npcId = "ghost_01x";
npc02->npcName = "Evil Ghost";
npc02->setTexture(ghostTexture);
npc02->walkSpeed = 1.5f;
npc02->rotationSpeed = 8.0f;
npc02->modelScale = 0.01f;
npc02->hp = 30;
//npc02->modelScale = 0.1f;
npc02->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY()));
npc02->position = params.ghostPosition;
npc02->setTarget(npc02->position);
npc02->canAttack = true;
npc02->attackTarget = player.get();
npc02->setupHitSparks(sparkTexture);
npcs.push_back(std::move(npc02));
// Teleport zone visuals: an upward fountain of sparks parked at the // Teleport zone visuals: an upward fountain of sparks parked at the
// teleport position, so the player can spot the zone from a distance. // teleport position, so the player can spot the zone from a distance.
@@ -150,12 +126,13 @@ namespace ZL
std::vector<Vector3f> teleportEmitPoints; std::vector<Vector3f> teleportEmitPoints;
teleportEmitPoints.push_back(Vector3f{ teleportPosition.x(), teleportPosition.y(), teleportPosition.z() }); teleportEmitPoints.push_back(Vector3f{ teleportPosition.x(), teleportPosition.y(), teleportPosition.z() });
teleportSparks->setEmissionPoints(teleportEmitPoints); teleportSparks->setEmissionPoints(teleportEmitPoints);
teleportSparks->setTexture(sparkTexture); teleportSparks->setTexture(sparkTexture2);
teleportSparks->setEmissionRate(50.0f); teleportSparks->setEmissionRate(50.0f);
teleportSparks->setMaxParticles(80); teleportSparks->setMaxParticles(80);
teleportSparks->setParticleSize(0.15f); teleportSparks->setParticleSize(0.05f);
teleportSparks->setBiasX(0.0f); teleportSparks->setBiasX(0.0f);
teleportSparks->setEmissionDirection(Vector3f{ 0.0f, 1.0f, 0.0f }); teleportSparks->setEmissionDirection(Vector3f{ 0.0f, 1.0f, 0.0f });
teleportSparks->setEmissionRadius(teleportRadius);
teleportSparks->setSpeedRange(0.5f, 1.0f); teleportSparks->setSpeedRange(0.5f, 1.0f);
teleportSparks->setZSpeedRange(0.5f, 1.5f); teleportSparks->setZSpeedRange(0.5f, 1.5f);
teleportSparks->setScaleRange(0.5f, 1.0f); teleportSparks->setScaleRange(0.5f, 1.0f);
-1
View File
@@ -26,7 +26,6 @@ namespace ZL
std::string navigationJsonPath; std::string navigationJsonPath;
std::string scriptPath; std::string scriptPath;
Eigen::Vector3f playerPosition = Eigen::Vector3f::Zero(); Eigen::Vector3f playerPosition = Eigen::Vector3f::Zero();
Eigen::Vector3f ghostPosition = Eigen::Vector3f::Zero();
Eigen::Vector3f teleportPosition = Eigen::Vector3f::Zero(); Eigen::Vector3f teleportPosition = Eigen::Vector3f::Zero();
float teleportRadius = 0.0f; float teleportRadius = 0.0f;
}; };
+22 -6
View File
@@ -16,8 +16,8 @@ namespace ZL {
SparkEmitter::SparkEmitter() SparkEmitter::SparkEmitter()
: emissionRate(100.0f), isActive(true), drawDataDirty(true), maxParticles(200), : emissionRate(100.0f), isActive(true), drawDataDirty(true), maxParticles(200),
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f), configured(false), shaderProgramName("default"), emissionRadius(0.0f), particleSize(0.04f), biasX(0.3f),
useWorldSpace(false) { configured(false), useWorldSpace(false) {
particles.resize(maxParticles); particles.resize(maxParticles);
drawPositions.reserve(maxParticles * 6); drawPositions.reserve(maxParticles * 6);
drawTexCoords.reserve(maxParticles * 6); drawTexCoords.reserve(maxParticles * 6);
@@ -38,6 +38,7 @@ namespace ZL {
scaleRange(copyFrom.scaleRange), scaleRange(copyFrom.scaleRange),
lifeTimeRange(copyFrom.lifeTimeRange), lifeTimeRange(copyFrom.lifeTimeRange),
shaderProgramName(copyFrom.shaderProgramName), shaderProgramName(copyFrom.shaderProgramName),
emissionRadius(copyFrom.emissionRadius),
configured(copyFrom.configured), useWorldSpace(copyFrom.useWorldSpace), configured(copyFrom.configured), useWorldSpace(copyFrom.useWorldSpace),
emissionDirection(copyFrom.emissionDirection) emissionDirection(copyFrom.emissionDirection)
{ {
@@ -49,8 +50,8 @@ namespace ZL {
SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions, float rate) SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions, float rate)
: emissionPoints(positions), emissionRate(rate), isActive(true), : emissionPoints(positions), emissionRate(rate), isActive(true),
drawDataDirty(true), maxParticles(positions.size() * 100), drawDataDirty(true), maxParticles(positions.size() * 100),
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f), configured(false), shaderProgramName("default"), emissionRadius(0.0f), particleSize(0.04f), biasX(0.3f),
useWorldSpace(false) { configured(false), useWorldSpace(false) {
particles.resize(maxParticles); particles.resize(maxParticles);
drawPositions.reserve(maxParticles * 6); drawPositions.reserve(maxParticles * 6);
drawTexCoords.reserve(maxParticles * 6); drawTexCoords.reserve(maxParticles * 6);
@@ -64,8 +65,8 @@ namespace ZL {
float rate) float rate)
: emissionPoints(positions), texture(tex), emissionRate(rate), : emissionPoints(positions), texture(tex), emissionRate(rate),
isActive(true), drawDataDirty(true), maxParticles(positions.size() * 100), isActive(true), drawDataDirty(true), maxParticles(positions.size() * 100),
shaderProgramName("default"), particleSize(0.04f), biasX(0.3f), configured(false), shaderProgramName("default"), emissionRadius(0.0f), particleSize(0.04f), biasX(0.3f),
useWorldSpace(false) { configured(false), useWorldSpace(false) {
particles.resize(maxParticles); particles.resize(maxParticles);
drawPositions.reserve(maxParticles * 6); drawPositions.reserve(maxParticles * 6);
drawTexCoords.reserve(maxParticles * 6); drawTexCoords.reserve(maxParticles * 6);
@@ -320,12 +321,27 @@ namespace ZL {
// lerpT=0 → текущая позиция (новейшая эмиссия), lerpT=1 → предыдущая (самая старая) // lerpT=0 → текущая позиция (новейшая эмиссия), lerpT=1 → предыдущая (самая старая)
bool canInterp = (prevEmissionPoints.size() == emissionPoints.size()); bool canInterp = (prevEmissionPoints.size() == emissionPoints.size());
// Reusable RNG for the radius perturbation. Rejection-sampled in a unit
// cube so the resulting offset is uniformly distributed in the unit
// ball, then scaled to emissionRadius.
static std::random_device rrd;
static std::mt19937 rgen(rrd());
std::uniform_real_distribution<float> radiusDist(-1.0f, 1.0f);
for (int i = 0; i < (int)emissionPoints.size(); ++i) { for (int i = 0; i < (int)emissionPoints.size(); ++i) {
Vector3f birthPos = emissionPoints[i]; Vector3f birthPos = emissionPoints[i];
if (canInterp && lerpT > 0.0f) { if (canInterp && lerpT > 0.0f) {
birthPos = emissionPoints[i] * (1.0f - lerpT) + prevEmissionPoints[i] * lerpT; birthPos = emissionPoints[i] * (1.0f - lerpT) + prevEmissionPoints[i] * lerpT;
} }
if (emissionRadius > 0.0f) {
Vector3f offset;
do {
offset = Vector3f{ radiusDist(rgen), radiusDist(rgen), radiusDist(rgen) };
} while (offset.squaredNorm() > 1.0f);
birthPos = birthPos + offset * emissionRadius;
}
bool particleFound = false; bool particleFound = false;
for (auto& particle : particles) { for (auto& particle : particles) {
+7
View File
@@ -50,6 +50,10 @@ namespace ZL {
std::string shaderProgramName; std::string shaderProgramName;
// Sparks spawn within this radius of each emission point. 0 means
// "spawn exactly at the point" (legacy behavior).
float emissionRadius;
bool configured; bool configured;
// Rebuilds the CPU-side quad/UV arrays. If cameraViewMatrix is identity, // Rebuilds the CPU-side quad/UV arrays. If cameraViewMatrix is identity,
// particles are rendered as axis-aligned quads in world XY and sorted by // particles are rendered as axis-aligned quads in world XY and sorted by
@@ -90,6 +94,9 @@ namespace ZL {
void setScaleRange(float minVal, float maxVal) { scaleRange.min = minVal; scaleRange.max = maxVal; } void setScaleRange(float minVal, float maxVal) { scaleRange.min = minVal; scaleRange.max = maxVal; }
void setLifeTimeRange(float minVal, float maxVal) { lifeTimeRange.min = minVal; lifeTimeRange.max = maxVal; } void setLifeTimeRange(float minVal, float maxVal) { lifeTimeRange.min = minVal; lifeTimeRange.max = maxVal; }
void setEmissionDirection(const Vector3f& dir) { emissionDirection = dir; } void setEmissionDirection(const Vector3f& dir) { emissionDirection = dir; }
// 0 = spawn exactly at the emission point; >0 = spawn at a uniform random
// point inside a 3D ball of this radius around the emission point.
void setEmissionRadius(float r) { emissionRadius = r; }
void markConfigured() { configured = true; } void markConfigured() { configured = true; }
bool isConfigured() const { return configured; } bool isConfigured() const { return configured; }
+3 -2
View File
@@ -112,7 +112,8 @@ void DialogueOverlay::drawDialogue(Renderer& renderer, const PresentationModel&
// const float H = Environment::projectionHeight; // const float H = Environment::projectionHeight;
const UiRect portraitRect{ 24.0f, 24.0f, 182.0f, 182.0f }; const UiRect portraitRect{ 24.0f, 24.0f, 182.0f, 182.0f };
const UiRect textboxRect{ 220.0f, 24.0f, max(300.0f, W - 244.0f), 182.0f }; const UiRect textboxRect{ 220.0f, 24.0f, max(200.0f, W - 244.0f), 182.0f };
lastDialogueAdvanceRect = { portraitRect.x, portraitRect.y, textboxRect.x + textboxRect.w - portraitRect.x, textboxRect.h }; lastDialogueAdvanceRect = { portraitRect.x, portraitRect.y, textboxRect.x + textboxRect.w - portraitRect.x, textboxRect.h };
lastCutsceneAdvanceRect = {}; lastCutsceneAdvanceRect = {};
cutsceneAdvanceEnabled = false; cutsceneAdvanceEnabled = false;
@@ -150,7 +151,7 @@ void DialogueOverlay::drawDialogue(Renderer& renderer, const PresentationModel&
nameRenderer->drawText(model.speaker, nameX, nameY, 1.0f, false, { 1.0f, 0.88f, 0.45f, 1.0f }); nameRenderer->drawText(model.speaker, nameX, nameY, 1.0f, false, { 1.0f, 0.88f, 0.45f, 1.0f });
} }
const std::string wrappedBody = wrapText(model.visibleText, 56); const std::string wrappedBody = wrapText(model.visibleText, 90);
bodyRenderer->drawText(wrappedBody, bodyX, bodyY, 1.0f, false, { 1.0f, 1.0f, 1.0f, 1.0f }); bodyRenderer->drawText(wrappedBody, bodyX, bodyY, 1.0f, false, { 1.0f, 1.0f, 1.0f, 1.0f });
lastChoiceRects.clear(); lastChoiceRects.clear();
+36 -1
View File
@@ -354,6 +354,12 @@ namespace ZL {
data.interactionRadius = item.value("interactionRadius", 0.0f); data.interactionRadius = item.value("interactionRadius", 0.0f);
data.animationIdlePath = item.value("animationIdlePath", ""); data.animationIdlePath = item.value("animationIdlePath", "");
data.animationWalkPath = item.value("animationWalkPath", ""); data.animationWalkPath = item.value("animationWalkPath", "");
data.animationActionIdlePath = item.value("animationActionIdlePath", "");
data.animationActionAttackPath = item.value("animationActionAttackPath", "");
data.animationStandToActionPath = item.value("animationStandToActionPath", "");
data.animationActionToStandPath = item.value("animationActionToStandPath", "");
data.animationActionToDeathPath = item.value("animationActionToDeathPath", "");
data.animationDeathIdlePath = item.value("animationDeathIdlePath", "");
data.positionX = item.value("positionX", 0.0f); data.positionX = item.value("positionX", 0.0f);
data.positionY = item.value("positionY", 0.0f); data.positionY = item.value("positionY", 0.0f);
@@ -370,6 +376,9 @@ namespace ZL {
data.facingAngle = item.value("facingAngle", 0.0f); data.facingAngle = item.value("facingAngle", 0.0f);
data.hp = item.value("hp", 100.0f);
data.canAttack = item.value("canAttack", false);
// Load gift data if available // Load gift data if available
if (item.contains("gift") && item["gift"].is_object()) { if (item.contains("gift") && item["gift"].is_object()) {
const auto& giftData = item["gift"]; const auto& giftData = item["gift"];
@@ -429,7 +438,7 @@ namespace ZL {
{ {
npc->loadAnimation(AnimationState::WALK, npcData.animationWalkPath, zipPath); npc->loadAnimation(AnimationState::WALK, npcData.animationWalkPath, zipPath);
} }
std::cout << " Loaded WALK animation: " << npcData.animationWalkPath << std::endl; std::cout << " Loaded WALK animation: " << npcData.animationWalkPath << std::endl;
} }
catch (const std::exception& e) { catch (const std::exception& e) {
@@ -437,6 +446,30 @@ namespace ZL {
continue; continue;
} }
// Optional combat/death slots — empty path means "skip this slot".
auto loadOptionalAnim = [&](AnimationState state, const std::string& path, const char* label) {
if (path.empty()) return;
try {
if (path.size() >= 5 && path.substr(path.size() - 5) == ".anim") {
npc->loadBinaryAnimation(state, path, zipPath);
}
else {
npc->loadAnimation(state, path, zipPath);
}
std::cout << " Loaded " << label << " animation: " << path << std::endl;
}
catch (const std::exception& e) {
std::cerr << "GameObjectLoader: Failed to load " << label
<< " animation for '" << npcData.name << "': " << e.what() << std::endl;
}
};
loadOptionalAnim(AnimationState::ACTION_IDLE, npcData.animationActionIdlePath, "ACTION_IDLE");
loadOptionalAnim(AnimationState::ACTION_ATTACK, npcData.animationActionAttackPath, "ACTION_ATTACK");
loadOptionalAnim(AnimationState::STAND_TO_ACTION, npcData.animationStandToActionPath, "STAND_TO_ACTION");
loadOptionalAnim(AnimationState::ACTION_TO_STAND, npcData.animationActionToStandPath, "ACTION_TO_STAND");
loadOptionalAnim(AnimationState::ACTION_TO_DEATH, npcData.animationActionToDeathPath, "ACTION_TO_DEATH");
loadOptionalAnim(AnimationState::DEATH_IDLE, npcData.animationDeathIdlePath, "DEATH_IDLE");
// Load textures: per-mesh map takes precedence; fall back to single texturePath. // Load textures: per-mesh map takes precedence; fall back to single texturePath.
try { try {
if (!npcData.meshTextures.empty()) { if (!npcData.meshTextures.empty()) {
@@ -474,6 +507,8 @@ namespace ZL {
npc->modelScale = npcData.modelScale; npc->modelScale = npcData.modelScale;
npc->position = Eigen::Vector3f(npcData.positionX, npcData.positionY, npcData.positionZ); npc->position = Eigen::Vector3f(npcData.positionX, npcData.positionY, npcData.positionZ);
npc->facingAngle = npcData.facingAngle; npc->facingAngle = npcData.facingAngle;
npc->hp = npcData.hp;
npc->canAttack = npcData.canAttack;
npc->interactionRadius = npcData.interactionRadius; npc->interactionRadius = npcData.interactionRadius;
// Set NPC metadata // Set NPC metadata
+9
View File
@@ -51,6 +51,13 @@ namespace ZL {
std::map<std::string, std::string> meshTextures; std::map<std::string, std::string> meshTextures;
std::string animationIdlePath; std::string animationIdlePath;
std::string animationWalkPath; std::string animationWalkPath;
// Optional combat / death animations. Empty path = slot not loaded.
std::string animationActionIdlePath;
std::string animationActionAttackPath;
std::string animationStandToActionPath;
std::string animationActionToStandPath;
std::string animationActionToDeathPath;
std::string animationDeathIdlePath;
float positionX = 0.0f; float positionX = 0.0f;
float positionY = 0.0f; float positionY = 0.0f;
float positionZ = 0.0f; float positionZ = 0.0f;
@@ -61,6 +68,8 @@ namespace ZL {
float modelCorrectionRotY = 0.0f; float modelCorrectionRotY = 0.0f;
float modelCorrectionRotZ = 0.0f; float modelCorrectionRotZ = 0.0f;
float facingAngle = 0.0f; float facingAngle = 0.0f;
float hp = 100.0f;
bool canAttack = false;
GiftData gift; GiftData gift;
float interactionRadius = 0.0f; float interactionRadius = 0.0f;
}; };
+15 -2
View File
@@ -249,11 +249,24 @@ int main(int argc, char *argv[]) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
#ifdef FULLSCREEN
// Match the desktop's current resolution so the framebuffer matches
// the screen and we don't force a video mode change.
/*SDL_DisplayMode dm;
if (SDL_GetCurrentDisplayMode(0, &dm) == 0) {
ZL::Environment::width = dm.w;
ZL::Environment::height = dm.h;
}*/
const Uint32 windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_FULLSCREEN_DESKTOP;
#else
const Uint32 windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
#endif
ZL::Environment::window = SDL_CreateWindow( ZL::Environment::window = SDL_CreateWindow(
"Bishkek Witcher Game", "Bishkek Witcher Game",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
CONST_WIDTH, CONST_HEIGHT, ZL::Environment::width, ZL::Environment::height,
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN windowFlags
); );
SDL_GLContext ctx = SDL_GL_CreateContext(ZL::Environment::window); SDL_GLContext ctx = SDL_GL_CreateContext(ZL::Environment::window);