diff --git a/.gitignore b/.gitignore index ae6aa09..37f748e 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,10 @@ # Vagrant path .vagrant/ + +# Build paths +bin +obj + +*.swp +*.swo diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2ba4c6e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +sudo: required +dist: trusty +language: cpp + +install: + - sudo apt-get update + - sudo apt-get install libsdl2-2.0-0 libsdl2-dev libsdl2-image-2.0-0 libsdl2-image-dev libsdl2-ttf-2.0-0 libsdl2-ttf-dev libsdl2-mixer-2.0-0 libsdl2-mixer-dev -y +compiler: + - g++ +script: + - make diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5d11869 --- /dev/null +++ b/Makefile @@ -0,0 +1,92 @@ +NAME = game +SRC_DIR = src +INC_DIR = include +OBJ_DIR = obj +BIN_DIR = bin + +TARGET = $(BIN_DIR)/$(NAME) + +CC = g++ +CFLAGS = -pedantic -std=c++11 -MMD -g3 -g -fPIC\ + -W -Wall -Wextra -Wshadow -Wcast-align -Wcast-qual -Wctor-dtor-privacy\ + -Wdisabled-optimization -Wformat=2 -Wlogical-op -Wmissing-declarations\ + -Wmissing-include-dirs -Wnoexcept -Woverloaded-virtual -Wredundant-decls\ + -Wsign-promo -Wstrict-null-sentinel -Wundef\ + -Wzero-as-null-pointer-constant -Wuseless-cast -Wnon-virtual-dtor +INCLUDES = -I$(INC_DIR) `sdl2-config --cflags` +LIBS = `sdl2-config --libs` -lSDL2_image -lSDL2_ttf -lSDL2_mixer -ldl + +SRC = ${wildcard $(SRC_DIR)/*.cpp} +OBJ = ${addprefix $(OBJ_DIR)/, ${notdir ${SRC:.cpp=.o}}} + +RMDIR = rm -rf + +#-------------------------------------------------------------- +ifeq ($(OS), Windows_NT) + +SDL_PATH = C:\SDL-2.0.5 + +INCLUDES = -Iinclude/ -I$(SDL_PATH)\include + +LIBS = -L $(SDL_PATH)\lib -lSDL2main\ + -lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf -lm + +NAME := $(NAME).exe +#-------------------------------------------------------------- +else + +UNAME_S := $(shell uname -s) + +ifeq ($(UNAME_S), Darwin) + +LIBS = -lm -framework SDL2 -framework SDL2_image -framework SDL2_mixer\ + -framework SDL_TTF + +#------------------------------------------------------------- + +endif +endif + +.PHONY: clean depend dist-clean dist + +all: +#------------------------------------------------------------- + @mkdir -p $(OBJ_DIR) $(BIN_DIR) + $(MAKE) $(TARGET) +#------------------------------------------------------------- + +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp + @echo Building $@ + @$(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@ + +$(TARGET): $(OBJ) + @echo Building $@ + $(CC) $(CFLAGS) $(INCLUDES) $(OBJ) -o $@ $(LIBS) + @echo Done. + +run: + $(TARGET) + +reset: + make dist-clean + make -j + +crun: + make dist-clean + make -j + make run + +arun: + make -j + make run + +clean: + @echo Cleaning... + @$(RMDIR) *~ *.o + +dist-clean: clean + @$(RMDIR) $(TARGET)/$(NAME) + @$(RMDIR) *.tar.gz $(OBJ_DIR) $(BIN_DIR) + +print-%: + @echo $* = $($*) diff --git a/include/Animation.h b/include/Animation.h new file mode 100644 index 0000000..20f1d55 --- /dev/null +++ b/include/Animation.h @@ -0,0 +1,26 @@ +#ifndef ANIMATION_H +#define ANIMATION_H + +#include "GameObject.h" +#include "Timer.h" +#include "Sprite.h" + +class Animation : public GameObject{ +private: + Timer end_timer; + float time_limit; + bool one_time_only; + Sprite sprite; + +public: + Animation(float x, float y, float crotation, string csprite, int frame_count, float frame_time, bool ends); + void update(float delta); + void render(); + + bool is_dead(); + + void notify_collision(GameObject & object); + bool is(string type); +}; + +#endif diff --git a/include/BattleState.h b/include/BattleState.h new file mode 100644 index 0000000..019cc70 --- /dev/null +++ b/include/BattleState.h @@ -0,0 +1,31 @@ +#ifndef BATTLESTATE_H +#define BATTLESTATE_H + +#include "State.h" +#include "Sprite.h" +#include "Text.h" +#include "Timer.h" +#include "Music.h" +#include "Fighter.h" + +#include + +class BattleState : public State{ + private: + Sprite background[3]; + vector fighters; + Music music; + void read_level_design(string stage); + + public: + BattleState(string stage, string cmusic); + ~BattleState(); + + void update(float delta); + void render(); + + void pause(); + void resume(); +}; + +#endif diff --git a/include/Camera.h b/include/Camera.h new file mode 100644 index 0000000..d187023 --- /dev/null +++ b/include/Camera.h @@ -0,0 +1,23 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include "GameObject.h" +#include "Vector.h" + +#define LAYERS 4 + +class Camera{ +private: + static GameObject * focus; + +public: + static Vector pos[LAYERS]; + static float layer_speed[LAYERS]; + static Vector speed; + + static void follow(GameObject * new_focus); + static void unfollow(); + static void update(float delta); +}; + +#endif diff --git a/include/Collision.h b/include/Collision.h new file mode 100644 index 0000000..4b17130 --- /dev/null +++ b/include/Collision.h @@ -0,0 +1,70 @@ +#ifndef COLLISION_H +#define COLLISION_H + +#include +#include + +#include "Rectangle.h" +#include "Vector.h" + +class Collision { +public: + static inline bool is_colliding(Rectangle& a, Rectangle& b, float angleOfA, float angleOfB){ + Vector A[] = { Vector( a.get_draw_x(), a.get_draw_y() + a.get_height() ), + Vector( a.get_draw_x() + a.get_width(), a.get_draw_y() + a.get_height() ), + Vector( a.get_draw_x() + a.get_width(), a.get_draw_y() ), + Vector( a.get_draw_x(), a.get_draw_y() ) + }; + Vector B[] = { Vector( b.get_draw_x(), b.get_draw_y() + b.get_height() ), + Vector( b.get_draw_x() + b.get_width(), b.get_draw_y() + b.get_height() ), + Vector( b.get_draw_x() + b.get_width(), b.get_draw_y() ), + Vector( b.get_draw_x(), b.get_draw_y() ) + }; + + for (auto& v : A) { + v = rotate(v - a.get_center(), angleOfA) + a.get_center(); + } + + for (auto& v : B) { + v = rotate(v - b.get_center(), angleOfB) + b.get_center(); + } + + Vector axes[] = { norm(A[0] - A[1]), norm(A[1] - A[2]), norm(B[0] - B[1]), norm(B[1] - B[2]) }; + + for (auto& axis : axes) { + float P[4]; + + for (int i = 0; i < 4; ++i) P[i] = dot(A[i], axis); + + float minA = *std::min_element(P, P + 4); + float maxA = *std::max_element(P, P + 4); + + for (int i = 0; i < 4; ++i) P[i] = dot(B[i], axis); + + float minB = *std::min_element(P, P + 4); + float maxB = *std::max_element(P, P + 4); + + if (maxA < minB || minA > maxB) + return false; + } + + return true; + } + +private: + static inline float mag(const Vector& p){ + return std::sqrt(p.x * p.x + p.y * p.y); + } + static inline Vector norm(const Vector& p){ + return p * (1.f / mag(p)); + } + static inline float dot(const Vector& a, const Vector& b){ + return a.x * b.x + a.y * b.y; + } + static inline Vector rotate(const Vector& p, float angle){ + float cs = std::cos(angle), sn = std::sin(angle); + return Vector ( p.x * cs - p.y * sn, p.x * sn + p.y * cs ); + } +}; + +#endif diff --git a/include/EditState.h b/include/EditState.h new file mode 100644 index 0000000..d763497 --- /dev/null +++ b/include/EditState.h @@ -0,0 +1,29 @@ +#ifndef EDITSTATE_H +#define EDITSTATE_H + +#include "State.h" +#include "Sprite.h" +#include "Text.h" +#include "Timer.h" +#include "Fighter.h" + +class EditState : public State{ +private: + Sprite background[3]; + Fighter * test_fighter; + string stage; + + void read_level_design(); + void update_level_design(); + +public: + EditState(string stage); + + void update(float delta); + void render(); + + void pause(); + void resume(); +}; + +#endif diff --git a/include/EditableFloor.h b/include/EditableFloor.h new file mode 100644 index 0000000..f8b8c4b --- /dev/null +++ b/include/EditableFloor.h @@ -0,0 +1,32 @@ +#ifndef EDITABLEFLOOR_H +#define EDITABLEFLOOR_H + +#include "Floor.h" +#include "Sprite.h" + +class EditableFloor : public Floor{ +private: + enum FloorState{SELECTED, NOT_SELECTED}; + Sprite normal_sprite, platform_sprite, selected_sprite; + FloorState state; + bool deleted; + bool selected; + +public: + + EditableFloor(float x, float y, float crotation, bool cplatform); + EditableFloor(float x, float y, float width, float crotation, bool cplatform); + + ~EditableFloor(); + + void update(float delta); + void render(); + bool is_dead(); + + void notify_collision(GameObject & object); + + string get_information(); + void set_selected(bool cselected); +}; + +#endif diff --git a/include/Fighter.h b/include/Fighter.h new file mode 100644 index 0000000..da85e09 --- /dev/null +++ b/include/Fighter.h @@ -0,0 +1,50 @@ +#ifndef FIGHTER_H +#define FIGHTER_H + +#include "GameObject.h" +#include "Sprite.h" +#include "Vector.h" +#include "Timer.h" + +class Fighter : public GameObject{ + private: + enum FighterState {IDLE, RUNNING, JUMPING, FALLING, CROUCH}; + enum Orientation {LEFT, RIGHT}; + Sprite sprite[10]; + FighterState state; + Orientation orientation; + Vector speed; + Vector acceleration; + float vertical_speed; + bool on_floor, pass_through; + int last_collided_floor; + float max_speed; + int remaining_life; + void test_limits(); + int special; + Timer crouch_timer; + + public: + Fighter(string name, float x, float y); + ~Fighter(); + + void update(float delta); + void post_collision_update(float delta); + void render(); + bool is_dead(); + + int get_remaining_life(); + int get_special(); + + void notify_collision(GameObject & object); + bool is(string type); + + void change_state(FighterState cstate); + void reset_position(float x, float y); + + static const int MAX_LIFE = 500; + + static const int MAX_SPECIAL = 400; +}; + +#endif diff --git a/include/FighterStats.h b/include/FighterStats.h new file mode 100644 index 0000000..5c09012 --- /dev/null +++ b/include/FighterStats.h @@ -0,0 +1,40 @@ +#ifndef FIGHTERSTATS_H +#define FIGHTERSTATS_H + +#include + +#include "GameObject.h" +#include "Sprite.h" +#include "Fighter.h" + +using std::string; + +class FighterStats : public GameObject{ + public: + FighterStats(Fighter *p_fighter, int p_index_fighter, int p_side, double p_x, double p_y); + ~FighterStats(); + + void update(float delta); + + void render(); + + bool is_dead(); + + void notify_collision(GameObject &object); + + bool is(string type); + + private: + Sprite bg; + Sprite empty_bg; + Sprite life; + Sprite special; + double percent_to_draw_life; + double percent_to_draw_special; + int index_fighter; + int side; + double x, y; + Fighter *fighter; +}; + +#endif diff --git a/include/Floor.h b/include/Floor.h new file mode 100644 index 0000000..107d4b9 --- /dev/null +++ b/include/Floor.h @@ -0,0 +1,30 @@ +#ifndef FLOOR_H +#define FLOOR_H + +#include "GameObject.h" +#include "Sprite.h" + +class Floor : public GameObject{ +private: + int id; + +protected: + bool is_platform; + +public: + Floor(float x, float y, float width, float crotation, bool cplatform); + ~Floor(); + + void update(float delta); + void render(); + bool is_dead(); + + void notify_collision(GameObject & object); + bool is(string type); + + int get_id(); + + static int floor_id; +}; + +#endif diff --git a/include/Game.h b/include/Game.h new file mode 100644 index 0000000..5d6354c --- /dev/null +++ b/include/Game.h @@ -0,0 +1,49 @@ +#ifndef GAME_H +#define GAME_H + +#include "SDL2/SDL.h" +#include "SDL2/SDL_image.h" +#include "SDL2/SDL_mixer.h" +#include "SDL_ttf.h" +#include +#include +#include + +#include "State.h" + +using std::string; +using std::stack; +using std::unique_ptr; + +class Game{ +private: + static Game * instance; + + int frame_start; + float delta; + + State * stored_state; + SDL_Window * window; + SDL_Renderer * renderer = nullptr; + + stack< unique_ptr > state_stack; + + void calculate_delta_time(); + void manage_stack(); + +public: + Game(string title, int width, int height); + ~Game(); + + static Game & get_instance(); + SDL_Renderer * get_renderer(); + State & get_current_state(); + + void push(State * state); + + void run(); + + float get_delta_time(); +}; + +#endif diff --git a/include/GameObject.h b/include/GameObject.h new file mode 100644 index 0000000..448ca95 --- /dev/null +++ b/include/GameObject.h @@ -0,0 +1,24 @@ +#ifndef GAMEOBJECT_H +#define GAMEOBJECT_H + +#include "Rectangle.h" +#include + +using std::string; + +class GameObject{ +public: + virtual ~GameObject(){}; + virtual void update(float delta) = 0; + virtual void post_collision_update(float){}; + + virtual void render() = 0; + virtual bool is_dead() = 0; + virtual void notify_collision(GameObject & object) = 0; + virtual bool is(string type) = 0; + + Rectangle box; + float rotation = 0; +}; + +#endif diff --git a/include/InputManager.h b/include/InputManager.h new file mode 100644 index 0000000..9f7b2f2 --- /dev/null +++ b/include/InputManager.h @@ -0,0 +1,68 @@ +#ifndef INPUTMANAGER_H +#define INPUTMANAGER_H + +#include "SDL2/SDL.h" + +#include +#include +#include + +#define ii pair + +using std::unordered_map; +using std::map; +using std::pair; + +class InputManager{ +private: + static InputManager * input_manager; + + bool mouse_state[6]; + int mouse_update[6]; + + unordered_map key_state; + unordered_map key_update; + map event_responded; + + bool m_quit_requested; + int update_counter; + + int mouse_x; + int mouse_y; + + bool can_respond(int key, int operation, bool response); + +public: + InputManager(); + ~InputManager(); + + void update(); + + bool key_press(int key, bool response = false); + bool key_release(int key, bool response = false); + bool is_key_down(int key, bool response = false); + + bool mouse_press(int button); + bool mouse_release(int button); + bool is_mouse_down(int button); + + int get_mouse_x(); + int get_mouse_y(); + + bool quit_requested(); + + static InputManager * get_instance(); + + // FIXME not being used + static const int LEFT_ARROW_KEY = SDLK_LEFT; + static const int RIGHT_ARROW_KEY = SDLK_RIGHT; + static const int DOWN_ARROW_KEY = SDLK_DOWN; + static const int UP_ARROW_KEY = SDLK_UP; + static const int ESCAPE_KEY = SDLK_ESCAPE; + static const int SPACE_KEY = SDLK_SPACE; + static const int LEFT_MOUSE_BUTTON = SDL_BUTTON_LEFT; + static const int RIGHT_MOUSE_BUTTON = SDL_BUTTON_RIGHT; + static const int ENTER_KEY = SDLK_RETURN; +}; + +#endif diff --git a/include/MenuState.h b/include/MenuState.h new file mode 100644 index 0000000..c60beff --- /dev/null +++ b/include/MenuState.h @@ -0,0 +1,28 @@ +#ifndef MENU_STATE_H +#define MENU_STATE_H + +#include "State.h" +#include "Sprite.h" +#include "Text.h" +#include "Timer.h" + +class MenuState : public State { +private: + Sprite background, green_ship, red_ship, title, planet; + vector options; + Text* start_option; + int current_option; + bool start_pressed, show_text; + Timer text_timer; + +public: + MenuState(); + + void update(float delta); + void render(); + + void pause(); + void resume(); +}; + +#endif diff --git a/include/Music.h b/include/Music.h new file mode 100644 index 0000000..9094c77 --- /dev/null +++ b/include/Music.h @@ -0,0 +1,27 @@ +#ifndef MUSIC_H +#define MUSIC_H + +#include "SDL_mixer.h" + +#include +#include + +using std::string; +using std::shared_ptr; + +class Music{ +private: + shared_ptr music; + +public: + Music(); + Music(string file); + + void play(int times = -1); + void stop(); + void open(string file); + bool is_open(); + +}; + +#endif diff --git a/include/Rectangle.h b/include/Rectangle.h new file mode 100644 index 0000000..3cb7bc7 --- /dev/null +++ b/include/Rectangle.h @@ -0,0 +1,32 @@ +#ifndef RECTANGLE_H +#define RECTANGLE_H + +#include "SDL2/SDL.h" + +#include "Vector.h" + +class Rectangle{ +public: + + float x; + float y; + float width; + float height; + + Rectangle(); + Rectangle(float x, float y, float width, float height); + bool is_inside(float mx, float my); + float get_x() const; + float get_y() const; + void set_x(float cx); + void set_y(float cy); + float get_draw_x() const; + float get_draw_y() const; + float get_width() const; + float get_height() const; + void set_width(float w); + void set_height(float h); + Vector get_center() const; +}; + +#endif diff --git a/include/Resources.h b/include/Resources.h new file mode 100644 index 0000000..186e5aa --- /dev/null +++ b/include/Resources.h @@ -0,0 +1,37 @@ +#ifndef RESOURCES_H +#define RESOURCES_H + +#include "SDL2/SDL_image.h" +#include "SDL2/SDL_mixer.h" +#include "SDL2/SDL_ttf.h" + +#include +#include +#include + +using std::unordered_map; +using std::string; +using std::shared_ptr; + +class Resources{ +private: + static unordered_map > image_table; + static unordered_map > music_table; + static unordered_map > sound_table; + static unordered_map > font_table; + +public: + static shared_ptr get_image(string file); + static void clear_images(); + + static shared_ptr get_music(string file); + static void clear_music(); + + static shared_ptr get_sound(string file); + static void clear_sound(); + + static shared_ptr get_font(string file, int size); + static void clear_fonts(); +}; + +#endif diff --git a/include/Sound.h b/include/Sound.h new file mode 100644 index 0000000..77801d4 --- /dev/null +++ b/include/Sound.h @@ -0,0 +1,28 @@ +#ifndef SOUND_H +#define SOUND_H + +#include "SDL_mixer.h" + +#include +#include + +using std::string; +using std::shared_ptr; + +class Sound{ +private: + shared_ptr sound; + int channel; + +public: + Sound(); + Sound(string file); + + void play(int times); + void stop(); + void open(string file); + bool is_open(); + +}; + +#endif diff --git a/include/Sprite.h b/include/Sprite.h new file mode 100644 index 0000000..40f3ef1 --- /dev/null +++ b/include/Sprite.h @@ -0,0 +1,49 @@ +#ifndef SPRITE_H +#define SPRITE_H + +#include "SDL2/SDL.h" +#include "SDL2/SDL_image.h" +#include +#include + +using std::string; +using std::shared_ptr; + +class Sprite{ +private: + shared_ptr texture = nullptr; + int width; + int height; + int frame_count; + int current_frame; + float time_elapsed; + float frame_time; + SDL_Rect clip_rect; + float scale_x; + float scale_y; + +public: + Sprite(); + Sprite(string file, int cframe_count = 1, float cframe_time = 1, int cur_frame = 0); + ~Sprite(); + + int get_width(); + int get_height(); + bool is_open(); + + void open(string file); + void set_clip(int x, int y, int w, int h); + void set_frame(int frame); + void set_frame_count(int cframe_count); + void set_frame_time(float cframe_time); + + void update(float delta); + void render(int x, int y, float angle = 0, SDL_RendererFlip flip = SDL_FLIP_NONE); + + void set_scale_x(float scale); + void set_scale_y(float scale); + void update_scale_x(float scale); + void restart_count(); +}; + +#endif diff --git a/include/State.h b/include/State.h new file mode 100644 index 0000000..b1c52e3 --- /dev/null +++ b/include/State.h @@ -0,0 +1,40 @@ +#ifndef STATE_H +#define STATE_H + +#include "GameObject.h" + +#include +#include + +using std::vector; +using std::unique_ptr; + +class State{ +protected: + bool m_pop_requested; + bool m_quit_requested; + + vector> object_array; + + virtual void update_array(float delta); + virtual void render_array(); + +public: + State(); + virtual ~State(); + + virtual void update(float delta) = 0; + virtual void render() = 0; + + virtual void pause() = 0; + virtual void resume() = 0; + + virtual void add_object(GameObject * object); + + virtual void load_assets(); + + bool pop_requested(); + bool quit_requested(); +}; + +#endif diff --git a/include/StateData.h b/include/StateData.h new file mode 100644 index 0000000..344d8ac --- /dev/null +++ b/include/StateData.h @@ -0,0 +1,9 @@ +#ifndef STATEDATA_H +#define STATEDATA_H + +class StateData{ +public: + bool player_victory; +}; + +#endif diff --git a/include/Text.h b/include/Text.h new file mode 100644 index 0000000..be14af4 --- /dev/null +++ b/include/Text.h @@ -0,0 +1,53 @@ +#ifndef TEXT_H +#define TEXT_H + +#include "SDL_ttf.h" + +#include "Rectangle.h" + +#include +#include + +using std::string; +using std::shared_ptr; + +class Text{ +public: + enum TextStyle {SOLID, SHADED, BLENDED}; + +private: + shared_ptr font; + SDL_Texture * texture; + + string text; + TextStyle style; + int font_size; + SDL_Color color; + Rectangle box; + SDL_Rect clip_rect; + +public: + Text(); + Text(string cfont_file, int cfont_size, TextStyle cstyle, string ctext, SDL_Color ccolor, int x = 0, int y = 0); + ~Text(); + + void render(int camera_x = 0, int camera_y = 0); + + void set_pos(int x, int y, bool center_x = false, bool center_y = false); + + void set_text(string ctext); + void set_color(SDL_Color ccolor); + void set_style(TextStyle cstyle); + void set_font_size(int cfont_size); + + float get_x(); + float get_y(); + float get_width(); + float get_height(); + string get_text(); + + void remake_texture(); + void open(string font_name, int cfont_size); +}; + +#endif diff --git a/include/TimeCounter.h b/include/TimeCounter.h new file mode 100644 index 0000000..dc31eb8 --- /dev/null +++ b/include/TimeCounter.h @@ -0,0 +1,39 @@ +#ifndef TIMECOUNTER_H +#define TIMECOUNTER_H + +#include + +#include "GameObject.h" +#include "Sprite.h" +#include "Timer.h" +#include "Text.h" + +using std::string; + +class TimeCounter : public GameObject{ + public: + TimeCounter(); + ~TimeCounter(); + + void update(float delta); + + void render(); + + bool is_dead(); + + void notify_collision(GameObject &object); + + bool is(string type); + + static const int total_time = 100; + + private: + Sprite bg; + Timer timer; + Text *text; + int remaining_seconds; + + string get_time_string(); +}; + +#endif diff --git a/include/Timer.h b/include/Timer.h new file mode 100644 index 0000000..9ce439d --- /dev/null +++ b/include/Timer.h @@ -0,0 +1,15 @@ +#ifndef TIMER_H +#define TIMER_H + +class Timer{ +private: + float time; + +public: + Timer(); + void update(float delta); + void restart(); + float get(); +}; + +#endif diff --git a/include/Vector.h b/include/Vector.h new file mode 100644 index 0000000..9e150c9 --- /dev/null +++ b/include/Vector.h @@ -0,0 +1,17 @@ +#ifndef VECTOR_H +#define VECTOR_H + +class Vector{ +public: + float x; + float y; + Vector(float mx = 0, float my = 0); + void rotate(Vector origin, float angle); + void transform(float module, float angle); + + Vector operator+(const Vector& rhs) const; + Vector operator-(const Vector& rhs) const; + Vector operator*(const float rhs) const; +}; + +#endif diff --git a/res/edit_state/floor/editable_floor.png b/res/edit_state/floor/editable_floor.png new file mode 100644 index 0000000..2f3084b Binary files /dev/null and b/res/edit_state/floor/editable_floor.png differ diff --git a/res/edit_state/floor/editable_platform.png b/res/edit_state/floor/editable_platform.png new file mode 100644 index 0000000..a983f2c Binary files /dev/null and b/res/edit_state/floor/editable_platform.png differ diff --git a/res/edit_state/floor/selected_editable_floor.png b/res/edit_state/floor/selected_editable_floor.png new file mode 100644 index 0000000..a00a946 Binary files /dev/null and b/res/edit_state/floor/selected_editable_floor.png differ diff --git a/res/edit_state/test_fighter/crouch.png b/res/edit_state/test_fighter/crouch.png new file mode 100644 index 0000000..cad7d99 Binary files /dev/null and b/res/edit_state/test_fighter/crouch.png differ diff --git a/res/edit_state/test_fighter/falling.png b/res/edit_state/test_fighter/falling.png new file mode 100644 index 0000000..17ce17b Binary files /dev/null and b/res/edit_state/test_fighter/falling.png differ diff --git a/res/edit_state/test_fighter/idle.png b/res/edit_state/test_fighter/idle.png new file mode 100644 index 0000000..1c0474b Binary files /dev/null and b/res/edit_state/test_fighter/idle.png differ diff --git a/res/edit_state/test_fighter/jumping.png b/res/edit_state/test_fighter/jumping.png new file mode 100644 index 0000000..bc59716 Binary files /dev/null and b/res/edit_state/test_fighter/jumping.png differ diff --git a/res/edit_state/test_fighter/running.png b/res/edit_state/test_fighter/running.png new file mode 100644 index 0000000..f6cad96 Binary files /dev/null and b/res/edit_state/test_fighter/running.png differ diff --git a/res/flesh/crouch.png b/res/flesh/crouch.png new file mode 100644 index 0000000..8986e8d Binary files /dev/null and b/res/flesh/crouch.png differ diff --git a/res/flesh/falling.png b/res/flesh/falling.png new file mode 100644 index 0000000..25a55c9 Binary files /dev/null and b/res/flesh/falling.png differ diff --git a/res/flesh/idle.png b/res/flesh/idle.png new file mode 100644 index 0000000..029dae9 Binary files /dev/null and b/res/flesh/idle.png differ diff --git a/res/flesh/jumping.png b/res/flesh/jumping.png new file mode 100644 index 0000000..8124040 Binary files /dev/null and b/res/flesh/jumping.png differ diff --git a/res/flesh/running.png b/res/flesh/running.png new file mode 100644 index 0000000..3b7e244 Binary files /dev/null and b/res/flesh/running.png differ diff --git a/res/font/8-BIT WONDER.TXT b/res/font/8-BIT WONDER.TXT new file mode 100644 index 0000000..c015ac9 --- /dev/null +++ b/res/font/8-BIT WONDER.TXT @@ -0,0 +1,22 @@ +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + VIEW THIS USING A MONOSPACED FONT!!! +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + :: SPECIFIC INFO FOR THIS FONT :: + + Heh, I'm a big fan of 8-bit things, so I made an 8-bit font. The original plan + was to make it a pixel-perfect low resolution font, and that part does work as + well, but as an additional bonus it looks pretty interesting if used at bigger + sizes in italic mode. + + This font was made for making titles for my website and my pictures, so it has + certain specific optimizations. When used on-screen it will look the best with + font sizes starting with 9 and then any next 9. That is 9, 18, 27, 36, etc. + + Enjoy, and don't forget to send feedback and tell me what characters you would + like to see added. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + Revision: 0.80 | 26.01.2001 + [EOF] \ No newline at end of file diff --git a/res/font/8-BIT WONDER.ttf b/res/font/8-BIT WONDER.ttf new file mode 100644 index 0000000..6d9b397 Binary files /dev/null and b/res/font/8-BIT WONDER.ttf differ diff --git a/res/font/README.TXT b/res/font/README.TXT new file mode 100644 index 0000000..92680cb --- /dev/null +++ b/res/font/README.TXT @@ -0,0 +1,53 @@ +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + VIEW THIS USING A MONOSPACED FONT!!! +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + :: INTRODUCTION :: + + Greetings, welcome to the readme file for my fonts. That's it. :) + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + :: TERMS OF USE :: + + Currently all my fonts are freeware. That doesn't mean however, that you can + do whatever you want with them. Here are the rules for use and distribution: + + 1. You WILL NOT modify the fonts, the copyright notices, or this text file. + 2. You WILL NOT sell the fonts in ANY way. + 3. Whatever you do with it, you take full responsibility. + + Those are the rules you MUST follow to use or distribute these fonts. If you + disagree, stop using the fonts immediately. If I grow aware of any violations + of these terms, expect legal action to be taken. So, you have been warned. + + And, if you decide to use any of the fonts for commercial purposes, it would + be really nice to receive a product sample. I can make a special version of a + font exclusively for you as well. Just e-mail me if you are interested. +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + :: ABOUT ME :: + + Well, not much to say, really. I'm a guy from Latvia, called Joiro Hatagaya, + I like making fonts, 2D/3D graphics, music, games and programs and many other + things. Send me feedback to make me happy and give me energy to do more. :) + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + :: CONTACT INFORMATION AND WEBSITES :: + + E-MAIL: JOIRO@HOTMAIL.COM | Please put "FONTS" in caps as the subject. Do not + send spam, or you WILL regret it. + + WEBSITE: JOIRO.THE3DSTUDIO.COM | Come here to see some of my 3D work and find + out what other things I do. + + NEW!!! + FONT WEBSITE: http://www.typesource.com/Presents/Hatagaya/Fonts.html | Yes! I + now have a special mini-website for my fonts only, where you can find all the + latest versions of these fonts and also brand new fonts. So go visit it! NOW! + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + Revision: 1.20 | 05.01.2001 + [EOF] \ No newline at end of file diff --git a/res/hud/left_empty_background.png b/res/hud/left_empty_background.png new file mode 100644 index 0000000..4c66f2f Binary files /dev/null and b/res/hud/left_empty_background.png differ diff --git a/res/hud/left_life.png b/res/hud/left_life.png new file mode 100644 index 0000000..5392e26 Binary files /dev/null and b/res/hud/left_life.png differ diff --git a/res/hud/left_special_bar.png b/res/hud/left_special_bar.png new file mode 100644 index 0000000..3964a4c Binary files /dev/null and b/res/hud/left_special_bar.png differ diff --git a/res/hud/life1_frame.png b/res/hud/life1_frame.png new file mode 100644 index 0000000..7c35123 Binary files /dev/null and b/res/hud/life1_frame.png differ diff --git a/res/hud/life2_frame.png b/res/hud/life2_frame.png new file mode 100644 index 0000000..7f7cd02 Binary files /dev/null and b/res/hud/life2_frame.png differ diff --git a/res/hud/life3_frame.png b/res/hud/life3_frame.png new file mode 100644 index 0000000..29cd7bc Binary files /dev/null and b/res/hud/life3_frame.png differ diff --git a/res/hud/life4_frame.png b/res/hud/life4_frame.png new file mode 100644 index 0000000..1a04e59 Binary files /dev/null and b/res/hud/life4_frame.png differ diff --git a/res/hud/right_empty_background.png b/res/hud/right_empty_background.png new file mode 100644 index 0000000..4c66f2f Binary files /dev/null and b/res/hud/right_empty_background.png differ diff --git a/res/hud/right_life.png b/res/hud/right_life.png new file mode 100644 index 0000000..2445c98 Binary files /dev/null and b/res/hud/right_life.png differ diff --git a/res/hud/right_special_bar.png b/res/hud/right_special_bar.png new file mode 100644 index 0000000..15d5070 Binary files /dev/null and b/res/hud/right_special_bar.png differ diff --git a/res/hud/time_board.png b/res/hud/time_board.png new file mode 100644 index 0000000..34ea258 Binary files /dev/null and b/res/hud/time_board.png differ diff --git a/res/menu/background.jpg b/res/menu/background.jpg new file mode 100644 index 0000000..b54256f Binary files /dev/null and b/res/menu/background.jpg differ diff --git a/res/menu/green_ship.png b/res/menu/green_ship.png new file mode 100644 index 0000000..4ba6629 Binary files /dev/null and b/res/menu/green_ship.png differ diff --git a/res/menu/planet.png b/res/menu/planet.png new file mode 100644 index 0000000..b5b5143 Binary files /dev/null and b/res/menu/planet.png differ diff --git a/res/menu/red_ship.png b/res/menu/red_ship.png new file mode 100644 index 0000000..7020bd5 Binary files /dev/null and b/res/menu/red_ship.png differ diff --git a/res/menu/title.png b/res/menu/title.png new file mode 100644 index 0000000..5ad7979 Binary files /dev/null and b/res/menu/title.png differ diff --git a/res/stage_1/background_0.png b/res/stage_1/background_0.png new file mode 100644 index 0000000..5e438ad Binary files /dev/null and b/res/stage_1/background_0.png differ diff --git a/res/stage_1/background_1.png b/res/stage_1/background_1.png new file mode 100644 index 0000000..ba4c169 Binary files /dev/null and b/res/stage_1/background_1.png differ diff --git a/res/stage_1/background_4.png b/res/stage_1/background_4.png new file mode 100644 index 0000000..be1c110 Binary files /dev/null and b/res/stage_1/background_4.png differ diff --git a/res/stage_1/floor.png b/res/stage_1/floor.png new file mode 100644 index 0000000..47ff545 Binary files /dev/null and b/res/stage_1/floor.png differ diff --git a/res/stage_1/level_design.dat b/res/stage_1/level_design.dat new file mode 100644 index 0000000..a3ce7a2 --- /dev/null +++ b/res/stage_1/level_design.dat @@ -0,0 +1,9 @@ +A:C8?B=CB>*??:8?=@;==*?:8::::::*:8::::::*: +?;A8;CACCB*?>C8CC<;BB*?=8::::::*:8::::::*: +<@A8BA;:C>*==::CB*; +A==8B@=>?*:B<:=*=>C8::::::*<;8=:??B;*; +;;A;8:C>A?@8@=@8::::::*7<>8:A==>@*: +C?C8:@BB>B*?8::::::*7B8?B:@;>*: +*??:8?=@;==*B?8::::::*:8::::::*: +?;A8@<>:<=*?>C8CC<;BB*B?8::::::*:8::::::*: +<@A8BA;:C>*==::CB*; +A==8B@=>?*:B<:=*=>C8::::::*<;8=:??B;*; +;;A;8:C>A?@8@=@8::::::*7<>8:A==>@*: +C?C8:@BB>B*?8::::::*7B8?B:@;>*: += time_limit or not one_time_only; +} + +void Animation::notify_collision(GameObject &){ + //nothing to do +} + +bool Animation::is(string type){ + return type == "animation"; +} diff --git a/src/BattleState.cpp b/src/BattleState.cpp new file mode 100644 index 0000000..7fb7e26 --- /dev/null +++ b/src/BattleState.cpp @@ -0,0 +1,110 @@ +#include "BattleState.h" + +#include +#include + +#include "InputManager.h" +#include "Game.h" +#include "Floor.h" +#include "MenuState.h" +#include "FighterStats.h" +#include "TimeCounter.h" + +#define N_BACKGROUND 2 + +using std::fstream; +using std::stringstream; +using std::to_string; + +BattleState::BattleState(string stage, string cmusic){ + for(int i = 0; i < N_BACKGROUND; i++) + background[i] = Sprite("stage_" + stage + "/background_" + to_string(i) + ".png"); + + music = Music("stage_" + stage + "/" + cmusic); + + read_level_design(stage); + + + music.play(); + + fighters.push_back(new Fighter("flesh", 100, 100)); + fighters.push_back(new Fighter("flesh", 200, 100)); + fighters.push_back(new Fighter("flesh", 300, 100)); + fighters.push_back(new Fighter("flesh", 400, 100)); + + for(unsigned i = 0; i < fighters.size(); i++) + add_object(fighters[i]); + + add_object(new TimeCounter()); + + add_object(new FighterStats(fighters[3], 4, 1, 1147, 679.5)); + add_object(new FighterStats(fighters[2], 3, 1, 1147, 599.5)); + add_object(new FighterStats(fighters[1], 2, 0, 133, 679.5)); + add_object(new FighterStats(fighters[0], 1, 0, 133, 599.5)); + + add_object(new TimeCounter()); +} + +BattleState::~BattleState(){ +// for(unsigned i = 0; i < fighters.size(); i++) +// delete(fighters[i]); + +// fighters.clear(); +} + +void BattleState::update(float delta){ + InputManager * inputManager = InputManager::get_instance(); + + if(inputManager->key_press(SDLK_ESCAPE)){ + music.stop(); + m_quit_requested = true; + Game::get_instance().push(new MenuState()); + return; + } + + if(inputManager->quit_requested()){ + music.stop(); + m_quit_requested = true; + Game::get_instance().push(new MenuState()); + return; + } + + for(int i = 0; i < N_BACKGROUND; i++) + background[i].update(delta); + + update_array(delta); +} + +void BattleState::render(){ + for(int i = 0; i < N_BACKGROUND; i++) + background[i].render(0, 0); + + render_array(); +} + +void BattleState::pause(){ + +} + +void BattleState::resume(){ + +} + +void BattleState::read_level_design(string stage){ + float x, y, width, crotation; + int platform; + fstream level_design("res/stage_" + stage + "/level_design.dat"); + if(not level_design.is_open()){ + printf("Level design of stage %s can't be opened\n", stage.c_str()); + exit(-5); + } + string s; + while(std::getline(level_design, s)){ + for(auto & c : s) c -= 10; + stringstream cim(s); + cim >> x >> y >> width >> crotation >> platform; + //printf("Battle: %.f %.f %.f %.f\n", x, y, width, crotation); + add_object(new Floor(x, y, width, crotation, (bool) platform)); + } + level_design.close(); +} diff --git a/src/Camera.cpp b/src/Camera.cpp new file mode 100644 index 0000000..42518f0 --- /dev/null +++ b/src/Camera.cpp @@ -0,0 +1,50 @@ +#include "Camera.h" +#include "InputManager.h" + +GameObject * Camera::focus = nullptr; +Vector Camera::speed; +Vector Camera::pos[LAYERS]; +float Camera::layer_speed[LAYERS] = {4, 6, 16, 32}; + +#define PENGUIN_LAYER 0 +#define SCREEN_WIDTH 1024 +#define SCREEN_HEIGHT 600 +//FIXME layer virar variável + +void Camera::follow(GameObject * new_focus){ + focus = new_focus; +} + +void Camera::unfollow(){ + focus = nullptr; +} + +void Camera::update(float delta){ + InputManager * inputManager = InputManager::get_instance(); + + if(focus == nullptr){ + bool directions[] = { + inputManager->is_key_down(InputManager::LEFT_ARROW_KEY), + inputManager->is_key_down(InputManager::UP_ARROW_KEY), + inputManager->is_key_down(InputManager::RIGHT_ARROW_KEY), + inputManager->is_key_down(InputManager::DOWN_ARROW_KEY) + }; + + speed.x = 0; + speed.y = 0; + for(int i = 0; i < 4; ++i){ + speed.x += ((i+1) % 2) * (i - 1) * directions[i] * delta; + speed.y += (i % 2) * (i - 2) * directions[i] * delta; + } + + for(int i = 0; i < LAYERS; ++i){ + pos[i].x += speed.x * layer_speed[i]; + pos[i].y += speed.y * layer_speed[i]; + } + }else{ + for(int i = 0; i < LAYERS; ++i){ + pos[i].x = (-focus->box.get_x() + SCREEN_WIDTH/2.0) * layer_speed[i]/layer_speed[PENGUIN_LAYER]; + pos[i].y = (-focus->box.get_y() + SCREEN_HEIGHT/2.0) * layer_speed[i]/layer_speed[PENGUIN_LAYER]; + } + } +} diff --git a/src/EditState.cpp b/src/EditState.cpp new file mode 100644 index 0000000..93b8b32 --- /dev/null +++ b/src/EditState.cpp @@ -0,0 +1,126 @@ +#include "EditState.h" + +#include +#include + +#include "InputManager.h" +#include "Game.h" +#include "Fighter.h" +#include "EditableFloor.h" +#include "MenuState.h" + +#define WIDTH 1280 +#define HEIGHT 720 +#define CONTROL 1073742048 + +#define N_BACKGROUND 2 + +using std::ifstream; +using std::ofstream; +using std::stringstream; +using std::to_string; + +EditState::EditState(string cstage) : stage(cstage){ + for(int i = 0; i < N_BACKGROUND; i++) + background[i] = Sprite("stage_" + stage + "/background_" + to_string(i) + ".png"); + + test_fighter = new Fighter("edit_state/test_fighter", WIDTH/2, HEIGHT/2 - 200); + add_object(test_fighter); + + read_level_design(); + + //TODO ler os tiles que já tem e colocar +} + +void EditState::update(float delta){ + InputManager * inputManager = InputManager::get_instance(); + + if(inputManager->key_press(SDLK_ESCAPE)){ + m_quit_requested = true; + Game::get_instance().push(new MenuState()); + return; + } + + if(inputManager->quit_requested()){ + m_quit_requested = true; + return; + } + + if(inputManager->mouse_press(InputManager::RIGHT_MOUSE_BUTTON)){ + int x = inputManager->get_mouse_x(); + int y = inputManager->get_mouse_y(); + test_fighter->reset_position(x, y); + } + + if(inputManager->key_press(SDLK_f) or inputManager->key_press(SDLK_p)){ + int x = inputManager->get_mouse_x(); + int y = inputManager->get_mouse_y(); + bool is_platform = inputManager->key_press(SDLK_p); + for(auto & go : object_array){ + if(go->is("floor")){ + ((EditableFloor *) go.get())->set_selected(false); + } + } + auto go = new EditableFloor(x, y, 0, is_platform); + go->set_selected(true); + add_object(go); + } + + if(inputManager->is_key_down(CONTROL) and inputManager->key_press(SDLK_c)){ + update_level_design(); + } + + //printf("Floors\n------------------------------------\n"); + update_array(delta); + //printf("-------------------------------\n"); +} + +void EditState::render(){ + for(int i = 0; i < N_BACKGROUND; i++) + background[i].render(0, 0); + + render_array(); +} + +void EditState::pause(){ + +} + +void EditState::resume(){ + +} + +void EditState::read_level_design(){ + float x, y, width, crotation; + int platform; + ifstream level_design("res/stage_" + stage + "/level_design.dat"); + if(not level_design.is_open()){ + printf("Level design of stage %s can't be opened\n", stage.c_str()); + exit(-5); + } + string s; + while(std::getline(level_design, s)){ + for(auto & c : s) c -= 10; + stringstream cim(s); + cim >> x >> y >> width >> crotation >> platform; + //printf("Edit: %.f %.f %.f %.f\n", x, y, width, crotation); + add_object(new EditableFloor(x, y, width, crotation, (bool) platform)); + } + level_design.close(); +} + +void EditState::update_level_design(){ + ifstream level_design("res/stage_" + stage + "/level_design.dat", std::ios::binary); + ofstream old_level_design("res/stage_" + stage + "/level_design.dat.old", std::ios::trunc | std::ios::binary); + old_level_design << level_design.rdbuf(); + level_design.close(); + old_level_design.close(); + + ofstream new_level_design("res/stage_" + stage + "/level_design.dat", std::ios::trunc); + for(auto & go : object_array){ + if(go->is("floor")){ + new_level_design << ((EditableFloor *) go.get())->get_information() << std::endl; + } + } + new_level_design.close(); +} diff --git a/src/EditableFloor.cpp b/src/EditableFloor.cpp new file mode 100644 index 0000000..edb690c --- /dev/null +++ b/src/EditableFloor.cpp @@ -0,0 +1,121 @@ +#include "EditableFloor.h" + +#include "InputManager.h" +#include "Collision.h" +#include "Rectangle.h" + +#include + +#define LAYER 0 +#define PI 3.14159265358979 + +EditableFloor::EditableFloor(float x, float y, float crotation, bool cplatform) : Floor(x, y, 100, crotation, cplatform), normal_sprite(Sprite("edit_state/floor/editable_floor.png")), platform_sprite(Sprite("edit_state/floor/editable_platform.png")), selected_sprite(Sprite("edit_state/floor/selected_editable_floor.png")) { + box = Rectangle(x, y, normal_sprite.get_width(), normal_sprite.get_height()); + deleted = false; + selected = false; +} + +EditableFloor::EditableFloor(float x, float y, float width, float crotation, bool cplatform) : EditableFloor(x, y, crotation, cplatform){ + normal_sprite.set_scale_x(width / normal_sprite.get_width()); + platform_sprite.set_scale_x(width / platform_sprite.get_width()); + selected_sprite.set_scale_x(width / selected_sprite.get_width()); + box.width = normal_sprite.get_width(); +} + +EditableFloor::~EditableFloor(){ + +} + +void EditableFloor::update(float delta){ + InputManager * inputManager = InputManager::get_instance(); + + if(inputManager->mouse_press(InputManager::LEFT_MOUSE_BUTTON)){ + int x = inputManager->get_mouse_x(); + int y = inputManager->get_mouse_y(); + Rectangle mouse = Rectangle(x, y, 1, 1); + if(Collision::is_colliding(box, mouse, rotation, 0)){ + selected = true; + }else{ + selected = false; + } + } + + + if(selected){ + float value = 0.5 * delta; + if(inputManager->is_key_down(SDLK_RIGHT)){ + box.x += value; + } + if(inputManager->is_key_down(SDLK_LEFT)){ + box.x -= value; + } + if(inputManager->is_key_down(SDLK_UP)){ + box.y -= value; + } + if(inputManager->is_key_down(SDLK_DOWN)){ + box.y += value; + } + + if(inputManager->is_key_down(SDLK_z)){ + rotation += 0.01 * value; + } + if(inputManager->is_key_down(SDLK_x)){ + rotation -= 0.01 * value; + } + if(inputManager->is_key_down(SDLK_r)){ + rotation = 0; + } + if(inputManager->key_press(SDLK_k)){ + is_platform = !is_platform; + } + + if(inputManager->is_key_down(SDLK_PERIOD)){ + normal_sprite.update_scale_x(0.005 * value); + platform_sprite.update_scale_x(0.005 * value); + selected_sprite.update_scale_x(0.005 * value); + box.width = normal_sprite.get_width(); + } + if(inputManager->is_key_down(SDLK_COMMA)){ + normal_sprite.update_scale_x(-0.005 * value); + platform_sprite.update_scale_x(-0.005 * value); + selected_sprite.update_scale_x(-0.005 * value); + box.width = normal_sprite.get_width(); + } + if(inputManager->is_key_down(SDLK_DELETE)){ + deleted = true; + } + } + + //printf("%f, %f, %.f, %f, %f %d\n", box.x, box.y, box.width, box.height, rotation * 180.0 / PI, (int) is_platform); +} + +void EditableFloor::render(){ + if(selected){ + selected_sprite.render(box.get_draw_x(), box.get_draw_y(), rotation); + } + + if(is_platform) + platform_sprite.render(box.get_draw_x(), box.get_draw_y(), rotation); + else + normal_sprite.render(box.get_draw_x(), box.get_draw_y(), rotation); +} + +bool EditableFloor::is_dead(){ + return deleted; +} + +void EditableFloor::notify_collision(GameObject &){ +} + +string EditableFloor::get_information(){ + char info[500]; + sprintf(info, "%f %f %f %f %d", box.x, box.y, box.width, rotation * 180 / PI, (int) is_platform); + string s(info); + for(auto & c : s) c += 10; + + return s; +} + +void EditableFloor::set_selected(bool cselected){ + selected = cselected; +} diff --git a/src/Fighter.cpp b/src/Fighter.cpp new file mode 100644 index 0000000..b7d14cf --- /dev/null +++ b/src/Fighter.cpp @@ -0,0 +1,210 @@ +#include "Fighter.h" + +#include "InputManager.h" +#include "Camera.h" +#include "Floor.h" + +#include +#include + +#define LAYER 0 + +#define IDLE Fighter::FighterState::IDLE +#define RUNNING Fighter::FighterState::RUNNING +#define JUMPING Fighter::FighterState::JUMPING +#define FALLING Fighter::FighterState::FALLING +#define CROUCH Fighter::FighterState::CROUCH + +#define LEFT Fighter::Orientation::LEFT +#define RIGHT Fighter::Orientation::RIGHT + +#define PI 3.14159265358979 + +#define CROUCH_COOLDOWN 100.0 + +//TODO reavaliar se precisa ou não de Camera +Fighter::Fighter(string name, float x, float y){ + sprite[IDLE] = Sprite(name + "/idle.png", 8, 10); + sprite[RUNNING] = Sprite(name + "/running.png", 8, 10); + sprite[JUMPING] = Sprite(name + "/jumping.png", 6, 10); + sprite[FALLING] = Sprite(name + "/falling.png", 7, 10); + sprite[CROUCH] = Sprite(name + "/crouch.png", 6, 20); + + state = IDLE; + + remaining_life = MAX_LIFE; + special = 0; + + vertical_speed = rotation = 0; + speed = Vector(0, 0); + //FIXME recebe no construtor + acceleration = Vector(0, 0.1); + max_speed = 9; + + orientation = RIGHT; + + on_floor = false; + last_collided_floor = 0; + pass_through = false; + + box = Rectangle(x, y, sprite[RUNNING].get_width(), sprite[RUNNING].get_height()); +} + +Fighter::~Fighter(){ +} + +void Fighter::update(float delta){ + InputManager * inputManager = InputManager::get_instance(); + + //FIXME + if(inputManager->is_key_down(InputManager::SPACE_KEY)){ + remaining_life--; + + special++; + + //FIXME XGH + if(special > MAX_SPECIAL) + special = MAX_SPECIAL; + } + + speed.x = 0; + on_floor = false; + + if(state != CROUCH){ + if(inputManager->is_key_down(SDLK_a, true)){ + if(state == IDLE) change_state(RUNNING); + speed.x = -2; + orientation = LEFT; + } + if(inputManager->is_key_down(SDLK_d, true)){ + if(state == IDLE) change_state(RUNNING); + speed.x = 2; + orientation = RIGHT; + } + } + + if(inputManager->is_key_down(SDLK_s)){ + change_state(CROUCH); + } + + if(inputManager->is_key_down(SDLK_SPACE) && speed.y == 0){ + speed.y = -5; + } + + if(speed.x == 0 && speed.y == 0 && not inputManager->is_key_down(SDLK_s)){ + change_state(IDLE); + } + + sprite[state].update(delta); +} + +void Fighter::notify_collision(GameObject & object){ + //FIXME tá feio + float floor_y = object.box.y + (box.x - object.box.x) * tan(object.rotation) - object.box.height * 0.5; + if(object.is("floor") && speed.y >= 0 && not on_floor && abs(floor_y - (box.y + box.height * 0.5)) < 10){ + if(pass_through){ + if(object.is("platform")){ + if(((Floor&)object).get_id() == last_collided_floor) + return; + else + pass_through = false; + } + } + + + speed.y = 0; + box.y = object.box.y + (box.x - object.box.x) * tan(object.rotation) - (box.height + object.box.height ) * 0.5; + + if(state != IDLE and state != CROUCH) change_state(RUNNING); + on_floor = true; + last_collided_floor = ((Floor&)object).get_id(); + pass_through = false; + } +} + +void Fighter::post_collision_update(float delta){ + InputManager * inputManager = InputManager::get_instance(); + + // check pass through when double crouching + if(inputManager->key_press(SDLK_s)){ + //FIXME só checa se tiver no chão + if(crouch_timer.get() < CROUCH_COOLDOWN){ + pass_through = true; + } + + crouch_timer.restart(); + change_state(CROUCH); + } + + speed.y = std::min(speed.y + !on_floor * acceleration.y * delta, max_speed); + box.x += speed.x * delta; + if(not on_floor) box.y += speed.y * delta; + + test_limits(); + + if(speed.y < 0){ + change_state(JUMPING); + }else if(speed.y > 0 && not on_floor){ + change_state(FALLING); + } + + crouch_timer.update(delta); +} + +void Fighter::render(){ + int x = box.get_draw_x() + 0 * Camera::pos[LAYER].x; + int y = box.get_draw_y() + 0 * Camera::pos[LAYER].x; + + SDL_RendererFlip flip = (orientation == LEFT) ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; + sprite[state].render(x, y, rotation, flip); +} + +bool Fighter::is_dead(){ + return false; +} + +int Fighter::get_remaining_life(){ + return remaining_life; +} + +int Fighter::get_special(){ + return special; +} + +bool Fighter::is(string type){ + return type == "fighter"; +} + +void Fighter::change_state(FighterState cstate){ + if(state == cstate) return; + + sprite[state].restart_count(); + float old_width = sprite[state].get_width(); + float old_height = sprite[state].get_height(); + state = cstate; + float new_width = sprite[state].get_width(); + float new_height = sprite[state].get_height(); + + float x = box.x - (new_width - old_width) * 0.5; + float y = box.y - (new_height - old_height) * 0.5; + + box.x = x; + box.y = y; + box = Rectangle(x, y, sprite[state].get_width(), sprite[state].get_height()); +} + +void Fighter::test_limits(){ + //TODO Matar personagem ao cair do cenario + if(box.x < box.width / 2) box.x = box.width / 2; + if(box.x > 1280 - box.width / 2) box.x = 1280 - box.width / 2; + if(box.y < 0 or box.y > 720){ + box.y = 0; + pass_through = false; + } +} + +void Fighter::reset_position(float x, float y){ + box.x = x; + box.y = y; + speed.y = 0; +} diff --git a/src/FighterStats.cpp b/src/FighterStats.cpp new file mode 100644 index 0000000..38c00e2 --- /dev/null +++ b/src/FighterStats.cpp @@ -0,0 +1,87 @@ +#include "FighterStats.h" +#include "InputManager.h" + +using std::to_string; + +//FIXME: Trocar side pra enum +FighterStats::FighterStats(Fighter *p_fighter, int p_index_fighter, int p_side, double p_x, double p_y){ + fighter = p_fighter; + side = p_side; + x = p_x; + y = p_y; + percent_to_draw_life = 1.0; + index_fighter = p_index_fighter; + + bg = Sprite("hud/life" + to_string(index_fighter) + "_frame.png"); + + //Left + if(side == 0){ + empty_bg = Sprite("hud/left_empty_background.png"); + life = Sprite("hud/left_life.png"); + special = Sprite("hud/left_special_bar.png"); + } + + //Right + if(side == 1){ + empty_bg = Sprite("hud/right_empty_background.png"); + life = Sprite("hud/right_life.png"); + special = Sprite("hud/right_special_bar.png"); + } + + box = Rectangle(x, y, bg.get_width(), bg.get_height()); +} + +FighterStats::~FighterStats(){ + +} + +void FighterStats::update(float){ + percent_to_draw_life = (fighter->get_remaining_life() * 1.0) / Fighter::MAX_LIFE; + percent_to_draw_special = (fighter->get_special() * 1.0) / Fighter::MAX_SPECIAL; + + //Left + if(side == 0){ + special.set_clip(special.get_width() * (1 - percent_to_draw_special) , 0, special.get_width() * percent_to_draw_special, special.get_height()); + life.set_clip(0, 0, life.get_width() * percent_to_draw_life, life.get_height()); + } + + //Right + if(side == 1){ + special.set_clip(0, 0, special.get_width() * percent_to_draw_special, special.get_height()); + life.set_clip(life.get_width() * (1 - percent_to_draw_life), 0, life.get_width() * percent_to_draw_life, life.get_height()); + } +} + +void FighterStats::render(){ + int offset = -3 * ((index_fighter + 1) % 2); + + //Left + if(side == 0){ + special.render(82, box.get_draw_y()); + + empty_bg.render(82, box.get_draw_y() + 22 + offset); + bg.render(box.get_draw_x(), box.get_draw_y()); + life.render(box.get_draw_x() + 82, box.get_draw_y() + 22 + offset); + } + + //Right + if(side == 1){ + special.render(box.get_draw_x() - 12 + special.get_width() * (1 - percent_to_draw_special), box.get_draw_y()); + + empty_bg.render(box.get_draw_x() + 8, box.get_draw_y() + 22 + offset); + bg.render(box.get_draw_x(), box.get_draw_y()); + life.render(box.get_draw_x() + 8 + life.get_width() * (1 - percent_to_draw_life), box.get_draw_y() + 22 + offset); + } +} + +bool FighterStats::is_dead(){ + return false; +} + +void FighterStats::notify_collision(GameObject &){ + +} + +bool FighterStats::is(string){ + return false; +} diff --git a/src/Floor.cpp b/src/Floor.cpp new file mode 100644 index 0000000..f660dad --- /dev/null +++ b/src/Floor.cpp @@ -0,0 +1,48 @@ +#include "Floor.h" + +#include "InputManager.h" +#include "Camera.h" + +#define LAYER 0 +#define PI 3.14159265358979 + +#define HEIGHT 20 + +int Floor::floor_id = 1; + +//TODO reavaliar se precisa ou não de Camera +Floor::Floor(float x, float y, float width, float crotation, bool cplatform){ + is_platform = cplatform; + rotation = crotation * PI / 180.0; + box = Rectangle(x, y, width, HEIGHT); + id = floor_id++; +} + +Floor::~Floor(){ +} + +void Floor::update(float){ + //printf("%.f %.f %.f %.f %.f\n", box.x, box.y, box.width, box.height, rotation); +} + +void Floor::render(){ +} + +bool Floor::is_dead(){ + return false; +} + +void Floor::notify_collision(GameObject &){ +} + +bool Floor::is(string type){ + if(type == "platform"){ + if(is_platform) return true; + } + + return type == "floor"; +} + +int Floor::get_id(){ + return id; +} diff --git a/src/Game.cpp b/src/Game.cpp new file mode 100644 index 0000000..14d71d4 --- /dev/null +++ b/src/Game.cpp @@ -0,0 +1,148 @@ +#include "Game.h" + +#include "InputManager.h" +#include "Resources.h" + +#include + +Game * Game::instance = nullptr; + +Game::Game(string title, int width, int height){ + instance = instance ? instance : this; + frame_start = SDL_GetTicks(); + delta = 0; + + srand(time(nullptr)); + + int sdl_init = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER); + if(sdl_init){ + printf("%s\n", SDL_GetError()); + exit(-1); + } + + int img_flags = IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF; + int img_init = IMG_Init(img_flags); + if(img_init != img_flags){ + printf("%s\n", SDL_GetError()); + exit(-1); + } + + window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, 0); + if(window == nullptr){ + printf("%s\n", SDL_GetError()); + exit(-1); + } + + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if(renderer == nullptr){ + printf("%s\n", SDL_GetError()); + exit(-1); + } + + int mix_flags = MIX_INIT_OGG; + int sdl_mix = Mix_Init(mix_flags); + if(sdl_mix != mix_flags){ + printf("%s\n", SDL_GetError()); + exit(-1); + } + + int mix_open = Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 1024); + if(mix_open){ + printf("%s\n", SDL_GetError()); + exit(-1); + } + + int ttf_init = TTF_Init(); + if(ttf_init){ + printf("%s\n", TTF_GetError()); + exit(-1); + } + + stored_state = nullptr; +} + +Game::~Game(){ + if(stored_state != nullptr) + delete stored_state; + + while(not state_stack.empty()) + state_stack.pop(); + + TTF_Quit(); + Mix_Quit(); + IMG_Quit(); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); +} + +Game & Game::get_instance(){ + return *instance; +} + +SDL_Renderer * Game::get_renderer(){ + return renderer; +} + +State & Game::get_current_state(){ + return *(state_stack.top()); +} + +void Game::push(State * state){ + stored_state = state; +} + +void Game::run(){ + //TODO condição de saída do loop + if(stored_state != nullptr){ + state_stack.emplace(stored_state); + get_current_state().load_assets(); + stored_state = nullptr; + }else{ + return; + } + + while(not state_stack.empty()){ + InputManager::get_instance()->update(); + + this->calculate_delta_time(); + get_current_state().update(delta); + get_current_state().render(); + + SDL_RenderPresent(renderer); + manage_stack(); + SDL_Delay(10); + } +} + +void Game::calculate_delta_time(){ + int new_frame_start = SDL_GetTicks(); + + delta = std::min((new_frame_start - frame_start)/10.0, 1.0); + + frame_start = new_frame_start; +} + +float Game::get_delta_time(){ + return delta; +} + +void Game::manage_stack(){ + if(get_current_state().quit_requested()){ + state_stack.pop(); + Resources::clear_images(); + Resources::clear_music(); + Resources::clear_fonts(); + + if(not state_stack.empty()) + get_current_state().resume(); + } + + if(stored_state != nullptr){ + if(not state_stack.empty()) + get_current_state().pause(); + state_stack.emplace(stored_state); + get_current_state().load_assets(); + stored_state = nullptr; + } +} diff --git a/src/InputManager.cpp b/src/InputManager.cpp new file mode 100644 index 0000000..4e9ab91 --- /dev/null +++ b/src/InputManager.cpp @@ -0,0 +1,128 @@ +#include "InputManager.h" + +#include +#include + +#define FIRST_TIME 1492356064 + +InputManager * InputManager::input_manager; + +InputManager::InputManager(){ + memset(mouse_state, false, sizeof mouse_state); + memset(mouse_update, 0, sizeof mouse_update); + m_quit_requested = false; + update_counter = 0; + mouse_x = 0; + mouse_y = 0; +} + +InputManager::~InputManager(){ + key_state.clear(); + key_update.clear(); + event_responded.clear(); +} + +void InputManager::update(){ + SDL_Event event; + + SDL_GetMouseState(&mouse_x, &mouse_y); + + m_quit_requested = false; + + update_counter++; + + event_responded.clear(); + while(SDL_PollEvent(&event)){ + + int key_id, button_id; + switch (event.type) { + case SDL_KEYDOWN: + if(event.key.repeat) break; + key_id = event.key.keysym.sym; + key_state[key_id] = true; + key_update[key_id] = update_counter; + break; + + case SDL_KEYUP: + key_id = event.key.keysym.sym; + key_state[key_id] = false; + key_update[key_id] = update_counter; + break; + + case SDL_MOUSEBUTTONDOWN: + button_id = event.button.button; + mouse_state[button_id] = true; + mouse_update[button_id] = update_counter; + break; + + case SDL_MOUSEBUTTONUP: + button_id = event.button.button; + mouse_state[button_id] = false; + mouse_update[button_id] = update_counter; + break; + + case SDL_QUIT: + m_quit_requested = true; + break; + + default: + break; + } + } +} + +bool InputManager::key_press(int key, bool response){ + if(not can_respond(key, 0, response)) return false; + bool v = (key_state[key] && key_update[key] == update_counter); + return v; +} + +bool InputManager::key_release(int key, bool response){ + if(not can_respond(key, 1, response)) return false; + bool v = (!key_state[key] && key_update[key] == update_counter); + return v; +} + +bool InputManager::is_key_down(int key, bool response){ + if(not can_respond(key, 2, response)) return false; + bool v = key_state[key]; + return v; +} + +bool InputManager::mouse_press(int button){ + return mouse_state[button] && mouse_update[button] == update_counter; +} + +bool InputManager::mouse_release(int button){ + return !mouse_state[button] && mouse_update[button] == update_counter; +} +bool InputManager::is_mouse_down(int button){ + return mouse_state[button]; +} + +int InputManager::get_mouse_x(){ + return mouse_x; +} + +int InputManager::get_mouse_y(){ + return mouse_y; +} + +bool InputManager::quit_requested(){ + return m_quit_requested; +} + +InputManager * InputManager::get_instance(){ + if(input_manager == nullptr) input_manager = new InputManager(); + return input_manager; +} + +bool InputManager::can_respond(int key, int operation, bool response){ + //TODO mudar pra ser id de gameObject + if(not response) return true; + if(event_responded[ii(key, operation)]) + return false; + else + return (event_responded[ii(key, operation)] = true); + +} diff --git a/src/Main.cpp b/src/Main.cpp new file mode 100644 index 0000000..74a3adb --- /dev/null +++ b/src/Main.cpp @@ -0,0 +1,13 @@ +#include "Game.h" +#include "MenuState.h" + +int main(int, char **){ + Game game("Wenova - Rise of Conquerors", 1280, 720); + + State * state = new MenuState(); + game.push(state); + + game.run(); + + return 0; +} diff --git a/src/MenuState.cpp b/src/MenuState.cpp new file mode 100644 index 0000000..d62d369 --- /dev/null +++ b/src/MenuState.cpp @@ -0,0 +1,160 @@ +#include "MenuState.h" +#include "InputManager.h" +#include "BattleState.h" +#include "EditState.h" +#include "Game.h" +#include "Resources.h" + +#define OPTION_OFFSET 50 +#define FONT_X 640 +#define FONT_Y 680 +#define RED { 236, 0, 46, 1 } +#define WHITE { 255, 255, 255, 255 } + +// FIXME fix time +#define FRAME_TIME 7.5 +#define TEXT_TIMER_COOLDOWN 50 + +MenuState::MenuState() : current_option(0) { + start_pressed = false; + show_text = true; + + background = Sprite("menu/background.jpg"); + title = Sprite("menu/title.png", 5, FRAME_TIME); + planet = Sprite("menu/planet.png", 8, FRAME_TIME); + green_ship = Sprite("menu/green_ship.png", 8, FRAME_TIME, 2); + red_ship = Sprite("menu/red_ship.png", 8, FRAME_TIME); + + start_option = new Text("font/8-BIT WONDER.ttf", 30, Text::TextStyle::SOLID, "PRESS START", WHITE, FONT_X, FONT_Y); + + options.push_back(new Text("font/8-BIT WONDER.ttf", 30, Text::TextStyle::SOLID, "START", RED, FONT_X, FONT_Y)); + options.push_back(new Text("font/8-BIT WONDER.ttf", 30, Text::TextStyle::SOLID, "CONTINUE", RED, FONT_X, FONT_Y)); + options.push_back(new Text("font/8-BIT WONDER.ttf", 30, Text::TextStyle::SOLID, "OPTIONS", RED, FONT_X, FONT_Y)); + options.push_back(new Text("font/8-BIT WONDER.ttf", 30, Text::TextStyle::SOLID, "EXIT", RED, FONT_X, FONT_Y)); +} + +void MenuState::update(float delta){ + title.update(delta); + planet.update(delta); + green_ship.update(delta); + red_ship.update(delta); + + InputManager * input_manager = InputManager::get_instance(); + + // handling general inputs + if(input_manager->quit_requested()){ + m_quit_requested = true; + return; + } + + if(input_manager->key_press(SDLK_ESCAPE)){ + if(start_pressed){ + start_pressed = false; + current_option = 0; + } + else{ + m_quit_requested = true; + return; + } + } + + // handling options input + if(input_manager->key_press(SDLK_LEFT) && current_option != 0){ + current_option--; + } + + if(input_manager->key_press(SDLK_RIGHT) && current_option != (int)options.size() - 1){ + current_option++; + } + + if(input_manager->is_key_down(SDLK_w) and input_manager->is_key_down(SDLK_r) and input_manager->is_key_down(SDLK_c)){ + m_quit_requested = true; + Game::get_instance().push(new EditState("1")); + return; + } + + // TODO when press space switch case in options + if(input_manager->key_press(SDLK_RETURN)){ + if(not start_pressed){ + start_pressed = true; + current_option = 0; + } + else{ + switch(current_option){ + case 0: + m_quit_requested = true; + Game::get_instance().push(new BattleState("1", "swamp_song.ogg")); + return; + + case 1: + printf("CONTINUE SELECTED\n"); + break; + + case 2: + printf("OPTIONS SELECTED\n"); + break; + + case 3: + m_quit_requested = true; + return; + } + } + } + + if(start_pressed){ + // handling options positioning + options[current_option]->set_pos(FONT_X, FONT_Y, true, true); + options[current_option]->set_color(WHITE); + + // positioning options before current option + for(int idx = 0; idx < current_option; idx++){ + Text* next_option = options[idx + 1]; + + int new_x = next_option->get_x() - options[idx]->get_width() - OPTION_OFFSET; + options[idx]->set_pos(new_x, FONT_Y, false, true); + options[idx]->set_color(RED); + } + + // positioning options after current option + for(unsigned int idx = current_option + 1; idx < options.size(); idx++){ + Text* prev_option = options[idx - 1]; + + int new_x = prev_option->get_x() + prev_option->get_width() + OPTION_OFFSET; + options[idx]->set_pos(new_x, FONT_Y, false, true); + options[idx]->set_color(RED); + } + } + + if(text_timer.get() > TEXT_TIMER_COOLDOWN){ + show_text = !show_text; + text_timer.restart(); + } + + text_timer.update(delta); +} + +void MenuState::render(){ + background.render(0, 0); + + planet.render(423, 177); + green_ship.render(805, 405); + red_ship.render(36, 400); + title.render(260, 0); + + if(start_pressed){ + for(auto option_text : options){ + option_text->render(0, 0); + } + } + else if(show_text){ + start_option->render(0, 0); + } +} + +void MenuState::pause(){ + +} + +void MenuState::resume(){ + +} diff --git a/src/Music.cpp b/src/Music.cpp new file mode 100644 index 0000000..c516130 --- /dev/null +++ b/src/Music.cpp @@ -0,0 +1,35 @@ +#include "Music.h" + +#include "Resources.h" + +Music::Music(){ + music = nullptr; +} + +Music::Music(string file){ + this->open(file); +} + +void Music::play(int times){ + int mix_play_music = Mix_PlayMusic(music.get(), times); + if(mix_play_music){ + printf("Play music: %s\n", Mix_GetError()); + exit(-1); + } +} + +void Music::stop(){ + int mix_fade_out_music = Mix_FadeOutMusic(100); + if(!mix_fade_out_music){ + printf("Stop music: %s\n", Mix_GetError()); + exit(-1); + } +} + +void Music::open(string file){ + music = Resources::get_music("res/" + file); +} + +bool Music::is_open(){ + return music != nullptr; +} diff --git a/src/Rectangle.cpp b/src/Rectangle.cpp new file mode 100644 index 0000000..6cd362d --- /dev/null +++ b/src/Rectangle.cpp @@ -0,0 +1,57 @@ +#include "Rectangle.h" + +Rectangle::Rectangle(){} + +Rectangle::Rectangle(float ax, float ay, float w, float h) : x(ax), y(ay), width(w), height(h){} + +bool Rectangle::is_inside(float mx, float my){ + bool horizontal_in = (mx >= x - width/2 and mx <= x + width/2); + bool vertical_in = (my >= y - height/2 and my <= y + height/2); + + return horizontal_in and vertical_in; +} + +float Rectangle::get_x() const{ + return x; +} + +float Rectangle::get_y() const{ + return y; +} + +void Rectangle::set_x(float cx){ + this->x = cx; +} + +void Rectangle::set_y(float cy){ + this->y = cy; +} + +float Rectangle::get_draw_x() const{ + return x - width/2; +} + +float Rectangle::get_draw_y() const{ + return y - height/2; +} + +float Rectangle::get_width() const{ + return width; +} + +float Rectangle::get_height() const{ + return height; +} + +void Rectangle::set_width(float w){ + width = w; +} + +void Rectangle::set_height(float h){ + height = h; +} + + +Vector Rectangle::get_center() const{ + return Vector(x, y); +} diff --git a/src/Resources.cpp b/src/Resources.cpp new file mode 100644 index 0000000..6717bb7 --- /dev/null +++ b/src/Resources.cpp @@ -0,0 +1,115 @@ +#include "Resources.h" + +#include "Game.h" + +#include + +unordered_map > Resources::image_table; +unordered_map > Resources::music_table; +unordered_map > Resources::sound_table; +unordered_map > Resources::font_table; + +shared_ptr Resources::get_image(string file){ + if(image_table.find(file) == image_table.end()){ + SDL_Texture * tx = + IMG_LoadTexture(Game::get_instance().get_renderer(), file.c_str()); + + if(tx == nullptr){ + printf("%s: %s\n", SDL_GetError(), file.c_str()); + exit(-1); + } + + shared_ptr texture(tx, [](SDL_Texture * txt) { SDL_DestroyTexture(txt); }); + + image_table.emplace(file, texture); + } + + return image_table[file]; +} + +void Resources::clear_images(){ + for(auto texture : image_table){ + if(texture.second.unique()){ + image_table.erase(texture.first); + } + } +} + +shared_ptr Resources::get_music(string file){ + if(music_table.find(file) == music_table.end()){ + Mix_Music * mx = Mix_LoadMUS(file.c_str()); + + if(mx == nullptr){ + printf("%s: %s\n", Mix_GetError(), file.c_str()); + exit(-1); + } + + shared_ptr music(mx, [](Mix_Music * msc) { Mix_FreeMusic(msc); }); + + music_table.emplace(file, music); + } + + return music_table[file]; + +} + +void Resources::clear_music(){ + for(auto music : music_table){ + if(music.second.unique()){ + music_table.erase(music.first); + } + } +} + +shared_ptr Resources::get_sound(string file){ + if(sound_table.find(file) == sound_table.end()){ + Mix_Chunk * ck = Mix_LoadWAV(file.c_str()); + + if(ck == nullptr){ + printf("%s: %s\n", Mix_GetError(), file.c_str()); + exit(-1); + } + + shared_ptr sound(ck, [](Mix_Chunk * chk) { Mix_FreeChunk(chk); }); + + sound_table.emplace(file, sound); + } + + return sound_table[file]; + +} + +void Resources::clear_sound(){ + for(auto sound : sound_table){ + if(sound.second.unique()){ + sound_table.erase(sound.first); + } + } +} + +shared_ptr Resources::get_font(string file, int size){ + string tsize = std::to_string(size); + if(font_table.find(file + tsize) == font_table.end()){ + TTF_Font * ft = TTF_OpenFont(file.c_str(), size); + + if(ft == nullptr){ + printf("%s: %s\n", SDL_GetError(), file.c_str()); + exit(-1); + } + + shared_ptr font(ft, [](TTF_Font * fnt) { TTF_CloseFont(fnt); }); + + font_table.emplace(file + tsize, font); + } + + return font_table[file + tsize]; + +} + +void Resources::clear_fonts(){ + for(auto font : font_table){ + if(font.second.unique()){ + font_table.erase(font.first); + } + } +} diff --git a/src/Sound.cpp b/src/Sound.cpp new file mode 100644 index 0000000..91e034f --- /dev/null +++ b/src/Sound.cpp @@ -0,0 +1,31 @@ +#include "Sound.h" + +#include "Resources.h" + +Sound::Sound(){ + sound = nullptr; +} + +Sound::Sound(string file){ + this->open(file); +} + +void Sound::play(int times){ + channel = Mix_PlayChannel(-1, sound.get(), times); + if(channel == -1){ + printf("Play chunk: %s\n", Mix_GetError()); + exit(-1); + } +} + +void Sound::stop(){ + Mix_HaltChannel(channel); +} + +void Sound::open(string file){ + sound = Resources::get_sound("res/" + file); +} + +bool Sound::is_open(){ + return sound != nullptr; +} diff --git a/src/Sprite.cpp b/src/Sprite.cpp new file mode 100644 index 0000000..f515e8f --- /dev/null +++ b/src/Sprite.cpp @@ -0,0 +1,111 @@ +#include + +#include "Sprite.h" +#include "Game.h" +#include "Resources.h" + +#define PI 3.14159265358979 + +Sprite::Sprite(){ + texture = nullptr; + scale_x = scale_y = 1; + frame_count = 1; + frame_time = 1; + current_frame = time_elapsed = 0; +} + +Sprite::Sprite(string file, int cframe_count, float cframe_time, int cur_frame){ + frame_count = cframe_count; + frame_time = cframe_time; + current_frame = cur_frame; + time_elapsed = 0; + texture = nullptr; + open("res/" + file); + + scale_x = scale_y = 1; +} + +Sprite::~Sprite(){ +} + +int Sprite::get_width(){ + return width * scale_x; +} + +int Sprite::get_height(){ + return height * scale_y; +} + +bool Sprite::is_open(){ + return texture != nullptr; +} + +void Sprite::open(string file){ + + texture = Resources::get_image(file); + + int query_texture = SDL_QueryTexture(texture.get(), nullptr, nullptr, + &width, &height); + + width /= frame_count; + if(query_texture){ + printf("Open: %s\n", SDL_GetError()); + exit(-1); + } + + set_clip(current_frame * width, 0, width, height); +} + +void Sprite::set_clip(int x, int y, int w, int h){ + clip_rect = SDL_Rect{x, y, w, h}; +} + +void Sprite::set_frame(int frame){ + current_frame = frame; + set_clip(current_frame * width, 0, width, height); +} + +void Sprite::set_frame_count(int cframe_count){ + frame_count = cframe_count; +} + +void Sprite::set_frame_time(float cframe_time){ + frame_time = cframe_time; +} + +void Sprite::update(float delta){ + time_elapsed += delta; + if(time_elapsed >= frame_time){ + time_elapsed = 0; + current_frame = (current_frame + 1) % frame_count; + set_clip(current_frame * width, 0, width, height); + } +} + +void Sprite::render(int x, int y, float angle, SDL_RendererFlip flip){ + SDL_Rect dstrect = SDL_Rect{x, y, (int)(clip_rect.w * scale_x), (int)(clip_rect.h * scale_y)}; + + angle *= (180 / PI); + int render_copy = SDL_RenderCopyEx(Game::get_instance().get_renderer(), texture.get(), + &clip_rect, &dstrect, angle, nullptr, flip); + if(render_copy){ + printf("Render: %s\n", SDL_GetError()); + exit(-1); + } +} + +void Sprite::set_scale_x(float scale){ + scale_x = scale; +} + +void Sprite::set_scale_y(float scale){ + scale_y = scale; +} + +void Sprite::update_scale_x(float scale){ + scale_x += scale; +} + +void Sprite::restart_count(){ + current_frame = 0; +} diff --git a/src/State.cpp b/src/State.cpp new file mode 100644 index 0000000..8efc0fd --- /dev/null +++ b/src/State.cpp @@ -0,0 +1,73 @@ +#include "State.h" + +#include "Collision.h" + +State::State(){ + m_pop_requested = m_quit_requested = false; +} + +State::~State(){ + +} + +void State::add_object(GameObject * object){ + object_array.emplace_back(object); +} + +bool State::pop_requested(){ + return m_pop_requested; +} + +bool State::quit_requested(){ + return m_quit_requested; +} + +void State::update_array(float delta){ + //pre collision update + for(unsigned it = 0; it < object_array.size(); ++it){ + object_array[it]->update(delta); + if(object_array[it]->is_dead()){ + object_array.erase(object_array.begin() + it); + break; + } + } + + //collision tests + for(unsigned i = 0; i < object_array.size(); ++i){ + for(unsigned j = i + 1; j < object_array.size(); ++j){ + auto a = object_array[i].get(); + auto b = object_array[j].get(); + if(Collision::is_colliding(a->box, b->box, a->rotation, b->rotation)){ + a->notify_collision(*b); + b->notify_collision(*a); + } + } + } + + //post collision update + for(unsigned it = 0; it < object_array.size(); ++it){ + object_array[it]->post_collision_update(delta); + if(object_array[it]->is_dead()){ + object_array.erase(object_array.begin() + it); + break; + } + } + + //death check + for(unsigned it = 0; it < object_array.size(); ++it){ + if(object_array[it]->is_dead()){ + object_array.erase(object_array.begin() + it); + break; + } + } +} + +void State::render_array(){ + for(auto & go : object_array){ + go->render(); + } +} + +void State::load_assets(){ + //NOTHING TO DO +} diff --git a/src/Text.cpp b/src/Text.cpp new file mode 100644 index 0000000..558498e --- /dev/null +++ b/src/Text.cpp @@ -0,0 +1,139 @@ +#include "Text.h" + +#include "Game.h" +#include "Resources.h" + +#define SOLID Text::TextStyle::SOLID +#define SHADED Text::TextStyle::SHADED +#define BLENDED Text::TextStyle::BLENDED + +Text::Text(){ + texture = nullptr; +} + +Text::Text(string cfont_file, int cfont_size, TextStyle cstyle, string ctext, SDL_Color ccolor, int x, int y){ + font_size = cfont_size; + style = cstyle; + text = ctext; + color = ccolor; + box.set_x(x); + box.set_y(y); + texture = nullptr; + open(cfont_file, font_size); + remake_texture(); + set_pos(x, y, true, true); +} + +Text::~Text(){ + if(texture != nullptr){ + SDL_DestroyTexture(texture); + } +} + +void Text::render(int camera_x, int camera_y){ + SDL_Rect src_rect = { 0, 0, (int)box.get_width(), (int)box.get_height() }; + SDL_Rect dest_rect = { (int)box.get_x() + camera_x, (int)box.get_y() + camera_y, (int)box.get_width(), (int)box.get_height() }; + + + int render_copy = SDL_RenderCopy(Game::get_instance().get_renderer(), texture, + &src_rect, &dest_rect); + if(render_copy){ + printf("Render text: %s\n", SDL_GetError()); + exit(-1); + } +} + +void Text::set_pos(int x, int y, bool center_x, bool center_y){ + box.set_x(x - (center_x ? clip_rect.w * 0.5 : 0)); + box.set_y(y - (center_y ? clip_rect.h * 0.5 : 0)); +} + + +void Text::set_text(string ctext){ + text = ctext; + remake_texture(); +} + +void Text::set_color(SDL_Color ccolor){ + color = ccolor; + remake_texture(); +} + +void Text::set_style(TextStyle cstyle){ + style = cstyle; + remake_texture(); +} + +void Text::set_font_size(int cfont_size){ + font_size = cfont_size; + remake_texture(); +} + +float Text::get_x(){ + return box.get_x(); +} + +float Text::get_y(){ + return box.get_y(); +} + +float Text::get_width(){ + return box.get_width(); +} + +float Text::get_height(){ + return box.get_height(); +} + +string Text::get_text(){ + return text; +} + +void Text::remake_texture(){ + if(texture != nullptr){ + SDL_DestroyTexture(texture); + } + + SDL_Surface * surface; + switch(style){ + case SOLID: + + surface = TTF_RenderText_Solid(font.get(), text.c_str(), color); + + break; + case SHADED: + + + surface = TTF_RenderText_Shaded(font.get(), text.c_str(), color, {0, 0, 0, 255}); + + break; + case BLENDED: + + surface = TTF_RenderText_Blended(font.get(), text.c_str(), color); + break; + } + + if(surface == nullptr){ + printf("%s\n", SDL_GetError()); + exit(-3); + } + + texture = SDL_CreateTextureFromSurface(Game::get_instance().get_renderer(), surface); + SDL_FreeSurface(surface); + + int w, h; + int query_texture = SDL_QueryTexture(texture, nullptr, nullptr, &w, &h); + if(query_texture){ + printf("Remake texture: %s\n", SDL_GetError()); + exit(-1); + } + + box.set_width(w); + box.set_height(h); + clip_rect.w = w; + clip_rect.h = h; +} + +void Text::open(string file, int size){ + font = Resources::get_font("res/" + file, size); +} diff --git a/src/TimeCounter.cpp b/src/TimeCounter.cpp new file mode 100644 index 0000000..92ed14b --- /dev/null +++ b/src/TimeCounter.cpp @@ -0,0 +1,49 @@ +#include "TimeCounter.h" + +using std::to_string; + +TimeCounter::TimeCounter(){ + remaining_seconds = total_time; + + text = new Text("font/8-BIT WONDER.ttf", 50, Text::TextStyle::SOLID, "99", + {255, 255, 255, 255}); + + text->set_pos(640, 664, true, true); + + bg = Sprite("hud/time_board.png"); + box = Rectangle(640, 664, bg.get_width(), bg.get_height()); +} + +TimeCounter::~TimeCounter(){ + +} + +void TimeCounter::update(float delta){ + timer.update(delta); + remaining_seconds = total_time - (timer.get() / total_time); + //FIXME + if(remaining_seconds < 0) + remaining_seconds = 0; + text->set_text(get_time_string()); +} + +void TimeCounter::render(){ + bg.render(box.get_draw_x(), box.get_draw_y()); + text->render(); +} + +string TimeCounter::get_time_string(){ + return (remaining_seconds < 10 ? "0" : "") + to_string(remaining_seconds); +} + +bool TimeCounter::is_dead(){ + return false; +} + +void TimeCounter::notify_collision(GameObject &){ + +} + +bool TimeCounter::is(string){ + return false; +} diff --git a/src/Timer.cpp b/src/Timer.cpp new file mode 100644 index 0000000..c54a7c7 --- /dev/null +++ b/src/Timer.cpp @@ -0,0 +1,17 @@ +#include "Timer.h" + +Timer::Timer(){ + time = 0; +} + +void Timer::update(float delta){ + time += delta; +} + +void Timer::restart(){ + time = 0; +} + +float Timer::get(){ + return time; +} diff --git a/src/Vector.cpp b/src/Vector.cpp new file mode 100644 index 0000000..47fa35f --- /dev/null +++ b/src/Vector.cpp @@ -0,0 +1,26 @@ +#include "Vector.h" +#include + +Vector::Vector(float mx, float my) : x(mx), y(my){} + +void Vector::rotate(Vector origin, float angle){ + x = origin.x + 200 * cos(angle); + y = origin.y + 200 * sin(angle); +} + +void Vector::transform(float module, float angle){ + x = module * cos(angle); + y = module * sin(angle); +} + +Vector Vector::operator+(const Vector& rhs) const { + return Vector(x + rhs.x, y + rhs.y); +} + +Vector Vector::operator-(const Vector& rhs) const { + return Vector(x - rhs.x, y - rhs.y); +} + +Vector Vector::operator*(const float rhs) const { + return Vector(x * rhs, y * rhs); +}