diff --git a/CMakeLists.txt b/CMakeLists.txt index 930d73c..4632eee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ add_executable(PlatScifi src/worldobjects/staticdeadly.h src/worldobjects/leveltp.h src/worldobjects/spike.h + src/worldobjects/animal.cpp ) target_link_libraries(PlatScifi PRIVATE sfml-graphics) target_compile_features(PlatScifi PRIVATE cxx_std_17) @@ -67,6 +68,7 @@ set(ASSETS LiberationSans-Regular.ttf enemy_bird_0.png enemy_bird_1.png + enemy_fipa_0.png ) foreach(ASSET ${ASSETS}) diff --git a/src/assets/enemy_fipa_0.png b/src/assets/enemy_fipa_0.png new file mode 100644 index 0000000..3cdf909 Binary files /dev/null and b/src/assets/enemy_fipa_0.png differ diff --git a/src/assets/level_1.csv b/src/assets/level_1.csv index 32ab4d3..64c3688 100644 --- a/src/assets/level_1.csv +++ b/src/assets/level_1.csv @@ -18,4 +18,5 @@ LevelTp, 55, 28 // Moving Platform MovingCollider, 8, 32, 5, 2, 300, 20, 32 // Animal -Animal, 50, 15 \ No newline at end of file +Animal, bird, 50, 15 +Animal, fipa, 55, 15 \ No newline at end of file diff --git a/src/renderer.cpp b/src/renderer.cpp index a6533df..8c5a10a 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -80,7 +80,7 @@ void Renderer::renderWorld(GameState& gameState) { for (std::vector::iterator it = gameState.objectsBegin(); it != gameState.objectsEnd(); it++) { renderFromData((*it)->getRenderData()); } - // TODO: Render UI + // TODO: render on-screen debug messages (ex. "enemy_fipa spawned") // TODO: refactor/abstract this sf::Text levelName; levelName.setFont(assetHandler.getMainFont()); diff --git a/src/worldobjects/animal.cpp b/src/worldobjects/animal.cpp new file mode 100644 index 0000000..abdbbeb --- /dev/null +++ b/src/worldobjects/animal.cpp @@ -0,0 +1,33 @@ +#include "animal.h" + +// Define the default data for all named animals () +const std::map Animal::animalDefaults = { + { + "bird", // Name + { + "Bird", // Display name + "enemy_bird", // Texture prefix + 1, // Animation frame max + 2.0, // Width + 2.0, // Height + { + // Object attributes + ObjectAttribute::Collision + } + } + }, + { + "fipa", + { + "Fipa", + "enemy_fipa", + 0, + 3.0, + 3.0, + { + ObjectAttribute::Collision, + ObjectAttribute::Deadly + } + } + } +}; diff --git a/src/worldobjects/animal.h b/src/worldobjects/animal.h index a2dc07f..adfb665 100644 --- a/src/worldobjects/animal.h +++ b/src/worldobjects/animal.h @@ -1,8 +1,20 @@ #pragma once +#include +#include #include "../worldobject.h" #include "../constants.h" +// The default data for a specific animal type +struct AnimalData { + std::string displayName; + std::string texturePrefix; + int animFrameMax; + double width; + double height; + std::set objectAttributes; +}; + // The data for an animal in the world class Animal : public WorldObject { private: @@ -10,19 +22,41 @@ class Animal : public WorldObject { double vely = 0; bool onGround = false; long frameCount = 0; + std::string texturePrefix; int animFrameNum = 0; int animFrameMax = 1; + bool moveRight = false; public: - // Constructor - Animal(double spawnx, double spawny) : WorldObject() { + // Define the default data for all named animals () + static const std::map animalDefaults; + + // Constructor (define everything specifically) + Animal(std::string texturePrefix, int animFrameMax, double spawnx, double spawny) : WorldObject() { locx = spawnx; locy = spawny; width = 2.0; height = 2.0; + this->texturePrefix = texturePrefix; + this->animFrameMax = animFrameMax; objectAttributes.insert(ObjectAttribute::Collision); } + // Constructor for named animals + Animal(std::string name, double spawnx, double spawny) : WorldObject() { + locx = spawnx; + locy = spawny; + if (animalDefaults.find(name) == animalDefaults.end()) { + // Could not find the animal + name = "bird"; + } + width = (*animalDefaults.find(name)).second.width; + height = (*animalDefaults.find(name)).second.height; + this->texturePrefix = (*animalDefaults.find(name)).second.texturePrefix; + this->animFrameMax = (*animalDefaults.find(name)).second.animFrameMax; + objectAttributes = (*animalDefaults.find(name)).second.objectAttributes; + } + // TODO: different AI based on animal type (enum? stringly typed?) // Input: acceleration @@ -54,7 +88,7 @@ class Animal : public WorldObject { // Override update: gravity and acceleration, and AI logic UpdateResult update(WorldState& worldState, std::vector& objects) { // AI Logic - accelerate(-0.01, 0); + accelerate(moveRight ? 0.01 : -0.01, 0); jump(); // Physics if (!LEVEL_DESIGN_MODE) { @@ -83,6 +117,8 @@ class Animal : public WorldObject { velx = 0; collided = true; overlapped = true; + // For AI + moveRight = !moveRight; } } if (locx + width >= object->getLocx() && locx < object->getLocx() + object->getWidth()) { @@ -140,7 +176,7 @@ class Animal : public WorldObject { width, height, { 255, 0, 0 }, - "assets/enemy_bird_" + std::to_string(animFrameNum) + ".png" + "assets/" + texturePrefix + "_" + std::to_string(animFrameNum) + ".png" }; } }; diff --git a/src/worldspawner.cpp b/src/worldspawner.cpp index e2b254f..35af812 100644 --- a/src/worldspawner.cpp +++ b/src/worldspawner.cpp @@ -1,3 +1,4 @@ +#include #include "worldspawner.h" void WorldSpawner::spawnWorld(GameState& gameState, std::string levelName) { @@ -64,14 +65,23 @@ void WorldSpawner::spawnWorld(GameState& gameState, std::string levelName) { std::stoi(parsed[7]) )); } else if (parsed[0] == "Animal") { - if (parsed.size() < 3) throw; - gameState.spawnObject(new Animal( - std::stoi(parsed[1]), std::stoi(parsed[2]) - )); + if (parsed.size() < 4) throw; + if (parsed.size() < 5) { + // Size of 4: spawn a named animal + gameState.spawnObject(new Animal( + parsed[1], std::stoi(parsed[2]), std::stoi(parsed[3]) + )); + } else { + // Size of 5 or more: spawn a specific animal + gameState.spawnObject(new Animal( + parsed[1], std::stoi(parsed[2]), std::stoi(parsed[3]), std::stoi(parsed[4]) + )); + } } } } catch (std::exception& e) { // Failed: re-clear the world + std::cout << "ERR: Failed to re-clear the world (level: " << levelName << ")" << std::endl; gameState.clear(); return; }