big refactor

This commit is contained in:
2025-04-11 22:01:30 +02:00
parent 68412a7d7a
commit 928842c4c2
42 changed files with 1429 additions and 3312 deletions
-1
View File
@@ -1,3 +1,2 @@
bin/
.vscode/
.DS_STORE/
+27
View File
@@ -0,0 +1,27 @@
{
"configurations": [
{
"name": "macOS",
"includePath": [
"${workspaceFolder}/include",
"${workspaceFolder}/external/engine",
"${workspaceFolder}/external/raygui",
"${workspaceFolder}/external/rini",
"/opt/homebrew/Cellar/raylib/5.5/include"
],
"cStandard": "c99",
},
{
"name": "Windows",
"includePath": [
"${workspaceFolder}/include",
"${workspaceFolder}/external/engine",
"${workspaceFolder}/external/raygui",
"${workspaceFolder}/external/rini",
"C:/raylib/include"
],
"cStandard": "c99",
}
],
"version": 4
}
+29 -2
View File
@@ -1,5 +1,32 @@
COMPILER = gcc
C_FLAGS = -std=c99 -g -Wall -Werror -Wpedantic -Wshadow -Wstrict-prototypes -Wno-unknown-pragmas
C_FLAG_STD_C99 = -std=c99 # Use the C99 language standard
C_FLAG_DEBUG_INFO = -g # Include debug information
# C_FLAG_HEADERS = -H # Prints paths of all headers
C_FLAG_ALL_WARNINGS = -Wall # Enable most common warning
C_FLAG_PEDANTIC = -Wpedantic # Enforce ISO C standard strictly
C_FLAG_SHADOW = -Wshadow # Warn if a local variable shadows another
C_FLAG_STRICT_PROTOS = -Wstrict-prototypes # Warn if function declarations are missing parameter types
C_FLAG_NO_UNKNOWN_PRAGMAS = -Wno-unknown-pragmas # Suppress warnings about unknown pragmas
C_FLAGS = \
$(C_FLAG_STD_C99) \
$(C_FLAG_DEBUG_INFO) \
$(C_FLAG_HEADERS) \
$(C_FLAG_ALL_WARNINGS) \
$(C_FLAG_PEDANTIC) \
$(C_FLAG_SHADOW) \
$(C_FLAG_STRICT_PROTOS) \
$(C_FLAG_NO_UNKNOWN_PRAGMAS)
INCLUDE_FLAGS = -Iinclude
EXTERNAL_ENGINE = -Iexternal/engine
EXTERNAL_RAYGUI = -Iexternal/raygui
EXTERNAL_RINI = -Iexternal/rini
EXTERNAL_FLAGS = \
$(EXTERNAL_ENGINE) \
$(EXTERNAL_RINI) \
$(EXTERNAL_RAYGUI)
ifeq ($(OS), Windows_NT)
SRC = src\main.c
@@ -14,7 +41,7 @@ endif
$(shell mkdir -p bin/osx bin/win)
build: $(SRC)
$(COMPILER) $(SRC) -o $(TARGET) $(C_FLAGS) $(RAYLIB_FLAGS)
$(COMPILER) $(SRC) -o $(TARGET) $(C_FLAGS) $(INCLUDE_FLAGS) $(EXTERNAL_FLAGS) $(RAYLIB_FLAGS)
clean:
rm -f $(TARGET)
+9
View File
@@ -0,0 +1,9 @@
startScreen = 0 ; LOGO = 0, MENU = 1, GAMEPLAY = 2
display_title = The Last Hive
display_width = 1280 ; in Pixel
display_height = 720 ; in Pixel
showFPS = 1 ; OFF = 0, ON = 1
font_path = assets/SproutLandsUI/fonts/pixelFont-7-8x14-sproutLands.fnt
View File
+2
View File
@@ -1,6 +1,8 @@
#ifndef CAMERA_H
#define CAMERA_H
#include "input.h"
#include <raymath.h>
Camera2D CreateCamera(int screenWidth, int screenHeight, Vector2 playerPosition, float initZoom){
return (Camera2D){
+67
View File
@@ -0,0 +1,67 @@
#ifndef CONFIG_H
#define CONFIG_H
#define RINI_IMPLEMENTATION
#define RINI_VALUE_DELIMITER '='
#include <rini.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char *path;
rini_config rini;
} Config;
void InitConfig(Config *config, const char* path) {
config->rini = rini_load_config(path);
if (config->rini.count == 0) {
printf("Warning: Loaded config has no entries. Path: %s\n", config->path);
}
}
int GetConfigInt(Config *config, const char *key) {
return rini_get_config_value_fallback(config->rini, key, 0);
}
int GetConfigIntOrDefault(Config *config, const char *key, int default_value) {
return rini_get_config_value_fallback(config->rini, key, default_value);
}
const char* GetConfigString(Config *config, const char *key) {
return rini_get_config_value_text(config->rini, key);
}
const char* GetConfigStringOrDefault(Config *config, const char *key, const char *default_value) {
return rini_get_config_value_text_fallback(config->rini, key, default_value);
}
const char* GetConfigDescription(Config *config, const char *key) {
return rini_get_config_value_description(config->rini, key);
}
int SetConfigInt(Config *config, const char *key, int value, const char *desc) {
return rini_set_config_value(&config->rini, key, value, desc);
}
int SetConfigString(Config *config, const char *key, const char *text, const char *desc) {
return rini_set_config_value_text(&config->rini, key, text, desc);
}
int SetConfigDescription(Config *config, const char *key, const char *desc) {
return rini_set_config_value_description(&config->rini, key, desc);
}
int AddCommentLine(Config *config, const char *comment) {
return rini_set_config_comment_line(&config->rini, comment);
}
void SaveConfig(Config *config) {
rini_save_config(config->rini, config->path);
}
void CleanUpConfig(Config *config) {
rini_unload_config(&config->rini);
}
#endif // CONFIG_H
+1 -1
View File
@@ -4,7 +4,7 @@
typedef struct {
int width;
int height;
const char* name;
const char* title;
} Display;
#endif // DISPLAY_H
+1 -1
View File
@@ -2,7 +2,7 @@
#define GUI_H
#define RAYGUI_IMPLEMENTATION
#include "../../include/raygui.h"
#include <raygui.h>
typedef struct
{
+2
View File
@@ -1,6 +1,8 @@
#ifndef INPUT_H
#define INPUT_H
#include <raymath.h>
typedef struct {
Vector2 screenPosition;
Vector2 worldPosition;
View File
+14 -6
View File
@@ -1,30 +1,38 @@
#ifndef PHYSICS_H
#define PHYSICS_H
typedef struct {
#include <raymath.h>
typedef struct
{
Vector2 position;
Vector2 direction;
Vector2 velocity;
float speed;
} Physics;
void UpdatePhysicsDirection(Vector2 *direction, Vector2 vector){
void UpdatePhysicsDirection(Vector2 *direction, Vector2 vector)
{
*direction = Vector2Normalize(vector);
}
void UpdatePhysicsVelocity(Vector2 *velocity, Vector2 *direction, float *speed){
void UpdatePhysicsVelocity(Vector2 *velocity, Vector2 *direction, float *speed)
{
*velocity = Vector2Scale(*direction, *speed * GetFrameTime());
}
void UpdatePhysicsPosition(Vector2 *position, Vector2 *velocity) {
void UpdatePhysicsPosition(Vector2 *position, Vector2 *velocity)
{
*position = Vector2Add(*position, *velocity);
}
void UpdatePhysicsSpeed(float *speed, float *newSpeed){
void UpdatePhysicsSpeed(float *speed, float *newSpeed)
{
*speed = *newSpeed;
}
void UpdatePhysics(Physics *physics, Vector2 direction){
void UpdatePhysics(Physics *physics, Vector2 direction)
{
UpdatePhysicsDirection(&physics->direction, direction);
UpdatePhysicsVelocity(&physics->velocity, &physics->direction, &physics->speed);
UpdatePhysicsPosition(&physics->position, &physics->velocity);
View File
+29 -21
View File
@@ -1,46 +1,54 @@
#ifndef SPRITE_H
#define SPRITE_H
typedef struct {
Texture2D texture;
Rectangle sourceRec;
Rectangle destRec;
Vector2 frameSize;
Vector2 origin;
float rotation;
#include <raymath.h>
typedef struct
{
Texture2D texture;
Rectangle sourceRec;
Rectangle destRec;
Vector2 frameSize;
Vector2 origin;
float rotation;
Color color;
} Sprite;
void UpdateSpriteSourceRec(Sprite *sprite, const Vector2 *vector){
void UpdateSpriteSourceRec(Sprite *sprite, const Vector2 *vector)
{
sprite->sourceRec.x = vector->x;
sprite->sourceRec.y = vector->y;
}
void UpdateSpriteDestRec(Sprite *sprite, const Vector2 *vector){
void UpdateSpriteDestRec(Sprite *sprite, const Vector2 *vector)
{
sprite->destRec.x = vector->x;
sprite->destRec.y = vector->y;
}
void UpdateSpriteRotation(Sprite *sprite, Vector2 *vector){
if(Vector2Length(*vector) <= 0.0f){
void UpdateSpriteRotation(Sprite *sprite, Vector2 *vector)
{
if (Vector2Length(*vector) <= 0.0f)
{
return;
}
float targetRotation = (float)atan2(vector->y, vector->x) * (180.0f / PI) + 90.0f;
sprite->rotation = Lerp(sprite->rotation, targetRotation, 0.1f);
sprite->rotation = Lerp(sprite->rotation, targetRotation, 0.1f);
}
void RenderSprite(const Sprite *sprite){
void RenderSprite(const Sprite *sprite)
{
DrawTexturePro(
sprite->texture,
sprite->sourceRec,
sprite->destRec,
sprite->origin,
sprite->rotation,
sprite->color
);
sprite->texture,
sprite->sourceRec,
sprite->destRec,
sprite->origin,
sprite->rotation,
sprite->color);
}
void PrintSprite(const Sprite *sprite) {
void PrintSprite(const Sprite *sprite)
{
printf("Sprite:\n");
printf(" Sprite.texture: id = %d\n", sprite->texture.id);
printf(" Sprite.sourceRec: x = %f, y = %f, width = %f, height = %f\n", sprite->sourceRec.x, sprite->sourceRec.y, sprite->sourceRec.width, sprite->sourceRec.height);
View File
View File
+842
View File
@@ -0,0 +1,842 @@
/**********************************************************************************************
*
* rini v1.0 - A simple and easy-to-use config init files reader and writer
*
* DESCRIPTION:
* Load and save config init properties
*
* FEATURES:
* - Config files reading and writing
* - Supported value types: int, string
* - Support comment lines and empty lines
* - Support custom line comment delimiter
* - Support custom value delimiters
* - Support value description comments
* - Support custom description custom delimiter
* - Support multi-word text values w/o quote delimiters
* - Minimal C standard lib dependency (optional)
* - Customizable maximum config values capacity
*
* LIMITATIONS:
* - Config [sections] not supported
* - Saving config file requires complete rewrite
*
* POSSIBLE IMPROVEMENTS:
* - Support config [sections]
* - Support disabled entries recognition
*
* CONFIGURATION:
* #define RINI_IMPLEMENTATION
* Generates the implementation of the library into the included file.
* If not defined, the library is in header only mode and can be included in other headers
* or source files without problems. But only ONE file should hold the implementation.
*
* #define RINI_MAX_LINE_SIZE
* Defines the maximum size of line buffer to read from file.
* Default value: 512 bytes (considering [key + text + desc] is 256 max size by default)
*
* #define RINI_MAX_KEY_SIZE
* Defines the maximum size of value key
* Default value: 64 bytes
*
* #define RINI_MAX_TEXT_SIZE
* Defines the maximum size of value text
* Default value: 128 bytes
*
* #define RINI_MAX_DESC_SIZE
* Defines the maximum size of value description
* Default value: 128 bytes
*
* #define RINI_MAX_VALUE_CAPACITY
* Defines the maximum number of values supported
* Default value: 128 entries support
*
* #define RINI_LINE_COMMENT_DELIMITER
* Define character used to comment lines, placed at beginning of line
* Most .ini files use semicolon ';' but '#' is also used
* Default value: '#'
*
* #define RINI_LINE_SECTION_DELIMITER
* Defines section lines start character
* Sections loading is not supported, lines are just skipped for now
* Default value: '['
*
* #define RINI_VALUE_DELIMITER
* Defines a key value delimiter, in case it is defined.
* Most .ini files use '=' as value delimiter but ':' is also used or just whitespace
* Default value: ' '
*
* #define RINI_VALUE_QUOTATION_MARKS
* Defines quotation marks to be used around text values
* Text values are determined checking text with atoi(), only for integer values,
* in case of float values they are always considered as text
* Default value: '\"'
*
* #define RINI_DESCRIPTION_DELIMITER
* Defines a property line-end comment delimiter
* This implementation allows adding inline comments after the value.
* Default value: '#'
*
* DEPENDENCIES: C standard library:
* - stdio.h: fopen(), feof(), fgets(), fclose(), fprintf()
* - stdlib.h: malloc(), calloc(), free()
* - string.h: memset(), memcpy(), strcmp(), strlen()
*
* VERSIONS HISTORY:
* 2.0 (26-Jan-2023) ADDED: Support custom comment lines (as config entries)
* ADDED: Use of quotation-marks and marks customization
* ADDED: rini_set_config_comment_line()
* ADDED: rini_get_config_value_fallback(), with fallback return value
* ADDED: rini_get_config_text_fallback(), with fallback return value
* REMOVED: Config header requirement, use comments entries
* REVIEWED: Some configuration default values, capacities
* REVIEWED: rini_save_config(), removed parameter
* REVIEWED: rini_save_config_from_memory(), removed parameter
*
* 1.0 (18-May-2023) First release, basic read/write functionality
*
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2023-2025 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose, including commercial
* applications, and to alter it and redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not claim that you
* wrote the original software. If you use this software in a product, an acknowledgment
* in the product documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
* as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
**********************************************************************************************/
#ifndef RINI_H
#define RINI_H
#define RINI_VERSION "1.0"
// Function specifiers in case library is build/used as a shared library (Windows)
// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
#if defined(_WIN32)
#if defined(BUILD_LIBTYPE_SHARED)
#define RINIAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll)
#elif defined(USE_LIBTYPE_SHARED)
#define RINIAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll)
#endif
#endif
// Function specifiers definition
#ifndef RINIAPI
#define RINIAPI // Functions defined as 'extern' by default (implicit specifiers)
#endif
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
// Allow custom memory allocators
#ifndef RINI_MALLOC
#define RINI_MALLOC(sz) malloc(sz)
#endif
#ifndef RINI_CALLOC
#define RINI_CALLOC(n,sz) calloc(n,sz)
#endif
#ifndef RINI_FREE
#define RINI_FREE(p) free(p)
#endif
// Simple log system to avoid printf() calls if required
// NOTE: Avoiding those calls, also avoids const strings memory usage
#define RINI_SUPPORT_LOG_INFO
#if defined(RINI_SUPPORT_LOG_INFO)
#define RINI_LOG(...) printf(__VA_ARGS__)
#else
#define RINI_LOG(...)
#endif
#if !defined(RINI_MAX_LINE_SIZE)
#define RINI_MAX_LINE_SIZE 512
#endif
#if !defined(RINI_MAX_KEY_SIZE)
#define RINI_MAX_KEY_SIZE 64
#endif
#if !defined(RINI_MAX_TEXT_SIZE)
#define RINI_MAX_TEXT_SIZE 128
#endif
#if !defined(RINI_MAX_DESC_SIZE)
#define RINI_MAX_DESC_SIZE 128
#endif
#if !defined(RINI_MAX_VALUE_CAPACITY)
#define RINI_MAX_VALUE_CAPACITY 128
#endif
// Line comment delimiter (starting string)
#if !defined(RINI_LINE_COMMENT_DELIMITER)
#define RINI_LINE_COMMENT_DELIMITER '#'
#endif
// Line section delimiter -NOT USED-
#if !defined(RINI_LINE_SECTION_DELIMITER)
#define RINI_LINE_SECTION_DELIMITER '['
#endif
// Value delimiter, separator between key and value
#if !defined(RINI_VALUE_DELIMITER)
#define RINI_VALUE_DELIMITER ' '
#endif
// Use quotation marks for text values
// NOTE: Integer values do not use quotation-marks
#define RINI_USE_TEXT_QUOTATION_MARKS 1
// Text value quoation marks
#if !defined(RINI_VALUE_QUOTATION_MARKS)
#define RINI_VALUE_QUOTATION_MARKS '\"'
#endif
// Description delimiter, separator between value and description
#if !defined(RINI_DESCRIPTION_DELIMITER)
#define RINI_DESCRIPTION_DELIMITER '#'
#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
// Config value entry
typedef struct {
char key[RINI_MAX_KEY_SIZE]; // Config value key identifier
char text[RINI_MAX_TEXT_SIZE]; // Config value text
char desc[RINI_MAX_DESC_SIZE]; // Config value description
} rini_config_value;
// Config data
typedef struct {
rini_config_value *values; // Config values array
unsigned int count; // Config values count
unsigned int capacity; // Config values capacity
} rini_config;
#if defined(__cplusplus)
extern "C" { // Prevents name mangling of functions
#endif
//------------------------------------------------------------------------------------
// Functions declaration
//------------------------------------------------------------------------------------
RINIAPI rini_config rini_load_config(const char *file_name); // Load config from file (*.ini) or create a new config object (pass NULL)
RINIAPI rini_config rini_load_config_from_memory(const char *text); // Load config from text buffer
RINIAPI void rini_save_config(rini_config config, const char *file_name); // Save config to file, with custom header
RINIAPI char *rini_save_config_to_memory(rini_config config); // Save config to text buffer ('\0' EOL)
RINIAPI void rini_unload_config(rini_config *config); // Unload config data from memory
RINIAPI int rini_get_config_value(rini_config config, const char *key); // Get config value int for provided key, returns 0 if not found
RINIAPI const char *rini_get_config_value_text(rini_config config, const char *key); // Get config value text for provided key
RINIAPI const char *rini_get_config_value_description(rini_config config, const char *key); // Get config value description for provided key
RINIAPI int rini_get_config_value_fallback(rini_config config, const char *key, int fallback); // Get config value for provided key with default value fallback if not found or not valid
RINIAPI const char *rini_get_config_value_text_fallback(rini_config config, const char *key, const char *fallback); // Get config value text for provided key with fallback if not found or not valid
RINIAPI int rini_set_config_comment_line(rini_config *config, const char *comment); // Set config comment line
// Set config value int/text and description for existing key or create a new entry
// NOTE: When setting a text value, if id does not exist, a new entry is automatically created
RINIAPI int rini_set_config_value(rini_config *config, const char *key, int value, const char *desc);
RINIAPI int rini_set_config_value_text(rini_config *config, const char *key, const char *text, const char *desc);
// Set config value description for existing key
// WARNING: Key must exist to add description, if a description exists, it is updated
RINIAPI int rini_set_config_value_description(rini_config *config, const char *key, const char *desc);
#ifdef __cplusplus
}
#endif
#endif // RINI_H
/***********************************************************************************
*
* RINI IMPLEMENTATION
*
************************************************************************************/
#if defined(RINI_IMPLEMENTATION)
#include <stdio.h> // Required for: fopen(), feof(), fgets(), fclose(), fprintf()
#include <stdlib.h> // Required for: malloc(), calloc(), free()
#include <string.h> // Required for: memset(), memcpy(), strcmp(), strlen()
//----------------------------------------------------------------------------------
// Defines and macros
//----------------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------------
// Global variables definition
//----------------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------------
// Module internal functions declaration
//----------------------------------------------------------------------------------
static int rini_read_config_key(const char *buffer, char *key); // Get key from a buffer line containing key-value-(description)
static int rini_read_config_value_text(const char *buffer, char *text, char *desc); // Get value text (and description) from a buffer line
static int rini_text_to_int(const char *text); // Convert text to int value (if possible), same as atoi()
//----------------------------------------------------------------------------------
// Module functions definition
//----------------------------------------------------------------------------------
// Load config from file (.ini)
rini_config rini_load_config(const char *file_name)
{
rini_config config = { 0 };
unsigned int value_counter = 0;
// Init config data to max capacity
config.capacity = RINI_MAX_VALUE_CAPACITY;
config.values = (rini_config_value *)RINI_CALLOC(RINI_MAX_VALUE_CAPACITY, sizeof(rini_config_value));
if (file_name != NULL)
{
FILE *rini_file = fopen(file_name, "rt");
if (rini_file != NULL)
{
char buffer[RINI_MAX_LINE_SIZE] = { 0 }; // Buffer to read every text line
// First pass to count valid config lines
while (fgets(buffer, RINI_MAX_LINE_SIZE, rini_file))
{
// WARNING: fgets() keeps line endings, doesn't have any special options for converting line endings,
// but on Windows, when reading file 'rt', line endings are converted from \r\n to just \n
// Skip commented lines and empty lines
// NOTE: We are also skipping sections delimiters
if ((buffer[0] != RINI_LINE_COMMENT_DELIMITER) &&
(buffer[0] != RINI_LINE_SECTION_DELIMITER) &&
(buffer[0] != '\n') && (buffer[0] != '\0')) value_counter++;
}
// WARNING: We can't store more values than its max capacity
config.count = (value_counter > RINI_MAX_VALUE_CAPACITY)? RINI_MAX_VALUE_CAPACITY : value_counter;
if (config.count > 0)
{
rewind(rini_file);
value_counter = 0;
// Second pass to read config data
while (fgets(buffer, RINI_MAX_LINE_SIZE, rini_file))
{
// WARNING: fgets() keeps line endings, doesn't have any special options for converting line endings,
// but on Windows, when reading file 'rt', line endings are converted from \r\n to just \n
// Skip commented lines and empty lines
if ((buffer[0] != RINI_LINE_COMMENT_DELIMITER) &&
(buffer[0] != RINI_LINE_SECTION_DELIMITER) &&
(buffer[0] != '\n') && (buffer[0] != '\0'))
{
// Get key identifier string
memset(config.values[value_counter].key, 0, RINI_MAX_KEY_SIZE);
rini_read_config_key(buffer, config.values[value_counter].key);
rini_read_config_value_text(buffer, config.values[value_counter].text, config.values[value_counter].desc);
value_counter++;
// Stop reading if first count reached to avoid overflow in case count == RINI_MAX_VALUE_CAPACITY
if (value_counter >= config.count) break;
}
}
}
fclose(rini_file);
}
}
return config;
}
// Load config from text buffer
// NOTE: Comments and empty lines are ignored
rini_config rini_load_config_from_memory(const char *text)
{
#define RINI_MAX_TEXT_LINES RINI_MAX_VALUE_CAPACITY*2 // Consider possible comments and empty lines
rini_config config = { 0 };
unsigned int value_counter = 0;
// Init config data to max capacity
config.capacity = RINI_MAX_VALUE_CAPACITY;
config.values = (rini_config_value *)RINI_CALLOC(RINI_MAX_VALUE_CAPACITY, sizeof(rini_config_value));
if (text != NULL)
{
// Split text by line-breaks
const char *lines[RINI_MAX_TEXT_LINES] = { 0 };
int textSize = (int)strlen(text);
lines[0] = text;
int line_counter = 1;
for (int i = 0, k = 1; (i < textSize) && (line_counter < RINI_MAX_TEXT_LINES); i++)
{
if (text[i] == '\n')
{
lines[k] = &text[i + 1]; // WARNING: next value is valid?
line_counter += 1;
k++;
}
}
// Count possible values in lines
for (int l = 0; l < line_counter; l++)
{
// Skip commented lines and empty lines
// NOTE: We are also skipping sections delimiters
if ((lines[l][0] != RINI_LINE_COMMENT_DELIMITER) &&
(lines[l][0] != RINI_LINE_SECTION_DELIMITER) &&
(lines[l][0] != '\n') && (lines[l][0] != '\0')) value_counter++;
}
// WARNING: We can't store more values than its max capacity
config.count = (value_counter > RINI_MAX_VALUE_CAPACITY)? RINI_MAX_VALUE_CAPACITY : value_counter;
// Process lines to get keys and values
if (config.count > 0)
{
value_counter = 0;
// Second pass to read config data
for (int l = 0; l < line_counter; l++)
{
// Skip commented lines and empty lines
if ((lines[l][0] != RINI_LINE_COMMENT_DELIMITER) &&
(lines[l][0] != RINI_LINE_SECTION_DELIMITER) &&
(lines[l][0] != '\n') && (lines[l][0] != '\0'))
{
// Get key identifier string
memset(config.values[value_counter].key, 0, RINI_MAX_KEY_SIZE);
rini_read_config_key(lines[l], config.values[value_counter].key);
rini_read_config_value_text(lines[l], config.values[value_counter].text, config.values[value_counter].desc);
value_counter++;
// Stop reading if first count reached to avoid overflow in case count == RINI_MAX_VALUE_CAPACITY
if (value_counter >= config.count) break;
}
}
}
}
return config;
}
// Save config to file (*.ini)
void rini_save_config(rini_config config, const char *file_name)
{
FILE *rini_file = fopen(file_name, "wt");
if (rini_file != NULL)
{
for (unsigned int i = 0; i < config.count; i++)
{
if ((config.values[i].key[0] == '\0') && (config.values[i].text[0] == RINI_LINE_COMMENT_DELIMITER))
{
if (config.values[i].desc[0] != '\0') fprintf(rini_file, "%c %s\n", RINI_LINE_COMMENT_DELIMITER, config.values[i].desc);
else fprintf(rini_file, "%c\n", RINI_LINE_COMMENT_DELIMITER);
}
else
{
#if RINI_USE_TEXT_QUOTATION_MARKS
// If text is not an integer value, append text quotation-marks
// TODO: Check also for float values? atof()
bool valueIsInt = false;
int value = atoi(config.values[i].text); // WARNING: Returns 0 if input not valid --> Check if (config.values[i].text len == 1 && config.values[i].text[0] == '0')
if (((value == 0) && (strlen(config.values[i].text) == 1) && (config.values[i].text[0] == '0')) || (value != 0)) valueIsInt = true;
if (valueIsInt)
{
fprintf(rini_file, "%-28s %c %6s %c %s\n", config.values[i].key, RINI_VALUE_DELIMITER,
config.values[i].text, RINI_DESCRIPTION_DELIMITER, config.values[i].desc);
}
else
{
fprintf(rini_file, "%-28s %c %c%6s%c %c %s\n", config.values[i].key, RINI_VALUE_DELIMITER,
RINI_VALUE_QUOTATION_MARKS, config.values[i].text, RINI_VALUE_QUOTATION_MARKS,
RINI_DESCRIPTION_DELIMITER, config.values[i].desc);
}
#else
fprintf(rini_file, "%-28s %c %6s %c %s\n", config.values[i].key, RINI_VALUE_DELIMITER,
config.values[i].text, RINI_DESCRIPTION_DELIMITER, config.values[i].desc);
#endif
}
}
fclose(rini_file);
}
}
// Save config to text buffer ('\0' EOL)
char *rini_save_config_to_memory(rini_config config)
{
#define RINI_MAX_TEXT_FILE_SIZE 4096
// Verify required config size is smaller than memory buffer size
// NOTE: We add 64 extra possible characters by entry line
int requiredSize = 0;
for (unsigned int i = 0; i < config.count; i++) requiredSize += ((int)strlen(config.values[i].key) + (int)strlen(config.values[i].text) + (int)strlen(config.values[i].desc) + 64);
if (requiredSize > RINI_MAX_TEXT_FILE_SIZE) RINI_LOG("WARNING: Required config.ini size is bigger than max supported memory size, increase RINI_MAX_TEXT_FILE_SIZE\n");
// NOTE: Using a static buffer to avoid de-allocation requirement on user side
static char text[RINI_MAX_TEXT_FILE_SIZE] = { 0 };
memset(text, 0, RINI_MAX_TEXT_FILE_SIZE);
int offset = 0;
for (unsigned int i = 0; i < config.count; i++)
{
if ((config.values[i].key[0] == '\0') && (config.values[i].text[0] == RINI_LINE_COMMENT_DELIMITER))
{
if (config.values[i].desc[0] != '\0') offset += sprintf(text + offset, "%c %s\n", RINI_LINE_COMMENT_DELIMITER, config.values[i].desc);
else offset += sprintf(text + offset, "%c\n", RINI_LINE_COMMENT_DELIMITER);
}
else
{
#if RINI_USE_TEXT_QUOTATION_MARKS
// If text is not an integer value, append text quotation-marks
// TODO: Check also for float values? atof()
bool valueIsInt = false;
int value = atoi(config.values[i].text); // WARNING: Returns 0 if input not valid --> Check if (config.values[i].text len == 1 && config.values[i].text[0] == '0')
if (((value == 0) && (strlen(config.values[i].text) == 1) && (config.values[i].text[0] == '0')) || (value != 0)) valueIsInt = true;
if (valueIsInt)
{
offset += sprintf(text + offset, "%-28s %c %6s %c %s\n", config.values[i].key, RINI_VALUE_DELIMITER,
config.values[i].text, RINI_DESCRIPTION_DELIMITER, config.values[i].desc);
}
else
{
offset += sprintf(text + offset, "%-28s %c %c%6s%c %c %s\n", config.values[i].key, RINI_VALUE_DELIMITER,
RINI_VALUE_QUOTATION_MARKS, config.values[i].text, RINI_VALUE_QUOTATION_MARKS,
RINI_DESCRIPTION_DELIMITER, config.values[i].desc);
}
#else
offset += sprintf(text + offset, "%-28s %c %6s %c %s\n", config.values[i].key, RINI_VALUE_DELIMITER,
config.values[i].text, RINI_DESCRIPTION_DELIMITER, config.values[i].desc);
#endif
}
}
return text;
}
// Unload config data
void rini_unload_config(rini_config *config)
{
RINI_FREE(config->values);
config->values = NULL;
config->count = 0;
config->capacity = 0;
}
// Get config value for provided key, returns 0 if not found or not valid
int rini_get_config_value(rini_config config, const char *key)
{
int value = 0;
for (unsigned int i = 0; i < config.count; i++)
{
if (strcmp(key, config.values[i].key) == 0) // Key found
{
value = rini_text_to_int(config.values[i].text);
break;
}
}
return value;
}
// Get config value for provided key with default value fallback if not found or not valid
int rini_get_config_value_fallback(rini_config config, const char *key, int fallback)
{
int value = fallback;
for (unsigned int i = 0; i < config.count; i++)
{
if (strcmp(key, config.values[i].key) == 0) // Key found
{
// TODO: Detect if conversion fails...
value = rini_text_to_int(config.values[i].text);
break;
}
}
return value;
}
// Get config text for string id
const char *rini_get_config_value_text(rini_config config, const char *key)
{
const char *text = NULL;
for (unsigned int i = 0; i < config.count; i++)
{
if (strcmp(key, config.values[i].key) == 0) // Key found
{
text = config.values[i].text;
break;
}
}
return text;
}
// Get config value text for provided key with fallback if not found or not valid
RINIAPI const char *rini_get_config_value_text_fallback(rini_config config, const char *key, const char *fallback)
{
const char *text = fallback;
for (unsigned int i = 0; i < config.count; i++)
{
if (strcmp(key, config.values[i].key) == 0) // Key found
{
text = config.values[i].text;
break;
}
}
return text;
}
// Get config description for string id
const char *rini_get_config_value_description(rini_config config, const char *key)
{
const char *desc = NULL;
for (unsigned int i = 0; i < config.count; i++)
{
if (strcmp(key, config.values[i].key) == 0) // Key found
{
desc = config.values[i].desc;
break;
}
}
return desc;
}
// Set config comment line
int rini_set_config_comment_line(rini_config *config, const char *comment)
{
int result = -1;
char text[2] = { RINI_LINE_COMMENT_DELIMITER, '\0' };
result = rini_set_config_value_text(config, NULL, text, comment);
return result;
}
// Set config value and description for existing key or create a new entry
int rini_set_config_value(rini_config *config, const char *key, int value, const char *desc)
{
int result = -1;
char value_text[RINI_MAX_TEXT_SIZE] = { 0 };
sprintf(value_text, "%i", value);
result = rini_set_config_value_text(config, key, value_text, desc);
return result;
}
// Set config value text and description for existing key or create a new entry
// NOTE: When setting a text value, if id does not exist, a new entry is automatically created
int rini_set_config_value_text(rini_config *config, const char *key, const char *text, const char *desc)
{
int result = -1;
if ((text == NULL) || (text[0] == '\0')) return result;
if (key != NULL)
{
// Try to find key and update text and description
for (unsigned int i = 0; i < config->count; i++)
{
if (strcmp(key, config->values[i].key) == 0) // Key found
{
memset(config->values[i].text, 0, RINI_MAX_TEXT_SIZE);
memcpy(config->values[i].text, text, strlen(text));
memset(config->values[i].desc, 0, RINI_MAX_DESC_SIZE);
if (desc != NULL) memcpy(config->values[i].desc, desc, strlen(desc));
result = 0;
break;
}
}
}
// Key not found, we add a new entry if possible
if (result == -1)
{
if (config->count < config->capacity)
{
// NOTE: Supporting comment line entries
if ((key == NULL) && (text[0] == RINI_LINE_COMMENT_DELIMITER))
{
config->values[config->count].key[0] = '\0';
config->values[config->count].text[0] = RINI_LINE_COMMENT_DELIMITER;
if (desc != NULL) for (int i = 0; (i < RINI_MAX_DESC_SIZE) && (desc[i] != '\0'); i++) config->values[config->count].desc[i] = desc[i];
else config->values[config->count].desc[0] = '\0';
}
else
{
// NOTE: We do a manual copy to avoid possible overflows on input data
for (int i = 0; (i < RINI_MAX_KEY_SIZE) && (key[i] != '\0'); i++) config->values[config->count].key[i] = key[i];
for (int i = 0; (i < RINI_MAX_TEXT_SIZE) && (text[i] != '\0'); i++) config->values[config->count].text[i] = text[i];
for (int i = 0; (i < RINI_MAX_DESC_SIZE) && (desc[i] != '\0'); i++) config->values[config->count].desc[i] = desc[i];
}
config->count++;
result = 0;
}
}
return result;
}
// Set config value description for existing key
// WARNING: Key must exist to add description, if a description exists, it is updated
int rini_set_config_value_description(rini_config *config, const char *key, const char *desc)
{
int result = 1;
for (unsigned int i = 0; i < config->count; i++)
{
if (strcmp(key, config->values[i].key) == 0) // Key found
{
memset(config->values[i].desc, 0, RINI_MAX_DESC_SIZE);
if (desc != NULL) memcpy(config->values[i].desc, desc, strlen(desc));
result = 0;
break;
}
}
return result;
}
//----------------------------------------------------------------------------------
// Module internal functions declaration
//----------------------------------------------------------------------------------
// Get string id from a buffer line containing id-value pair
static int rini_read_config_key(const char *buffer, char *key)
{
int len = 0;
while ((buffer[len] != '\0') && (buffer[len] != ' ') && (buffer[len] != RINI_VALUE_DELIMITER)) len++; // Skip keyentifier
memcpy(key, buffer, len);
return len;
}
// Get config string-value from a buffer line containing id-value pair
static int rini_read_config_value_text(const char *buffer, char *text, char *desc)
{
char *buffer_ptr = (char *)buffer;
// Expected config line structure:
// [key][spaces?][delimiter?][spaces?][quot-mark?][textValue][quot-mark?][spaces?][[;][#]description?]
// We need to skip spaces, check for delimiter (if required), skip spaces, and get text value
while ((buffer_ptr[0] != '\0') && (buffer_ptr[0] != ' ')) buffer_ptr++; // Skip keyentifier
while ((buffer_ptr[0] != '\0') && (buffer_ptr[0] == ' ')) buffer_ptr++; // Skip line spaces before text value or delimiter
#if defined(RINI_VALUE_DELIMITER)
if (buffer_ptr[0] == RINI_VALUE_DELIMITER)
{
buffer_ptr++; // Skip delimiter
while ((buffer_ptr[0] != '\0') && (buffer_ptr[0] == ' ')) buffer_ptr++; // Skip line spaces before text value
}
#endif
// Now buffer_ptr should be pointing to the start of value
int len = 0;
while ((buffer_ptr[len] != '\0') &&
(buffer_ptr[len] != '\r') &&
(buffer_ptr[len] != '\n')) len++; // Get text-value and description length (to the end of line)
// Now we got the length from text-value start to end of line
int value_len = len;
int desc_pos = 0;
#if defined(RINI_DESCRIPTION_DELIMITER)
// Scan text looking for text-value description (if used)
for (; desc_pos < len; desc_pos++)
{
if (buffer_ptr[desc_pos] == RINI_DESCRIPTION_DELIMITER)
{
value_len = desc_pos - 1;
while (buffer_ptr[value_len] == ' ') value_len--;
value_len++;
desc_pos++; // Skip delimiter and following spaces
while (buffer_ptr[desc_pos] == ' ') desc_pos++;
break;
}
}
#endif
#if RINI_USE_TEXT_QUOTATION_MARKS
// Remove starting quotation-mark from text (if being used)
if (buffer_ptr[0] == RINI_VALUE_QUOTATION_MARKS)
{
buffer_ptr++; desc_pos--; len--; value_len--;
// Remove ending quotation-mark from text (if being used)
if (buffer_ptr[value_len - 1] == RINI_VALUE_QUOTATION_MARKS) { value_len--; }
}
#endif
// Clear text buffers to be updated
memset(text, 0, RINI_MAX_TEXT_SIZE);
memset(desc, 0, RINI_MAX_DESC_SIZE);
// Copy value-text and description to provided pointers
memcpy(text, buffer_ptr, value_len);
memcpy(desc, buffer_ptr + desc_pos, ((len - desc_pos) > (RINI_MAX_DESC_SIZE - 1))? (RINI_MAX_DESC_SIZE - 1) : (len - desc_pos));
return len;
}
// Convert text to int value (if possible), same as atoi()
static int rini_text_to_int(const char *text)
{
int value = 0;
int sign = 1;
if ((text[0] == '+') || (text[0] == '-'))
{
if (text[0] == '-') sign = -1;
text++;
}
int i = 0;
for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0');
//if (strlen(text) != i) // Text starts with numbers but contains some text after that...
return value*sign;
}
#endif // RINI_IMPLEMENTATION
+73
View File
@@ -0,0 +1,73 @@
#ifndef APP_H
#define APP_H
#include <config.h>
#include <display.h>
#include "screen.h"
#include "applicationstate.h"
#include "font.h"
#include "logo.h"
#include "menu.h"
#include "game.h"
typedef struct {
Config config;
State state;
Display display;
Font font;
LogoState logo;
GameState game;
MenuState menu;
} Application;
void ConfigApp(Application* app){
InitConfig(&app->config, "config/config.ini");
app->state.currentScreen = GetConfigInt(&app->config, "startScreen");
app->state.running = true;
app->state.gameStarted = false;
app->state.flagFPS = GetConfigInt(&app->config, "showFPS");
app->display.title = GetConfigString(&app->config, "display_title");
app->display.width = GetConfigInt(&app->config, "display_width");
app->display.height = GetConfigInt(&app->config, "display_height");
}
void InitApp(Application *app){
InitFont(&app->font);
InitLogo(&app->logo);
InitMenu(&app->menu);
InitGame(&app->game,&app->display);
}
void UpdateApp(Application *app){
while (app->state.running)
{
switch (app->state.currentScreen)
{
case LOGO:
{
UpdateLogo(&app->logo, &app->state);
RenderLogo(&app->logo, &app->state);
}break;
case MENU:
{
UpdateMenu(&app->menu, &app->state);
RenderMenu(&app->menu, &app->state, &app->font, app->display.title);
}break;
case GAMEPLAY:
{
UpdateGame(&app->game, &app->state);
RenderGame(&app->game, &app->state);
}break;
default: break;
}
}
}
void CleanupApp(Application *app){
CleanupMenu(&app->menu);
CleanupGame(&app->game);
CleanupFont(&app->font);
}
#endif //APP_H
@@ -1,7 +1,7 @@
#ifndef BACKGROUND_H
#define BACKGROUND_H
#include "../engine/sprite.h"
#include "raylib.h"
#include <sprite.h>
#include <raylib.h>
typedef struct {
Sprite sprite;
+4 -4
View File
@@ -1,9 +1,9 @@
#ifndef BEE_H
#define BEE_H
#include "../engine/sprite.h"
#include "../engine/animation.h"
#include "../engine/physics.h"
#include "../engine/object.h"
#include <sprite.h>
#include <animation.h>
#include <physics.h>
#include <object.h>
typedef enum {
FLYING
+20 -18
View File
@@ -1,44 +1,46 @@
#ifndef Floor_H
#define Floor_H
#include "../engine/sprite.h"
#include <sprite.h>
typedef struct {
typedef struct
{
Sprite sprite;
Rectangle bounds;
} Floor;
Floor CreateFloor(Texture2D texture, float textureWidth){
Floor CreateFloor(Texture2D texture, float textureWidth)
{
return (Floor){
.sprite = {
.texture = texture,
.frameSize = {textureWidth,textureWidth},
.sourceRec = { 0.0f, 0.0f, textureWidth, textureWidth},
.frameSize = {textureWidth, textureWidth},
.sourceRec = {0.0f, 0.0f, textureWidth, textureWidth},
.destRec = {0.0f, 0.0f, textureWidth, textureWidth},
.origin = {textureWidth / 2, textureWidth / 2},
.rotation = 0.0f,
.color = WHITE
},
.bounds = {0.0f,0.0f,0.0f,0.0f}
};
.color = WHITE},
.bounds = {0.0f, 0.0f, 0.0f, 0.0f}};
}
void UpdateFloor(Floor *floor, const Camera2D *camera){
void UpdateFloor(Floor *floor, const Camera2D *camera)
{
floor->bounds.x = (int)((camera->target.x - (GetScreenWidth() / 2.0f / camera->zoom)) / floor->sprite.frameSize.x) - 1;
floor->bounds.width = (int)((camera->target.x + (GetScreenWidth() / 2.0f / camera->zoom)) / floor->sprite.frameSize.x) + 1;
floor->bounds.y = (int)((camera->target.y - (GetScreenHeight() / 2.0f / camera->zoom)) / floor->sprite.frameSize.y) - 1;
floor->bounds.height = (int)((camera->target.y + (GetScreenHeight() / 2.0f / camera->zoom)) / floor->sprite.frameSize.y) + 1;
}
void RenderFloor(Floor *floor){
void RenderFloor(Floor *floor)
{
for (int y = (int)floor->bounds.y; y <= (int)floor->bounds.height; y++)
{
for (int x = (int)floor->bounds.x; x <= (int)floor->bounds.width; x++)
{
for (int x = (int)floor->bounds.x; x <= (int)floor->bounds.width; x++)
{
Vector2 floorPosition = {x * floor->sprite.frameSize.x, y * floor->sprite.frameSize.y};
UpdateSpriteDestRec(&floor->sprite, &floorPosition);
RenderSprite(&floor->sprite);
}
Vector2 floorPosition = {x * floor->sprite.frameSize.x, y * floor->sprite.frameSize.y};
UpdateSpriteDestRec(&floor->sprite, &floorPosition);
RenderSprite(&floor->sprite);
}
}
}
#endif //Floor_H
#endif // Floor_H
+30
View File
@@ -0,0 +1,30 @@
#ifndef FLOWERS_H
#define FLOWERS_H
#include <object.h>
ObjectArray CreateFlowers(Texture2D texture, float frameWidth, float rotation, float fieldSize)
{
ObjectArray flowers = CreateObjectArray(&(Object){
.sprite = {
.texture = texture,
.sourceRec = {0.0f, 0.0f, frameWidth, frameWidth},
.destRec = {0.0f, 0.0f, frameWidth, frameWidth},
.origin = {frameWidth / 2, frameWidth / 2},
.rotation = rotation,
.color = WHITE},
.position = {0.0f, 0.0f}},
10);
if (!flowers.data)
{
printf("Memory allocation failed.\n");
exit(1);
}
SetRandomPositionForObjectArray(&flowers, (Rectangle){-fieldSize, -fieldSize, fieldSize * 2, fieldSize * 2});
return flowers;
}
#endif // FLOWERS_H
+13
View File
@@ -0,0 +1,13 @@
#ifndef FONT_H
#define FONT_H
void InitFont(Font *font){
*font = LoadFont("assets/SproutLandsUI/fonts/pixelFont-7-8x14-sproutLands.fnt");
SetTextureFilter(font->texture, TEXTURE_FILTER_POINT);
}
void CleanupFont(Font *font){
UnloadFont(*font);
}
#endif //FONT_H
+84 -47
View File
@@ -1,12 +1,14 @@
#ifndef GAME_H
#define GAME_H
#include "../engine/object.h"
#include "../engine/camera.h"
#include "../engine/input.h"
#include "../engine/postfx.h"
#include "../engine/soundtrack.h"
#include "../engine/asset.h"
#include "../engine/applicationstate.h"
#include <object.h>
#include <camera.h>
#include <input.h>
#include <postfx.h>
#include <soundtrack.h>
#include <asset.h>
#include "applicationstate.h"
#include "gameassets.h"
#include "floor.h"
#include "player.h"
@@ -14,7 +16,8 @@
#include "hive.h"
#include "flowers.h"
typedef struct {
typedef struct
{
Assets assets;
Texture2D textures[TEX_COUNT];
PostProcessing postFX[SHADER_COUNT];
@@ -30,52 +33,69 @@ typedef struct {
Image image;
} GameState;
void InitGame(GameState* g, Display *display){
void InitGame(GameState *g, Display *display)
{
g->assets = (Assets){
.textureAssets = texAssets,
.shaderAssets = shaderAssets,
.soundtrackAssets = soundtrackAssets
};
.soundtrackAssets = soundtrackAssets};
for (int i = 0; i < TEX_COUNT; i++){
for (int i = 0; i < TEX_COUNT; i++)
{
g->textures[i] = LoadTexture(g->assets.textureAssets[i].path);
}
for (int i = 0; i < SHADER_COUNT; i++){
for (int i = 0; i < SHADER_COUNT; i++)
{
g->postFX[i] = CreatePostFX(g->assets.shaderAssets[i].path);
}
for (int i = 0; i < SOUNDTRACK_COUNT; i++){
for (int i = 0; i < SOUNDTRACK_COUNT; i++)
{
g->soundtracks[i] = LoadSoundtrack(g->assets.soundtrackAssets[i].path);
}
// Target texture
g->target = LoadRenderTexture(display->width, display->height);
// Static Entities
g->flowers = CreateFlowers(g->textures[FLOWER], g->assets.textureAssets[FLOWER].frameWidth, g->assets.textureAssets[FLOWER].rotation, 50.0f);
g->flowers = CreateFlowers(g->textures[FLOWER], g->assets.textureAssets[FLOWER].frameWidth, g->assets.textureAssets[FLOWER].rotation, 50.0f);
g->floor = CreateFloor(g->textures[GRASS], g->assets.textureAssets[GRASS].frameWidth);
g->hive = CreateHive(g->textures[HIVE], g->assets.textureAssets[HIVE].frameWidth);
// Animated Entities
g->player = CreatePlayer(IDLE_DOWN, HOE, g->textures[PLAYER], g->assets.textureAssets[PLAYER].frameWidth, 4, 2, g->hive.position, g->assets.textureAssets[PLAYER].rotation, 50.0f);
g->bee1 = CreateBee(FLYING, g->textures[BEE], g->assets.textureAssets[BEE].frameWidth, 4, 10, g->hive.position, 10.0f, &g->flowers, &g->hive);
// I/O
g->camera = CreateCamera(display->width, display->height, g->player.physics.position, 10.0f);
g->mouse = CreateMouse(0.10f, 5.0f, 10.0f, &g->textures[CURSOR]);
g->mouse = CreateMouse(0.10f, 5.0f, 10.0f, &g->textures[CURSOR]);
}
void UpdateGame(GameState* g, State *state){
if (GetFPS() <= 60){ SetTargetFPS(0); }
if (IsKeyPressed(KEY_ESCAPE)){ state->currentScreen = MENU; }
if (WindowShouldClose() && !IsKeyPressed(KEY_ESCAPE)) { state->running = false; }
for (int i = 0; i < SHADER_COUNT; i++){ UpdatePostFX(&g->postFX[i]); }
for (int i = 0; i < SOUNDTRACK_COUNT; i++){ UpdateSoundtrack(&g->soundtracks[i]); }
void UpdateGame(GameState *g, State *state)
{
if (GetFPS() <= 60)
{
SetTargetFPS(0);
}
if (IsKeyPressed(KEY_ESCAPE))
{
state->currentScreen = MENU;
}
if (WindowShouldClose() && !IsKeyPressed(KEY_ESCAPE))
{
state->running = false;
}
for (int i = 0; i < SHADER_COUNT; i++)
{
UpdatePostFX(&g->postFX[i]);
}
for (int i = 0; i < SOUNDTRACK_COUNT; i++)
{
UpdateSoundtrack(&g->soundtracks[i]);
}
UpdateMouse(&g->mouse, &g->camera);
UpdateCamera2D(&g->camera, &g->player.physics.position, &g->mouse);
@@ -84,7 +104,8 @@ void UpdateGame(GameState* g, State *state){
UpdateBee(&g->bee1, &g->flowers, &g->hive);
}
void RenderComponents(GameState* g){
void RenderComponents(GameState *g)
{
RenderFloor(&g->floor);
RenderSprite(&g->hive.sprite);
RenderObjectArray(&g->flowers);
@@ -92,32 +113,48 @@ void RenderComponents(GameState* g){
RenderSprite(&g->bee1.sprite);
}
void RenderGame(GameState* g, const State *appState){
void RenderGame(GameState *g, const State *appState)
{
BeginTextureMode(g->target);
ClearBackground(BLACK);
BeginMode2D(g->camera);
RenderComponents(g);
EndMode2D();
DrawTextureRec(g->mouse.cursorTexture, (Rectangle){ 0, 0, g->mouse.cursorTexture.width, g->mouse.cursorTexture.height }, (Vector2){ g->mouse.screenPosition.x, g->mouse.screenPosition.y}, WHITE);
ClearBackground(BLACK);
BeginMode2D(g->camera);
RenderComponents(g);
EndMode2D();
DrawTextureRec(g->mouse.cursorTexture, (Rectangle){0, 0, g->mouse.cursorTexture.width, g->mouse.cursorTexture.height}, (Vector2){g->mouse.screenPosition.x, g->mouse.screenPosition.y}, WHITE);
EndTextureMode();
BeginDrawing();
ClearBackground(BLACK);
for (int i = 0; i < SHADER_COUNT; i++){
RenderPostFX(&g->postFX[i], &g->target);
}
DrawText( TextFormat("%d", g->player.inventar.seedCount), 10, 10, 50, WHITE);
if (appState->flagFPS){
DrawFPS(10, 10);
}
ClearBackground(BLACK);
for (int i = 0; i < SHADER_COUNT; i++)
{
RenderPostFX(&g->postFX[i], &g->target);
}
DrawText(TextFormat("%d", g->player.inventar.seedCount), 10, 10, 50, WHITE);
if (appState->flagFPS)
{
DrawFPS(10, 10);
}
EndDrawing();
}
void CleanupGame(GameState* g){
for (int i = 0; i < SHADER_COUNT; i++){ UnloadPostFX(&g->postFX[i]); }
for (int i = 0; i < TEX_COUNT; i++){ if (g->textures[i].id > 0) { UnloadTexture(g->textures[i]); } }
for (int i = 0; i < SOUNDTRACK_COUNT; i++){ UnloadMusicStream(g->soundtracks[i].music); }
void CleanupGame(GameState *g)
{
for (int i = 0; i < SHADER_COUNT; i++)
{
UnloadPostFX(&g->postFX[i]);
}
for (int i = 0; i < TEX_COUNT; i++)
{
if (g->textures[i].id > 0)
{
UnloadTexture(g->textures[i]);
}
}
for (int i = 0; i < SOUNDTRACK_COUNT; i++)
{
UnloadMusicStream(g->soundtracks[i].music);
}
UnloadRenderTexture(g->target);
free(g->flowers.data);
}
+10 -11
View File
@@ -1,10 +1,11 @@
#ifndef GAMEASSETS_H
#define GAMEASSETS_H
#include "../engine/asset.h"
#include <asset.h>
////////////////////////////////////////////////////////////////////////////
typedef enum {
typedef enum
{
PLAYER,
PLAYER_ACTIONS,
TOOLS,
@@ -24,31 +25,29 @@ const TextureAsset texAssets[TEX_COUNT] = {
{"assets/character_bee.png", 16.0f, 16.0f, 0.0f},
{"assets/grass.png", 16.0f, 16.0f, 0.0f},
{"assets/hive.png", 32.0f, 32.0f, 0.0f},
{"assets/SproutLandsUI/Spritesheets/Mouse sprites/TriangleMouseicon1.png", 16.0f, 16.0f, 0.0f }
};
{"assets/SproutLandsUI/Spritesheets/Mouse sprites/TriangleMouseicon1.png", 16.0f, 16.0f, 0.0f}};
////////////////////////////////////////////////////////////////////////////
typedef enum {
typedef enum
{
POST_FX,
SHADER_COUNT
} ShaderID;
const ShaderAsset shaderAssets[SHADER_COUNT] = {
{"shaders/postfx.fs"}
};
{"shaders/postfx.fs"}};
////////////////////////////////////////////////////////////////////////////
typedef enum {
typedef enum
{
BEE_AMBIENT,
SOUNDTRACK_COUNT
} SoundtrackID;
const SoundtrackAsset soundtrackAssets[SOUNDTRACK_COUNT] = {
{"assets/beez.mp3"}
};
{"assets/beez.mp3"}};
////////////////////////////////////////////////////////////////////////////
+6 -7
View File
@@ -1,19 +1,18 @@
#ifndef HIVE_H
#define HIVE_H
#include "../engine/object.h"
#include <object.h>
Object CreateHive(Texture2D texture, float frameWidth){
return (Object) {
Object CreateHive(Texture2D texture, float frameWidth)
{
return (Object){
.sprite = {
.texture = texture,
.sourceRec = (Rectangle){0.0f, 0.0f, frameWidth, frameWidth},
.destRec = (Rectangle){0.0f, 0.0f, frameWidth, frameWidth},
.origin = (Vector2){frameWidth / 2, frameWidth / 2},
.rotation = 0.0f,
.color = WHITE
},
.position = (Vector2){0.0f, 0.0f}
};
.color = WHITE},
.position = (Vector2){0.0f, 0.0f}};
}
#endif // HIVE_H
+1 -1
View File
@@ -1,7 +1,7 @@
#ifndef LOGO_H
#define LOGO_H
#include "../engine/applicationstate.h"
#include "applicationstate.h"
typedef struct {
int frameCounter;
+80 -61
View File
@@ -1,17 +1,19 @@
#ifndef MENU_H
#define MENU_H
#include "../engine/gui.h"
#include "../engine/applicationstate.h"
#include "../engine/asset.h"
#include "../engine/gamegui.h"
#include <gui.h>
#include <asset.h>
#include <gamegui.h>
#include <input.h>
#include <soundtrack.h>
#include "applicationstate.h"
#include "menuassets.h"
#include "colors.h"
#include "../engine/input.h"
#include "background.h"
#include "../engine/soundtrack.h"
typedef struct {
typedef struct
{
Assets assets;
Mouse mouse;
Texture2D textures[MENU_TEX_COUNT];
@@ -26,23 +28,26 @@ typedef struct {
Background foreground;
} MenuState;
void InitMenu(MenuState *m){
void InitMenu(MenuState *m)
{
m->assets = (Assets){
.textureAssets = menuTextureAssets,
.soundAssets = menuSoundAssets,
.soundtrackAssets = menuSoundtrackAssets
};
.soundtrackAssets = menuSoundtrackAssets};
for (int i = 0; i < MENU_TEX_COUNT; i++){
for (int i = 0; i < MENU_TEX_COUNT; i++)
{
m->textures[i] = LoadTexture(m->assets.textureAssets[i].path);
}
for (int i = 0; i < MENU_SOUND_COUNT; i++){
for (int i = 0; i < MENU_SOUND_COUNT; i++)
{
m->sounds[i] = LoadSound(m->assets.soundAssets[i].path);
}
for (int i = 0; i < MENU_SOUNDTRACK_COUNT; i++){
for (int i = 0; i < MENU_SOUNDTRACK_COUNT; i++)
{
m->soundtracks[i] = LoadSoundtrack(m->assets.soundtrackAssets[i].path);
}
@@ -57,55 +62,54 @@ void InitMenu(MenuState *m){
int screenHeight = GetScreenHeight();
Vector2 screenCenter = {screenWidth / 2, screenHeight / 2};
int boardWidth = screenWidth / m->menuScale * 2.5f;
float boardScaleFactor = (float)boardWidth / m->assets.textureAssets[MENU_BOARD_TEX].frameWidth;
int boardHeight = (int)(m->assets.textureAssets[MENU_BOARD_TEX].frameHeight * boardScaleFactor);
Vector2 boardSource = {128.0f,0.0f};
Vector2 boardSource = {128.0f, 0.0f};
Rectangle boardDestRec = {
.x = screenCenter.x - (boardWidth / 2),
.y = screenCenter.y - (boardHeight / 2),
.width = boardWidth,
.height = boardHeight
};
.height = boardHeight};
m->board = CreateImageElement(&m->textures[MENU_BOARD_TEX], &m->assets.textureAssets[MENU_BOARD_TEX], &boardSource, &boardDestRec);
int buttonWidth = screenWidth / m->menuScale;
float buttonScaleFactor = (float)buttonWidth / m->assets.textureAssets[MENU_BIG_BUTTONS_TEX].frameWidth;
int buttonHeight = (int)(m->assets.textureAssets[MENU_BIG_BUTTONS_TEX].frameHeight * buttonScaleFactor);
int buttonSpacing = buttonHeight;
Vector2 playButtonNotPressed = { 0.0f, 96.0f};
Vector2 quitButtonNotPressed = { 0.0f, 0.0f};
Vector2 playButtonHover = { 96.0f, 96.0f};
Vector2 quitButtonHover = { 96.0f, 0.0f};
Vector2 playButtonPressed = { 192.0f, 96.0f};
Vector2 quitButtonPressed = { 192.0f, 0.0f};
Vector2 playButtonNotPressed = {0.0f, 96.0f};
Vector2 quitButtonNotPressed = {0.0f, 0.0f};
Vector2 playButtonHover = {96.0f, 96.0f};
Vector2 quitButtonHover = {96.0f, 0.0f};
Vector2 playButtonPressed = {192.0f, 96.0f};
Vector2 quitButtonPressed = {192.0f, 0.0f};
Rectangle playButtonDestRec = {
.x = screenCenter.x - (buttonWidth / 2),
.y = screenCenter.y - (buttonHeight / 2),
.width = buttonWidth,
.height = buttonHeight
};
.height = buttonHeight};
Rectangle quitButtonDestRec = {
.x = screenCenter.x - (buttonWidth / 2),
.y = screenCenter.y - (buttonHeight / 2) + buttonSpacing,
.width = buttonWidth,
.height = buttonHeight
};
.height = buttonHeight};
m->playButton = CreateImageButton(NOT_CLICKED, &m->textures[MENU_BIG_BUTTONS_TEX], &m->assets.textureAssets[MENU_BIG_BUTTONS_TEX], &playButtonNotPressed, &playButtonPressed, &playButtonHover, &playButtonDestRec);
m->quitButton = CreateImageButton(NOT_CLICKED, &m->textures[MENU_BIG_BUTTONS_TEX], &m->assets.textureAssets[MENU_BIG_BUTTONS_TEX], &quitButtonNotPressed, &quitButtonPressed, &quitButtonHover, &quitButtonDestRec);
}
void UpdateMenu(MenuState *m, State *state){
void UpdateMenu(MenuState *m, State *state)
{
for (int i = 0; i < MENU_SOUNDTRACK_COUNT; i++){ UpdateSoundtrack(&m->soundtracks[i]); }
for (int i = 0; i < MENU_SOUNDTRACK_COUNT; i++)
{
UpdateSoundtrack(&m->soundtracks[i]);
}
UpdateBackground(&m->background);
UpdateBackground(&m->midground);
@@ -113,48 +117,63 @@ void UpdateMenu(MenuState *m, State *state){
UpdateMouseScreen(&m->mouse);
if (isImageButtonClicked(&m->playButton, m->sounds[HOVER_SOUND], m->sounds[CLICK_SOUND])){
if (isImageButtonClicked(&m->playButton, m->sounds[HOVER_SOUND], m->sounds[CLICK_SOUND]))
{
state->gameStarted = true;
state->currentScreen = GAMEPLAY;
}
if (isImageButtonClicked(&m->quitButton, m->sounds[HOVER_SOUND], m->sounds[CLICK_SOUND]) || (WindowShouldClose() && !IsKeyPressed(KEY_ESCAPE))) {
if (isImageButtonClicked(&m->quitButton, m->sounds[HOVER_SOUND], m->sounds[CLICK_SOUND]) || (WindowShouldClose() && !IsKeyPressed(KEY_ESCAPE)))
{
state->running = false;
}
}
void RenderMenu(MenuState *m, State *appState,Font *font, const char* name){
void RenderMenu(MenuState *m, State *appState, Font *font, const char *title)
{
BeginDrawing();
ClearBackground(WHITE);
RenderBackground(&m->background);
RenderBackground(&m->midground);
RenderBackground(&m->foreground);
renderImageElement(&m->board);
renderImageButton(&m->playButton);
renderImageButton(&m->quitButton);
int textWidth = MeasureText(name, font->baseSize);
DrawTextEx(*font, name, (Vector2){ GetScreenWidth() / 2 - textWidth / 2, textWidth / 4}, (float)font->baseSize, 2, WHITE);
DrawTexturePro(
m->mouse.cursorTexture,
(Rectangle){ 0, 0, m->mouse.cursorTexture.width, m->mouse.cursorTexture.height},
(Rectangle){m->mouse.screenPosition.x, m->mouse.screenPosition.y, m->mouse.cursorTexture.width, m->mouse.cursorTexture.height},
(Vector2){0.0f, 0.0f},
0.0f,
WHITE
);
ClearBackground(WHITE);
RenderBackground(&m->background);
RenderBackground(&m->midground);
RenderBackground(&m->foreground);
renderImageElement(&m->board);
renderImageButton(&m->playButton);
renderImageButton(&m->quitButton);
int textWidth = MeasureText(title, font->baseSize);
DrawTextEx(*font, title, (Vector2){GetScreenWidth() / 2 - textWidth / 2, textWidth / 4}, (float)font->baseSize, 2, WHITE);
DrawTexturePro(
m->mouse.cursorTexture,
(Rectangle){0, 0, m->mouse.cursorTexture.width, m->mouse.cursorTexture.height},
(Rectangle){m->mouse.screenPosition.x, m->mouse.screenPosition.y, m->mouse.cursorTexture.width, m->mouse.cursorTexture.height},
(Vector2){0.0f, 0.0f},
0.0f,
WHITE);
if (appState->flagFPS)
{
DrawFPS(10, 10);
}
if (appState->flagFPS){
DrawFPS(10, 10);
}
EndDrawing();
}
void CleanupMenu(MenuState *m){
for (int i = 0; i < MENU_SOUND_COUNT; i++){ UnloadSound(m->sounds[i]); }
for (int i = 0; i < MENU_TEX_COUNT; i++){ if (m->textures[i].id > 0) { UnloadTexture(m->textures[i]); } }
for (int i = 0; i < MENU_SOUNDTRACK_COUNT; i++){ UnloadMusicStream(m->soundtracks[i].music); }
void CleanupMenu(MenuState *m)
{
for (int i = 0; i < MENU_SOUND_COUNT; i++)
{
UnloadSound(m->sounds[i]);
}
for (int i = 0; i < MENU_TEX_COUNT; i++)
{
if (m->textures[i].id > 0)
{
UnloadTexture(m->textures[i]);
}
}
for (int i = 0; i < MENU_SOUNDTRACK_COUNT; i++)
{
UnloadMusicStream(m->soundtracks[i].music);
}
}
#endif // MENU_H
+11 -11
View File
@@ -1,8 +1,9 @@
#ifndef MENUASSETS_H
#define MENUASSETS_H
#include "../engine/asset.h"
#include <asset.h>
typedef enum {
typedef enum
{
MENU_CURSOR_TEX,
MENU_BIG_BUTTONS_TEX,
MENU_BOARD_TEX,
@@ -13,24 +14,24 @@ typedef enum {
} MenuTextureID;
const TextureAsset menuTextureAssets[MENU_TEX_COUNT] = {
{"assets/SproutLandsUI/Spritesheets/Mouse sprites/TriangleMouseicon1.png", 16.0f, 16.0f, 0.0f },
{"assets/SproutLandsUI/Spritesheets/Mouse sprites/TriangleMouseicon1.png", 16.0f, 16.0f, 0.0f},
{"assets/SproutLandsUI/Spritesheets/UIBigPlayButtonHover.png", 96.0f, 32.0f, 0.0f},
{"assets/SproutLandsUI/Spritesheets/Settingmenu.png", 128.0f, 144.0f, 0.0f},
{"assets/Clouds/Clouds1/1.png", 576.0f, 324.0f, 0.0f},
{"assets/Clouds/Clouds1/2.png", 576.0f, 324.0f, 0.0f},
{"assets/Clouds/Clouds1/4.png", 576.0f, 324.0f, 0.0f}
};
{"assets/Clouds/Clouds1/4.png", 576.0f, 324.0f, 0.0f}};
typedef enum {
typedef enum
{
MENU_MUSIC,
MENU_SOUNDTRACK_COUNT
} MenuSoundtrackID;
const SoundtrackAsset menuSoundtrackAssets[MENU_SOUNDTRACK_COUNT] = {
{"assets/merrygoround.wav"}
};
{"assets/merrygoround.wav"}};
typedef enum {
typedef enum
{
HOVER_SOUND,
CLICK_SOUND,
MENU_SOUND_COUNT
@@ -38,7 +39,6 @@ typedef enum {
const SoundAsset menuSoundAssets[MENU_SOUND_COUNT] = {
{"assets/audio/WAV/Minimalist1.wav"},
{"assets/audio/WAV/Abstract1.wav"}
};
{"assets/audio/WAV/Abstract1.wav"}};
#endif // MENUASSETS_H
+65 -49
View File
@@ -1,25 +1,30 @@
#ifndef PLAYER_H
#define PLAYER_H
#include "../engine/sprite.h"
#include "../engine/animation.h"
#include "../engine/physics.h"
#include "../engine/input.h"
#include <sprite.h>
#include <animation.h>
#include <physics.h>
#include <input.h>
#include "inventar.h"
typedef enum {
typedef enum
{
IDLE,
WALKING,
USING_TOOL
} PlayerState;
typedef enum {
typedef enum
{
NO_TOOL,
HOE,
AXT,
CAN
} SelectedTool;
typedef enum {
typedef enum
{
IDLE_DOWN,
IDLE_UP,
IDLE_LEFT,
@@ -42,7 +47,8 @@ typedef enum {
CAN_LEFT,
} PlayerAnimationState;
typedef struct {
typedef struct
{
SelectedTool selectedTool;
Sprite sprite;
Animation animation;
@@ -50,7 +56,8 @@ typedef struct {
Inventar inventar;
} Player;
Player CreatePlayer(PlayerAnimationState animState, SelectedTool selectedTool, Texture2D texture, float frameWidth, int maxFrame, int framesSpeed, Vector2 startPosition, float rotation, float speed){
Player CreatePlayer(PlayerAnimationState animState, SelectedTool selectedTool, Texture2D texture, float frameWidth, int maxFrame, int framesSpeed, Vector2 startPosition, float rotation, float speed)
{
return (Player){
.selectedTool = selectedTool,
.sprite = {
@@ -60,71 +67,80 @@ Player CreatePlayer(PlayerAnimationState animState, SelectedTool selectedTool, T
.destRec = {0.0f, 0.0f, frameWidth, frameWidth},
.origin = {frameWidth / 2, frameWidth / 2},
.rotation = rotation,
.color = WHITE
},
.animation = {
.state = animState,
.currentFrame = 0,
.maxFrame = maxFrame,
.framesCounter = 0,
.framesSpeed = framesSpeed,
.animTimer = 0.0f
},
.physics = {
.position = startPosition,
.direction = (Vector2){0.0f,0.0f},
.speed = speed
},
.inventar = {
.seedCount = 6
}
};
.color = WHITE},
.animation = {.state = animState, .currentFrame = 0, .maxFrame = maxFrame, .framesCounter = 0, .framesSpeed = framesSpeed, .animTimer = 0.0f},
.physics = {.position = startPosition, .direction = (Vector2){0.0f, 0.0f}, .speed = speed},
.inventar = {.seedCount = 6}};
}
void UpdatePlayerAnimationState(int *state, SelectedTool *selectedTool, Vector2 *dir) {
void UpdatePlayerAnimationState(int *state, SelectedTool *selectedTool, Vector2 *dir)
{
if (IsKeyPressed(KEY_ZERO)){
if (IsKeyPressed(KEY_ZERO))
{
*selectedTool = NO_TOOL;
}
if (IsKeyPressed(KEY_ONE)){
if (IsKeyPressed(KEY_ONE))
{
*selectedTool = HOE;
}
if (IsKeyPressed(KEY_TWO)){
if (IsKeyPressed(KEY_TWO))
{
*selectedTool = AXT;
}
if (IsKeyPressed(KEY_THREE)){
if (IsKeyPressed(KEY_THREE))
{
*selectedTool = CAN;
}
if (dir->x == 0 && dir->y == 0) {
switch (*state) {
case WALKING_DOWN: *state = IDLE_DOWN; break;
case WALKING_UP: *state = IDLE_UP; break;
case WALKING_LEFT: *state = IDLE_LEFT; break;
case WALKING_RIGHT: *state = IDLE_RIGHT; break;
default: break;
if (dir->x == 0 && dir->y == 0)
{
switch (*state)
{
case WALKING_DOWN:
*state = IDLE_DOWN;
break;
case WALKING_UP:
*state = IDLE_UP;
break;
case WALKING_LEFT:
*state = IDLE_LEFT;
break;
case WALKING_RIGHT:
*state = IDLE_RIGHT;
break;
default:
break;
}
} else {
if (dir->y == 1) *state = WALKING_DOWN;
else if (dir->y == -1) *state = WALKING_UP;
else if (dir->x == -1) *state = WALKING_LEFT;
else if (dir->x == 1) *state = WALKING_RIGHT;
}
else
{
if (dir->y == 1)
*state = WALKING_DOWN;
else if (dir->y == -1)
*state = WALKING_UP;
else if (dir->x == -1)
*state = WALKING_LEFT;
else if (dir->x == 1)
*state = WALKING_RIGHT;
}
}
void UpdatePlayer(Player* player){
if (IsKeyPressed(KEY_SPACE) && player->inventar.seedCount >0){
void UpdatePlayer(Player *player)
{
if (IsKeyPressed(KEY_SPACE) && player->inventar.seedCount > 0)
{
player->inventar.seedCount--;
}
}
Vector2 dir = GetDirectionVector();
UpdatePlayerAnimationState(&player->animation.state, &player->selectedTool, &dir);
UpdatePhysics(&player->physics, dir);
UpdateSpriteDestRec(&player->sprite, &player->physics.position);
UpdateAnimation(&player->animation, GetFrameTime());
UpdateSpriteSourceRec(&player->sprite, &(Vector2){player->sprite.frameSize.x * player->animation.currentFrame, player->sprite.frameSize.y * player->animation.state});
UpdateSpriteSourceRec(&player->sprite, &(Vector2){player->sprite.frameSize.x * player->animation.currentFrame, player->sprite.frameSize.y * player->animation.state});
}
#endif // PLAYER_H
-2966
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -3,8 +3,8 @@
typedef enum Screen {
LOGO = 0,
MENU,
GAMEPLAY
MENU = 1,
GAMEPLAY = 2
} Screen;
#endif // SCREEN_H
-33
View File
@@ -1,33 +0,0 @@
#ifndef APP_H
#define APP_H
#include "engine/applicationstate.h"
#include "engine/display.h"
#include "engine/screen.h"
#include "game/logo.h"
#include "game/menu.h"
#include "game/game.h"
typedef struct {
State state;
Display display;
Font font;
LogoState logo;
GameState game;
MenuState menu;
} Application;
void InitApp(Application* app){
app->state.currentScreen = LOGO;
app->state.running = true;
app->state.gameStarted = false;
app->state.flagFPS = true;
app->display = (Display){
.name = "The Last Hive",
.width = 1280,
.height = 720
};
app->font = LoadFont("assets/SproutLandsUI/fonts/pixelFont-7-8x14-sproutLands.fnt");
}
#endif //APP_H
-29
View File
@@ -1,29 +0,0 @@
#ifndef FLOWERS_H
#define FLOWERS_H
#include "../engine/object.h"
ObjectArray CreateFlowers(Texture2D texture,float frameWidth, float rotation, float fieldSize) {
ObjectArray flowers = CreateObjectArray(&(Object){
.sprite = {
.texture = texture,
.sourceRec = {0.0f, 0.0f, frameWidth, frameWidth},
.destRec = {0.0f, 0.0f, frameWidth, frameWidth},
.origin = {frameWidth / 2, frameWidth / 2},
.rotation = rotation,
.color = WHITE
},
.position = {0.0f, 0.0f}
}, 10);
if (!flowers.data) {
printf("Memory allocation failed.\n");
exit(1);
}
SetRandomPositionForObjectArray(&flowers, (Rectangle){-fieldSize, -fieldSize, fieldSize * 2, fieldSize * 2});
return flowers;
}
#endif // FLOWERS_H
+5 -39
View File
@@ -1,49 +1,15 @@
#include <raylib.h>
#include <stdio.h>
#include <stdlib.h>
#include "../include/raymath.h"
#include "app.h"
int main(void)
{
Application app;
InitApp(&app);
InitWindow(app.display.width, app.display.height, app.display.name);
ConfigApp(&app);
InitWindow(app.display.width, app.display.height, app.display.title);
InitAudioDevice();
SetTextureFilter(app.font.texture, TEXTURE_FILTER_POINT);
SetTraceLogLevel(LOG_NONE);
InitLogo(&app.logo);
InitMenu(&app.menu);
InitGame(&app.game,&app.display);
while (app.state.running)
{
switch (app.state.currentScreen)
{
case LOGO:
{
UpdateLogo(&app.logo, &app.state);
RenderLogo(&app.logo, &app.state);
}break;
case MENU:
{
UpdateMenu(&app.menu, &app.state);
RenderMenu(&app.menu, &app.state, &app.font, app.display.name);
}break;
case GAMEPLAY:
{
UpdateGame(&app.game, &app.state);
RenderGame(&app.game, &app.state);
}break;
default: break;
}
}
CleanupMenu(&app.menu);
CleanupGame(&app.game);
UnloadFont(app.font);
InitApp(&app);
UpdateApp(&app);
CleanupApp(&app);
CloseWindow();
return 0;
}