Code works more or less good
This commit is contained in:
parent
3495d28edf
commit
348aa632dd
@ -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
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -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": [
|
||||||
{
|
{
|
||||||
|
|||||||
BIN
resources/w/ghost_skin002.png
(Stored with Git LFS)
Normal file
BIN
resources/w/ghost_skin002.png
(Stored with Git LFS)
Normal file
Binary file not shown.
1995
resources/w/room002.txt
Normal file
1995
resources/w/room002.txt
Normal file
File diff suppressed because it is too large
Load Diff
1995
resources/w/room003.txt
Normal file
1995
resources/w/room003.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
resources/w/star.png
(Stored with Git LFS)
Normal file
BIN
resources/w/star.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -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;
|
||||||
|
|||||||
@ -15,7 +15,7 @@ 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:
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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; }
|
||||||
|
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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"];
|
||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
17
src/main.cpp
17
src/main.cpp
@ -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);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user