Добавлена система NPC, а также боевая система.

This commit is contained in:
DeLiss
2026-06-11 12:45:30 +05:00
parent 5d4940f754
commit f0a928938b
19 changed files with 427 additions and 19 deletions

View File

@@ -182,7 +182,7 @@ void Game::handleTile(char tile) {
std::cout << "Enemy encountered. Combat will be added later.\n";
}
else if (tile == 'N') {
std::cout << "NPC: Hello, traveler.\n";
interactWithNPC(*location);
}
else if (tile == 'D') {
if (!location->riddle.isSolved) {
@@ -314,4 +314,128 @@ void Game::openChest(Location& location) {
}
std::cout << "Chest not found. Something went wrong.\n";
}
void Game::applyDialogResult(NPC& npc, const DialogOption& option) {
if (option.result == DialogResultType::GiveItem) {
player.inventory.addItem(option.value);
std::cout << "Received item ID: " << option.value << "\n";
}
else if (option.result == DialogResultType::BecomeHostile) {
npc.state = NPCState::Hostile;
startCombat(npc);
}
else if (option.result == DialogResultType::OpenLocation) {
for (auto& location : locations) {
if (location.id == option.value) {
location.riddle.isSolved = true;
std::cout << "Location " << option.value << " has been unlocked.\n";
return;
}
}
std::cout << "Location " << option.value << " not found.\n";
}
else if (option.result == DialogResultType::Fight) {
startCombat(npc);
}
else {
std::cout << "Nothing happened.\n";
}
}
void Game::startCombat(NPC& npc) {
std::cout << "\nCombat started with " << npc.name << "!\n";
while (player.hp > 0 && npc.hp > 0) {
std::cout << "\nYour HP: " << player.hp << "\n";
std::cout << npc.name << " HP: " << npc.hp << "\n";
std::cout << "1. Attack\n";
std::cout << "2. Use small potion 101\n";
std::cout << "3. Run\n";
std::cout << "> ";
int choice;
std::cin >> choice;
if (choice == 1) {
int damage = player.getDamage(itemDatabase);
npc.hp -= damage;
std::cout << "You dealt " << damage << " damage.\n";
if (npc.hp <= 0) {
npc.defeated = true;
std::cout << "You defeated " << npc.name << ".\n";
return;
}
}
else if (choice == 2) {
player.usePotion(101, itemDatabase);
}
else if (choice == 3) {
std::cout << "You ran away.\n";
return;
}
else {
std::cout << "Invalid action.\n";
continue;
}
player.hp -= npc.damage;
std::cout << npc.name << " dealt " << npc.damage << " damage.\n";
if (player.hp <= 0) {
std::cout << "You died.\n";
isRunning = false;
return;
}
}
}
void Game::interactWithNPC(Location& location) {
for (auto& npc : location.npcs) {
if (npc.position.x == player.position.x && npc.position.y == player.position.y) {
if (npc.defeated) {
std::cout << "There is nobody useful here anymore.\n";
return;
}
std::cout << "\n=== " << npc.name << " ===\n";
if (npc.state == NPCState::Hostile) {
std::cout << npc.name << " is hostile.\n";
startCombat(npc);
return;
}
std::cout << npc.dialogText << "\n";
for (int i = 0; i < npc.options.size(); i++) {
std::cout << i + 1 << ". " << npc.options[i].text << "\n";
}
std::cout << "> ";
int choice;
std::cin >> choice;
if (choice < 1 || choice > npc.options.size()) {
std::cout << "Invalid dialog option.\n";
return;
}
const DialogOption& option = npc.options[choice - 1];
std::cout << option.message << "\n";
applyDialogResult(npc, option);
return;
}
}
std::cout << "NPC not found.\n";
}

View File

@@ -64,7 +64,23 @@ bool Location::loadFromFile(const std::string& path) {
if (tile == 'C') {
Chest chest;
chest.pos = { x, y };
chest.itemIds = { 101, 102 };
if (id == 1) {
chest.itemIds = { 101, 101 };
}
else if (id == 2) {
chest.itemIds = { 101, 201 };
}
else if (id == 3) {
chest.itemIds = { 102 };
}
else if (id == 4) {
chest.itemIds = { 102, 202 };
}
else if (id == 5) {
chest.itemIds = { 101, 102, 202 };
}
chests.push_back(chest);
}
else if (tile == 'E') {
@@ -76,9 +92,26 @@ bool Location::loadFromFile(const std::string& path) {
}
else if (tile == 'N') {
NPC npc;
npc.pos = { x, y };
npc.name = "Stranger";
npcs.push_back(npc);
std::string npcPath;
if (id == 1) {
npcPath = "data/npcs/npc_1.txt";
}
else if (id == 2) {
npcPath = "data/npcs/npc_2.txt";
}
else if (id == 3) {
npcPath = "data/npcs/npc_3.txt";
}
else {
npcPath = "data/npcs/npc_1.txt";
}
if (npc.loadFromFile(npcPath)) {
npc.position = { x, y };
npcs.push_back(npc);
}
}
}
}

81
src/npc.cpp Normal file
View File

@@ -0,0 +1,81 @@
#include "../include/NPC.h"
#include <fstream>
#include <iostream>
NPCState NPC::parseState(const std::string& value) {
if (value == "FRIENDLY") return NPCState::Friendly;
if (value == "HOSTILE") return NPCState::Hostile;
return NPCState::Friendly;
}
DialogResultType NPC::parseDialogResult(const std::string& value) {
if (value == "GIVE_ITEM") return DialogResultType::GiveItem;
if (value == "BECOME_HOSTILE") return DialogResultType::BecomeHostile;
if (value == "OPEN_LOCATION") return DialogResultType::OpenLocation;
if (value == "FIGHT") return DialogResultType::Fight;
return DialogResultType::Nothing;
}
bool NPC::loadFromFile(const std::string& path) {
std::ifstream file(path);
if (!file.is_open()) {
std::cout << "Cannot open NPC file: " << path << "\n";
return false;
}
std::string line;
DialogOption option1;
DialogOption option2;
DialogOption option3;
while (std::getline(file, line)) {
if (line.empty()) {
continue;
}
if (line == "END") {
break;
}
size_t pos = line.find('=');
if (pos == std::string::npos) {
continue;
}
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
if (key == "ID") id = std::stoi(value);
else if (key == "NAME") name = value;
else if (key == "STATE") state = parseState(value);
else if (key == "HP") hp = std::stoi(value);
else if (key == "DAMAGE") damage = std::stoi(value);
else if (key == "DIALOG_TEXT") dialogText = value;
else if (key == "OPTION_1") option1.text = value;
else if (key == "RESULT_1") option1.result = parseDialogResult(value);
else if (key == "VALUE_1") option1.value = std::stoi(value);
else if (key == "MESSAGE_1") option1.message = value;
else if (key == "OPTION_2") option2.text = value;
else if (key == "RESULT_2") option2.result = parseDialogResult(value);
else if (key == "VALUE_2") option2.value = std::stoi(value);
else if (key == "MESSAGE_2") option2.message = value;
else if (key == "OPTION_3") option3.text = value;
else if (key == "RESULT_3") option3.result = parseDialogResult(value);
else if (key == "VALUE_3") option3.value = std::stoi(value);
else if (key == "MESSAGE_3") option3.message = value;
}
if (!option1.text.empty()) options.push_back(option1);
if (!option2.text.empty()) options.push_back(option2);
if (!option3.text.empty()) options.push_back(option3);
return true;
}