From 418f45f78bbf743021c53030da2ad2158fa355d2 Mon Sep 17 00:00:00 2001 From: Frederico Linhares Date: Tue, 29 Mar 2022 11:51:11 -0300 Subject: Initial commit --- src/candy_gear.cpp | 89 +++++++++++++ src/candy_gear.h | 33 +++++ src/color.c | 61 +++++++++ src/color.h | 40 ++++++ src/core.cpp | 292 +++++++++++++++++++++++++++++++++++++++++ src/core.h | 97 ++++++++++++++ src/font.c | 60 +++++++++ src/font.h | 40 ++++++ src/graphic.c | 67 ++++++++++ src/graphic.h | 33 +++++ src/key.c | 71 ++++++++++ src/key.h | 33 +++++ src/loader.c | 162 +++++++++++++++++++++++ src/loader.h | 159 ++++++++++++++++++++++ src/log.c | 38 ++++++ src/log.h | 33 +++++ src/main.cpp | 151 +++++++++++++++++++++ src/palette.c | 58 ++++++++ src/palette.h | 40 ++++++ src/palette_implementation.cpp | 35 +++++ src/palette_implementation.h | 33 +++++ src/pgm_image.cpp | 70 ++++++++++ src/pgm_image.h | 42 ++++++ src/point.c | 122 +++++++++++++++++ src/point.h | 40 ++++++ src/rect.c | 209 +++++++++++++++++++++++++++++ src/rect.h | 40 ++++++ src/sound.c | 73 +++++++++++ src/sound.h | 40 ++++++ src/sprite.c | 198 ++++++++++++++++++++++++++++ src/sprite.h | 40 ++++++ src/sprite_implementation.cpp | 58 ++++++++ src/sprite_implementation.h | 49 +++++++ src/texture.c | 223 +++++++++++++++++++++++++++++++ src/texture.h | 41 ++++++ 35 files changed, 2870 insertions(+) create mode 100644 src/candy_gear.cpp create mode 100644 src/candy_gear.h create mode 100644 src/color.c create mode 100644 src/color.h create mode 100644 src/core.cpp create mode 100644 src/core.h create mode 100644 src/font.c create mode 100644 src/font.h create mode 100644 src/graphic.c create mode 100644 src/graphic.h create mode 100644 src/key.c create mode 100644 src/key.h create mode 100644 src/loader.c create mode 100644 src/loader.h create mode 100644 src/log.c create mode 100644 src/log.h create mode 100644 src/main.cpp create mode 100644 src/palette.c create mode 100644 src/palette.h create mode 100644 src/palette_implementation.cpp create mode 100644 src/palette_implementation.h create mode 100644 src/pgm_image.cpp create mode 100644 src/pgm_image.h create mode 100644 src/point.c create mode 100644 src/point.h create mode 100644 src/rect.c create mode 100644 src/rect.h create mode 100644 src/sound.c create mode 100644 src/sound.h create mode 100644 src/sprite.c create mode 100644 src/sprite.h create mode 100644 src/sprite_implementation.cpp create mode 100644 src/sprite_implementation.h create mode 100644 src/texture.c create mode 100644 src/texture.h (limited to 'src') diff --git a/src/candy_gear.cpp b/src/candy_gear.cpp new file mode 100644 index 0000000..484ce6a --- /dev/null +++ b/src/candy_gear.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "candy_gear.h" + +#include +#include +#include + +#include + +static mrb_value +parse_node(mrb_state *mrb, const YAML::Node &node) +{ + mrb_value value; + std::string scalar; + + switch(node.Type()) + { + case YAML::NodeType::Null: + return mrb_nil_value(); + + case YAML::NodeType::Scalar: + scalar = node.as(); + return mrb_str_new(mrb, scalar.data(), scalar.size()); + + case YAML::NodeType::Sequence: + value = mrb_ary_new_capa(mrb, node.size()); + for (YAML::const_iterator it = node.begin(); it != node.end(); it++) + mrb_ary_push(mrb, value, parse_node(mrb, *it)); + return value; + + case YAML::NodeType::Map: + value = mrb_hash_new_capa(mrb, node.size()); + for(YAML::const_iterator it = node.begin(); it != node.end(); it++) + mrb_hash_set( + mrb, value, parse_node(mrb, it->first), parse_node(mrb, it->second)); + return value; + + case YAML::NodeType::Undefined: + default: + return mrb_nil_value(); + } +} + +static mrb_value +cg_mCandyGear_load_yaml(mrb_state *mrb, mrb_value self) +{ + const char *file_path; + + mrb_get_args(mrb, "z", &file_path); + + YAML::Node root = YAML::LoadFile(file_path); + + return parse_node(mrb, root); +} + +static mrb_value +cg_mCandyGear_quit(mrb_state *mrb, mrb_value self) +{ + cg_core.quit_game = SDL_TRUE; + + return self; +} + +void +cg_candy_gear_init(mrb_state *mrb) +{ + struct RClass *cg_m; + + cg_m = mrb_module_get(mrb, "CandyGear"); + mrb_define_class_method( + mrb, cg_m, "load_yaml", cg_mCandyGear_load_yaml, MRB_ARGS_REQ(1)); + mrb_define_class_method( + mrb, cg_m, "quit", cg_mCandyGear_quit, MRB_ARGS_NONE()); +} diff --git a/src/candy_gear.h b/src/candy_gear.h new file mode 100644 index 0000000..aca4740 --- /dev/null +++ b/src/candy_gear.h @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_GRAPHIC_H +#define CANDY_GEAR_CANDY_GEAR_H 1 + +#include "core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void +cg_candy_gear_init(mrb_state *mrb); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_CANDY_GEAR_H */ diff --git a/src/color.c b/src/color.c new file mode 100644 index 0000000..0ab9392 --- /dev/null +++ b/src/color.c @@ -0,0 +1,61 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "color.h" + +void +cg_free_color(mrb_state *mrb, void* obj) +{ + struct cg_color *ptr = obj; + + mrb_free(mrb, ptr); +} + +const struct mrb_data_type cg_color_type = { "CG_Color", cg_free_color }; + +static mrb_value +cg_cColor_initialize(mrb_state *mrb, mrb_value self) +{ + mrb_int red, green, blue, alpha; + struct cg_color *ptr; + + alpha = 0xff; + mrb_get_args(mrb, "iii|i", &red, &green, &blue, &alpha); + ptr = (struct cg_color *)DATA_PTR(self); + if(ptr) mrb_free(mrb, ptr); + ptr = (struct cg_color *)mrb_malloc(mrb, sizeof(struct cg_color)); + + ptr->data.r = red; + ptr->data.g = green; + ptr->data.b = blue; + ptr->data.a = alpha; + + mrb_data_init(self, ptr, &cg_color_type); + return self; +} + +void +cg_color_init(mrb_state *mrb) +{ + struct RClass *cg_m, *cg_cColor; + + cg_m = mrb_module_get(mrb, "CandyGear"); + cg_cColor = mrb_define_class_under(mrb, cg_m, "Color", mrb->object_class); + MRB_SET_INSTANCE_TT(cg_cColor, MRB_TT_DATA); + mrb_define_method( + mrb, cg_cColor, "initialize", cg_cColor_initialize, + MRB_ARGS_REQ(3) | MRB_ARGS_OPT(1)); +} diff --git a/src/color.h b/src/color.h new file mode 100644 index 0000000..ee1b3ed --- /dev/null +++ b/src/color.h @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_COLOR_H +#define CANDY_GEAR_COLOR_H 1 + +#include "core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct cg_color +{ + SDL_Color data; +}; + +extern const struct mrb_data_type cg_color_type; + +void +cg_color_init(mrb_state *mrb); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_COLOR_H */ diff --git a/src/core.cpp b/src/core.cpp new file mode 100644 index 0000000..12e5b01 --- /dev/null +++ b/src/core.cpp @@ -0,0 +1,292 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "core.h" + +#include + +/* + Stretches the image to fill the screen vertically, horizontally, or both; + without changing the game aspect ratio. + */ +void +cg_Engine_calculate_full_scale() +{ + double screen_ratio, game_ratio, scale; + + screen_ratio = + (double)cg_core.screen_width/(double)cg_core.screen_height; + game_ratio = (double)cg_core.game_width/(double)cg_core.game_height; + + // If screen is proportionally taller than game. + if(screen_ratio < game_ratio) + { + scale = (double)cg_core.screen_width/(double)cg_core.game_width; + + cg_core.screen_rect.w = cg_core.game_width * scale; + cg_core.screen_rect.h = cg_core.game_height * scale; + cg_core.screen_rect.x = 0; + cg_core.screen_rect.y = cg_core.screen_height/2 - + cg_core.screen_rect.h/2; + } + // If screen is proportionally wider than game. + else if(screen_ratio > game_ratio) + { + scale = (double)cg_core.screen_height/(double)cg_core.game_height; + + cg_core.screen_rect.w = cg_core.game_width * scale; + cg_core.screen_rect.h = cg_core.game_height * scale; + cg_core.screen_rect.x = cg_core.screen_width/2 - + cg_core.screen_rect.w/2; + cg_core.screen_rect.y = 0; + } + // If they have the same aspect ratio. + else + { + cg_core.screen_rect.x = 0; + cg_core.screen_rect.y = 0; + cg_core.screen_rect.w = cg_core.screen_width; + cg_core.screen_rect.h = cg_core.screen_height; + } +} + +static SDL_bool +load_variables(void *obj, LoaderStack *ls) +{ + YAML::Node root = YAML::LoadFile(cg_core.config_file); + + std::string game_name = root["name"].as().c_str(); + cg_core.game_name = new char[game_name.size() + 1]; + strcpy(cg_core.game_name, game_name.c_str()); + + // Based on NES. + cg_core.game_width = root["game"]["width"].as(); + cg_core.game_height = root["game"]["height"].as(); + cg_core.screen_width = root["screen"]["width"].as(); + cg_core.screen_height = root["screen"]["height"].as(); + + cg_core.fps = 30; + cg_core.max_frame_duration_ms = 1000 / cg_core.fps; + + return SDL_TRUE; +} + +static void +unload_variables(void *obj, LoaderStack *ls) +{ + delete[] cg_core.game_name; +} + +static SDL_bool +load_sdl(void *obj, LoaderStack *ls) +{ + if(SDL_Init(SDL_INIT_EVERYTHING) < 0) + { + const char* base_error = "SDL could not initialize! SDL Error → "; + LoaderStack_set_error(ls, base_error, SDL_GetError()); + return SDL_FALSE; + } + + return SDL_TRUE; +} + +static void +unload_sdl(void *obj, LoaderStack *ls) +{ + SDL_Quit(); +} + +static SDL_bool +load_sdl_image(void *obj, LoaderStack *ls) +{ + int flags = IMG_INIT_JPG|IMG_INIT_PNG|IMG_INIT_TIF; + if(!(IMG_Init(flags) & flags)) + { + const char* base_error = "Could not initialize SDL image → "; + LoaderStack_set_error(ls, base_error, IMG_GetError()); + return SDL_FALSE; + } + + return SDL_TRUE; +} + +static void +unload_sdl_image(void *obj, LoaderStack *ls) +{ + IMG_Quit(); +} + +static SDL_bool +load_sdl_mixer(void *obj, LoaderStack *ls) +{ + int flags = MIX_INIT_OGG|MIX_INIT_MOD; + int initted = Mix_Init(flags); + if(initted&flags != flags) + { + const char* base_error = "Could not initialize SDL mixer → "; + LoaderStack_set_error(ls, base_error, Mix_GetError()); + return SDL_FALSE; + } + + return SDL_TRUE; +} + +static void +unload_sdl_mixer(void *obj, LoaderStack *ls) +{ + while(Mix_Init(0)) Mix_Quit(); +} + +static SDL_bool +load_sdl_open_audio(void *obj, LoaderStack *ls) +{ + if(Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 1024) == -1) + { + const char* base_error = "Could not open SDL mixer audio → "; + LoaderStack_set_error(ls, base_error, Mix_GetError()); + return SDL_FALSE; + } + + return SDL_TRUE; +} + +static void +unload_sdl_open_audio(void *obj, LoaderStack *ls) +{ + Mix_CloseAudio(); +} + +static SDL_bool +load_sdl_ttf(void *obj, LoaderStack *ls) +{ + if(TTF_Init()==-1) + { + const char* base_error = "Could not initialize SDL ttf → "; + LoaderStack_set_error(ls, base_error, TTF_GetError()); + return SDL_FALSE; + } + + return SDL_TRUE; +} + +static void +unload_sdl_ttf(void *obj, LoaderStack *ls) +{ + TTF_Quit(); +} + +static SDL_bool +load_window(void *obj, LoaderStack *ls) +{ + cg_core.window = NULL; + cg_core.window = SDL_CreateWindow( + cg_core.game_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + cg_core.screen_width, cg_core.screen_height, SDL_WINDOW_SHOWN); + if(cg_core.window == NULL) + { + const char* base_error = "Window could not be created! SDL_Error → "; + LoaderStack_set_error(ls, base_error, SDL_GetError()); + return SDL_FALSE; + } + + return SDL_TRUE; +} + +static void +unload_window(void *obj, LoaderStack *ls) +{ + SDL_DestroyWindow(cg_core.window); +} + +static SDL_bool +load_sdl_renderer(void *obj, LoaderStack *ls) +{ + cg_core.renderer = NULL; + cg_core.renderer = SDL_CreateRenderer( + cg_core.window, -1, SDL_RENDERER_ACCELERATED | + SDL_RENDERER_TARGETTEXTURE); + if(cg_core.renderer == NULL) + { + const char* base_error = "Could not create SDL renderer → "; + LoaderStack_set_error(ls, base_error, SDL_GetError()); + return SDL_FALSE; + } + + return SDL_TRUE; +} + +static void +unload_sdl_renderer(void *obj, LoaderStack *ls) +{ + SDL_DestroyRenderer(cg_core.renderer); +} + +static SDL_bool +load_pre_screen(void *obj, LoaderStack *ls) +{ + cg_core.pre_screen_buffer = SDL_CreateTexture( + cg_core.renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, + cg_core.game_width, cg_core.game_height); + + if(cg_core.pre_screen_buffer == NULL) + { + const char* base_error = "Could not create renderering buffer → "; + LoaderStack_set_error(ls, base_error, SDL_GetError()); + return SDL_FALSE; + } + + cg_Engine_calculate_full_scale(); + + SDL_SetRenderTarget(cg_core.renderer, cg_core.pre_screen_buffer); + + return SDL_TRUE; +} + +static void +unload_pre_screen(void *obj, LoaderStack *ls) +{ + SDL_DestroyTexture(cg_core.pre_screen_buffer); +} + +void +cg_Core_init(LoaderStack *loader) +{ + LoaderStack_constructor(loader, NULL); + + LoaderStack_add(loader, &load_variables, &unload_variables); + LoaderStack_add(loader, &load_sdl, &unload_sdl); + LoaderStack_add(loader, &load_sdl_image, &unload_sdl_image); + LoaderStack_add(loader, &load_sdl_mixer, &unload_sdl_mixer); + LoaderStack_add(loader, &load_sdl_open_audio, &unload_sdl_open_audio); + LoaderStack_add(loader, &load_sdl_ttf, &unload_sdl_ttf); + LoaderStack_add(loader, &load_window, &unload_window); + LoaderStack_add(loader, &load_sdl_renderer, &unload_sdl_renderer); + LoaderStack_add(loader, &load_pre_screen, &unload_pre_screen); +} + +SDL_bool +cg_Core_load(LoaderStack *loader) +{ + if(!LoaderStack_load(loader)) return SDL_FALSE; + + return SDL_TRUE; +} + +void +cg_Core_unload(LoaderStack *loader) +{ + LoaderStack_destructor(loader); +} diff --git a/src/core.h b/src/core.h new file mode 100644 index 0000000..e5d651a --- /dev/null +++ b/src/core.h @@ -0,0 +1,97 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_CORE_H +#define CANDY_GEAR_CORE_H 1 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "loader.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The Core class stores all global states that the engine needs to work. + * Global variables are not evil if you use them carefully. + */ +typedef struct +{ + const char *config_file; + + /// Text displayed in the game window. + char *game_name; + + /** + * @{ + * This is the amount of pixel the games use to render a buffer. The image in + * this buffer is then rendered to the screen. + */ + int game_width, game_height; + /// @} + + /** + * @{ + * This is the ammount of pixel that the games uses when rendering to the + * screen. + */ + int screen_width, screen_height; + /// @} + + Uint32 fps; + Uint32 max_frame_duration_ms; + + SDL_Window *window; + SDL_Renderer *renderer; + + /// All rendering goes here before they are moved to the screen. + SDL_Texture *pre_screen_buffer; + SDL_Rect screen_rect; + + SDL_bool quit_game; +} cg_sCore; + +extern cg_sCore cg_core; + +/// \memberof cg_sCore @{ + +void +cg_Core_init(LoaderStack *loader); + +SDL_bool +cg_Core_load(LoaderStack *loader); + +void +cg_Core_unload(LoaderStack *loader); + +/// @} + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_CORE_H */ diff --git a/src/font.c b/src/font.c new file mode 100644 index 0000000..a333a30 --- /dev/null +++ b/src/font.c @@ -0,0 +1,60 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "font.h" +#include + +void +cg_free_font(mrb_state *mrb, void* obj) +{ + struct cg_font *ptr = obj; + + TTF_CloseFont(ptr->data); + mrb_free(mrb, ptr); +} + +const struct mrb_data_type cg_font_type = {"CG_Font", cg_free_font }; + +static mrb_value +cg_cFont_initialize(mrb_state *mrb, mrb_value self) +{ + mrb_int font_size; + char *file_path; + struct cg_font *ptr; + + mrb_get_args(mrb, "zi", &file_path, &font_size); + ptr = (struct cg_font *)DATA_PTR(self); + if(ptr) mrb_free(mrb, ptr); + ptr = (struct cg_font *)mrb_malloc(mrb, sizeof(struct cg_font)); + + ptr->data = TTF_OpenFont(file_path, font_size); + if(!ptr->data) mrb_raise(mrb, E_ARGUMENT_ERROR, TTF_GetError()); + + mrb_data_init(self, ptr, &cg_font_type); + return self; +} + +void +cg_font_init(mrb_state *mrb) +{ + struct RClass *cg_m, *cg_cFont; + + cg_m = mrb_module_get(mrb, "CandyGear"); + cg_cFont = mrb_define_class_under(mrb, cg_m, "Font", mrb->object_class); + MRB_SET_INSTANCE_TT(cg_cFont, MRB_TT_DATA); + mrb_define_method( + mrb, cg_cFont, "initialize", cg_cFont_initialize, MRB_ARGS_REQ(2)); +} diff --git a/src/font.h b/src/font.h new file mode 100644 index 0000000..55e4d16 --- /dev/null +++ b/src/font.h @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_FONT_H +#define CANDY_GEAR_FONT_H 1 + +#include "core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct cg_font +{ + TTF_Font *data; +}; + +extern const struct mrb_data_type cg_font_type; + +void +cg_font_init(mrb_state *mrb); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_FONT_H */ diff --git a/src/graphic.c b/src/graphic.c new file mode 100644 index 0000000..b58c08c --- /dev/null +++ b/src/graphic.c @@ -0,0 +1,67 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "graphic.h" + +#include "color.h" + +static mrb_value +cg_mGraphic_set_color(mrb_state *mrb, mrb_value self) +{ + struct cg_color *color; + mrb_get_args(mrb, "d", &color, &cg_color_type); + + SDL_SetRenderDrawColor( + cg_core.renderer, color->data.r, color->data.g, color->data.b, + color->data.a); + + return self; +} + +static mrb_value +cg_mGraphic_draw_point(mrb_state *mrb, mrb_value self) +{ + mrb_int x, y; + mrb_get_args(mrb, "ii", &x, &y); + SDL_RenderDrawPoint(cg_core.renderer, x, y); + + return self; +} + +static mrb_value +cg_mGraphic_draw_line(mrb_state *mrb, mrb_value self) +{ + mrb_int x1, y1, x2, y2; + mrb_get_args(mrb, "iiii", &x1, &y1, &x2, &y2); + SDL_RenderDrawLine(cg_core.renderer, x1, y1, x2, y2); + + return self; +} + +void +cg_graphic_init(mrb_state *mrb) +{ + struct RClass *cg_m, *cg_mGraphic; + + cg_m = mrb_module_get(mrb, "CandyGear"); + cg_mGraphic = mrb_define_module_under(mrb, cg_m, "Graphic"); + mrb_define_class_method( + mrb, cg_mGraphic, "set_color", cg_mGraphic_set_color, MRB_ARGS_REQ(1)); + mrb_define_class_method( + mrb, cg_mGraphic, "draw_point", cg_mGraphic_draw_point, MRB_ARGS_REQ(2)); + mrb_define_class_method( + mrb, cg_mGraphic, "draw_line", cg_mGraphic_draw_line, MRB_ARGS_REQ(4)); +} diff --git a/src/graphic.h b/src/graphic.h new file mode 100644 index 0000000..3fe9b1e --- /dev/null +++ b/src/graphic.h @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_GRAPHIC_H +#define CANDY_GEAR_GRAPHIC_H 1 + +#include "core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void +cg_graphic_init(mrb_state *mrb); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_GRAPHIC_H */ diff --git a/src/key.c b/src/key.c new file mode 100644 index 0000000..01fc42a --- /dev/null +++ b/src/key.c @@ -0,0 +1,71 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "key.h" + +void +cg_key_init(mrb_state *mrb) +{ + struct RClass *cg_m, *cg_mKey; + + cg_m = mrb_module_get(mrb, "CandyGear"); + cg_mKey = mrb_define_module_under(mrb, cg_m, "Key"); + + mrb_define_const(mrb, cg_mKey, "A", mrb_int_value(mrb, SDLK_a)); + mrb_define_const(mrb, cg_mKey, "B", mrb_int_value(mrb, SDLK_b)); + mrb_define_const(mrb, cg_mKey, "C", mrb_int_value(mrb, SDLK_c)); + mrb_define_const(mrb, cg_mKey, "D", mrb_int_value(mrb, SDLK_d)); + mrb_define_const(mrb, cg_mKey, "E", mrb_int_value(mrb, SDLK_e)); + mrb_define_const(mrb, cg_mKey, "F", mrb_int_value(mrb, SDLK_f)); + mrb_define_const(mrb, cg_mKey, "G", mrb_int_value(mrb, SDLK_g)); + mrb_define_const(mrb, cg_mKey, "H", mrb_int_value(mrb, SDLK_h)); + mrb_define_const(mrb, cg_mKey, "I", mrb_int_value(mrb, SDLK_i)); + mrb_define_const(mrb, cg_mKey, "J", mrb_int_value(mrb, SDLK_j)); + mrb_define_const(mrb, cg_mKey, "K", mrb_int_value(mrb, SDLK_k)); + mrb_define_const(mrb, cg_mKey, "L", mrb_int_value(mrb, SDLK_l)); + mrb_define_const(mrb, cg_mKey, "M", mrb_int_value(mrb, SDLK_m)); + mrb_define_const(mrb, cg_mKey, "N", mrb_int_value(mrb, SDLK_n)); + mrb_define_const(mrb, cg_mKey, "O", mrb_int_value(mrb, SDLK_o)); + mrb_define_const(mrb, cg_mKey, "P", mrb_int_value(mrb, SDLK_p)); + mrb_define_const(mrb, cg_mKey, "Q", mrb_int_value(mrb, SDLK_q)); + mrb_define_const(mrb, cg_mKey, "R", mrb_int_value(mrb, SDLK_r)); + mrb_define_const(mrb, cg_mKey, "S", mrb_int_value(mrb, SDLK_s)); + mrb_define_const(mrb, cg_mKey, "T", mrb_int_value(mrb, SDLK_t)); + mrb_define_const(mrb, cg_mKey, "U", mrb_int_value(mrb, SDLK_u)); + mrb_define_const(mrb, cg_mKey, "V", mrb_int_value(mrb, SDLK_v)); + mrb_define_const(mrb, cg_mKey, "W", mrb_int_value(mrb, SDLK_w)); + mrb_define_const(mrb, cg_mKey, "X", mrb_int_value(mrb, SDLK_x)); + mrb_define_const(mrb, cg_mKey, "Y", mrb_int_value(mrb, SDLK_y)); + mrb_define_const(mrb, cg_mKey, "Z", mrb_int_value(mrb, SDLK_x)); + + mrb_define_const(mrb, cg_mKey, "UP", mrb_int_value(mrb, SDLK_UP)); + mrb_define_const(mrb, cg_mKey, "DOWN", mrb_int_value(mrb, SDLK_DOWN)); + mrb_define_const(mrb, cg_mKey, "LEFT", mrb_int_value(mrb, SDLK_LEFT)); + mrb_define_const(mrb, cg_mKey, "RIGHT", mrb_int_value(mrb, SDLK_RIGHT)); + + mrb_define_const( + mrb, cg_mKey, "BACKSPACE", mrb_int_value(mrb, SDLK_BACKSPACE)); + mrb_define_const(mrb, cg_mKey, "TAB", mrb_int_value(mrb, SDLK_TAB)); + mrb_define_const( + mrb, cg_mKey, "LEFT_SHIFT", mrb_int_value(mrb, SDLK_LSHIFT)); + mrb_define_const( + mrb, cg_mKey, "RIGHT_SHIFT", mrb_int_value(mrb, SDLK_RSHIFT)); + mrb_define_const(mrb, cg_mKey, "SPACE", mrb_int_value(mrb, SDLK_SPACE)); + mrb_define_const(mrb, cg_mKey, "LEFT_ALT", mrb_int_value(mrb, SDLK_LALT)); + mrb_define_const(mrb, cg_mKey, "RIGHT_ALT", mrb_int_value(mrb, SDLK_RALT)); + mrb_define_const(mrb, cg_mKey, "LEFT_CTRL", mrb_int_value(mrb, SDLK_LCTRL)); + mrb_define_const(mrb, cg_mKey, "RIGHT_CTRL", mrb_int_value(mrb, SDLK_RCTRL)); +} diff --git a/src/key.h b/src/key.h new file mode 100644 index 0000000..5544518 --- /dev/null +++ b/src/key.h @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_KEY_H +#define CANDY_GEAR_KEY_H 1 + +#include "core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void +cg_key_init(mrb_state *mrb); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_KEY_H */ diff --git a/src/loader.c b/src/loader.c new file mode 100644 index 0000000..c24d47e --- /dev/null +++ b/src/loader.c @@ -0,0 +1,162 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "loader.h" + +void +LoaderStack_constructor(LoaderStack *self, void *obj) +{ + self->_last_loaded = 0; + + self->_loader_capacity = 10; // Totally arbitrary value. + self->_loader_size = 0; + + self->_obj = obj; + + self->error_message = NULL; + + self->_loaders = malloc(sizeof(Loader) * self->_loader_capacity); +} + +void +LoaderStack_destructor(LoaderStack *self) +{ + if(self->error_message != NULL) + free(self->error_message); + + if(LoaderStack_is_loaded(self)) LoaderStack_unload(self); + free(self->_loaders); +} + +static void +LoaderStack_partial_unload(LoaderStack *self, int32_t step) +{ + // Already unloaded, nothing to do. + if(self->_last_loaded <= step) return; + + for(; self->_last_loaded >= step; self->_last_loaded--) + if(self->_loaders[self->_last_loaded].unload != NULL) + self->_loaders[self->_last_loaded].unload(self->_obj, self); + + // This number will be one before the last loaded after the unloading loop + // is over. + self->_last_loaded++; +} + +SDL_bool +LoaderStack_partial_load(LoaderStack *self, int32_t step) +{ + SDL_bool error = SDL_FALSE; + + // Already loaded, nothing to do. + if(self->_last_loaded >= step) return SDL_TRUE; + + for(;self->_last_loaded < step; self->_last_loaded++) + { + error = !self->_loaders[self->_last_loaded].load(self->_obj, self); + if(error) break; + } + + // Self number will be one after the last loeaded after the loading loop is + // over. + self->_last_loaded--; + + if(error) LoaderStack_partial_unload(self, 0); + + return !error; +} + +SDL_bool +LoaderStack_is_loaded(LoaderStack *self) +{ + return self->_last_loaded > 0; +} + +void +LoaderStack_add( + LoaderStack *self, + SDL_bool (*load)(void *obj, LoaderStack *ls), + void (*unload)(void *obj, LoaderStack *ls)) +{ + Loader *l; + uint32_t next_loader = self->_loader_size; + + // Expand if is full. + if(self->_loader_size == self->_loader_capacity) + { + self->_loader_capacity += 5; // Totally arbitrary value. + self->_loaders = realloc(self->_loaders, + sizeof(Loader) * self->_loader_capacity); + } + + l = &(self->_loaders[next_loader]); + l->load = load; + l->unload = unload; + + self->_loader_size++; +} + +void +LoaderStack_set_error( + LoaderStack *self, const char* base_error, const char* additional_error) +{ + if(self->error_message != NULL) free(self->error_message); + + if(additional_error != NULL) + { + self->error_message = + malloc(strlen(base_error) + strlen(additional_error) + 1); + + strcpy(self->error_message, base_error); + strcat(self->error_message, additional_error); + } + else + { + self->error_message = malloc(strlen(base_error)); + + strcpy(self->error_message, base_error); + } +} + +SDL_bool +LoaderStack_load(LoaderStack *self) +{ + if(LoaderStack_is_loaded(self)) return SDL_TRUE; + + if(LoaderStack_partial_load(self, self->_loader_size)) + return SDL_TRUE; + else + return SDL_FALSE; +} + +SDL_bool +LoaderStack_reload(LoaderStack *self) +{ + if(!LoaderStack_is_loaded(self)) return SDL_FALSE; + + LoaderStack_partial_unload(self, self->_loader_size); + return LoaderStack_partial_load(self, self->_loader_size); +} + +void +LoaderStack_unload(LoaderStack *self) +{ + if(!LoaderStack_is_loaded(self)) return; + + LoaderStack_partial_unload(self, 0); +} diff --git a/src/loader.h b/src/loader.h new file mode 100644 index 0000000..93ec42b --- /dev/null +++ b/src/loader.h @@ -0,0 +1,159 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_LOADER_STACK_H +#define CANDY_GEAR_LOADER_STACK_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct LoaderStack_s; + +/** + * Stores a reversible action. + */ +typedef struct +{ + SDL_bool (*load)(void *obj, struct LoaderStack_s *ls); + void (*unload)(void *obj, struct LoaderStack_s *ls); +} Loader; + +/** + * Stores a sequence of functions that must be executed and rolled back in + * order. + * + * For example, if the variable _loaders contain A→B→C→D→E, it will load A, + * then B, then C, etc. If D fails, it unloads C, then B, then A. + */ +struct LoaderStack_s +{ + void *_obj; + + int32_t _last_loaded; + + uint32_t _loader_capacity; + uint32_t _loader_size; + Loader *_loaders; + + char* error_message; +}; + +typedef struct LoaderStack_s LoaderStack; + +/// \memberof LoaderStack_s @{ + +/** + * Must be called to initialize the variables. + * + * @param[self] a pointer to the LoaderStack_s to be initalized. + * @param[obj] a pointer to an struct that will be send to every function + * inside the _loaders. + */ +void +LoaderStack_constructor(LoaderStack *self, void *obj); + +/** + * Must be called to finalize the variables and cleanup memory. + * + * @param[self] a pointer to the LoaderStack_s to be finalized. + */ +void +LoaderStack_destructor(LoaderStack *self); + +/** + * Adds a reversible action to the loader. The first action added is the first + * to de executed the last rolled back, and so on. Those functions are stored + * inside _loaders. + * + * @param[self] a pointer to the LoaderStack_s hat stores the action. + * @param[load] the do action. The function pointed by this variable must + * return SDL_TRUE on success and SDL_FALSE on fail. + * @param[unload] the undo/rollback action, if the action do not need a + * rollback, this pointer can be set to null. + */ +void +LoaderStack_add( + LoaderStack *self, + SDL_bool (*load)(void *obj, LoaderStack *ls), + void (*unload)(void *obj, LoaderStack *ls)); + +/** + * Can be used to set error before returning SDL_FALSE from a function used by + * _loaders + * + * @param[self] a pointer to the LoaderStack_s where the error happened. + * @param[base_error] the error message. + * @param[additional_error] if you need to concatenate two error messages, use + * this variable to a second message. Otherwise set to NULL. + */ +void +LoaderStack_set_error( + LoaderStack *self, const char* base_error, const char* additional_error); + +/** + * @return SDL_TRUE if you successfuly executed LoaderStack_load or + * LoaderStack_reload in the LoaderStack. SDL_FALSE after + * LoaderStack_constructor or LoaderStack_unload is executed. + */ +SDL_bool +LoaderStack_is_loaded(LoaderStack *self); + +/** + * Execute some of the load functions in the _loaders. If one of them fails, + * roll back everything inside _loaders. The LoaderStack knows which functions + * it already executed and will not rerun them unless they are rolled back + * first. + * + * @param[step] the number of functions to be executed. + * @return SDL_TRUE on success and SDL_FALSE on fail. + */ +SDL_bool +LoaderStack_partial_load(LoaderStack *self, int32_t step); + +/** + * Execute all of the load functions in the _loaders. If one of them fails, + * roll back everything inside _loaders. + * @return SDL_TRUE on success and SDL_FALSE on fail. + */ +SDL_bool +LoaderStack_load(LoaderStack *self); + +/** + * Roll back all loaded function inside loaders, if there are any. Then execute + * all of the load functions in the _loaders. If one of them fails, roll back + * everything inside _loaders. + * + * @return SDL_TRUE on success and SDL_FALSE on fail. + */ +SDL_bool +LoaderStack_reload(LoaderStack *self); + +/** + * Roll back all loaded function inside loaders, if there are any. + */ +void +LoaderStack_unload(LoaderStack *self); + +/// @} + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_LOADER_STACK_H */ diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..92862ae --- /dev/null +++ b/src/log.c @@ -0,0 +1,38 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "log.h" + +#include + +static mrb_value +cg_mLog_info(mrb_state *mrb, mrb_value self) +{ + const char *message; + mrb_get_args(mrb, "z", &message); + printf("%s\n", message); + + return self; +} + +void +cg_log_init(mrb_state *mrb) +{ + struct RClass *cg_m, *cg_mLog; + cg_m = mrb_module_get(mrb, "CandyGear"); + cg_mLog = mrb_define_module_under(mrb, cg_m, "Log"); + mrb_define_class_method(mrb, cg_mLog, "info", cg_mLog_info, MRB_ARGS_REQ(1)); +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..0196908 --- /dev/null +++ b/src/log.h @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_LOG_H +#define CANDY_GEAR_LOG_H 1 + +#include "core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void +cg_log_init(mrb_state *mrb); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_LOG_H */ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..8ebe77c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,151 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "candy_gear.h" +#include "color.h" +#include "core.h" +#include "font.h" +#include "graphic.h" +#include "key.h" +#include "log.h" +#include "palette.h" +#include "point.h" +#include "rect.h" +#include "sound.h" +#include "sprite.h" +#include "texture.h" + +cg_sCore cg_core; + +void handle_error(mrb_state *mrb) +{ + mrb_print_error(mrb); + cg_core.quit_game = SDL_TRUE; +} + +int main(int argc, char *argv[]) +{ + SDL_Event event; + Uint32 frame_start; + + mrb_state *mrb = mrb_open(); + mrb_value main_obj; + mrb_sym sym_init, sym_key_down, sym_key_up, sym_quit, sym_tick; + FILE *fp; + + LoaderStack core_loader; + + cg_core.config_file = argv[1]; + + cg_Core_init(&core_loader); + if(!cg_Core_load(&core_loader)) return 1; + + if (!mrb) { /* handle error */ } + mrb_define_module(mrb, "CandyGear"); + cg_candy_gear_init(mrb); + cg_color_init(mrb); + cg_font_init(mrb); + cg_graphic_init(mrb); + cg_key_init(mrb); + cg_log_init(mrb); + cg_palette_init(mrb); + cg_point_init(mrb); + cg_rect_init(mrb); + cg_sound_init(mrb); + cg_sprite_init(mrb); + cg_texture_init(mrb); + + main_obj = mrb_obj_iv_inspect(mrb, mrb->top_self); + sym_init = mrb_intern_cstr(mrb, "init"); + sym_key_down = mrb_intern_cstr(mrb, "key_down"); + sym_key_up = mrb_intern_cstr(mrb, "key_up"); + sym_quit = mrb_intern_cstr(mrb, "quit"); + sym_tick = mrb_intern_cstr(mrb, "tick"); + + fp = fopen(argv[2], "r"); + mrb_load_irep_file(mrb, fp); + fclose(fp); + if (mrb->exc) handle_error(mrb); + + mrb_funcall_id(mrb, main_obj, sym_init, 0); + if (mrb->exc) handle_error(mrb); + + frame_start = SDL_GetTicks(); + + // Game main loop. + while(!cg_core.quit_game) + { + // Get input. + while(SDL_PollEvent(&event) != 0) + { + switch(event.type) + { + case SDL_KEYDOWN: + mrb_funcall_id( + mrb, main_obj, sym_key_down, 1, + mrb_int_value(mrb, event.key.keysym.sym)); + break; + case SDL_KEYUP: + mrb_funcall_id( + mrb, main_obj, sym_key_up, 1, + mrb_int_value(mrb, event.key.keysym.sym)); + break; + case SDL_MOUSEMOTION: + break; + case SDL_MOUSEBUTTONDOWN: + break; + case SDL_MOUSEBUTTONUP: + break; + case SDL_QUIT: + mrb_funcall_id(mrb, main_obj, sym_quit, 0); + break; + } + } + + // Clear buffer. + SDL_SetRenderDrawColor(cg_core.renderer, 0x00, 0x00, 0x00, 0xff); + SDL_RenderClear(cg_core.renderer); + + mrb_funcall_id(mrb, main_obj, sym_tick, 0); + if (mrb->exc) handle_error(mrb); + + // Copy buffer to the screen and display it. + SDL_SetRenderTarget(cg_core.renderer, NULL); + SDL_RenderCopy( + cg_core.renderer, cg_core.pre_screen_buffer, NULL, &cg_core.screen_rect); + SDL_RenderPresent(cg_core.renderer); + SDL_SetRenderTarget(cg_core.renderer, cg_core.pre_screen_buffer); + + // Timer + { + Uint32 frame_stop, frame_duration; + + frame_stop = SDL_GetTicks(); + frame_duration = frame_stop - frame_start; + + // If frame take less time than maximum allowed. + if(cg_core.max_frame_duration_ms > frame_duration) + SDL_Delay(cg_core.max_frame_duration_ms - frame_duration); + + frame_start = frame_stop; + } + } + + mrb_close(mrb); + cg_Core_unload(&core_loader); + + return 0; +} diff --git a/src/palette.c b/src/palette.c new file mode 100644 index 0000000..9f98dc6 --- /dev/null +++ b/src/palette.c @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "palette.h" +#include "palette_implementation.h" + +void +cg_free_palette(mrb_state *mrb, void* obj) +{ + struct cg_palette *ptr = obj; + + mrb_free(mrb, ptr); +} + +const struct mrb_data_type cg_palette_type = { + "CG_Palette", cg_free_palette }; + +static mrb_value +cg_cPalette_initialize(mrb_state *mrb, mrb_value self) +{ + const char *file_path; + struct cg_palette *ptr; + + mrb_get_args(mrb, "z", &file_path); + ptr = (struct cg_palette *)DATA_PTR(self); + if(ptr) mrb_free(mrb, ptr); + ptr = (struct cg_palette *)mrb_malloc(mrb, sizeof(struct cg_palette)); + + cg_pallet_load_yaml(ptr, file_path); + + mrb_data_init(self, ptr, &cg_palette_type); + return self; +} + +void +cg_palette_init(mrb_state *mrb) +{ + struct RClass *cg_m, *cg_cPalette; + + cg_m = mrb_module_get(mrb, "CandyGear"); + cg_cPalette = mrb_define_class_under(mrb, cg_m, "Palette", mrb->object_class); + MRB_SET_INSTANCE_TT(cg_cPalette, MRB_TT_DATA); + mrb_define_method( + mrb, cg_cPalette, "initialize", cg_cPalette_initialize, MRB_ARGS_REQ(1)); +} diff --git a/src/palette.h b/src/palette.h new file mode 100644 index 0000000..b9e52c2 --- /dev/null +++ b/src/palette.h @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_PALETTE_H +#define CANDY_GEAR_PALETTE_H 1 + +#include "core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct cg_palette +{ + SDL_Color colors[256]; +}; + +extern const struct mrb_data_type cg_palette_type; + +void +cg_palette_init(mrb_state *mrb); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_PALETTE_H */ diff --git a/src/palette_implementation.cpp b/src/palette_implementation.cpp new file mode 100644 index 0000000..7f85c6a --- /dev/null +++ b/src/palette_implementation.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "palette_implementation.h" + +#include + +#include + +void cg_pallet_load_yaml(struct cg_palette *palette, const char *file_path) +{ + YAML::Node root = YAML::LoadFile(file_path); + for(std::size_t i = 0; i < root.size(); i++) + { + std::string color = root[i].as(); + + palette->colors[i].r = std::stoi(color.substr(0, 2), nullptr, 16); + palette->colors[i].g = std::stoi(color.substr(2, 2), nullptr, 16); + palette->colors[i].b = std::stoi(color.substr(4, 2), nullptr, 16); + palette->colors[i].a = std::stoi(color.substr(6, 2), nullptr, 16); + } +} diff --git a/src/palette_implementation.h b/src/palette_implementation.h new file mode 100644 index 0000000..50c9ef3 --- /dev/null +++ b/src/palette_implementation.h @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_PALETTE_IMPLEMENTATION_H +#define CANDY_GEAR_PALETTE_IMPLEMENTATION_H 1 + +#include "palette.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void +cg_pallet_load_yaml(struct cg_palette *palette, const char *file_path); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_PALETTE_IMPLEMENTATION_H */ diff --git a/src/pgm_image.cpp b/src/pgm_image.cpp new file mode 100644 index 0000000..a88455c --- /dev/null +++ b/src/pgm_image.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "pgm_image.h" + +#include +#include +#include +#include + +SDL_bool PGMImage_constructor(PGMImage *self, const char *file_path) +{ + self->data = nullptr; + int wh_pos; + std::string line; + std::ifstream file(file_path); + + if(!file) + { + self->error = "Failed to open PGM file"; + return SDL_FALSE; + } + + // Read file magic. + std::getline(file, line); + if(line != "P5") + { + self->error = "PGM file contains a invalid magic"; + return SDL_FALSE; + } + + // Read file comment. + std::getline(file, line); + + // Read file width and height. + std::getline(file, line); + wh_pos = line.find(" "); + std::from_chars(line.data(), line.data() + wh_pos, self->width); + std::from_chars( + line.data() + wh_pos + 1, line.data() + line.size(), self->height); + + // Read file maximum value. + std::getline(file, line); + std::from_chars(line.data(), line.data() + line.size(), self->max_value); + + // Read file values. + self->data_size = self->width * self->height; + self->data = new char[self->data_size]; + file.read(self->data, self->data_size); + + return SDL_TRUE; +} + +void PGMImage_destructor(PGMImage *self) +{ + if(self->data != nullptr) delete[] self->data; +} diff --git a/src/pgm_image.h b/src/pgm_image.h new file mode 100644 index 0000000..5a271d0 --- /dev/null +++ b/src/pgm_image.h @@ -0,0 +1,42 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_PGM_IMAGE_H +#define CANDY_GEAR_PGM_IMAGE_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct PGMImage_s +{ + int width, height, max_value, data_size; + char *data; + const char *error; +}; + +typedef struct PGMImage_s PGMImage; + +SDL_bool PGMImage_constructor(PGMImage *self, const char *file_path); +void PGMImage_destructor(PGMImage *self); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_PGM_IMAGE_H */ diff --git a/src/point.c b/src/point.c new file mode 100644 index 0000000..4d11480 --- /dev/null +++ b/src/point.c @@ -0,0 +1,122 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "point.h" + +void +cg_free_point(mrb_state *mrb, void* obj) +{ + struct cg_point *ptr = obj; + + mrb_free(mrb, ptr); +} + +const struct mrb_data_type cg_point_type = { + "CG_Point", cg_free_point }; + +static mrb_value +cg_cPoint_initialize(mrb_state *mrb, mrb_value self) +{ + mrb_int x, y; + struct cg_point *ptr; + + mrb_get_args(mrb, "ii", &x, &y); + ptr = (struct cg_point *)DATA_PTR(self); + if(ptr) mrb_free(mrb, ptr); + ptr = (struct cg_point *)mrb_malloc(mrb, sizeof(struct cg_point)); + + ptr->data.x = x; + ptr->data.y = y; + + mrb_data_init(self, ptr, &cg_point_type); + return self; +} + +static mrb_value +cg_cPoint_get_x(mrb_state *mrb, mrb_value self) +{ + struct cg_point *ptr; + + ptr = (struct cg_point *)DATA_PTR(self); + + return mrb_int_value(mrb, ptr->data.x); +} + +static mrb_value +cg_cPoint_set_x(mrb_state *mrb, mrb_value self) +{ + mrb_int x; + struct cg_point *ptr; + + mrb_get_args(mrb, "i", &x); + ptr = (struct cg_point *)DATA_PTR(self); + + ptr->data.x = x; + + return mrb_int_value(mrb, ptr->data.x); +} + +static mrb_value +cg_cPoint_get_y(mrb_state *mrb, mrb_value self) +{ + struct cg_point *ptr; + + ptr = (struct cg_point *)DATA_PTR(self); + + return mrb_int_value(mrb, ptr->data.y); +} + +static mrb_value +cg_cPoint_set_y(mrb_state *mrb, mrb_value self) +{ + mrb_int y; + struct cg_point *ptr; + + mrb_get_args(mrb, "i", &y); + ptr = (struct cg_point *)DATA_PTR(self); + + ptr->data.y = y; + + return mrb_int_value(mrb, ptr->data.y); +} + +static mrb_value +cg_cPoint_draw(mrb_state *mrb, mrb_value self) +{ + struct cg_point *ptr; + ptr = (struct cg_point *)DATA_PTR(self); + SDL_RenderDrawPoint(cg_core.renderer, ptr->data.x, ptr->data.y); + + return self; +} + +void +cg_point_init(mrb_state *mrb) +{ + struct RClass *cg_m, *cg_cPoint; + + cg_m = mrb_module_get(mrb, "CandyGear"); + cg_cPoint = mrb_define_class_under( + mrb, cg_m, "Point", mrb->object_class); + MRB_SET_INSTANCE_TT(cg_cPoint, MRB_TT_DATA); + mrb_define_method( + mrb, cg_cPoint, "initialize", cg_cPoint_initialize, MRB_ARGS_REQ(2)); + mrb_define_method(mrb, cg_cPoint, "x", cg_cPoint_get_x, MRB_ARGS_NONE()); + mrb_define_method(mrb, cg_cPoint, "x=", cg_cPoint_set_x, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, cg_cPoint, "y", cg_cPoint_get_y, MRB_ARGS_NONE()); + mrb_define_method(mrb, cg_cPoint, "y=", cg_cPoint_set_y, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, cg_cPoint, "draw", cg_cPoint_draw, MRB_ARGS_NONE()); +} diff --git a/src/point.h b/src/point.h new file mode 100644 index 0000000..7507d62 --- /dev/null +++ b/src/point.h @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_POINT_H +#define CANDY_GEAR_POINT_H 1 + +#include "core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct cg_point +{ + SDL_Point data; +}; + +extern const struct mrb_data_type cg_point_type; + +void +cg_point_init(mrb_state *mrb); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_POINT_H */ diff --git a/src/rect.c b/src/rect.c new file mode 100644 index 0000000..206ab45 --- /dev/null +++ b/src/rect.c @@ -0,0 +1,209 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rect.h" + +void +cg_free_rect(mrb_state *mrb, void* obj) +{ + struct cg_rect *ptr = obj; + + mrb_free(mrb, ptr); +} + +const struct mrb_data_type cg_rect_type = { + "CG_Rect", cg_free_rect }; + +static mrb_value +cg_cRect_initialize(mrb_state *mrb, mrb_value self) +{ + mrb_int x, y, width, height; + struct cg_rect *ptr; + + mrb_get_args(mrb, "iiii", &x, &y, &width, &height); + ptr = (struct cg_rect *)DATA_PTR(self); + if(ptr) mrb_free(mrb, ptr); + ptr = (struct cg_rect *)mrb_malloc(mrb, sizeof(struct cg_rect)); + + ptr->rect.x = x; + ptr->rect.y = y; + ptr->rect.w = width; + ptr->rect.h = height; + + mrb_data_init(self, ptr, &cg_rect_type); + return self; +} + +static mrb_value +cg_cRect_get_x(mrb_state *mrb, mrb_value self) +{ + struct cg_rect *ptr; + + ptr = (struct cg_rect *)DATA_PTR(self); + + return mrb_int_value(mrb, ptr->rect.x); +} + +static mrb_value +cg_cRect_set_x(mrb_state *mrb, mrb_value self) +{ + mrb_int x; + struct cg_rect *ptr; + + mrb_get_args(mrb, "i", &x); + ptr = (struct cg_rect *)DATA_PTR(self); + + ptr->rect.x = x; + + return mrb_int_value(mrb, ptr->rect.x); +} + +static mrb_value +cg_cRect_get_y(mrb_state *mrb, mrb_value self) +{ + struct cg_rect *ptr; + + ptr = (struct cg_rect *)DATA_PTR(self); + + return mrb_int_value(mrb, ptr->rect.y); +} + +static mrb_value +cg_cRect_set_y(mrb_state *mrb, mrb_value self) +{ + mrb_int y; + struct cg_rect *ptr; + + mrb_get_args(mrb, "i", &y); + ptr = (struct cg_rect *)DATA_PTR(self); + + ptr->rect.y = y; + + return mrb_int_value(mrb, ptr->rect.y); +} + +static mrb_value +cg_cRect_get_width(mrb_state *mrb, mrb_value self) +{ + struct cg_rect *ptr; + + ptr = (struct cg_rect *)DATA_PTR(self); + + return mrb_int_value(mrb, ptr->rect.w); +} + +static mrb_value +cg_cRect_set_width(mrb_state *mrb, mrb_value self) +{ + mrb_int width; + struct cg_rect *ptr; + + mrb_get_args(mrb, "i", &width); + ptr = (struct cg_rect *)DATA_PTR(self); + + ptr->rect.w = width; + + return mrb_int_value(mrb, ptr->rect.w); +} + +static mrb_value +cg_cRect_get_height(mrb_state *mrb, mrb_value self) +{ + struct cg_rect *ptr; + + ptr = (struct cg_rect *)DATA_PTR(self); + + return mrb_int_value(mrb, ptr->rect.h); +} + +static mrb_value +cg_cRect_set_height(mrb_state *mrb, mrb_value self) +{ + mrb_int height; + struct cg_rect *ptr; + + mrb_get_args(mrb, "i", &height); + ptr = (struct cg_rect *)DATA_PTR(self); + + ptr->rect.h = height; + + return mrb_int_value(mrb, ptr->rect.h); +} + +static mrb_value +cg_cRect_draw_lines(mrb_state *mrb, mrb_value self) +{ + struct cg_rect *ptr; + ptr = (struct cg_rect *)DATA_PTR(self); + SDL_RenderDrawRect(cg_core.renderer, &ptr->rect); + + return self; +} + +static mrb_value +cg_cRect_draw_fill(mrb_state *mrb, mrb_value self) +{ + struct cg_rect *ptr; + ptr = (struct cg_rect *)DATA_PTR(self); + SDL_RenderFillRect(cg_core.renderer, &ptr->rect); + + return self; +} + +static mrb_value +cg_cRect_collide(mrb_state *mrb, mrb_value self) +{ + struct cg_rect *ptr, *that; + + mrb_get_args(mrb, "d", &that, &cg_rect_type); + ptr = (struct cg_rect *)DATA_PTR(self); + + return mrb_bool_value( + ptr->rect.x <= that->rect.x + that->rect.w && + ptr->rect.x + ptr->rect.w >= that->rect.x && + ptr->rect.y <= that->rect.y + that->rect.h && + ptr->rect.y + ptr->rect.h >= that->rect.y); +} + +void +cg_rect_init(mrb_state *mrb) +{ + struct RClass *cg_m, *cg_cRect; + + cg_m = mrb_module_get(mrb, "CandyGear"); + cg_cRect = mrb_define_class_under(mrb, cg_m, "Rect", mrb->object_class); + MRB_SET_INSTANCE_TT(cg_cRect, MRB_TT_DATA); + mrb_define_method( + mrb, cg_cRect, "initialize", cg_cRect_initialize, MRB_ARGS_REQ(4)); + mrb_define_method(mrb, cg_cRect, "x", cg_cRect_get_x, MRB_ARGS_NONE()); + mrb_define_method(mrb, cg_cRect, "x=", cg_cRect_set_x, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, cg_cRect, "y", cg_cRect_get_y, MRB_ARGS_NONE()); + mrb_define_method(mrb, cg_cRect, "y=", cg_cRect_set_y, MRB_ARGS_REQ(1)); + mrb_define_method( + mrb, cg_cRect, "width", cg_cRect_get_width, MRB_ARGS_NONE()); + mrb_define_method( + mrb, cg_cRect, "width=", cg_cRect_set_width, MRB_ARGS_REQ(1)); + mrb_define_method( + mrb, cg_cRect, "height", cg_cRect_get_height, MRB_ARGS_NONE()); + mrb_define_method( + mrb, cg_cRect, "height=", cg_cRect_set_height, MRB_ARGS_REQ(1)); + mrb_define_method( + mrb, cg_cRect, "draw_lines", cg_cRect_draw_lines, MRB_ARGS_NONE()); + mrb_define_method( + mrb, cg_cRect, "draw_fill", cg_cRect_draw_fill, MRB_ARGS_NONE()); + mrb_define_method( + mrb, cg_cRect, "collide?", cg_cRect_collide, MRB_ARGS_REQ(1)); +} diff --git a/src/rect.h b/src/rect.h new file mode 100644 index 0000000..64a7ba0 --- /dev/null +++ b/src/rect.h @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_RECT_H +#define CANDY_GEAR_RECT_H 1 + +#include "core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct cg_rect +{ + SDL_Rect rect; +}; + +extern const struct mrb_data_type cg_rect_type; + +void +cg_rect_init(mrb_state *mrb); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_RECT_H */ diff --git a/src/sound.c b/src/sound.c new file mode 100644 index 0000000..8ca009c --- /dev/null +++ b/src/sound.c @@ -0,0 +1,73 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sound.h" + +void +cg_free_sound(mrb_state *mrb, void* obj) +{ + struct cg_sound *ptr = obj; + + Mix_FreeChunk(ptr->chunk); + mrb_free(mrb, ptr); +} + +const struct mrb_data_type cg_sound_type = { + "CG_Sound", cg_free_sound }; + +static mrb_value +cg_cSound_initialize(mrb_state *mrb, mrb_value self) +{ + const char *file_path; + + struct cg_sound *ptr; + + mrb_get_args(mrb, "z", &file_path); + ptr = (struct cg_sound *)DATA_PTR(self); + if(ptr) mrb_free(mrb, ptr); + ptr = (struct cg_sound *)mrb_malloc(mrb, sizeof(struct cg_sound)); + + ptr->chunk = Mix_LoadWAV(file_path); + if(ptr->chunk == NULL) mrb_raise(mrb, E_RUNTIME_ERROR, Mix_GetError()); + + mrb_data_init(self, ptr, &cg_sound_type); + return self; +} + +static mrb_value +cg_cSound_play(mrb_state *mrb, mrb_value self) +{ + struct cg_sound *ptr; + + ptr = (struct cg_sound *)DATA_PTR(self); + + Mix_PlayChannel(-1, ptr->chunk, 0); + + return self; +} + +void +cg_sound_init(mrb_state *mrb) +{ + struct RClass *cg_m, *cg_cSound; + + cg_m = mrb_module_get(mrb, "CandyGear"); + cg_cSound = mrb_define_class_under(mrb, cg_m, "Sound", mrb->object_class); + MRB_SET_INSTANCE_TT(cg_cSound, MRB_TT_DATA); + mrb_define_method( + mrb, cg_cSound, "initialize", cg_cSound_initialize, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, cg_cSound, "play", cg_cSound_play, MRB_ARGS_NONE()); +} diff --git a/src/sound.h b/src/sound.h new file mode 100644 index 0000000..f2cd2e7 --- /dev/null +++ b/src/sound.h @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_SOUND_H +#define CANDY_GEAR_SOUND_H 1 + +#include "core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct cg_sound +{ + Mix_Chunk *chunk; +}; + +extern const struct mrb_data_type cg_sound_type; + +void +cg_sound_init(mrb_state *mrb); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_SOUND_H */ diff --git a/src/sprite.c b/src/sprite.c new file mode 100644 index 0000000..b8495d4 --- /dev/null +++ b/src/sprite.c @@ -0,0 +1,198 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sprite.h" +#include "sprite_implementation.h" + +#include + +#include "point.h" +#include "texture.h" + +// This variable works as a constant +static mrb_sym id_at_texture; + +void +cg_free_sprite(mrb_state *mrb, void* obj) +{ + struct cg_sprite *ptr = obj; + + mrb_free(mrb, ptr); +} + +const struct mrb_data_type cg_sprite_type = { + "CG_Sprite", cg_free_sprite }; + +static mrb_value +cg_cSrpite_load_yaml(mrb_state *mrb, mrb_value klass) +{ + const char *file_path; + struct cg_SpriteArray_s sprite_ary; + mrb_value hash, texture; + + mrb_get_args(mrb, "zo", &file_path, &texture); + + if(!cg_SpriteArray_constructor(&sprite_ary, file_path)) + mrb_raise(mrb, E_RUNTIME_ERROR, "failed to open sprites"); + + hash = mrb_hash_new_capa(mrb, sprite_ary.len); + + for(int i = 0; i < sprite_ary.len; i++) + { + mrb_hash_set( + mrb, hash, + mrb_symbol_value(mrb_intern_cstr(mrb, sprite_ary.sprites[i].name)), + mrb_funcall( + mrb, klass, "new", 5, + texture, + mrb_int_value(mrb, sprite_ary.sprites[i].x), + mrb_int_value(mrb, sprite_ary.sprites[i].y), + mrb_int_value(mrb, sprite_ary.sprites[i].width), + mrb_int_value(mrb, sprite_ary.sprites[i].height))); + } + + cg_SpriteArray_destructor(&sprite_ary); + + return hash; +} + +static mrb_value +cg_cSprite_initialize(mrb_state *mrb, mrb_value self) +{ + mrb_value texture_v; + mrb_int x, y, width, height; + struct cg_sprite *ptr; + + mrb_get_args(mrb, "oiiii", &texture_v, &x, &y, &width, &height); + ptr = (struct cg_sprite *)DATA_PTR(self); + if(ptr) mrb_free(mrb, ptr); + ptr = (struct cg_sprite *)mrb_malloc(mrb, sizeof(struct cg_sprite)); + + mrb_iv_set(mrb, self, id_at_texture, texture_v); + ptr->rect.x = x; + ptr->rect.y = y; + ptr->rect.w = width; + ptr->rect.h = height; + + mrb_data_init(self, ptr, &cg_sprite_type); + return self; +} + +static mrb_value +cg_cSprite_texture(mrb_state *mrb, mrb_value self) +{ + return mrb_iv_get(mrb, self, id_at_texture); +} + +static mrb_value +cg_cSprite_width(mrb_state *mrb, mrb_value self) +{ + struct cg_sprite *ptr; + + ptr = (struct cg_sprite *)DATA_PTR(self); + + return mrb_int_value(mrb, ptr->rect.w); +} + +static mrb_value +cg_cSprite_height(mrb_state *mrb, mrb_value self) +{ + struct cg_sprite *ptr; + + ptr = (struct cg_sprite *)DATA_PTR(self); + + return mrb_int_value(mrb, ptr->rect.h); +} + +static mrb_value +cg_cSprite_draw(mrb_state *mrb, mrb_value self) +{ + SDL_Rect dst_rect; + + mrb_value texture_v; + + struct cg_sprite *ptr; + struct cg_texture *texture_ptr; + struct cg_point *point; + + mrb_get_args(mrb, "d", &point, &cg_point_type); + + ptr = (struct cg_sprite *)DATA_PTR(self); + texture_v = mrb_iv_get(mrb, self, id_at_texture); + texture_ptr = (struct cg_texture *)DATA_PTR(texture_v); + + dst_rect.x = point->data.x; + dst_rect.y = point->data.y; + dst_rect.w = ptr->rect.w; + dst_rect.h = ptr->rect.h; + + SDL_RenderCopy(cg_core.renderer, texture_ptr->data, &ptr->rect, &dst_rect); + + return self; +} + +static mrb_value +cg_cSprite_draw_xy(mrb_state *mrb, mrb_value self) +{ + SDL_Rect dst_rect; + + mrb_value texture_v; + + mrb_int x, y; + struct cg_sprite *ptr; + struct cg_texture *texture_ptr; + + mrb_get_args(mrb, "ii", &x, &y); + + ptr = (struct cg_sprite *)DATA_PTR(self); + texture_v = mrb_iv_get(mrb, self, id_at_texture); + texture_ptr = (struct cg_texture *)DATA_PTR(texture_v); + + dst_rect.x = x; + dst_rect.y = y; + dst_rect.w = ptr->rect.w; + dst_rect.h = ptr->rect.h; + + SDL_RenderCopy(cg_core.renderer, texture_ptr->data, &ptr->rect, &dst_rect); + + return self; +} + +void +cg_sprite_init(mrb_state *mrb) +{ + struct RClass *cg_m, *cg_cSprite; + + id_at_texture = mrb_intern_lit(mrb, "@texture"); + + cg_m = mrb_module_get(mrb, "CandyGear"); + cg_cSprite = mrb_define_class_under(mrb, cg_m, "Sprite", mrb->object_class); + MRB_SET_INSTANCE_TT(cg_cSprite, MRB_TT_DATA); + mrb_define_class_method( + mrb, cg_cSprite, "load_yaml", cg_cSrpite_load_yaml, MRB_ARGS_REQ(2)); + mrb_define_method( + mrb, cg_cSprite, "initialize", cg_cSprite_initialize, MRB_ARGS_REQ(5)); + mrb_define_method( + mrb, cg_cSprite, "texture", cg_cSprite_texture, MRB_ARGS_NONE()); + mrb_define_method( + mrb, cg_cSprite, "width", cg_cSprite_width, MRB_ARGS_NONE()); + mrb_define_method( + mrb, cg_cSprite, "height", cg_cSprite_height, MRB_ARGS_NONE()); + mrb_define_method( + mrb, cg_cSprite, "draw", cg_cSprite_draw, MRB_ARGS_REQ(1)); + mrb_define_method( + mrb, cg_cSprite, "draw_xy", cg_cSprite_draw_xy, MRB_ARGS_REQ(2)); +} diff --git a/src/sprite.h b/src/sprite.h new file mode 100644 index 0000000..7a85418 --- /dev/null +++ b/src/sprite.h @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_SPRITE_H +#define CANDY_GEAR_SPRITE_H 1 + +#include "core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct cg_sprite +{ + SDL_Rect rect; +}; + +extern const struct mrb_data_type cg_sprite_type; + +void +cg_sprite_init(mrb_state *mrb); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_SPRITE_H */ diff --git a/src/sprite_implementation.cpp b/src/sprite_implementation.cpp new file mode 100644 index 0000000..4ba0edf --- /dev/null +++ b/src/sprite_implementation.cpp @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sprite_implementation.h" + +#include + +#include + +SDL_bool +cg_SpriteArray_constructor( + struct cg_SpriteArray_s *self, const char *file_path) +{ + YAML::Node root = YAML::LoadFile(file_path); + self->len = root.size(); + self->sprites = new cg_Sprite_s[self->len]; + + int index = 0; + for(YAML::const_iterator sprite_node = root.begin(); + sprite_node != root.end(); + sprite_node++) + { + cg_Sprite_s *sprite = &self->sprites[index]; + + auto node_name = sprite_node->first.as(); + sprite->name = new char[node_name.size() + 1]; + strcpy(sprite->name, node_name.c_str()); + + sprite->x = sprite_node->second["x"].as(); + sprite->y = sprite_node->second["y"].as(); + sprite->width = sprite_node->second["width"].as(); + sprite->height = sprite_node->second["height"].as(); + + index++; + } + + return SDL_TRUE; +} + +void +cg_SpriteArray_destructor(struct cg_SpriteArray_s *self) +{ + for(auto i = 0; i < self->len; i++) delete[] self->sprites[i].name; + delete[] self->sprites; +} diff --git a/src/sprite_implementation.h b/src/sprite_implementation.h new file mode 100644 index 0000000..c421188 --- /dev/null +++ b/src/sprite_implementation.h @@ -0,0 +1,49 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_SPRITE_IMPLEMENTATION_H +#define CANDY_GEAR_SPRITE_IMPLEMENTATION_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct cg_Sprite_s +{ + char *name; + int32_t x, y, width, height; +}; + +struct cg_SpriteArray_s +{ + int32_t len; + struct cg_Sprite_s *sprites; +}; + +SDL_bool +cg_SpriteArray_constructor( + struct cg_SpriteArray_s *self, const char *file_path); + +void +cg_SpriteArray_destructor(struct cg_SpriteArray_s *self); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_SPRITE_IMPLEMENTATION_H */ diff --git a/src/texture.c b/src/texture.c new file mode 100644 index 0000000..f363c1f --- /dev/null +++ b/src/texture.c @@ -0,0 +1,223 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "texture.h" + +#include "color.h" +#include "core.h" +#include "font.h" +#include "palette.h" +#include "pgm_image.h" +#include "point.h" + +void +cg_free_texture(mrb_state *mrb, void* obj) +{ + struct cg_texture *ptr = obj; + + SDL_DestroyTexture(ptr->data); + mrb_free(mrb, ptr); +} + +const struct mrb_data_type cg_texture_type = { + "CG_Texture", cg_free_texture }; + +static mrb_value +texture_alloc(mrb_state *mrb, mrb_value class) +{ + struct RClass *cg_cTexture = mrb_class_ptr(class); + enum mrb_vtype ttype = MRB_INSTANCE_TT(cg_cTexture); + + // I do not know if I need this. If things break, try uncomment this line. + // if (ttype == 0) ttype = MRB_TT_OBJECT; + return mrb_obj_value( + (struct RObject*)mrb_obj_alloc(mrb, ttype, cg_cTexture)); +} + +static mrb_value +cg_cTexture_from_image(mrb_state *mrb, mrb_value self) +{ + PGMImage pgm_img; + + struct mrb_value texture = texture_alloc(mrb, self); + const char *file_path; + struct cg_palette *palette; + struct cg_texture *ptr; + mrb_bool palette_given; + SDL_Surface *surface = NULL; + + mrb_get_args( + mrb, "z|d?", &file_path, &palette, &cg_palette_type, &palette_given); + ptr = (struct cg_texture *)DATA_PTR(texture); + if(ptr) mrb_free(mrb, ptr); + ptr = (struct cg_texture *)mrb_malloc(mrb, sizeof(struct cg_texture)); + if(palette_given) + { + if(!PGMImage_constructor(&pgm_img, file_path)) + mrb_raise(mrb, E_RUNTIME_ERROR, pgm_img.error); + + // Depth = 8 bits image. + // Pitch = 1 byte (8 bits) * image width. + surface = SDL_CreateRGBSurfaceWithFormatFrom( + pgm_img.data, pgm_img.width, pgm_img.height, 8, pgm_img.width, + SDL_PIXELFORMAT_INDEX8); + if(!surface) mrb_raise(mrb, E_RUNTIME_ERROR, SDL_GetError()); + SDL_SetPaletteColors(surface->format->palette, palette->colors, 0, 256); + } + else + { + surface = IMG_Load(file_path); + if(!surface) mrb_raise(mrb, E_RUNTIME_ERROR, IMG_GetError()); + } + + ptr->data = SDL_CreateTextureFromSurface(cg_core.renderer, surface); + SDL_SetTextureBlendMode(ptr->data, SDL_BLENDMODE_BLEND); + SDL_FreeSurface(surface); + if(palette_given) PGMImage_destructor(&pgm_img); + if(ptr->data == NULL) mrb_raise(mrb, E_RUNTIME_ERROR, SDL_GetError()); + + SDL_QueryTexture(ptr->data, NULL, NULL, &ptr->width, &ptr->height); + + mrb_data_init(texture, ptr, &cg_texture_type); + return texture; +} + +mrb_value +cg_cText_from_text(mrb_state *mrb, mrb_value self) +{ + char *text; + SDL_Surface *surface = NULL; + mrb_bool background; + struct mrb_value texture = texture_alloc(mrb, self); + struct cg_color *tx_color_ptr, *bg_color_ptr; + struct cg_font *font_ptr; + struct cg_texture *ptr; + + mrb_get_args( + mrb, "zdd|d?", &text, &font_ptr, &cg_font_type, + &tx_color_ptr, &cg_color_type, &bg_color_ptr, &cg_color_type, + &background); + + ptr = (struct cg_texture *)DATA_PTR(texture); + if(ptr) mrb_free(mrb, ptr); + ptr = (struct cg_texture *)mrb_malloc(mrb, sizeof(struct cg_texture)); + + // Create surface. + if(background) + surface = TTF_RenderUTF8_Shaded( + font_ptr->data, text, tx_color_ptr->data, bg_color_ptr->data); + else + surface = TTF_RenderUTF8_Solid(font_ptr->data, text, tx_color_ptr->data); + if(surface == NULL) mrb_raise(mrb, E_RUNTIME_ERROR, TTF_GetError()); + + // Convert the surface to a texture. + ptr->data = SDL_CreateTextureFromSurface(cg_core.renderer, surface); + SDL_FreeSurface(surface); + if(ptr->data == NULL) mrb_raise(mrb, E_RUNTIME_ERROR, SDL_GetError()); + + SDL_QueryTexture(ptr->data, NULL, NULL, &ptr->width, &ptr->height); + mrb_data_init(texture, ptr, &cg_texture_type); + return texture; +} + +static mrb_value +cg_cTexture_width(mrb_state *mrb, mrb_value self) +{ + struct cg_texture *ptr; + + ptr = (struct cg_texture *)DATA_PTR(self); + + return mrb_int_value(mrb, ptr->width); +} + +static mrb_value +cg_cTexture_height(mrb_state *mrb, mrb_value self) +{ + struct cg_texture *ptr; + + ptr = (struct cg_texture *)DATA_PTR(self); + + return mrb_int_value(mrb, ptr->height); +} + +static mrb_value +cg_cTexture_draw(mrb_state *mrb, mrb_value self) +{ + SDL_Rect dst_rect; + + struct cg_texture *ptr; + struct cg_point *point; + + mrb_get_args(mrb, "d", &point, &cg_point_type); + ptr = (struct cg_texture *)DATA_PTR(self); + + dst_rect.x = point->data.x; + dst_rect.y = point->data.y; + dst_rect.w = ptr->width; + dst_rect.h = ptr->height; + + SDL_RenderCopy(cg_core.renderer, ptr->data, NULL, &dst_rect); + + return self; +} + +static mrb_value +cg_cTexture_draw_xy(mrb_state *mrb, mrb_value self) +{ + SDL_Rect dst_rect; + + struct cg_texture *ptr; + mrb_int x, y; + + mrb_get_args(mrb, "ii", &x, &y); + ptr = (struct cg_texture *)DATA_PTR(self); + + dst_rect.x = x; + dst_rect.y = y; + dst_rect.w = ptr->width; + dst_rect.h = ptr->height; + + SDL_RenderCopy(cg_core.renderer, ptr->data, NULL, &dst_rect); + + return self; +} + +void +cg_texture_init(mrb_state *mrb) +{ + struct RClass *cg_m, *cg_cTexture; + + cg_m = mrb_module_get(mrb, "CandyGear"); + cg_cTexture = mrb_define_class_under( + mrb, cg_m, "Texture", mrb->object_class); + MRB_SET_INSTANCE_TT(cg_cTexture, MRB_TT_DATA); + + mrb_undef_class_method(mrb, cg_cTexture, "new"); + mrb_define_class_method( + mrb, cg_cTexture, "from_image", cg_cTexture_from_image, + MRB_ARGS_REQ(1) | MRB_ARGS_OPT(1)); + mrb_define_class_method( + mrb, cg_cTexture, "from_text", cg_cText_from_text, + MRB_ARGS_REQ(3)|MRB_ARGS_OPT(1)); + mrb_define_method( + mrb, cg_cTexture, "width", cg_cTexture_width, MRB_ARGS_NONE()); + mrb_define_method( + mrb, cg_cTexture, "height", cg_cTexture_height, MRB_ARGS_NONE()); + mrb_define_method( + mrb, cg_cTexture, "draw", cg_cTexture_draw, MRB_ARGS_REQ(1)); + mrb_define_method( + mrb, cg_cTexture, "draw_xy", cg_cTexture_draw_xy, MRB_ARGS_REQ(2)); +} diff --git a/src/texture.h b/src/texture.h new file mode 100644 index 0000000..f89542f --- /dev/null +++ b/src/texture.h @@ -0,0 +1,41 @@ +/* + * Copyright 2022 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_TEXTURE_H +#define CANDY_GEAR_TEXTURE_H 1 + +#include "core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct cg_texture +{ + SDL_Texture *data; + int width, height; +}; + +extern const struct mrb_data_type cg_texture_type; + +void +cg_texture_init(mrb_state *mrb); + +#ifdef __cplusplus +}; +#endif + +#endif /* CANDY_GEAR_TEXTURE_H */ -- cgit v1.2.3