adg5/Engine.cpp
2025-04-21 15:55:46 +10:00

228 lines
5.5 KiB
C++

#include <SDL3/SDL.h>
#include "libtcod.hpp"
#include "Actor.h"
#include "Map.h"
#include "Engine.h"
#include "Destructible.h"
#include "Attacker.h"
#include "Ai.h"
#include "Gui.h"
#include "Container.h"
Engine::Engine(int screenWidth, int screenHeight, tcod::Context *context, tcod::Console *console) {
this->context = context;
this->console = console;
this->screenWidth = screenWidth;
this->screenHeight = screenHeight;
gui = nullptr;
map = nullptr;
player = nullptr;
fovRadius = 10;
computeFov = true;
gameStatus = STARTUP;
mouse.cx = 0;
mouse.cy = 0;
mouse.rbutton_pressed = false;
mouse.lbutton_pressed = false;
}
void Engine::init() {
gui = new Gui(context, console);
player = new Actor(40, 25, "@", "player", TCOD_ColorRGB(255, 255, 255));
player->destructible = new PlayerDestructible(30, 2, "player corpse");
player->attacker = new Attacker(5);
player->ai = new PlayerAi();
player->container = new Container(26);
actors.push(player);
map = new Map(80, 45);
map->init(true);
gui->message(TCOD_ColorRGB(150,0,0),
"Welcome stranger!\nPrepare to perish in the Tombs of Andrew's Dunegon.");
}
Engine::~Engine() {
actors.clearAndDelete();
delete map;
delete gui;
}
bool Engine::update() {
if (gameStatus == STARTUP) map->computeFov();
gameStatus = IDLE;
player->update();
if (gameStatus == NEW_TURN) {
for (Actor** iterator = actors.begin(); iterator != actors.end();
iterator++) {
Actor* actor = *iterator;
if (actor != player) {
actor->update();
}
}
}
if (gameStatus == QUIT) {
return false;
}
return true;
}
void Engine::render(bool present) {
console->clear();
// draw the map
map->render(*console);
// draw the actors
for (Actor** iterator = actors.begin();
iterator != actors.end(); iterator++) {
Actor* actor = *iterator;
if (actor != player && map->isInFov(actor->x, actor->y)) {
actor->render(*console);
}
}
player->render(*console);
gui->render();
if (present)
context->present(*console);
}
void Engine::sendToBack(Actor* actor) {
actors.remove(actor);
actors.insertBefore(actor, 0);
}
Actor* Engine::getClosestMonster(int x, int y, float range) const {
Actor* closest = NULL;
float bestDistance = 1E6f;
for (Actor** iterator = actors.begin();
iterator != actors.end(); iterator++) {
Actor* actor = *iterator;
if (actor != player && actor->destructible
&& !actor->destructible->isDead()) {
float distance = actor->getDistance(x, y);
if (distance < bestDistance && (distance <= range || range == 0.0f)) {
bestDistance = distance;
closest = actor;
}
}
}
return closest;
}
bool Engine::pickATile(int* x, int* y, float maxRange) {
while (true) {
render(false);
// highlight the possible range
for (int cx = 0; cx < map->width; cx++) {
for (int cy = 0; cy < map->height; cy++) {
if (map->isInFov(cx, cy)
&& (maxRange == 0 || player->getDistance(cx, cy) <= maxRange)) {
TCOD_ColorRGBA col = console->at(cx, cy).bg;
col.r = (int)((float)col.r * 1.2f);
col.g = (int)((float)col.g * 1.2f);
col.b = (int)((float)col.b * 1.2f);
console->at(cx, cy).bg = col;
}
}
}
SDL_Event event;
while (SDL_PollEvent(&event)) {
engine->context->convert_event_coordinates(event);
switch (event.type) {
case SDL_EVENT_MOUSE_MOTION:
engine->mouse.cx = event.motion.x;
engine->mouse.cy = event.motion.y;
break;
case SDL_EVENT_MOUSE_BUTTON_DOWN:
engine->mouse.lbutton_pressed = event.button.button == SDL_BUTTON_LEFT;
engine->mouse.rbutton_pressed = event.button.button == SDL_BUTTON_RIGHT;
break;
case SDL_EVENT_QUIT:
engine->gameStatus = Engine::QUIT;
return false;
}
}
if (map->isInFov(mouse.cx, mouse.cy)
&& (maxRange == 0 || player->getDistance(mouse.cx, mouse.cy) <= maxRange)) {
console->at(mouse.cx, mouse.cy).bg = TCOD_ColorRGBA(100, 100, 100, 255);
if (mouse.lbutton_pressed) {
*x = mouse.cx;
*y = mouse.cy;
return true;
}
}
if (mouse.rbutton_pressed) {
return false;
}
context->present(*console);
}
}
Actor* Engine::getActor(int x, int y) const {
for (Actor** iterator = actors.begin();
iterator != actors.end(); iterator++) {
Actor* actor = *iterator;
if (actor->x == x && actor->y == y && actor->destructible
&& !actor->destructible->isDead()) {
return actor;
}
}
return nullptr;
}
void Engine::save() {
if (player->destructible->isDead()) {
TCODSystem::deleteFile("game.sav");
}
else {
TCODZip zip;
// save the map first
zip.putInt(map->width);
zip.putInt(map->height);
map->save(zip);
// then the player
player->save(zip);
// then all the other actors
zip.putInt(actors.size() - 1);
for (Actor** it = actors.begin(); it != actors.end(); it++) {
if (*it != player) {
(*it)->save(zip);
}
}
// finally the message log
gui->save(zip);
zip.saveToFile("game.sav");
}
}
void Engine::load() {
if (TCODSystem::fileExists("game.sav")) {
TCODZip zip;
zip.loadFromFile("game.sav");
// load the map
int width = zip.getInt();
int height = zip.getInt();
map = new Map(width, height);
map->load(zip);
// then the player
player = new Actor(0, 0, "?", "", TCOD_ColorRGB(255, 255, 255));
player->load(zip);
actors.push(player);
// then all other actors
int nbActors = zip.getInt();
while (nbActors > 0) {
Actor* actor = new Actor(0, 0, "?", "", TCOD_ColorRGB(255, 255, 255));
actor->load(zip);
actors.push(actor);
nbActors--;
}
// finally the message log
gui = new Gui(context, console);
gui->load(zip);
}
else {
engine->init();
}
}