game/sdlpp.hpp

1103 lines
37 KiB
C++
Raw Normal View History

2020-07-26 20:37:20 +00:00
#ifndef SDLPP_HPP
#define SDLPP_HPP
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
2020-08-21 19:40:15 +00:00
#include <algorithm>
2020-08-21 18:53:21 +00:00
#include <cmath>
2020-07-26 20:37:20 +00:00
#include <iostream>
#include <limits>
2020-07-26 20:37:20 +00:00
#include <memory>
2020-08-21 19:40:15 +00:00
#include <mutex>
2020-08-22 23:08:10 +00:00
#include <unordered_set>
2020-07-26 20:37:20 +00:00
#include <vector>
2020-08-23 07:47:31 +00:00
#define SDLPP_TEXT_LEFT 0x0001
#define SDLPP_TEXT_RIGHT 0x0002
#define SDLPP_TEXT_CENTER 0x0004
#define SDLPP_TEXT_TOP 0x0008
#define SDLPP_TEXT_BOTTOM 0x0010
2020-07-26 20:37:20 +00:00
namespace SDLPP {
2020-08-23 07:50:05 +00:00
int hex2num( char c );
2020-08-23 07:50:05 +00:00
std::tuple< int, int, int, int > getColorsHEX( const std::string &color );
SDL_Color getSDLColorHEX( const std::string &color );
std::tuple< int, int, int, int > getColorsSDLColor( const SDL_Color &color );
SDL_Color getSDLColorTuple( const std::tuple< int, int, int, int > &tuple );
2020-07-26 20:37:20 +00:00
class Window {
public:
2020-08-23 07:50:05 +00:00
Window()
: Window( "SDL Window", 640, 480, SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED ) {}
Window( const std::string &window_name )
: Window( window_name, 640, 480, SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED ) {}
Window( const std::string &window_name, uint32_t width, uint32_t height )
: Window( window_name, width, height, SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED ) {}
Window( const std::string &window_name, uint32_t width, uint32_t height,
uint32_t posx, uint32_t posy ) {
window = SDL_CreateWindow( window_name.c_str(), posx, posy, width,
height, SDL_WINDOW_SHOWN );
if ( window == NULL ) {
std::cerr << "SDL could not create a window! SDL_Error: "
<< SDL_GetError() << std::endl;
2020-07-26 20:37:20 +00:00
throw "Couldn't create window";
}
}
~Window() {
2020-08-23 07:50:05 +00:00
SDL_DestroyWindow( window );
2020-07-26 20:37:20 +00:00
}
SDL_Window *getWindowPtr() {
return window;
}
2020-08-23 07:50:05 +00:00
2020-07-26 20:37:20 +00:00
private:
SDL_Window *window = NULL;
};
class Renderer {
public:
Renderer() = delete;
2020-08-23 07:50:05 +00:00
Renderer( Window &window ) {
renderer = SDL_CreateRenderer( window.getWindowPtr(), -1,
SDL_RENDERER_ACCELERATED );
if ( renderer == NULL ) {
std::cerr << "SDL could not create a renderer! SDL_Error: "
<< SDL_GetError();
2020-07-26 20:37:20 +00:00
throw "Couldn't create renderer";
}
2020-08-23 07:50:05 +00:00
SDL_SetRenderDrawColor( renderer, 0xFF, 0xFF, 0xFF, 0xFF );
2020-07-26 20:37:20 +00:00
}
~Renderer() {
2020-08-23 07:50:05 +00:00
SDL_DestroyRenderer( renderer );
2020-07-26 20:37:20 +00:00
}
SDL_Renderer *getRendererPtr() {
return renderer;
}
2020-08-23 07:50:05 +00:00
std::pair< int, int > getDimensions() const {
2020-07-26 20:37:20 +00:00
int width = 0, height = 0;
2020-08-23 07:50:05 +00:00
SDL_GetRendererOutputSize( renderer, &width, &height );
return { width, height };
2020-07-26 20:37:20 +00:00
}
int getWidth() const {
return getDimensions().first;
}
int getHeight() const {
return getDimensions().second;
}
int getSmallerSide() const {
auto dimensions = getDimensions();
2020-08-23 07:50:05 +00:00
return dimensions.first < dimensions.second ? dimensions.first
: dimensions.second;
}
int getLargerSide() const {
auto dimensions = getDimensions();
2020-08-23 07:50:05 +00:00
return dimensions.first > dimensions.second ? dimensions.first
: dimensions.second;
}
2020-08-23 07:50:05 +00:00
void setBlendMode( SDL_BlendMode blendMode ) {
SDL_SetRenderDrawBlendMode( renderer, blendMode );
}
2020-08-23 07:50:05 +00:00
void setRenderColiders( bool render ) {
render_coliders = render;
}
bool getRenderColiders() {
return render_coliders;
}
2020-08-23 07:50:05 +00:00
2020-07-26 20:37:20 +00:00
private:
SDL_Renderer *renderer = NULL;
bool render_coliders = false;
2020-07-26 20:37:20 +00:00
};
class Font {
public:
Font() = delete;
2020-08-23 07:50:05 +00:00
Font( const std::string &font, int size ) {
font_ptr = TTF_OpenFont( font.c_str(), size );
if ( font_ptr == NULL ) {
std::cerr << "Unable to load font '" << font
<< "': TTF Error: " << TTF_GetError() << std::endl;
2020-08-22 23:14:59 +00:00
throw "TTF_OpenFont error";
}
}
~Font() {
TTF_CloseFont( font_ptr );
}
const TTF_Font *getFont() const {
return font_ptr;
}
TTF_Font *getFont() {
return font_ptr;
}
2020-08-23 07:50:05 +00:00
void setOutline( int size ) {
TTF_SetFontOutline( font_ptr, size );
}
int getOutline() {
2020-08-23 07:50:05 +00:00
return TTF_GetFontOutline( font_ptr );
}
2020-08-23 07:50:05 +00:00
void setStyle( int style ) {
TTF_SetFontStyle( font_ptr, style );
}
2020-08-23 07:50:05 +00:00
void setHinting( int hinting ) {
TTF_SetFontHinting( font_ptr, hinting );
}
2020-08-23 07:50:05 +00:00
private:
TTF_Font *font_ptr;
};
2020-07-26 20:37:20 +00:00
class Texture {
public:
Texture() = delete;
2020-08-23 07:50:05 +00:00
Texture( std::shared_ptr< Renderer > &renderer,
const std::string &img_path )
: Texture( renderer, img_path, "" ) {}
Texture( std::shared_ptr< Renderer > &renderer, const std::string &img_path,
const std::string &color_key ) {
SDL_Surface *surface = IMG_Load( img_path.c_str() );
if ( surface == NULL ) {
std::cerr << "Unable to load image '" << img_path
<< "': IMG Error: " << IMG_GetError() << std::endl;
2020-07-26 20:37:20 +00:00
throw "IMG_Load error";
}
2020-08-23 07:50:05 +00:00
if ( !color_key.empty() ) {
auto colors = getColorsHEX( color_key );
SDL_SetColorKey( surface, SDL_TRUE,
SDL_MapRGB( surface->format,
std::get< 0 >( colors ),
std::get< 1 >( colors ),
std::get< 2 >( colors ) ) );
2020-07-26 20:37:20 +00:00
}
2020-08-23 07:50:05 +00:00
setTextureFromSurface( renderer, surface );
}
Texture( std::shared_ptr< Renderer > &renderer, Font &font,
const std::string &text, const std::string &color = "FFFFFF",
const std::string &outline_color = "000000",
const int outline_size = -1 ) {
if ( outline_size != -1 ) {
font.setOutline( outline_size );
2020-07-26 20:37:20 +00:00
}
int og_outline = 0;
SDL_Surface *bg_surface = NULL;
2020-08-23 07:50:05 +00:00
if ( ( og_outline = font.getOutline() ) != 0 ) {
bg_surface = TTF_RenderUTF8_Blended(
font.getFont(), text.c_str(), getSDLColorHEX( outline_color ) );
if ( bg_surface == NULL ) {
std::cerr << "Unable to render text '" << text
<< "': TTF Error: " << TTF_GetError() << std::endl;
throw "TTF_RenderUTF8_Shaded error";
}
2020-08-23 07:50:05 +00:00
font.setOutline( 0 );
}
2020-08-23 07:50:05 +00:00
SDL_Surface *surface = TTF_RenderUTF8_Blended(
font.getFont(), text.c_str(), getSDLColorHEX( color ) );
if ( surface == NULL ) {
std::cerr << "Unable to render text '" << text
<< "': TTF Error: " << TTF_GetError() << std::endl;
throw "TTF_RenderUTF8_Shaded error";
}
2020-08-23 07:50:05 +00:00
if ( og_outline != 0 ) {
SDL_Rect rect = { og_outline, og_outline, surface->w, surface->h };
SDL_SetSurfaceBlendMode( surface, SDL_BLENDMODE_BLEND );
SDL_BlitSurface( surface, NULL, bg_surface, &rect );
SDL_FreeSurface( surface );
surface = bg_surface;
bg_surface = NULL;
2020-08-23 07:50:05 +00:00
font.setOutline( og_outline );
}
2020-08-23 07:50:05 +00:00
setTextureFromSurface( renderer, surface );
2020-07-26 20:37:20 +00:00
}
~Texture() {
2020-08-23 07:50:05 +00:00
SDL_DestroyTexture( texture );
2020-07-26 20:37:20 +00:00
}
SDL_Texture *getTexturePtr() {
return texture;
}
2020-08-23 07:50:05 +00:00
2020-07-26 20:37:20 +00:00
private:
2020-08-23 07:50:05 +00:00
void setTextureFromSurface( std::shared_ptr< Renderer > &renderer,
SDL_Surface *surface ) {
texture =
SDL_CreateTextureFromSurface( renderer->getRendererPtr(), surface );
if ( texture == NULL ) {
std::cerr << "Unable to create texture from surface! SDL Error: "
<< SDL_GetError() << std::endl;
throw "Texture error";
}
2020-08-23 07:50:05 +00:00
SDL_FreeSurface( surface );
}
2020-07-26 20:37:20 +00:00
SDL_Texture *texture = NULL;
};
class CollisionPolygon {
public:
2020-08-23 07:50:05 +00:00
CollisionPolygon( double x, double y ) {
2020-07-26 20:37:20 +00:00
original_x = x;
original_y = y;
position_x = 0;
position_y = 0;
}
virtual ~CollisionPolygon() {}
2020-08-23 07:50:05 +00:00
virtual bool colidesWith( const CollisionPolygon &other ) const = 0;
2020-07-26 20:37:20 +00:00
virtual bool isCircle() const = 0;
2020-08-23 07:50:05 +00:00
virtual bool isInfinite() const {
return infinite;
}
virtual void setInfinite() {
infinite = true;
}
2020-07-26 20:37:20 +00:00
virtual int topmost() const = 0;
virtual int bottommost() const = 0;
virtual int leftmost() const = 0;
virtual int rightmost() const = 0;
2020-08-23 07:50:05 +00:00
virtual void updateCollision( int x, int y, int w, int h ) {
2020-07-26 20:37:20 +00:00
position_x = original_x * w + x;
position_y = original_y * h + y;
}
2020-08-23 07:50:05 +00:00
virtual void render( Renderer &renderer,
const std::tuple< int, int, int, int > &color ) = 0;
virtual void render( Renderer &renderer ) = 0;
2020-07-26 20:37:20 +00:00
int getX() const {
return position_x;
}
int getY() const {
return position_y;
}
2020-08-23 07:50:05 +00:00
void setColor( const std::string &color ) {
sdl_color = getSDLColorHEX( color );
}
2020-08-23 07:50:05 +00:00
void setOutlineColor( const std::string &color ) {
sdl_outline = getSDLColorHEX( color );
2020-08-22 23:08:10 +00:00
}
2020-08-23 07:50:05 +00:00
2020-07-26 20:37:20 +00:00
protected:
double original_x;
double original_y;
int position_x;
int position_y;
bool infinite = false;
2020-08-23 07:50:05 +00:00
SDL_Color sdl_color = { 0, 0, 0, 0 };
SDL_Color sdl_outline = { 0, 0, 0, 0 };
2020-07-26 20:37:20 +00:00
};
2020-08-21 19:40:15 +00:00
class Scene;
2020-07-26 20:37:20 +00:00
class RenderObject {
public:
2020-08-23 07:50:05 +00:00
RenderObject( std::shared_ptr< Renderer > &r ) : renderer( r ) {}
2020-07-26 20:37:20 +00:00
virtual ~RenderObject() {}
virtual void render() = 0;
virtual int leftmost() = 0;
virtual int topmost() = 0;
virtual int rightmost() = 0;
virtual int bottommost() = 0;
2020-07-26 20:37:20 +00:00
virtual int collisionPushX() = 0;
virtual int collisionPushY() = 0;
virtual int collisionWidth() = 0;
virtual int collisionHeight() = 0;
2020-08-23 07:50:05 +00:00
virtual void specialAction( int code ) = 0;
virtual std::pair< std::pair< double, double >,
std::pair< double, double > >
getDoubleRect() = 0;
virtual void setPos( double x, double y ) = 0;
2020-08-24 14:33:53 +00:00
virtual std::pair< double, double > getPos() const = 0;
2020-08-23 07:50:05 +00:00
bool colidesWith( const RenderObject &other ) const {
if ( !hasCollisions() || !other.hasCollisions() || getHidden() ||
other.getHidden() ) {
2020-07-26 20:37:20 +00:00
return false;
}
2020-08-23 07:50:05 +00:00
for ( const auto &x : collisions ) {
for ( const auto &y : other.getCollisions() ) {
if ( x->colidesWith( *y ) )
2020-07-26 20:37:20 +00:00
return true;
}
}
return false;
}
2020-08-23 07:50:05 +00:00
template < class T > void addCollision( const T &p ) {
collisions.push_back( std::make_shared< T >( p ) );
collisions.back()->updateCollision( collisionPushX(), collisionPushY(),
collisionWidth(),
collisionHeight() );
2020-07-26 20:37:20 +00:00
}
bool hasCollisions() const {
return !collisions.empty();
}
2020-08-23 07:50:05 +00:00
const std::vector< std::shared_ptr< CollisionPolygon > > &
getCollisions() const {
2020-07-26 20:37:20 +00:00
return collisions;
}
2020-08-23 07:50:05 +00:00
virtual void setTexture( std::shared_ptr< Texture > &t ) {
2020-07-26 20:37:20 +00:00
texture = t;
}
2020-08-23 07:50:05 +00:00
virtual void setTexture( const std::string &img_path ) {
texture = std::make_shared< Texture >( renderer, img_path );
2020-07-26 20:37:20 +00:00
}
2020-08-23 07:50:05 +00:00
virtual void setTexture( Font &font, const std::string &text,
const std::string &color = "FFFFFF",
const std::string &outline_color = "000000",
int outline_size = -1 ) {
texture = std::make_shared< Texture >( renderer, font, text, color,
outline_color, outline_size );
}
2020-08-23 07:50:05 +00:00
virtual void setColor( const std::string &color ) = 0;
virtual void setOutlineColor( const std::string &color ) = 0;
2020-08-22 12:29:39 +00:00
virtual void unsetTexture() {
texture.reset();
}
virtual void unsetColor() {
polygon.reset();
}
2020-07-26 20:37:20 +00:00
// per second, relative to window width
2020-08-23 07:50:05 +00:00
void setMovementSpeed( double speed ) {
2020-07-26 20:37:20 +00:00
movementSpeed = speed;
}
2020-08-23 07:50:05 +00:00
void addMovement( int x, int y ) {
2020-07-26 20:37:20 +00:00
movementDirection.first += x;
movementDirection.second += y;
}
void resetMovementX() {
movementDirection.first = 0;
}
void resetMovementY() {
movementDirection.second = 0;
}
2020-07-26 20:37:20 +00:00
void clearColided() {
colidedWith.clear();
}
2020-08-23 07:50:05 +00:00
void addColided( std::shared_ptr< RenderObject > &obj ) {
colidedWith.push_back( obj );
2020-07-26 20:37:20 +00:00
}
2020-08-23 07:50:05 +00:00
std::vector< std::shared_ptr< RenderObject > > &getColidedWith() {
2020-07-26 20:37:20 +00:00
return colidedWith;
}
2020-08-23 07:50:05 +00:00
void setId( uint64_t input_id ) {
2020-07-26 20:37:20 +00:00
id = input_id;
}
uint64_t getId() {
return id;
}
2020-08-23 07:50:05 +00:00
void setHidden( bool hid ) {
hidden = hid;
}
2020-08-21 16:54:42 +00:00
bool getHidden() const {
return hidden;
}
2020-08-21 19:40:15 +00:00
void destroy() {
2020-08-23 07:50:05 +00:00
setHidden( true );
2020-08-21 19:40:15 +00:00
kill = true;
}
bool getKilled() {
return kill;
}
2020-08-23 07:50:05 +00:00
void setColiderColor( const std::string &color ) {
colider_color = getColorsHEX( color );
}
2020-08-23 07:50:05 +00:00
virtual void move( int ticks ) = 0;
virtual void custom_move( int ticks ) = 0;
2020-07-26 20:37:20 +00:00
virtual void updateSizeAndPosition() = 0;
virtual SDL_Rect getRect() = 0;
2020-08-23 07:50:05 +00:00
void setPermanent( bool perm ) {
permanent = perm;
}
bool getPermanent() const {
return permanent;
}
virtual void centerX() = 0;
2020-08-23 07:50:05 +00:00
2020-07-26 20:37:20 +00:00
protected:
2020-08-23 07:50:05 +00:00
std::vector< std::shared_ptr< CollisionPolygon > > collisions;
std::shared_ptr< Texture > texture;
std::shared_ptr< Renderer > renderer;
std::shared_ptr< CollisionPolygon > polygon;
2020-07-26 20:37:20 +00:00
double movementSpeed;
2020-08-23 07:50:05 +00:00
std::pair< int, int > movementDirection;
std::vector< std::shared_ptr< RenderObject > > colidedWith;
2020-07-26 20:37:20 +00:00
uint64_t id;
bool hidden = false;
2020-08-21 19:40:15 +00:00
bool kill = false;
2020-08-23 07:50:05 +00:00
std::tuple< int, int, int, int > colider_color = { 0x00, 0xFF, 0xFF, 0xFF };
2020-08-21 19:40:15 +00:00
uint64_t scene_id;
bool permanent = false;
2020-08-23 07:50:05 +00:00
2020-08-21 19:40:15 +00:00
private:
2020-08-23 07:50:05 +00:00
void setSceneID( int id ) {
2020-08-21 19:40:15 +00:00
scene_id = id;
}
friend Scene;
2020-07-26 20:37:20 +00:00
};
class Scene {
public:
2020-08-23 07:50:05 +00:00
Scene( std::shared_ptr< Renderer > &r ) : renderer( r ) {
SDL_SetRenderDrawColor( renderer->getRendererPtr(), 0xFF, 0xFF, 0xFF,
0xFF );
2020-07-26 20:37:20 +00:00
prev_ticks = SDL_GetTicks();
}
2020-08-23 07:50:05 +00:00
void addObject( const std::shared_ptr< RenderObject > &obj ) {
2020-08-21 19:40:15 +00:00
render_mutex.lock();
2020-08-23 07:50:05 +00:00
renderObjects.push_back( obj );
obj->setSceneID( ++max_object_id );
if ( obj->hasCollisions() ) {
collisionObjects.push_back( obj );
2020-07-26 20:37:20 +00:00
}
2020-08-23 07:50:05 +00:00
if ( renderObjects.size() == 1 ) {
leftmost_obj = obj;
rightmost_obj = obj;
} else {
auto rect = obj->getDoubleRect();
auto leftmost_rect = leftmost_obj->getDoubleRect();
2020-08-23 07:50:05 +00:00
if ( rect.first.first < leftmost_rect.first.first )
leftmost_obj = obj;
auto rightmost_rect = rightmost_obj->getDoubleRect();
2020-08-23 07:50:05 +00:00
if ( rect.first.first + rect.second.first >
rightmost_rect.first.first + rightmost_rect.second.first )
rightmost_obj = obj;
}
2020-08-21 19:40:15 +00:00
render_mutex.unlock();
2020-07-26 20:37:20 +00:00
}
2020-08-23 07:50:05 +00:00
std::shared_ptr< RenderObject > getObject( int index ) {
2020-07-26 20:37:20 +00:00
return renderObjects[index];
}
2020-08-23 07:50:05 +00:00
std::vector< std::shared_ptr< RenderObject > > getObjects() {
2020-08-22 23:08:10 +00:00
return renderObjects;
}
2020-07-26 20:37:20 +00:00
void movement() {
2020-08-21 19:40:15 +00:00
checkKilled();
render_mutex.lock();
2020-07-26 20:37:20 +00:00
int now_ticks = SDL_GetTicks();
2020-08-23 07:50:05 +00:00
for ( const auto &x : renderObjects ) {
x->move( now_ticks - prev_ticks );
2020-07-26 20:37:20 +00:00
}
prev_ticks = now_ticks;
2020-08-21 19:40:15 +00:00
render_mutex.unlock();
2020-07-26 20:37:20 +00:00
}
2020-08-23 07:50:05 +00:00
std::vector< std::shared_ptr< RenderObject > >
getCollisions( RenderObject &r ) {
if ( r.getHidden() )
2020-08-21 16:54:42 +00:00
return {};
2020-08-23 07:50:05 +00:00
std::vector< std::shared_ptr< RenderObject > > ret{};
for ( const auto &x : collisionObjects ) {
if ( x->colidesWith( r ) ) {
ret.push_back( x );
2020-07-26 20:37:20 +00:00
}
}
return ret;
}
2020-08-23 07:50:05 +00:00
std::vector< std::shared_ptr< RenderObject > >
getCollisions( RenderObject &r,
const std::unordered_set< int > &objectIDs ) {
if ( r.getHidden() )
2020-08-22 23:08:10 +00:00
return {};
2020-08-23 07:50:05 +00:00
std::vector< std::shared_ptr< RenderObject > > ret{};
for ( const auto &x : collisionObjects ) {
if ( objectIDs.find( x->getId() ) != objectIDs.end() &&
x->colidesWith( r ) ) {
ret.push_back( x );
2020-08-22 23:08:10 +00:00
}
}
return ret;
}
2020-08-23 07:50:05 +00:00
void renderScene( bool clear_scene = true ) {
2020-08-21 19:40:15 +00:00
checkKilled();
render_mutex.lock();
2020-08-23 07:50:05 +00:00
if ( clear_scene )
SDL_RenderClear( renderer->getRendererPtr() );
if ( background && background->getTexturePtr() )
SDL_RenderCopy( renderer->getRendererPtr(),
background->getTexturePtr(), NULL, NULL );
for ( const auto &x : renderObjects ) {
2020-07-26 20:37:20 +00:00
x->render();
}
2020-08-21 19:40:15 +00:00
render_mutex.unlock();
2020-07-26 20:37:20 +00:00
}
void presentScene() {
2020-08-23 07:50:05 +00:00
SDL_RenderPresent( renderer->getRendererPtr() );
2020-07-26 20:37:20 +00:00
}
2020-08-23 07:50:05 +00:00
void setBackground( std::shared_ptr< Texture > bg ) {
2020-07-26 20:37:20 +00:00
background = bg;
}
2020-08-23 07:50:05 +00:00
void setBackground( const std::string &img_path ) {
background = std::make_shared< Texture >( renderer, img_path );
2020-07-26 20:37:20 +00:00
}
void updateSizeAndPosition() {
2020-08-21 19:40:15 +00:00
checkKilled();
render_mutex.lock();
2020-08-23 07:50:05 +00:00
for ( auto &x : renderObjects ) {
2020-07-26 20:37:20 +00:00
x->updateSizeAndPosition();
2020-08-23 07:50:05 +00:00
for ( auto &col : x->getCollisions() ) {
col->updateCollision( x->collisionPushX(), x->collisionPushY(),
x->collisionWidth(),
x->collisionHeight() );
}
2020-07-26 20:37:20 +00:00
}
2020-08-21 19:40:15 +00:00
render_mutex.unlock();
2020-07-26 20:37:20 +00:00
}
2020-08-23 07:50:05 +00:00
void moveEverything( double x, double y ) {
2020-08-21 19:40:15 +00:00
checkKilled();
render_mutex.lock();
2020-08-23 07:50:05 +00:00
for ( auto &obj : renderObjects ) {
if ( obj->getPermanent() )
2020-08-22 23:08:10 +00:00
continue;
auto curPos = obj->getDoubleRect();
obj->setPos( curPos.first.first + x, curPos.first.second + y );
}
2020-08-21 19:40:15 +00:00
render_mutex.unlock();
}
2020-08-23 07:50:05 +00:00
const std::shared_ptr< RenderObject > &leftmost() {
return leftmost_obj;
}
2020-08-23 07:50:05 +00:00
const std::shared_ptr< RenderObject > &rightmost() {
return rightmost_obj;
}
2020-08-23 07:50:05 +00:00
std::pair< int, int > getDimensions() const {
return renderer->getDimensions();
}
int getWidth() const {
return renderer->getWidth();
}
int getHeight() const {
return renderer->getHeight();
}
Renderer &getRenderer() {
return *renderer;
}
2020-08-23 07:50:05 +00:00
void setPrevTicks( int ticks ) {
2020-08-21 16:54:42 +00:00
prev_ticks = ticks;
}
2020-08-23 07:50:05 +00:00
2020-07-26 20:37:20 +00:00
private:
2020-08-21 19:40:15 +00:00
void checkKilled() {
render_mutex.lock();
2020-08-23 07:50:05 +00:00
std::vector< int > killed;
std::vector< int > killed_collisions;
2020-08-23 07:50:05 +00:00
for ( long unsigned int i = 0; i < renderObjects.size(); i++ ) {
if ( renderObjects[i]->getKilled() )
killed.push_back( i );
if ( i < collisionObjects.size() && collisionObjects[i]->getKilled() )
killed_collisions.push_back( i );
2020-08-21 19:40:15 +00:00
}
2020-08-23 07:50:05 +00:00
std::reverse( killed.begin(), killed.end() );
std::reverse( killed_collisions.begin(), killed_collisions.end() );
2020-08-23 07:50:05 +00:00
for ( auto &index : killed ) {
renderObjects.erase( renderObjects.begin() + index );
2020-08-21 19:40:15 +00:00
}
for ( auto &index : killed_collisions ) {
collisionObjects.erase( collisionObjects.begin() + index );
}
2020-08-21 19:40:15 +00:00
render_mutex.unlock();
}
2020-08-23 07:50:05 +00:00
std::vector< std::shared_ptr< RenderObject > > renderObjects;
std::vector< std::shared_ptr< RenderObject > > collisionObjects;
std::shared_ptr< Renderer > renderer;
std::shared_ptr< Texture > background;
2020-07-26 20:37:20 +00:00
int prev_ticks = 0;
2020-08-23 07:50:05 +00:00
std::shared_ptr< RenderObject > leftmost_obj;
std::shared_ptr< RenderObject > rightmost_obj;
2020-08-21 19:40:15 +00:00
uint64_t max_object_id = 0;
std::mutex render_mutex;
2020-07-26 20:37:20 +00:00
};
class Rect : public CollisionPolygon {
public:
2020-08-23 07:50:05 +00:00
Rect( double x, double y, double w, double h ) : CollisionPolygon( x, y ) {
w_ = w;
h_ = h;
}
virtual ~Rect() {}
2020-08-23 07:50:05 +00:00
virtual bool colidesWith( const CollisionPolygon &other ) const override;
virtual bool isCircle() const override {
return false;
}
virtual int topmost() const override {
2020-08-23 07:50:05 +00:00
return ( !isInfinite() || original_y != -1 ) * getY() +
isInfinite() * -1;
}
virtual int bottommost() const override {
2020-08-23 07:50:05 +00:00
return ( !isInfinite() || h_ != -1 ) * ( getY() + pixel_h ) +
isInfinite() * -1;
};
virtual int leftmost() const override {
2020-08-23 07:50:05 +00:00
return ( !isInfinite() || original_x != -1 ) * getX() +
isInfinite() * -1;
}
virtual int rightmost() const override {
2020-08-23 07:50:05 +00:00
return ( !isInfinite() || w_ != -1 ) * ( getX() + pixel_w ) +
isInfinite() * -1;
}
2020-08-23 07:50:05 +00:00
virtual void updateCollision( int x, int y, int w, int h ) override {
position_x = original_x * w + x;
position_y = original_y * h + y;
pixel_w = w_ * w;
pixel_h = h_ * h;
}
2020-08-23 07:50:05 +00:00
virtual void
render( Renderer &renderer,
const std::tuple< int, int, int, int > &color ) override {
auto rect = getRect();
// outline with desired color at 50% opacity
2020-08-23 07:50:05 +00:00
SDL_SetRenderDrawColor( renderer.getRendererPtr(),
std::get< 0 >( color ), std::get< 1 >( color ),
std::get< 2 >( color ), 0x80 );
SDL_RenderDrawRect( renderer.getRendererPtr(), &rect );
// fill with desired color at 25% opacity
2020-08-23 07:50:05 +00:00
SDL_SetRenderDrawColor( renderer.getRendererPtr(),
std::get< 0 >( color ), std::get< 1 >( color ),
std::get< 2 >( color ), 0x40 );
SDL_RenderFillRect( renderer.getRendererPtr(), &rect );
}
2020-08-23 07:50:05 +00:00
virtual void render( Renderer &renderer ) override {
auto rect = getRect();
2020-08-23 07:50:05 +00:00
SDL_SetRenderDrawColor( renderer.getRendererPtr(), sdl_color.r,
sdl_color.g, sdl_color.b, sdl_color.a );
SDL_RenderFillRect( renderer.getRendererPtr(), &rect );
SDL_SetRenderDrawColor( renderer.getRendererPtr(), sdl_outline.r,
sdl_outline.g, sdl_outline.b, sdl_outline.a );
SDL_RenderDrawRect( renderer.getRendererPtr(), &rect );
}
2020-08-23 07:50:05 +00:00
private:
SDL_Rect getRect() {
2020-08-23 07:50:05 +00:00
if ( !isInfinite() )
return { leftmost(), topmost(), pixel_w, pixel_h };
2020-08-23 07:50:05 +00:00
SDL_Rect r = { 0, 0, 0, 0 };
if ( ( r.x = leftmost() ) == -1 )
r.x = 0;
2020-08-23 07:50:05 +00:00
if ( ( r.y = topmost() ) == -1 )
r.y = 0;
2020-08-23 07:50:05 +00:00
if ( rightmost() == -1 )
r.w = std::numeric_limits< int >::max();
else
r.w = pixel_w;
2020-08-23 07:50:05 +00:00
if ( bottommost() == -1 )
r.h = std::numeric_limits< int >::max();
else
r.h = pixel_h;
return r;
}
double w_;
double h_;
int pixel_w;
int pixel_h;
};
class Circle : public CollisionPolygon {
public:
2020-08-23 07:50:05 +00:00
Circle( double x, double y, double rad ) : CollisionPolygon( x, y ) {
original_rad = rad;
}
virtual ~Circle() {}
2020-08-23 07:50:05 +00:00
virtual bool colidesWith( const CollisionPolygon &other ) const override;
virtual bool isCircle() const override {
return true;
}
virtual int topmost() const override {
return getY() - rad_;
}
virtual int bottommost() const override {
return getY() + rad_;
};
virtual int leftmost() const override {
return getX() - rad_;
}
virtual int rightmost() const override {
return getX() + rad_;
}
virtual void updateCollision( int x, int y, int w, int h ) override {
position_x = original_x * w + x;
position_y = original_y * h + y;
rad_ = original_rad * w;
}
2020-08-23 07:50:05 +00:00
virtual void
render( Renderer &renderer,
const std::tuple< int, int, int, int > &color ) override {
std::vector< int > rect = { leftmost(), topmost(), rightmost(),
bottommost() };
auto center_x = getX();
auto center_y = getY();
auto radsq = rad_ * rad_;
2020-08-23 07:50:05 +00:00
for ( int i = rect[0]; i <= rect[2]; i++ ) {
auto xdiff = center_x - i;
auto xdist = xdiff * xdiff;
2020-08-23 07:50:05 +00:00
auto allowed_rad = sqrt( radsq - xdist );
SDL_SetRenderDrawColor(
renderer.getRendererPtr(), std::get< 0 >( color ),
std::get< 1 >( color ), std::get< 2 >( color ), 0x40 );
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
center_y - allowed_rad, i,
center_y + allowed_rad );
SDL_SetRenderDrawColor(
renderer.getRendererPtr(), std::get< 0 >( color ),
std::get< 1 >( color ), std::get< 2 >( color ), 0x80 );
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
center_y - allowed_rad, i,
center_y - allowed_rad + 2 );
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
center_y + allowed_rad, i,
center_y + allowed_rad - 2 );
}
SDL_SetRenderDrawColor( renderer.getRendererPtr(), 0xFF, 0, 0, 0xFF );
SDL_RenderDrawLine( renderer.getRendererPtr(), center_x, center_y,
center_x + rad_, center_y );
SDL_RenderDrawLine( renderer.getRendererPtr(), center_x, center_y,
center_x, center_y + rad_ );
SDL_RenderDrawLine( renderer.getRendererPtr(), center_x, center_y,
center_x - rad_, center_y );
SDL_RenderDrawLine( renderer.getRendererPtr(), center_x, center_y,
center_x, center_y - rad_ );
}
virtual void render( Renderer &renderer ) override {
std::vector< int > rect = { leftmost(), topmost(), rightmost(),
bottommost() };
auto center_x = getX();
auto center_y = getY();
auto radsq = rad_ * rad_;
2020-08-23 07:50:05 +00:00
for ( int i = rect[0]; i <= rect[2]; i++ ) {
auto xdiff = center_x - i;
auto xdist = xdiff * xdiff;
2020-08-23 07:50:05 +00:00
auto allowed_rad = sqrt( radsq - xdist );
SDL_SetRenderDrawColor( renderer.getRendererPtr(), sdl_color.r,
sdl_color.g, sdl_color.b, sdl_color.a );
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
center_y - allowed_rad, i,
center_y + allowed_rad );
SDL_SetRenderDrawColor( renderer.getRendererPtr(), sdl_outline.r,
sdl_outline.g, sdl_outline.b,
sdl_outline.a );
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
center_y - allowed_rad, i,
center_y - allowed_rad + 2 );
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
center_y + allowed_rad, i,
center_y + allowed_rad - 2 );
}
}
2020-08-23 07:50:05 +00:00
private:
int getRadius() const {
return rad_;
}
double original_rad;
int rad_;
};
2020-07-26 20:37:20 +00:00
class RectangleRender : public RenderObject {
public:
RectangleRender() = delete;
2020-08-23 07:50:05 +00:00
virtual ~RectangleRender(){};
RectangleRender( double x, double y, double w, double h,
std::shared_ptr< Renderer > &r )
: RenderObject( r ) {
og_x = x_ = x;
og_y = y_ = y;
og_w = w_ = w;
og_h = h_ = h;
2020-08-22 23:08:10 +00:00
updateSizeAndPosition();
2020-07-26 20:37:20 +00:00
}
2020-08-23 07:50:05 +00:00
RectangleRender( double x, double y, double w, double h,
std::shared_ptr< Renderer > &r,
std::shared_ptr< Texture > &t )
: RectangleRender( x, y, w, h, r ) {
setTexture( t );
}
RectangleRender( double x, double y, double w, double h,
std::shared_ptr< Renderer > &r,
const std::string &img_or_color, bool is_polygon = false )
: RectangleRender( x, y, w, h, r ) {
if ( !is_polygon ) {
setTexture( img_or_color );
} else {
2020-08-23 07:50:05 +00:00
setColor( img_or_color );
}
2020-07-26 20:37:20 +00:00
}
2020-08-23 07:50:05 +00:00
virtual void setColor( const std::string &color ) override {
if ( !polygon ) {
polygon = std::make_shared< Rect >( 0, 0, 1, 1 );
polygon->updateCollision( collisionPushX(), collisionPushY(),
collisionWidth(), collisionHeight() );
2020-08-22 23:08:10 +00:00
}
2020-08-23 07:50:05 +00:00
polygon->setColor( color );
}
2020-08-23 07:50:05 +00:00
virtual void setOutlineColor( const std::string &color ) override {
if ( !polygon ) {
polygon = std::make_shared< Rect >( 0, 0, 1, 1 );
polygon->updateCollision( collisionPushX(), collisionPushY(),
collisionWidth(), collisionHeight() );
2020-08-22 23:08:10 +00:00
}
2020-08-23 07:50:05 +00:00
polygon->setOutlineColor( color );
2020-08-22 23:08:10 +00:00
}
2020-08-23 07:50:05 +00:00
virtual void specialAction( int /*UNUSED*/ ) override{};
2020-08-22 23:08:10 +00:00
virtual void render() override {
2020-08-23 07:50:05 +00:00
if ( !getHidden() ) {
if ( polygon )
polygon->render( *renderer );
if ( texture != NULL )
SDL_RenderCopy( renderer->getRendererPtr(),
texture->getTexturePtr(), NULL, &rect );
2020-08-22 12:15:12 +00:00
}
2020-08-23 07:50:05 +00:00
if ( hasCollisions() && renderer->getRenderColiders() &&
!getHidden() ) {
for ( const auto &col : getCollisions() )
col->render( *renderer, colider_color );
}
2020-07-26 20:37:20 +00:00
}
2020-08-23 07:50:05 +00:00
virtual void move( int ticks ) override {
if ( permanent )
return;
2020-08-23 07:50:05 +00:00
auto addx =
static_cast< double >( movementSpeed * movementDirection.first ) *
( static_cast< double >( ticks ) / 1000 );
auto addy =
static_cast< double >( movementSpeed * movementDirection.second ) *
( static_cast< double >( ticks ) / 1000 );
if ( std::isnan( addx ) || std::isnan( addy ) )
2020-08-22 23:08:10 +00:00
return;
og_x += addx;
og_y += addy;
2020-08-23 07:50:05 +00:00
custom_move( ticks );
2020-08-22 23:08:10 +00:00
updateSizeAndPosition();
2020-07-26 20:37:20 +00:00
}
2020-08-23 07:50:05 +00:00
virtual void custom_move( int /*UNUSED*/ ) override {}
virtual std::pair< std::pair< double, double >,
std::pair< double, double > >
getDoubleRect() override {
return { { og_x, og_y }, { og_w, og_h } };
2020-07-27 18:05:59 +00:00
}
2020-08-23 07:50:05 +00:00
virtual void setPos( double x, double y ) override {
2020-08-22 23:08:10 +00:00
og_x = x;
og_y = y;
updateSizeAndPosition();
2020-07-27 18:05:59 +00:00
}
2020-08-24 14:33:53 +00:00
virtual std::pair< double, double > getPos() const override {
2020-08-23 07:50:05 +00:00
return { og_x, og_y };
2020-08-22 23:08:10 +00:00
}
virtual int leftmost() override {
2020-07-26 20:37:20 +00:00
return rect.x;
}
2020-08-22 23:08:10 +00:00
virtual int topmost() override {
2020-07-26 20:37:20 +00:00
return rect.y;
}
2020-08-22 23:08:10 +00:00
virtual int rightmost() override {
return rect.x + rect.w;
}
2020-08-22 23:08:10 +00:00
virtual int bottommost() override {
return rect.y + rect.h;
}
2020-08-22 23:08:10 +00:00
virtual int collisionPushX() override {
2020-07-26 20:37:20 +00:00
return rect.x;
}
2020-08-22 23:08:10 +00:00
virtual int collisionPushY() override {
2020-07-26 20:37:20 +00:00
return rect.y;
}
2020-08-22 23:08:10 +00:00
virtual int collisionWidth() override {
2020-07-26 20:37:20 +00:00
return rect.w;
}
2020-08-22 23:08:10 +00:00
virtual int collisionHeight() override {
2020-07-26 20:37:20 +00:00
return rect.h;
}
2020-08-22 23:08:10 +00:00
virtual void updateSizeAndPosition() override {
updateXY();
auto dimension = renderer->getSmallerSide();
rect.x = x_ * dimension;
rect.y = y_ * dimension;
rect.w = w_ * dimension;
rect.h = h_ * dimension;
2020-08-23 07:50:05 +00:00
if ( polygon )
polygon->updateCollision( collisionPushX(), collisionPushY(),
collisionWidth(), collisionHeight() );
for ( auto &x : collisions ) {
x->updateCollision( collisionPushX(), collisionPushY(),
collisionWidth(), collisionHeight() );
2020-08-22 23:08:10 +00:00
}
2020-07-26 20:37:20 +00:00
}
2020-08-22 23:08:10 +00:00
virtual SDL_Rect getRect() override {
return rect;
}
2020-08-22 23:08:10 +00:00
virtual void centerX() override {
centerx = true;
updateSizeAndPosition();
}
2020-08-23 07:50:05 +00:00
2020-07-27 18:05:59 +00:00
protected:
2020-08-22 23:08:10 +00:00
void updateXY() {
2020-08-23 07:50:05 +00:00
if ( !centerx ) {
2020-08-22 23:08:10 +00:00
x_ = og_x;
y_ = og_y;
return;
}
auto width = renderer->getWidth();
auto height = renderer->getHeight();
2020-08-23 07:50:05 +00:00
if ( width > height ) {
auto multiplier = static_cast< double >( width ) /
static_cast< double >( height );
x_ = og_x + static_cast< double >( multiplier - 1 ) / 2;
} else {
x_ = og_x;
}
2020-08-22 23:08:10 +00:00
y_ = og_y;
}
double og_x;
double og_y;
double og_w;
double og_h;
2020-07-26 20:37:20 +00:00
double x_;
double y_;
double w_;
double h_;
bool centerx = false;
2020-07-26 20:37:20 +00:00
SDL_Rect rect;
};
2020-08-23 07:47:31 +00:00
class TextRenderer : public RectangleRender {
public:
TextRenderer() = delete;
2020-08-23 07:50:05 +00:00
TextRenderer( double x, double y, double w, double h,
std::shared_ptr< Renderer > &r )
: RectangleRender( x, y, w, h, r ) {}
TextRenderer( double x, double y, double w, double h,
std::shared_ptr< Renderer > &r, Font &font,
const std::string &text, const std::string &color = "FFFFFF",
const std::string &outline_color = "000000",
int outline_size = -1, int flags = SDLPP_TEXT_CENTER )
: RectangleRender( x, y, w, h, r ) {
2020-08-23 07:47:31 +00:00
position_flags = flags;
2020-08-23 07:50:05 +00:00
setText( font, text, color, outline_color, outline_size );
2020-08-23 07:47:31 +00:00
}
2020-08-23 07:50:05 +00:00
void setText( Font &font, const std::string &text,
const std::string &color = "FFFFFF",
const std::string &outline_color = "000000",
int outline_size = -1 ) {
setTexture( font, text, color, outline_color, outline_size );
2020-08-23 07:47:31 +00:00
updateDstRect();
}
2020-08-23 07:50:05 +00:00
void setFlags( int flags ) {
2020-08-23 07:47:31 +00:00
position_flags = flags;
updateDstRect();
}
virtual void render() override {
2020-08-23 07:50:05 +00:00
if ( !getHidden() ) {
if ( polygon )
polygon->render( *renderer );
if ( texture != NULL )
SDL_RenderCopy( renderer->getRendererPtr(),
texture->getTexturePtr(), NULL, &dst_rect );
2020-08-23 07:47:31 +00:00
}
2020-08-23 07:50:05 +00:00
if ( hasCollisions() && renderer->getRenderColiders() &&
!getHidden() ) {
for ( const auto &col : getCollisions() )
col->render( *renderer, colider_color );
2020-08-23 07:47:31 +00:00
}
}
virtual void updateSizeAndPosition() override {
RectangleRender::updateSizeAndPosition();
updateDstRect();
}
2020-08-23 07:50:05 +00:00
2020-08-23 07:47:31 +00:00
private:
void updateDstRect() {
2020-08-23 07:50:05 +00:00
if ( !texture )
2020-08-23 07:47:31 +00:00
return;
int text_width{}, text_height{};
2020-08-23 07:50:05 +00:00
SDL_QueryTexture( texture->getTexturePtr(), NULL, NULL, &text_width,
&text_height );
if ( text_width < rect.w && text_height < rect.h ) {
2020-08-23 07:47:31 +00:00
dst_rect.w = text_width;
dst_rect.h = text_height;
} else {
2020-08-23 07:50:05 +00:00
double x_div = static_cast< double >( text_width ) /
static_cast< double >( rect.w );
double y_div = static_cast< double >( text_height ) /
static_cast< double >( rect.h );
if ( x_div > y_div ) {
2020-08-23 07:47:31 +00:00
dst_rect.w = text_width / x_div;
dst_rect.h = text_height / x_div;
} else {
dst_rect.w = text_width / y_div;
dst_rect.h = text_height / y_div;
}
}
2020-08-23 07:50:05 +00:00
if ( !( position_flags & SDLPP_TEXT_LEFT ||
position_flags & SDLPP_TEXT_RIGHT ) ) {
dst_rect.x = rect.x + ( rect.w - dst_rect.w ) / 2;
} else if ( position_flags & SDLPP_TEXT_LEFT ) {
2020-08-23 07:47:31 +00:00
dst_rect.x = rect.x;
2020-08-23 07:50:05 +00:00
} else if ( position_flags & SDLPP_TEXT_RIGHT ) {
2020-08-23 07:47:31 +00:00
dst_rect.x = rect.x + rect.w - dst_rect.w;
}
2020-08-23 07:50:05 +00:00
if ( !( position_flags & SDLPP_TEXT_TOP ||
position_flags & SDLPP_TEXT_BOTTOM ) ) {
dst_rect.y = rect.y + ( rect.h - dst_rect.h ) / 2;
} else if ( position_flags & SDLPP_TEXT_TOP ) {
2020-08-23 07:47:31 +00:00
dst_rect.y = rect.y;
2020-08-23 07:50:05 +00:00
} else if ( position_flags & SDLPP_TEXT_BOTTOM ) {
2020-08-23 07:47:31 +00:00
dst_rect.y = rect.y + rect.h - dst_rect.h;
}
}
int position_flags = 0;
SDL_Rect dst_rect{};
};
2020-07-26 20:37:20 +00:00
class CircleRender : public RenderObject {
public:
CircleRender() = delete;
2020-08-23 07:50:05 +00:00
virtual ~CircleRender(){};
CircleRender( int x, int y, int rad, std::shared_ptr< Renderer > &r )
: RenderObject( r ) {
2020-07-26 20:37:20 +00:00
x_ = x;
y_ = y;
rad_ = rad;
}
2020-08-23 07:50:05 +00:00
CircleRender( int x, int y, int rad, std::shared_ptr< Renderer > &r,
std::shared_ptr< Texture > &t )
: CircleRender( x, y, rad, r ) {
setTexture( t );
2020-07-26 20:37:20 +00:00
}
2020-08-23 07:50:05 +00:00
CircleRender( int x, int y, int rad, std::shared_ptr< Renderer > &r,
const std::string &img_path )
: CircleRender( x, y, rad, r ) {
auto texture = std::make_shared< Texture >( r, img_path );
setTexture( texture );
2020-07-26 20:37:20 +00:00
}
virtual void render();
virtual int leftmost() {
2020-08-23 07:50:05 +00:00
return x_ - rad_;
2020-07-26 20:37:20 +00:00
}
virtual int topmost() {
2020-08-23 07:50:05 +00:00
return y_ - rad_;
2020-07-26 20:37:20 +00:00
}
virtual int collisionPushX() {
return x_;
}
virtual int collisionPushY() {
return y_;
}
2020-08-23 07:50:05 +00:00
2020-07-26 20:37:20 +00:00
private:
int x_;
int y_;
int rad_;
};
bool init();
2020-08-23 07:50:05 +00:00
bool init( uint32_t SDL_OPTIONS );
bool init( uint32_t SDL_OPTIONS, int IMAGE_OPTIONS );
2020-07-26 20:37:20 +00:00
2020-08-23 07:50:05 +00:00
template < class T > void testPolymorphism( T &obj );
2020-07-26 20:37:20 +00:00
} // end of namespace SDLPP
#endif