<commit_before>#include <cassert>
#include <cstdio>

#include <memory>

#include <SDL.h>
#include <SDL_ttf.h>

#include "input.h"
#include "spritemgr.h"
#include "logger.h"
#include "draw.h"
#include "game.h"
#include "player_input.h"

using Window = std::unique_ptr<SDL_Window,  decltype(&SDL_DestroyWindow)>;
using Camera = geometry::Rectangle;

// The size of the game camera
// TODO: It seems as if Crystal Caves actually renders 12½ tiles on y axis - verify this and adjust accordingly
static constexpr geometry::Size CAMERA_SIZE = geometry::Size(320, 192);

// The size of the game camera after stretching, which is done in the original Crystal Caves
static constexpr geometry::Size CAMERA_SIZE_STRETCHED = geometry::Size(CAMERA_SIZE.x(), CAMERA_SIZE.y() * 1.2f);

// The size of the game camera after scaling - this should be based on the (dynamic) window size in the future
static constexpr geometry::Size CAMERA_SIZE_SCALED = CAMERA_SIZE_STRETCHED * 3.0f;

// The size of the game window
static constexpr geometry::Size WINDOW_SIZE = CAMERA_SIZE_SCALED;

// Game variables
static Game game;
static SpriteManager sprite_manager;
static bool paused(false);

// Used by the render_x functions and should be up to date before each call to render_x()
static std::unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)> game_surface(nullptr, &SDL_FreeSurface);
static Camera game_camera;
static unsigned game_tick;

// Debug information
static bool debug(false);
static bool debug_aabb(false);

Window init_SDL()
{
  // Init SDL
  if (SDL_Init(SDL_INIT_VIDEO) < 0)
  {
    LOG_CRITICAL("Could not initialize SDL: %s", SDL_GetError());
    return Window(nullptr, SDL_DestroyWindow);
  }

  // Init SDL_ttf
  if (TTF_Init() < 0)
  {
    LOG_CRITICAL("Could not initialize TTF: %s", TTF_GetError());
    return Window(nullptr, SDL_DestroyWindow);
  }

  atexit(SDL_Quit);

  return Window(SDL_CreateWindow("OpenCrystalCaves",
                                 0,
                                 0,
                                 WINDOW_SIZE.x(),
                                 WINDOW_SIZE.y(),
                                 SDL_WINDOW_SHOWN),
                SDL_DestroyWindow);
}

void read_input(Input* input)
{
  assert(input != nullptr);

  // Set any input that is pressed as repeated here
  input->up.repeated    = input->up.pressed;
  input->down.repeated  = input->down.pressed;
  input->left.repeated  = input->left.pressed;
  input->right.repeated = input->right.pressed;
  input->z.repeated     = input->z.pressed;
  input->x.repeated     = input->x.pressed;
  input->num_1.repeated = input->num_1.pressed;
  input->num_2.repeated = input->num_2.pressed;
  input->enter.repeated = input->enter.pressed;
  input->space.repeated = input->space.pressed;

  // Read events
  SDL_Event event;
  while (SDL_PollEvent(&event) != 0)
  {
    if (event.type == SDL_QUIT)
    {
      input->quit = true;
      return;  // Quit asap
    }

    else if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP)
    {
      switch (event.key.keysym.sym)
      {
        case SDLK_UP:
          input->up.pressed = event.type == SDL_KEYDOWN;
          if (!input->up.pressed) input->up.repeated = false;
          break;

        case SDLK_DOWN:
          input->down.pressed = event.type == SDL_KEYDOWN;
          if (!input->down.pressed) input->down.repeated = false;
          break;

        case SDLK_LEFT:
          input->left.pressed = event.type == SDL_KEYDOWN;
          if (!input->left.pressed) input->left.repeated = false;
          break;

        case SDLK_RIGHT:
          input->right.pressed = event.type == SDL_KEYDOWN;
          if (!input->right.pressed) input->right.repeated = false;
          break;

        case SDLK_z:
          input->z.pressed = event.type == SDL_KEYDOWN;
          if (!input->z.pressed) input->z.repeated = false;
          break;

        case SDLK_x:
          input->x.pressed = event.type == SDL_KEYDOWN;
          if (!input->x.pressed) input->x.repeated = false;
          break;

        case SDLK_1:
          input->num_1.pressed = event.type == SDL_KEYDOWN;
          if (!input->num_1.pressed) input->num_1.repeated = false;
          break;

        case SDLK_2:
          input->num_2.pressed = event.type == SDL_KEYDOWN;
          if (!input->num_2.pressed) input->num_2.repeated = false;
          break;

        case SDLK_RETURN:
          input->enter.pressed = event.type == SDL_KEYDOWN;
          if (!input->enter.pressed) input->enter.repeated = false;
          break;

        case SDLK_SPACE:
          input->space.pressed = event.type == SDL_KEYDOWN;
          if (!input->space.pressed) input->space.repeated = false;
          break;

        case SDLK_ESCAPE:
          input->quit = true;
          return;

        default:
          break;
      }
    }
  }
}

void render_background()
{
  const auto& level = game.get_level();
  const auto& items = game.get_items();

  const auto start_tile_x = game_camera.position.x() > 0 ? game_camera.position.x() / 16 : 0;
  const auto start_tile_y = game_camera.position.y() > 0 ? game_camera.position.y() / 16 : 0;
  const auto end_tile_x = (game_camera.position.x() + game_camera.size.x()) / 16;
  const auto end_tile_y = (game_camera.position.y() + game_camera.size.y()) / 16;

  for (int tile_y = start_tile_y; tile_y <= end_tile_y; tile_y++)
  {
    for (int tile_x = start_tile_x; tile_x <= end_tile_x; tile_x++)
    {
      auto item_id = level.get_tile_background(tile_x, tile_y);
      if (item_id != Item::invalid)
      {
        const auto& item = items[item_id];
        const auto sprite_id = [&item, &tile_x, &tile_y]()
        {
          if (item.is_multiple_2x2())
          {
            return item.get_sprite() + ((tile_y % 2) * 4) + (tile_x % 2);
          }
          else if (item.is_multiple_4x2())
          {
            return item.get_sprite() + ((tile_y % 2) * 4) + (tile_x % 4);
          }
          else
          {
            return item.get_sprite();
          }<commit_msg>Adjust how MULTIPLE_xxx is rendered<commit_after>#include <cassert>
#include <cstdio>

#include <memory>

#include <SDL.h>
#include <SDL_ttf.h>

#include "input.h"
#include "spritemgr.h"
#include "logger.h"
#include "draw.h"
#include "game.h"
#include "player_input.h"

using Window = std::unique_ptr<SDL_Window,  decltype(&SDL_DestroyWindow)>;
using Camera = geometry::Rectangle;

// The size of the game camera
// TODO: It seems as if Crystal Caves actually renders 12½ tiles on y axis - verify this and adjust accordingly
static constexpr geometry::Size CAMERA_SIZE = geometry::Size(320, 192);

// The size of the game camera after stretching, which is done in the original Crystal Caves
static constexpr geometry::Size CAMERA_SIZE_STRETCHED = geometry::Size(CAMERA_SIZE.x(), CAMERA_SIZE.y() * 1.2f);

// The size of the game camera after scaling - this should be based on the (dynamic) window size in the future
static constexpr geometry::Size CAMERA_SIZE_SCALED = CAMERA_SIZE_STRETCHED * 3.0f;

// The size of the game window
static constexpr geometry::Size WINDOW_SIZE = CAMERA_SIZE_SCALED;

// Game variables
static Game game;
static SpriteManager sprite_manager;
static bool paused(false);

// Used by the render_x functions and should be up to date before each call to render_x()
static std::unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)> game_surface(nullptr, &SDL_FreeSurface);
static Camera game_camera;
static unsigned game_tick;

// Debug information
static bool debug(false);
static bool debug_aabb(false);

Window init_SDL()
{
  // Init SDL
  if (SDL_Init(SDL_INIT_VIDEO) < 0)
  {
    LOG_CRITICAL("Could not initialize SDL: %s", SDL_GetError());
    return Window(nullptr, SDL_DestroyWindow);
  }

  // Init SDL_ttf
  if (TTF_Init() < 0)
  {
    LOG_CRITICAL("Could not initialize TTF: %s", TTF_GetError());
    return Window(nullptr, SDL_DestroyWindow);
  }

  atexit(SDL_Quit);

  return Window(SDL_CreateWindow("OpenCrystalCaves",
                                 0,
                                 0,
                                 WINDOW_SIZE.x(),
                                 WINDOW_SIZE.y(),
                                 SDL_WINDOW_SHOWN),
                SDL_DestroyWindow);
}

void read_input(Input* input)
{
  assert(input != nullptr);

  // Set any input that is pressed as repeated here
  input->up.repeated    = input->up.pressed;
  input->down.repeated  = input->down.pressed;
  input->left.repeated  = input->left.pressed;
  input->right.repeated = input->right.pressed;
  input->z.repeated     = input->z.pressed;
  input->x.repeated     = input->x.pressed;
  input->num_1.repeated = input->num_1.pressed;
  input->num_2.repeated = input->num_2.pressed;
  input->enter.repeated = input->enter.pressed;
  input->space.repeated = input->space.pressed;

  // Read events
  SDL_Event event;
  while (SDL_PollEvent(&event) != 0)
  {
    if (event.type == SDL_QUIT)
    {
      input->quit = true;
      return;  // Quit asap
    }

    else if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP)
    {
      switch (event.key.keysym.sym)
      {
        case SDLK_UP:
          input->up.pressed = event.type == SDL_KEYDOWN;
          if (!input->up.pressed) input->up.repeated = false;
          break;

        case SDLK_DOWN:
          input->down.pressed = event.type == SDL_KEYDOWN;
          if (!input->down.pressed) input->down.repeated = false;
          break;

        case SDLK_LEFT:
          input->left.pressed = event.type == SDL_KEYDOWN;
          if (!input->left.pressed) input->left.repeated = false;
          break;

        case SDLK_RIGHT:
          input->right.pressed = event.type == SDL_KEYDOWN;
          if (!input->right.pressed) input->right.repeated = false;
          break;

        case SDLK_z:
          input->z.pressed = event.type == SDL_KEYDOWN;
          if (!input->z.pressed) input->z.repeated = false;
          break;

        case SDLK_x:
          input->x.pressed = event.type == SDL_KEYDOWN;
          if (!input->x.pressed) input->x.repeated = false;
          break;

        case SDLK_1:
          input->num_1.pressed = event.type == SDL_KEYDOWN;
          if (!input->num_1.pressed) input->num_1.repeated = false;
          break;

        case SDLK_2:
          input->num_2.pressed = event.type == SDL_KEYDOWN;
          if (!input->num_2.pressed) input->num_2.repeated = false;
          break;

        case SDLK_RETURN:
          input->enter.pressed = event.type == SDL_KEYDOWN;
          if (!input->enter.pressed) input->enter.repeated = false;
          break;

        case SDLK_SPACE:
          input->space.pressed = event.type == SDL_KEYDOWN;
          if (!input->space.pressed) input->space.repeated = false;
          break;

        case SDLK_ESCAPE:
          input->quit = true;
          return;

        default:
          break;
      }
    }
  }
}

void render_background()
{
  const auto& level = game.get_level();
  const auto& items = game.get_items();

  const auto start_tile_x = game_camera.position.x() > 0 ? game_camera.position.x() / 16 : 0;
  const auto start_tile_y = game_camera.position.y() > 0 ? game_camera.position.y() / 16 : 0;
  const auto end_tile_x = (game_camera.position.x() + game_camera.size.x()) / 16;
  const auto end_tile_y = (game_camera.position.y() + game_camera.size.y()) / 16;

  for (int tile_y = start_tile_y; tile_y <= end_tile_y; tile_y++)
  {
    for (int tile_x = start_tile_x; tile_x <= end_tile_x; tile_x++)
    {
      auto item_id = level.get_tile_background(tile_x, tile_y);
      if (item_id != Item::invalid)
      {
        const auto& item = items[item_id];
        const auto sprite_id = [&item, &tile_x, &tile_y]()
        {
          if (item.is_multiple_2x2())
          {
            return item.get_sprite() + (((tile_y + 1) % 2) * 4) + (tile_x % 2);
          }
          else if (item.is_multiple_4x2())
          {
            return item.get_sprite() + (((tile_y + 1) % 2) * 4) + (tile_x % 4);
          }
          else
          {
            return item.get_sprite();
          }
{'fraction_non_alphanumeric': 0.12303621630560609, 'fraction_numerical': 0.008599305440714404, 'mean_word_length': 2.4390105203298265, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 125, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4695223', 'n_tokens_mistral': 4120, 'n_tokens_neox': 3884, 'n_words': 926}