spells pt1

This commit is contained in:
Andrew Pamment 2025-04-21 12:10:51 +10:00
parent abd3eb9a9b
commit 2686764cb3
10 changed files with 190 additions and 10 deletions

View File

@ -31,4 +31,10 @@ Actor::~Actor() {
if (ai) delete ai; if (ai) delete ai;
if (pickable) delete pickable; if (pickable) delete pickable;
if (container) delete container; if (container) delete container;
}
float Actor::getDistance(int cx, int cy) const {
int dx = x - cx;
int dy = y - cy;
return sqrtf(dx * dx + dy * dy);
} }

View File

@ -21,7 +21,7 @@ public:
Ai* ai; Ai* ai;
Pickable* pickable; // something that can be picked and used Pickable* pickable; // something that can be picked and used
Container* container; // something that can contain actors Container* container; // something that can contain actors
float getDistance(int cx, int cy) const;
Actor(int x, int y, std::string_view ch, std::string name, const TCOD_ColorRGB& col); Actor(int x, int y, std::string_view ch, std::string name, const TCOD_ColorRGB& col);
~Actor(); ~Actor();
void render(TCOD_Console& cons) const; void render(TCOD_Console& cons) const;

View File

@ -14,7 +14,7 @@ project ("ADG5")
find_package(libtcod CONFIG REQUIRED) find_package(libtcod CONFIG REQUIRED)
# Add source to this project's executable. # Add source to this project's executable.
add_executable (ADG5 "ADG5.cpp" "ADG5.h" "Actor.cpp" "Actor.h" "Map.h" "Map.cpp" "Engine.h" "Engine.cpp" "Destructible.h" "Destructible.cpp" "Attacker.h" "Attacker.cpp" "Ai.h" "Ai.cpp" "Gui.h" "Gui.cpp" "Container.h" "Container.cpp" "Pickable.h" "Pickable.cpp" "Healer.h" "Healer.cpp") add_executable (ADG5 "ADG5.cpp" "ADG5.h" "Actor.cpp" "Actor.h" "Map.h" "Map.cpp" "Engine.h" "Engine.cpp" "Destructible.h" "Destructible.cpp" "Attacker.h" "Attacker.cpp" "Ai.h" "Ai.cpp" "Gui.h" "Gui.cpp" "Container.h" "Container.cpp" "Pickable.h" "Pickable.cpp" "Healer.h" "Healer.cpp" "Lightningbolt.h" "Lightningbolt.cpp" "Fireball.h" "Fireball.cpp")
target_link_libraries(ADG5 target_link_libraries(ADG5
PRIVATE PRIVATE

View File

@ -22,6 +22,8 @@ Engine::Engine(int screenWidth, int screenHeight, tcod::Context *context, tcod::
gameStatus = STARTUP; gameStatus = STARTUP;
mouse.cx = 0; mouse.cx = 0;
mouse.cy = 0; mouse.cy = 0;
mouse.rbutton_pressed = false;
mouse.lbutton_pressed = false;
} }
void Engine::init() { void Engine::init() {
@ -62,7 +64,7 @@ bool Engine::update() {
return true; return true;
} }
void Engine::render() { void Engine::render(bool present) {
console->clear(); console->clear();
// draw the map // draw the map
map->render(*console); map->render(*console);
@ -77,10 +79,80 @@ void Engine::render() {
} }
player->render(*console); player->render(*console);
gui->render(); gui->render();
context->present(*console); if (present)
context->present(*console);
} }
void Engine::sendToBack(Actor* actor) { void Engine::sendToBack(Actor* actor) {
actors.remove(actor); actors.remove(actor);
actors.insertBefore(actor, 0); 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 (context->get_sdl_window() != nullptr) {
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 = col.r * 1.2f;
col.g = col.g * 1.2f;
col.b = 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;
}
}
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);
}
return false;
} }

View File

@ -30,8 +30,10 @@ public:
void init(); void init();
~Engine(); ~Engine();
bool update(); bool update();
void render(); void render(bool present = true);
void sendToBack(Actor* actor); void sendToBack(Actor* actor);
Actor* getClosestMonster(int x, int y, float range) const;
bool pickATile(int* x, int* y, float maxRange = 0.0f);
private: private:
bool computeFov; bool computeFov;
}; };

30
Fireball.cpp Normal file
View File

@ -0,0 +1,30 @@
#include "libtcod.hpp"
#include "Fireball.h"
#include "Engine.h"
#include "Gui.h"
#include "Destructible.h"
#include "Actor.h"
Fireball::Fireball(float range, float damage)
: LightningBolt(range, damage) {
}
bool Fireball::use(Actor* owner, Actor* wearer) {
engine->gui->message(TCOD_ColorRGB(0, 255, 255), "Left-click a target tile for the fireball,\nor right-click to cancel.");
int x, y;
if (!engine->pickATile(&x, &y)) {
return false;
}
engine->gui->message(TCOD_ColorRGB(255, 255, 100), "The fireball explodes, burning everything within %g tiles!", range);
for (Actor** iterator = engine->actors.begin();
iterator != engine->actors.end(); iterator++) {
Actor* actor = *iterator;
if (actor->destructible && !actor->destructible->isDead()
&& actor->getDistance(x, y) <= range) {
engine->gui->message(TCOD_ColorRGB(255, 255, 100), "The %s gets burned for %g hit points.",
actor->name.c_str(), damage);
actor->destructible->takeDamage(actor, damage);
}
}
return Pickable::use(owner, wearer);
}

10
Fireball.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include "Lightningbolt.h"
class Fireball : public LightningBolt {
public:
Fireball(float range, float damage);
bool use(Actor* owner, Actor* wearer);
};

24
Lightningbolt.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "Lightningbolt.h"
#include "Engine.h"
#include "Actor.h"
#include "Gui.h"
#include "Destructible.h"
LightningBolt::LightningBolt(float range, float damage)
: range(range), damage(damage) {
}
bool LightningBolt::use(Actor* owner, Actor* wearer) {
Actor* closestMonster = engine->getClosestMonster(wearer->x, wearer->y, range);
if (!closestMonster) {
engine->gui->message(TCOD_ColorRGB(200,200,200), "No enemy is close enough to strike.");
return false;
}
// hit closest monster for <damage> hit points
engine->gui->message(TCOD_ColorRGB(100, 255, 100),
"A lighting bolt strikes the %s with a loud crack of thunder!\n"
"The damage is %g hit points.",
closestMonster->name.c_str(), damage);
closestMonster->destructible->takeDamage(closestMonster, damage);
return Pickable::use(owner, wearer);
}

12
Lightningbolt.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include "Pickable.h"
class Actor;
class LightningBolt : public Pickable {
public:
float range, damage;
LightningBolt(float range, float damage);
bool use(Actor* owner, Actor* wearer);
};

34
Map.cpp
View File

@ -6,6 +6,8 @@
#include "Attacker.h" #include "Attacker.h"
#include "Ai.h" #include "Ai.h"
#include "Healer.h" #include "Healer.h"
#include "Lightningbolt.h"
#include "Fireball.h"
static const int ROOM_MAX_SIZE = 12; static const int ROOM_MAX_SIZE = 12;
static const int ROOM_MIN_SIZE = 6; static const int ROOM_MIN_SIZE = 6;
@ -190,9 +192,31 @@ void Map::addMonster(int x, int y) {
} }
void Map::addItem(int x, int y) { void Map::addItem(int x, int y) {
Actor* healthPotion = new Actor(x, y, "!", "health potion", TCODRandom* rng = TCODRandom::getInstance();
TCOD_ColorRGB(128, 0, 128)); int dice = rng->getInt(0, 100);
healthPotion->blocks = false; if (dice < 70) {
healthPotion->pickable = new Healer(4); // create a health potion
engine->actors.push(healthPotion); Actor* healthPotion = new Actor(x, y, "!", "health potion",
TCOD_ColorRGB(128, 0, 128));
healthPotion->blocks = false;
healthPotion->pickable = new Healer(4);
engine->actors.push(healthPotion);
}
else if (dice < 70 + 10) {
// create a scroll of lightning bolt
Actor* scrollOfLightningBolt = new Actor(x, y, "#", "scroll of lightning bolt",
TCOD_ColorRGB(255, 100, 100));
scrollOfLightningBolt->blocks = false;
scrollOfLightningBolt->pickable = new LightningBolt(5, 20);
engine->actors.push(scrollOfLightningBolt);
}
else if (dice < 70 + 10 + 10) {
// create a scroll of fireball
Actor* scrollOfFireball = new Actor(x, y, "#", "scroll of fireball",
TCOD_ColorRGB(255, 100, 100));
scrollOfFireball->blocks = false;
scrollOfFireball->pickable = new Fireball(3, 12);
engine->actors.push(scrollOfFireball);
}
} }