diff --git a/Makefile b/Makefile index e0bee7e..c71e051 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ RM := rm -f DEBUG ?= 1 COMMON_CFLAGS := -std=$(STD) -Werror -Wextra -Wall -MMD -MP -I$(SRC_DIR) `pkg-config --cflags glib-2.0` -COMMON_LDFLAGS := -std=$(STD) -lm `pkg-config --libs glib-2.0` +COMMON_LDFLAGS := -std=$(STD) -lm `pkg-config --libs glib-2.0 sdl3 sdl3-image` ifeq ($(DEBUG),1) COMMON_CFLAGS += -g -DDEBUG @@ -32,8 +32,8 @@ else endif -CLIENT_CFLAGS := $(COMMON_CFLAGS) `pkg-config --cflags sdl3 sdl3-image` -CLIENT_LDFLAGS := $(COMMON_LDFLAGS) `pkg-config --libs sdl3 sdl3-image` +CLIENT_CFLAGS := $(COMMON_CFLAGS) # `pkg-config --cflags sdl3` +CLIENT_LDFLAGS := $(COMMON_LDFLAGS) # `pkg-config --libs sdl3` SERVER_CFLAGS := $(COMMON_CFLAGS) SERVER_LDFLAGS := $(COMMON_LDFLAGS) diff --git a/res/test_map.png b/res/test_map.png new file mode 100644 index 0000000..74cf1ae Binary files /dev/null and b/res/test_map.png differ diff --git a/res/test_map_2.png b/res/test_map_2.png new file mode 100644 index 0000000..43d4c81 Binary files /dev/null and b/res/test_map_2.png differ diff --git a/src/client/client.c b/src/client/client.c index 2e7d20e..bde5efb 100644 --- a/src/client/client.c +++ b/src/client/client.c @@ -31,6 +31,18 @@ #include static GfxContext *gfx_ctx; +static World *test_world; + +World * +_make_test_World () +{ + World *self = World_load_from_file ("res/test_map_2.png"); + + /* TODO: add players and so on. That will be in GameState, + though. Or GameData. */ + + return self; +} SDL_AppResult SDL_AppInit (void **appstate, int argc, char **argv) @@ -55,6 +67,8 @@ SDL_AppInit (void **appstate, int argc, char **argv) } gfx_ctx = GfxContext_new (); + test_world = _make_test_World (); + GfxContext_cache_World (gfx_ctx, test_world); return SDL_APP_CONTINUE; } @@ -78,6 +92,7 @@ SDL_AppIterate (void *appstate) UNUSED (appstate); GfxContext_prerender (gfx_ctx); + GfxContext_render_World (gfx_ctx, test_world); GfxContext_postrender (gfx_ctx); return SDL_APP_CONTINUE; diff --git a/src/client/gfx_context.c b/src/client/gfx_context.c index b42934a..1bdb1f8 100644 --- a/src/client/gfx_context.c +++ b/src/client/gfx_context.c @@ -36,7 +36,7 @@ GfxContext_new () self->renderer = SDL_CreateRenderer (self->window, NULL); - if (!self->window) + if (!self->renderer) { g_critical ("Couldn't create renderer for context %p: %s", self, SDL_GetError ()); @@ -48,6 +48,7 @@ GfxContext_new () void GfxContext_prerender (GfxContext *self) { + SDL_SetRenderDrawColor (self->renderer, 0, 0, 0, 255); SDL_RenderClear (self->renderer); } @@ -64,3 +65,75 @@ GfxContext_free (GfxContext *self) SDL_DestroyWindow (self->window); g_free (self); } + +void +GfxContext_cache_World (GfxContext *self, World *world) +{ + if (self->world_texture) + { + SDL_DestroyTexture (self->world_texture); + } + + self->world_texture = SDL_CreateTexture (self->renderer, + SDL_PIXELFORMAT_RGBA32, + SDL_TEXTUREACCESS_TARGET, + world->width, world->height); + + if (!self->world_texture) + { + g_error ("Failed to create world texture: %s", SDL_GetError ()); + } + + SDL_Texture *old_target = SDL_GetRenderTarget (self->renderer); + + SDL_SetRenderTarget (self->renderer, self->world_texture); + SDL_SetRenderDrawColor (self->renderer, 0, 0, 0, 0); + SDL_RenderClear (self->renderer); + + for (unsigned int y = 0; y < world->height; ++y) + { + for (unsigned int x = 0; x < world->width; ++x) + { + MapTile *mt = + g_array_index (world->tiles, MapTile *, y * world->width + x); + + if (!mt) + continue; + + switch (mt->terrain_type) + { + case TERRAIN_WATER: + SDL_SetRenderDrawColor (self->renderer, 0, 0, 255, 255); + break; + case TERRAIN_FLAT: + SDL_SetRenderDrawColor (self->renderer, 0, 255, 0, 255); + break; + case TERRAIN_HILL: + SDL_SetRenderDrawColor (self->renderer, 0, 127, 0, 255); + break; + default: + SDL_SetRenderDrawColor (self->renderer, 255, 255, 255, 255); + break; + } + + SDL_RenderPoint (self->renderer, x, y); + } + } + + SDL_SetRenderTarget (self->renderer, old_target); +} + +void +GfxContext_render_World (GfxContext *self, World *world) +{ + UNUSED (world); + + if (!self->world_texture) + { + g_warning ("No world texture present"); + return; + } + + SDL_FRect dest = { 0, 0, 800, 600 }; + SDL_RenderTexture (self->renderer, self->world_texture, NULL, &dest); +} diff --git a/src/client/gfx_context.h b/src/client/gfx_context.h index 056d7d8..1403524 100644 --- a/src/client/gfx_context.h +++ b/src/client/gfx_context.h @@ -31,6 +31,10 @@ typedef struct GfxContext { SDL_Window *window; SDL_Renderer *renderer; + + SDL_Texture *world_texture; /* Generated once per every game. */ + SDL_Texture *player_texture; /* Probably changes every tick. */ + } GfxContext; GfxContext *GfxContext_new (); @@ -38,4 +42,5 @@ void GfxContext_prerender (GfxContext * self); void GfxContext_postrender (GfxContext * self); void GfxContext_free (GfxContext * self); -void GfxContext_renderWorld (GfxContext * self, World * world); +void GfxContext_cache_World (GfxContext * self, World * world); +void GfxContext_render_World (GfxContext * self, World * world); diff --git a/src/common/game_state.c b/src/common/game_state.c index 8179e75..0a798d0 100644 --- a/src/common/game_state.c +++ b/src/common/game_state.c @@ -1 +1 @@ -#include "game_state.h" +#include diff --git a/src/common/game_state.h b/src/common/game_state.h index 6c7f900..286a996 100644 --- a/src/common/game_state.h +++ b/src/common/game_state.h @@ -1,12 +1,9 @@ #pragma once -/* This houses the state of an actual, running game; your player, the - other players, the world map, buildings. */ - #include #include -#include "world.h" +#include typedef struct Player { @@ -24,5 +21,6 @@ typedef struct GameState { World *world; GArray *players; - Player *me; + + gboolean has_started:1; } GameState; diff --git a/src/common/world.c b/src/common/world.c index ea50eca..a825584 100644 --- a/src/common/world.c +++ b/src/common/world.c @@ -1 +1,91 @@ -#include "world.h" +#include + +static inline Uint32 +_surf_pixel (SDL_Surface *surface, int x, int y) +{ + int bpp = SDL_BYTESPERPIXEL (surface->format); + Uint8 *p = (Uint8 *) surface->pixels + y * surface->pitch + x * bpp; + + switch (bpp) + { + case 1: + return *p; + case 2: + return *(Uint16 *) p; + case 3: + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + { + return p[0] << 16 | p[1] << 8 | p[2]; + } + else + { + return p[0] | p[1] << 8 | p[2] << 16; + } + case 4: + return *(Uint32 *) p; + default: + return 0; // shouldn't happen + } +} + +World * +World_load_from_file (const char *path) +{ + World *self = g_new0 (World, 1); + self->buildings = g_array_new (FALSE, FALSE, sizeof (Building *)); + + SDL_Surface *surf = IMG_Load (path); + + if (!surf) + { + g_warning ("Failed to load map from path %s (returning NULL): %s", path, + SDL_GetError ()); + return NULL; + } + + self->width = surf->w; + self->height = surf->h; + self->tiles = + g_array_sized_new (FALSE, FALSE, sizeof (MapTile *), surf->w * surf->h); + + SDL_LockSurface (surf); + const SDL_PixelFormatDetails *details = + SDL_GetPixelFormatDetails (surf->format); + + for (int y = 0; y < surf->h; ++y) + { + for (int x = 0; x < surf->w; ++x) + { + MapTile *mt = g_new0 (MapTile, 1); + mt->x = x; + mt->y = y; + + Uint32 pixel = _surf_pixel (surf, x, y); + Uint8 r, g, b, a; + /* NULL is a palette, only used in indexed images. */ + SDL_GetRGBA (pixel, details, NULL, &r, &g, &b, &a); + + /* The ranges here can be tweaked to work better or + worse. */ + if (b > 196 && r < 50 && g < 50) + { + mt->terrain_type = TERRAIN_WATER; + } + else if (g > 200) + { + mt->terrain_type = TERRAIN_FLAT; + } + else + { + mt->terrain_type = TERRAIN_HILL; + } + + g_array_append_val (self->tiles, mt); + } + } + + SDL_UnlockSurface (surf); + SDL_DestroySurface (surf); + + return self; +} diff --git a/src/common/world.h b/src/common/world.h index 6c3f91a..a7c4a08 100644 --- a/src/common/world.h +++ b/src/common/world.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + #include typedef enum TerrainType @@ -26,15 +29,13 @@ typedef struct World GArray *buildings; } World; - -typedef struct Building Building; // Typedef is needed to avoid circular dependency -struct Building +typedef struct Building { MapTile *parent_tile; unsigned int level; - unsigned int (*at_what_cost) (Building * self, World * world); -}; + unsigned int (*at_what_cost) (struct Building * self, World * world); +} Building; -World *World_new (unsigned int width, unsigned int height); +World *World_load_from_file (const char *path); void World_tick (World * self);