430 lines
14 KiB
C++
430 lines
14 KiB
C++
#pragma once
|
|
|
|
#include <optional>
|
|
#include <functional>
|
|
|
|
#include "glm/glm.hpp"
|
|
|
|
#include "Timer.hpp"
|
|
#include "Box.hpp"
|
|
#include "Animation.hpp"
|
|
#include "Curve.hpp"
|
|
#include "Sprite.hpp"
|
|
#include "Character.hpp"
|
|
|
|
class Enemy
|
|
{
|
|
|
|
protected:
|
|
|
|
sb::Sprite sprite;
|
|
sb::Box box;
|
|
|
|
/* Enemies have an optional challenge coin defined by different parameters depending on the enemy type. */
|
|
std::optional<sb::Sprite> _coin;
|
|
bool _coin_taken = false, coin_collected = false;
|
|
|
|
/*!
|
|
* Default destructor, defined as virtual so that the auto generated derived destructors will be called.
|
|
*/
|
|
virtual ~Enemy() = default;
|
|
|
|
public:
|
|
|
|
/*!
|
|
* Reset enemy to original state. Does nothing unless the derived class implements a reset method.
|
|
*/
|
|
virtual void reset();
|
|
|
|
/*!
|
|
* Set the object's coin taken state to true, so the coin will be able to be collected if the checkpoint or end
|
|
* is reached.
|
|
*/
|
|
virtual void take_coin();
|
|
|
|
/*!
|
|
* @return coin taken state
|
|
*/
|
|
bool coin_taken() const;
|
|
|
|
/*!
|
|
* Set object's coin collected state to true, so even if the enemy is reset, the coin will remain taken and collected.
|
|
*/
|
|
virtual void collect_coin();
|
|
|
|
/*!
|
|
* Set the position of the enemy's coin. If the enemy object has no coin, do nothing.
|
|
*
|
|
* @param translation coin location
|
|
*/
|
|
void coin_translation(const glm::vec3& translation);
|
|
|
|
/*!
|
|
* @param timer timer that has been updating every unpaused frame to be used to measure distance to move this frame
|
|
*/
|
|
virtual void update(const sb::Timer& timer) = 0;
|
|
|
|
/*!
|
|
* Perform GL drawing operations using the given uniform locations and transformation matrices.
|
|
*
|
|
* @param transformation_uniform transformation uniform location in the shader program
|
|
* @param view view transformation matrix
|
|
* @param projection projection transformation matrix
|
|
* @param texture_flag_uniform uniform location in the shader program of the boolean that turns texture drawing on or off
|
|
* @param rotating_hue color of the hue rotation effect
|
|
* @param color_addition_uniform uniform location in the shader program of the RGBA value for color addition effect
|
|
*/
|
|
virtual void draw(GLuint transformation_uniform, const glm::mat4& view, const glm::mat4& projection, GLuint texture_flag_uniform,
|
|
const sb::Color& rotating_hue, GLuint color_addition_uniform);
|
|
|
|
/*!
|
|
* Check whether the enemy collides with the given ::Sprite positioned at the given ::Box. The sprite object may be used to perform
|
|
* pixel collision, so alpha pixels that collide will not be counted as a collision, but this is not implemented yet.
|
|
*
|
|
* The enemy and character are tracked without wrapping their coordinates, so the clip for wrapping must be given to this function
|
|
* in order to determine if they collide.
|
|
*
|
|
* @param box position in world coordinates to check for a collision with the enemy's own box object
|
|
* @param sprite graphics to use for per pixel collision
|
|
* @param clip_lower clip area lower bounds to wrap the position to
|
|
* @param clip_upper clip area upper bounds to wrap the position to
|
|
* @return true if the given box collides with this enemy's box
|
|
*/
|
|
virtual bool collide(const sb::Box& box, const sb::Sprite& sprite, const glm::vec3& clip_lower, const glm::vec3& clip_upper) const;
|
|
|
|
/*!
|
|
* Check whether the given box coordinates collide with the enemy's challenge coin. If the enemy doesn't have a challenge coin, this
|
|
* returns false.
|
|
*
|
|
* The enemy and character are tracked without wrapping their coordinates, so the clip for wrapping must be given to this function
|
|
* in order to determine if they collide.
|
|
*
|
|
* @param box position in world coordinates to check for a collision with the enemy's challenge coin
|
|
* @param clip_lower clip area lower bounds to wrap the position to
|
|
* @param clip_upper clip area upper bounds to wrap the position to
|
|
* @return true if the given box collides with the enemy's challenge coin
|
|
*/
|
|
virtual bool collide_coin(sb::Box box, const glm::vec3& clip_lower, const glm::vec3& clip_upper) const;
|
|
|
|
};
|
|
|
|
/*!
|
|
* Create an enemy which moves back and forth, cutting the curve perpendicular to the angle of the curve at a given point.
|
|
*/
|
|
class Slicer : public Enemy
|
|
{
|
|
|
|
private:
|
|
|
|
std::reference_wrapper<const Curve> curve;
|
|
float relative = 0.0f, speed = 0.0f, stray = 0.0f, coin_radius = 0.0f, coin_angle = 0.0f;
|
|
bool toward_end = true;
|
|
|
|
/*!
|
|
*/
|
|
glm::vec3 center() const;
|
|
|
|
/*!
|
|
*/
|
|
float angle() const;
|
|
|
|
/*!
|
|
*/
|
|
glm::vec3 start() const;
|
|
|
|
/*!
|
|
*/
|
|
glm::vec3 end() const;
|
|
|
|
public:
|
|
|
|
glm::vec3 position = {0.0f, 0.0f, 0.0f};
|
|
|
|
/*!
|
|
*/
|
|
Slicer(const Curve& curve, float relative, float speed = 0.51440329f, float stray = 0.24691358f);
|
|
|
|
/*!
|
|
* Add a challenge coin to this slicer enemy. The sprite will be owned, moved, and drawn by this object.
|
|
*
|
|
* @param sprite representation of the coin
|
|
* @param radius distance from the center of the slicer object
|
|
* @param angle angle position around the slicer object
|
|
*/
|
|
void coin(const sb::Sprite& sprite, float radius, float angle);
|
|
|
|
/*!
|
|
* If the slicer has a challenge coin, check if it collides with the given box.
|
|
*
|
|
* @see Enemy::collide_coin(const sb::Box&, const glm::vec3&, const glm::vec3&)
|
|
*/
|
|
bool collide_coin(sb::Box box, const glm::vec3& clip_lower, const glm::vec3& clip_upper) const;
|
|
|
|
/*!
|
|
*/
|
|
void update(const sb::Timer& timer);
|
|
};
|
|
|
|
/*!
|
|
* Create an enemy which rotates around a point on the curve at a given radius and speed.
|
|
*/
|
|
class Fish : public Enemy
|
|
{
|
|
|
|
private:
|
|
|
|
std::reference_wrapper<const Curve> curve;
|
|
float relative = 0.0f, speed = 0.0f, radius = 0.0f, angle = 0.0f, offset = 0.0f, coin_angle = 0.0f, coin_radius = 0.0f;
|
|
std::optional<float> first_update_time;
|
|
|
|
/*!
|
|
* @return vertex in world coordinates the fish is rotating around, always lies on the curve
|
|
*/
|
|
glm::vec3 center() const;
|
|
|
|
public:
|
|
|
|
/*!
|
|
* Create a Fish object from relative position on a given curve, speed, and radius. Optionally, an offset angle can be added to start
|
|
* the fish that amount away on its circle.
|
|
*
|
|
* The speed is in radians to rotate per second.
|
|
*
|
|
* @param curve curve to associate this enemy with
|
|
* @param relative the position on the curve from 0 to 1
|
|
* @param speed radians to rotate per second
|
|
* @param radius distance from the center to rotate around
|
|
* @param offset angle amount in radians to offset start point
|
|
*/
|
|
Fish(const Curve& curve, float relative, float speed = 0.01010029f, float radius = 0.12345679f, float offset = 0.0f);
|
|
|
|
/*!
|
|
* Add a challenge coin to this fish enemy. The coin will rotate on the same circle as the fish, offset by the given angle. The sprite will
|
|
* be owned, moved, and drawn by this object.
|
|
*
|
|
* @param sprite representation of the coin
|
|
* @param angle amount in radians to offset from the fish
|
|
*/
|
|
void coin(const sb::Sprite& sprite, float angle);
|
|
|
|
/*!
|
|
* Add a challenge coin to this fish enemy. The coin will be positioned at the given distance and angle away from the fish. The sprite will
|
|
* be owned, moved, and drawn by this object.
|
|
*
|
|
* @param sprite representation of the coin
|
|
* @param radius distance of the coin from the center of the fish
|
|
* @param angle angle around the fish's center to position the coin
|
|
*/
|
|
void coin(const sb::Sprite& sprite, float radius, float angle);
|
|
|
|
/*!
|
|
* If the fish has a challenge coin, check if it collides with the given box.
|
|
*
|
|
* @see Enemy::collide_coin(const sb::Box&, const glm::vec3&, const glm::vec3&)
|
|
*/
|
|
bool collide_coin(sb::Box box, const glm::vec3& clip_lower, const glm::vec3& clip_upper) const;
|
|
|
|
/*!
|
|
*/
|
|
void update(const sb::Timer& timer);
|
|
|
|
};
|
|
|
|
/*!
|
|
* Create an enemy which is a projectile that has been fired toward a location by a ::Projector object. It continues travelling in
|
|
* the same direction even after reaching the point, and goes off screen.
|
|
*/
|
|
class Projectile : public Enemy
|
|
{
|
|
|
|
private:
|
|
|
|
glm::vec3 position = glm::vec3{0.0f}, target = glm::vec3{0.0f};
|
|
float speed = 0.0f, angle = 0.0f;
|
|
|
|
public:
|
|
|
|
/*!
|
|
* Create a ::Projectile object, an ::Enemy directed toward a destination, given the destination, start, and speed.
|
|
*
|
|
* The speed is in world coordinates per second.
|
|
*
|
|
* @param position
|
|
* @param target
|
|
* @param speed distance in world coordinates the projectiles will travel per second
|
|
*/
|
|
Projectile(const glm::vec3& position, const glm::vec3& target, float speed = 0.51440329f);
|
|
|
|
/*!
|
|
* Turn the projectile into a coin.
|
|
*
|
|
* @param sprite sprite representing the coin, will be copied into the object, will be moved and drawn by the object
|
|
*/
|
|
void coinify(const sb::Sprite& sprite);
|
|
|
|
/*!
|
|
*/
|
|
void update(const sb::Timer& timer);
|
|
|
|
/*!
|
|
*/
|
|
void draw(GLuint transformation_uniform, const glm::mat4& view, const glm::mat4& projection, GLuint texture_flag_uniform,
|
|
const sb::Color& rotating_hue, GLuint color_addition_uniform);
|
|
|
|
/*!
|
|
* @return true if the projectile has left the clip space
|
|
*/
|
|
bool out_of_bounds() const;
|
|
|
|
/*!
|
|
* @return return if the projectile was transformed into a challenge coin
|
|
*/
|
|
bool coinified() const;
|
|
|
|
};
|
|
|
|
/*!
|
|
* Create an enemy which fires projectiles toward the player's position.
|
|
*/
|
|
class Projector : public Enemy
|
|
{
|
|
|
|
private:
|
|
|
|
const Character& character;
|
|
glm::vec3 position = glm::vec3{0.0f};
|
|
float speed = 0.0f;
|
|
float release_delay;
|
|
sb::Animation animation_charge = sb::Animation(std::bind(&Projector::charge, this)),
|
|
animation_release = sb::Animation(std::bind(&Projector::release, this));
|
|
std::vector<Projectile> projectiles;
|
|
int coin_frequency = 0, count = 0;
|
|
|
|
void charge();
|
|
|
|
void release();
|
|
|
|
public:
|
|
|
|
/*!
|
|
* Create a Projector object at a given position. Set the speed and rate of fire.
|
|
*
|
|
* The speed is in world coordinates per second, and the frequency is number of seconds between each projectile launch.
|
|
*
|
|
* @param position location to fire from in world coordinates
|
|
* @param speed distance in world coordinates the projectiles will travel per second
|
|
* @param rate amount of seconds between each projectile
|
|
* @param release_delay amount in seconds between charge and release
|
|
*/
|
|
Projector(const Character& character, const glm::vec3& position, float speed = 0.51440329f, float rate = 3.0f, float release_delay = 0.3f);
|
|
|
|
/*!
|
|
*/
|
|
void reset();
|
|
|
|
/*!
|
|
* Add a challenge coin to this projector enemy. The projector will transform every Nth projectile into a coin.
|
|
*
|
|
* @param sprite sprite to represent the coin, will be copied to the Nth projectile
|
|
* @param frequency every Nth projectile will be a challenge coin
|
|
*/
|
|
void coin(const sb::Sprite& sprite, int frequency);
|
|
|
|
/*!
|
|
* If the projector has a challenge coin, check if it collides with the given box.
|
|
*
|
|
* @see Enemy::collide_coin(const sb::Box&, const glm::vec3&, const glm::vec3&)
|
|
*/
|
|
bool collide_coin(sb::Box box, const glm::vec3& clip_lower, const glm::vec3& clip_upper) const;
|
|
|
|
/*!
|
|
* Remove all projectiles that are coinified.
|
|
*/
|
|
void take_coin();
|
|
|
|
/*!
|
|
*/
|
|
void update(const sb::Timer& timer);
|
|
|
|
/*!
|
|
*/
|
|
void draw(GLuint transformation_uniform, const glm::mat4& view, const glm::mat4& projection, GLuint texture_flag_uniform,
|
|
const sb::Color& rotating_hue, GLuint color_addition_uniform);
|
|
|
|
/*!
|
|
* Check if projectiles collide.
|
|
*
|
|
* @see Enemy::collide(const sb::Box&, const Sprite&, const glm::vec3&, const glm::vec3&)
|
|
*/
|
|
bool collide(const sb::Box& box, const sb::Sprite& sprite, const glm::vec3& clip_lower, const glm::vec3& clip_upper) const;
|
|
|
|
};
|
|
|
|
/*!
|
|
* Create an enemy which is a flame that constantly scrolls with a given angle direction and speed from its initial position, optionally
|
|
* mirroring to scroll the opposite direction at a given interval. The enemy wraps at the edges of the screen, so it is always on screen.
|
|
*/
|
|
class Flame : public Enemy
|
|
{
|
|
|
|
private:
|
|
|
|
sb::Box field;
|
|
glm::vec3 position = glm::vec3{0.0f}, initial_position = glm::vec3{0.0f};
|
|
float speed = 0.0f, angle = 0.0f, coin_angle = 0.0f, coin_radius = 0.0f, initial_angle = 0.0f;
|
|
sb::Animation animation_mirror = sb::Animation(std::bind(&Flame::mirror, this));
|
|
bool mirrored = false, _camo = false;
|
|
|
|
/*!
|
|
* Rotate direction 180 degrees.
|
|
*/
|
|
void mirror();
|
|
|
|
public:
|
|
|
|
/*!
|
|
* Create a ::Flame object, a scrolling ::Enemy moving away from the given start position with the given angle and speed. If
|
|
* mirror interval is given, the flame object will switch to the opposite direction every interval.
|
|
*
|
|
* The speed is in world coordinates per second.
|
|
*
|
|
* @param field
|
|
* @param position starting position
|
|
* @param speed distance in world coordinates the flame will travel per second
|
|
* @param angle direction the flame will travel
|
|
* @param mirror_interval optional interval in seconds the direction should mirror, negative values turn off mirroring
|
|
* @param camo disguise the flame as a coin
|
|
*/
|
|
Flame(const sb::Box& field, const glm::vec3& position, float speed, float angle, float mirror_interval = -1.0f, bool camo = false);
|
|
|
|
/*!
|
|
* Add a challenge coin to this flame enemy. The sprite will be owned, moved, and drawn by this object.
|
|
*
|
|
* @param sprite representation of the coin
|
|
* @param radius distance from the center of the flame object
|
|
* @param angle angle position around the slicer object
|
|
*/
|
|
void coin(const sb::Sprite& sprite, float radius, float angle);
|
|
|
|
/*!
|
|
* If the flame has a challenge coin, check if it collides with the given box.
|
|
*
|
|
* @see Enemy::collide_coin(const sb::Box&, const glm::vec3&, const glm::vec3&)
|
|
*/
|
|
bool collide_coin(sb::Box box, const glm::vec3& clip_lower, const glm::vec3& clip_upper) const;
|
|
|
|
inline void camo(bool state)
|
|
{
|
|
_camo = state;
|
|
}
|
|
|
|
bool camo() const
|
|
{
|
|
return _camo;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void update(const sb::Timer& timer);
|
|
};
|