Compare commits

...

36 Commits

Author SHA1 Message Date
08031810c9 Añadidas las unidades para trabajar con paletas 2024-02-05 16:43:30 +01:00
16baf48d8c Añadidos ficheros .gif 2024-02-05 16:33:53 +01:00
07caf71ce3 demo: El teclado se crea y se destruye según se use o no 2023-09-16 12:50:51 +02:00
c656d48b72 demo: El teclado se crea y se destruye según se use o no 2023-09-13 18:30:12 +02:00
2d03333463 on_screen_keyboard: arreglado bug al reducir el numero de columna o fila 2023-08-18 08:04:13 +02:00
477fc35e29 on_screen_keyboard: Añadidos bordes 2023-05-28 19:45:32 +02:00
d2e94ce74b on_screen_keyboard: Mejorada la calidad del cursor
on_screen_keyboard: Compatibilidad con fuentes de texto de tamaño variable
2023-05-28 19:35:49 +02:00
120c3791cf on_screen_keyboard: Ya se pueden añadir y quitar caracteres al texto de salida 2023-05-28 18:33:52 +02:00
921f9d2606 Input: Modificado el input por defecto para la acción de cancelar con teclado 2023-05-28 18:26:33 +02:00
c51eb06a61 on_screen_keyboard: Ya se dibuja el cursor 2023-05-28 17:44:59 +02:00
cc421e56ea on_screen_keyboard: Ya se dibuja el teclado en la pantalla 2023-05-28 12:58:02 +02:00
ad9a3fb594 utils: Añadida la función randColor 2023-05-28 11:58:34 +02:00
5d58d24cb9 on_screen_keyboard: Se empiezan a dibujar algunas teclas en pantalla 2023-05-28 10:38:04 +02:00
2b5a2cb588 utils: Añadidas las funciones darkenColor y lightenColor 2023-05-28 09:17:30 +02:00
73849a2fef on_screen_keyboard: Ya dibuja el objeto básico en pantalla 2023-05-28 09:09:26 +02:00
54947dce2d OnScreenKeyboard: Creado el esqueleto base 2023-05-26 11:00:51 +02:00
40701d9029 input: Retocados los mensajes de información 2023-05-26 08:43:20 +02:00
1746e38a56 demo: Pequeños retoques en el efecto de fuego 2023-05-26 08:39:43 +02:00
c12e8841b4 demo: Optimizado el calculo del efecto de fuego 2023-05-25 22:16:59 +02:00
ab90709963 demo: El efecto de fuego se ha optimizado pintando los pixels en una textura 2023-05-25 20:42:15 +02:00
6012b6ac5f demo: Se puede cambiar el efecto de fondo desde el teclado 2023-05-25 17:13:44 +02:00
c8960704c1 demo: El efecto de fuego ocupa toda la pantalla 2023-05-25 12:51:15 +02:00
f2ea6dbdb9 demo: Posibilidad de modificar el efecto de fuego con el teclado 2023-05-25 12:13:57 +02:00
f13947e73c demo: fire effect arreglado bug en la creación de la primera linea del efecto 2023-05-25 07:22:14 +02:00
5fa0c40102 demo: Añadido efecto de fuego en el fondo 2023-05-24 21:21:39 +02:00
bb6894e297 demo: Preparado todo para empezar a trabajar en el efecto de fuego 2023-05-24 18:37:46 +02:00
83d230846e demo: convertido todo el codigo que estava a lo bruto en el main a funciones y procedimientos 2023-05-24 17:43:38 +02:00
51c0a97a8a (cambio de ordenador) 2023-05-24 10:13:51 +02:00
1a40212b7a demo: Modificado el titulo de la ventana 2023-05-23 22:56:30 +02:00
0674e44b52 demo: Modificado el texto en pantalla 2023-05-23 22:55:56 +02:00
4424f2c2d3 input: Mejorado el texto informativo al inicializar el objeto 2023-05-23 20:59:54 +02:00
cb8d8e19e1 input fix: clearBindings no borraba completamente el vector de asignaciones 2023-05-23 20:12:07 +02:00
aca80e43a5 input: Añadido clearBindings() 2023-05-23 20:09:31 +02:00
71a81755d0 input: Añadidos más inputs por defecto 2023-05-23 20:07:29 +02:00
92aae07fd1 input: optimizado el constructor 2023-05-23 18:30:30 +02:00
bc017c1d55 demo: optimizada la inicialización de input 2023-05-23 18:27:27 +02:00
17 changed files with 2627 additions and 212 deletions

BIN
data/nokia.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

194
data/nokia.txt Normal file
View File

@@ -0,0 +1,194 @@
# box width
8
# box height
8
# 32 espacio ( )
4
# 33 !
2
# 34 "
3
# 35 #
5
# 36 $
5
# 37 %
6
# 38 &
6
# 39 '
1
# 40 (
3
# 41 )
3
# 42 *
5
# 43 +
5
# 44 ,
2
# 45 -
4
# 46 .
2
# 47 /
3
# 48 0
5
# 49 1
3
# 50 2
5
# 51 3
5
# 52 4
5
# 53 5
5
# 54 6
5
# 55 7
5
# 56 8
5
# 57 9
5
# 58 :
2
# 59 ;
2
# 60 <
4
# 61 =
4
# 62 >
4
# 63 ?
5
# 64 @
6
# 65 A
5
# 66 B
5
# 67 C
5
# 68 D
5
# 69 E
5
# 70 F
5
# 71 G
5
# 72 H
5
# 73 I
2
# 74 J
4
# 75 K
6
# 76 L
4
# 77 M
7
# 78 N
6
# 79 O
6
# 80 P
5
# 81 Q
6
# 82 R
5
# 83 S
4
# 84 T
6
# 85 U
5
# 86 V
6
# 87 W
7
# 88 X
6
# 89 Y
6
# 90 Z
5
# 91 [
2
# 92 \
3
# 93 ]
2
# 94 ^
3
# 95 _
6
# 96 `
2
# 97 a
5
# 98 b
5
# 99 c
4
# 100 d
5
# 101 e
5
# 102 f
3
# 103 g
5
# 104 h
5
# 105 i
2
# 106 j
3
# 107 k
5
# 108 l
2
# 109 m
8
# 110 n
5
# 111 o
5
# 112 p
5
# 113 q
5
# 114 r
4
# 115 s
4
# 116 t
3
# 117 u
5
# 118 v
5
# 119 w
7
# 120 x
5
# 121 y
5
# 122 z
5
# 123 { -> ñ
5
# 124 | -> ç
5
# 125 }
0
# 126 ~
0

BIN
data/pal01.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

BIN
data/pal02.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

BIN
data/williams.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

819
main.cpp
View File

@@ -15,37 +15,160 @@ Código fuente creado por JailDesigner
#include "units/texture.h" #include "units/texture.h"
#include "units/screen.h" #include "units/screen.h"
#include "units/input.h" #include "units/input.h"
#include "units/on_screen_keyboard.h"
#include "quickcg.h"
using namespace QuickCG;
using namespace std;
// Punteros
SDL_Event *event; SDL_Event *event;
SDL_Window *window; SDL_Window *window;
SDL_Renderer *renderer; SDL_Renderer *renderer;
Uint32 ticks = 0; Asset *asset;
Uint32 ticksSpeed = 15; JA_Music_t *music;
int counter = 0; JA_Sound_t *sound;
Input *input;
struct options_t *options;
Screen *screen;
Text *text;
Text *debugText;
Texture *texture;
MovingSprite *sprite;
OnScreenKeyboard *osk = nullptr;
// Variables de uso general
Uint32 ticks = 0; // Variable para la frecuencia de actualización de la lógica del programa
const Uint32 ticksSpeed = 15; // Variable para la frecuencia de actualización de la lógica del programa
bool should_exit = false; // Variable para saber si ha terminado el progra,a
int counter = 0; // Contador para lo que se necesite
string controllerName; // Nombre del primer mando detectado
string inputPressed; // Texto con el último input que se ha pulsado
int fx = 0; // Efecto seleccionado para el fondo
// 0 - Degradado
// 1 - Fuego
int fxTotal = 2; // Cantidad total de efectos disponibles para el fondo
// Variables para el efecto del degradado
int gradColorMin = 64; // Minimo color más alto del degradado int gradColorMin = 64; // Minimo color más alto del degradado
int gradColorMax = 192; // Minimo color más alto del degradado int gradColorMax = 192; // Minimo color más alto del degradado
int gradCurrentColor = 192; // Color actual más alto del degradado int gradCurrentColor = 192; // Color actual más alto del degradado
int gradBreathDirection = 0; // Indica si gradCurrentColor crece o decrece int gradBreathDirection = 0; // Indica si gradCurrentColor crece o decrece
int main(int argc, char *argv[]) // Variables para el efecto de fuego
const int fireDesp = 3; // Desplazamiento inferior del efecto de fuego para eliminar las primeras filas
const int fireWidth = 320; // Ancho del efecto de fuego
const int fireHeight = 240 + fireDesp; // Alto del efecto de fuego
Uint8 fire[fireHeight][fireWidth]; // Buffer con el fuego
Uint32 palette[256]; // Paleta de color para el fuego
int fireModifier = 28; // Valor para modificar el efecto de fuego (0-30)
SDL_Texture *fireTexture; // Textura donde pintar los pixels del fuego
Uint32 *fireBuffer; // Ubiación donde se guardan los pixeles del fuego
int pitch; // No se utiliza pero se necesita para dibujar los pixeles en la textura
SDL_Rect fireSource = {0, 0, fireWidth, fireHeight - fireDesp}; // Parte de la textura donde está el efecto de fuego que se copiará a la pantalla
// Inicializa las opciones
void initOptions();
// Inicializa la lista de recursos
void initAsset(char *argv[]);
// Inicializa SDL
void initSDL();
// Inicializa la ventana
void initWindow();
// Inicializa el gestor de eventos
void initEvent();
// Inicializa jail_audio
void initJailAudio();
// Inicializa el objeto screen
void initScreen();
// Inicializa el objeto input
void initInput();
// Inicializa el texto
void initText();
// Inicializa el teclado en pantalla
void initOnScreenKeyboard();
// Inicializa el sprite
void initSprite();
// Inicializa el efecto de fuego
void initFire();
// Inicializa todo
void initAll(char *argv[]);
// Comprueba el teclado y los eventos
void checkEvents();
// Comprueba el objeto input
void checkInput();
// Actualiza el sprite
void updateSprite();
// Actualiza el efecto de degradado
void updateGradient();
// Actualiza el efecto de fuego
void updateFire();
// Actualiza el efecto de fondo
void updateFX();
// Actualiza la lógica del programa
void update();
// Dibuja un degradado de fondo
void renderGradient();
// Dibuja el efecto de fuego
void renderFire();
// Dibuja el efecto de fondo
void renderFX();
// Dibuja el texto
void renderText();
// Dibuja los elementos del programa en pantalla
void render();
// Libera la memoria reservada
void freeAll();
// Inicializa las opciones
void initOptions()
{ {
// Inicializa las opciones options = new options_t;
struct options_t *options = new options_t;
initOptions(options); initOptions(options);
options->screen.nativeWidth = 320; options->screen.nativeWidth = 320;
options->screen.nativeHeight = 240; options->screen.nativeHeight = 240;
options->screen.nativeZoom = 2; options->screen.nativeZoom = 2;
options->screen.windowZoom = 1; options->screen.windowZoom = 1;
options->console = true; options->console = true;
}
// Inicializa la lista de recursos // Inicializa la lista de recursos
Asset *asset = new Asset(argv[0]); void initAsset(char *argv[])
{
asset = new Asset(argv[0]);
asset->add("/data/music.ogg", t_music); asset->add("/data/music.ogg", t_music);
asset->add("/data/sound.wav", t_sound); asset->add("/data/sound.wav", t_sound);
asset->add("/data/smb2.txt", t_font); asset->add("/data/smb2.txt", t_font);
asset->add("/data/smb2.png", t_bitmap); asset->add("/data/smb2.png", t_bitmap);
asset->add("/data/debug.txt", t_font); asset->add("/data/debug.txt", t_font);
asset->add("/data/debug.png", t_bitmap); asset->add("/data/debug.png", t_bitmap);
asset->add("/data/nokia.txt", t_font);
asset->add("/data/nokia.png", t_bitmap);
asset->add("/data/z80.png", t_bitmap); asset->add("/data/z80.png", t_bitmap);
asset->add("/data/notify.png", t_bitmap); asset->add("/data/notify.png", t_bitmap);
asset->add("/data/notify.wav", t_sound); asset->add("/data/notify.wav", t_sound);
@@ -55,11 +178,18 @@ int main(int argc, char *argv[])
{ {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
}
// Inicializa SDL y la ventana // Inicializa SDL
void initSDL()
{
SDL_Init(SDL_INIT_EVERYTHING); SDL_Init(SDL_INIT_EVERYTHING);
window = SDL_CreateWindow("jail_engine_demo", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, options->screen.nativeWidth * options->screen.nativeZoom * options->screen.windowZoom, options->screen.nativeHeight * options->screen.nativeZoom * options->screen.windowZoom, SDL_WINDOW_SHOWN); }
// window = SDL_CreateWindow("jail_engine_demo", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 320, 240, SDL_WINDOW_SHOWN);
// Inicializa la ventana
void initWindow()
{
window = SDL_CreateWindow("Jail Engine DEMO", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, options->screen.nativeWidth * options->screen.nativeZoom * options->screen.windowZoom, options->screen.nativeHeight * options->screen.nativeZoom * options->screen.windowZoom, SDL_WINDOW_SHOWN);
if (window != nullptr) if (window != nullptr)
{ {
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
@@ -76,46 +206,82 @@ int main(int argc, char *argv[])
{ {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
event = new SDL_Event(); }
// Inicializa jail_audio // Inicializa el gestor de eventos
void initEvent()
{
event = new SDL_Event();
}
// Inicializa jail_audio
void initJailAudio()
{
JA_Init(48000, AUDIO_S16, 2); JA_Init(48000, AUDIO_S16, 2);
JA_Music_t *music;
JA_Sound_t *sound;
music = JA_LoadMusic(asset->get("music.ogg").c_str()); music = JA_LoadMusic(asset->get("music.ogg").c_str());
sound = JA_LoadSound(asset->get("sound.wav").c_str()); sound = JA_LoadSound(asset->get("sound.wav").c_str());
// JA_PlayMusic(music, true);
}
// Inicializa el objeto screen // Inicializa el objeto screen
Screen *screen = new Screen(window, renderer, options); void initScreen()
{
screen = new Screen(window, renderer, options);
screen->addNotifier(asset->get("notify.png"), asset->get("smb2.png"), asset->get("smb2.txt"), asset->get("notify.wav")); screen->addNotifier(asset->get("notify.png"), asset->get("smb2.png"), asset->get("smb2.txt"), asset->get("notify.wav"));
}
// Inicializa el objeto input // Inicializa el objeto input
Input *input = new Input(asset->get("gamecontrollerdb.txt")); void initInput()
input->setVerbose(options->console); {
input->discoverGameController(); input = new Input(asset->get("gamecontrollerdb.txt"), options->console);
//input->bindKey(INPUT_UP, SDL_SCANCODE_UP); controllerName = input->getNumControllers() > 0 ? input->getControllerName(0) : "No se ha encontrado ningun mando";
//input->bindKey(INPUT_DOWN, SDL_SCANCODE_DOWN); }
//input->bindKey(INPUT_LEFT, SDL_SCANCODE_LEFT);
//input->bindKey(INPUT_RIGHT, SDL_SCANCODE_RIGHT); // Inicializa el texto
//input->bindGameControllerButton(INPUT_UP, SDL_CONTROLLER_BUTTON_DPAD_UP); void initText()
//input->bindGameControllerButton(INPUT_DOWN, SDL_CONTROLLER_BUTTON_DPAD_DOWN); {
//input->bindGameControllerButton(INPUT_LEFT, SDL_CONTROLLER_BUTTON_DPAD_LEFT); text = new Text(asset->get("smb2.txt"), asset->get("smb2.png"), renderer);
//input->bindGameControllerButton(INPUT_RIGHT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); debugText = new Text(asset->get("debug.txt"), asset->get("debug.png"), renderer);
string controllerName = ""; }
int numControllers = input->getNumControllers();
if (numControllers > 0) // Inicializa el teclado en pantalla
void initOnScreenKeyboard()
{
osk = new OnScreenKeyboard(renderer, input, asset->get("nokia.png"), asset->get("nokia.txt"), options, options->screen.nativeWidth - 30, options->screen.nativeHeight - 80, "JailDesigner");
osk->setBgColor({123, 99, 63});
osk->setCaption("JAILER_ID");
osk->setChars(USE_UPPER | USE_LOWER | USE_NUMBER);
}
// Elimina el teclado en pantalla
void deleteOnScreenKeyboard()
{
if (osk != nullptr)
{ {
// Obtiene el nombre del primer mando conectado delete osk;
controllerName = input->getControllerName(0); osk = nullptr;
} }
}
// Inicializa el texto // Dibuja el teclado en pantalla
Text *text = new Text(asset->get("smb2.txt"), asset->get("smb2.png"), renderer); void renderOnScreenKeyboard()
Text *debugText = new Text(asset->get("debug.txt"), asset->get("debug.png"), renderer); {
if (osk != nullptr)
osk->render();
}
// Inicializa el sprite // Actualiza el teclado en pantalla
Texture *texture = new Texture(renderer, asset->get("z80.png")); void updateOnScreenKeyboard()
MovingSprite *sprite = new MovingSprite(); {
if (osk != nullptr)
osk->update();
}
// Inicializa el sprite
void initSprite()
{
texture = new Texture(renderer, asset->get("z80.png"));
sprite = new MovingSprite();
sprite->setRenderer(renderer); sprite->setRenderer(renderer);
sprite->setTexture(texture); sprite->setTexture(texture);
sprite->setPosX(140); sprite->setPosX(140);
@@ -125,200 +291,436 @@ int main(int argc, char *argv[])
sprite->setSpriteClip({0, 0, 16, 32}); sprite->setSpriteClip({0, 0, 16, 32});
sprite->setVelX(1); sprite->setVelX(1);
sprite->setVelY(2); sprite->setVelY(2);
}
// Bucle principal // Inicializa el efecto de fuego
// JA_PlayMusic(music, true); void initFire()
bool should_exit = false; {
while (!should_exit) // Inicializa el generador de numeros aleatorios
srand((unsigned)SDL_GetTicks());
// Inicializa el buffer de fuego
for (int y = 0; y < fireHeight; ++y)
for (int x = 0; x < fireWidth; ++x)
fire[y][x] = 0;
fireTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, fireWidth, fireHeight);
// Inicializa la paleta
for (int x = 0; x < 256; ++x)
{ {
// HSLtoRGB is used to generate colors:
// Hue goes from 0 to 85: red to yellow
// Saturation is always the maximum: 255
// Lightness is 0..255 for x=0..128, and 255 for x=128..255
ColorRGB color = HSLtoRGB(ColorHSL(x / 3, 255, min(255, x * 2)));
// Comprueba el teclado y los eventos // Pon en la paleta el color calculado
while (SDL_PollEvent(event)) palette[x] = (Uint32)16777216 * 255 + 65536 * color.r + 256 * color.g + color.b;
}
// Coloca algo de fuego en la textura
for (int x = 0; x < 100; ++x)
updateFire();
}
// Inicializa todo
void initAll(char *argv[])
{
// Inicializa las opciones
initOptions();
// Inicializa la lista de recursos
initAsset(argv);
// Inicializa SDL
initSDL();
// Inicializa la ventana
initWindow();
// Inicializa el gestor de eventos
initEvent();
// Inicializa jail_audio
initJailAudio();
// Inicializa el objeto screen
initScreen();
// Inicializa el objeto input
initInput();
// Inicializa el texto
initText();
// Inicializa el teclado en pantalla
// initOnScreenKeyboard();
// Inicializa el sprite
initSprite();
// Inicializa el efecto de fuego
initFire();
}
// Comprueba el teclado y los eventos
void checkEvents()
{
while (SDL_PollEvent(event))
{
if (event->type == SDL_QUIT)
{ {
if (event->type == SDL_QUIT) should_exit = true;
break;
}
if (event->type == SDL_KEYDOWN)
{
switch (event->key.keysym.scancode)
{ {
case SDL_SCANCODE_ESCAPE:
should_exit = true; should_exit = true;
break; break;
}
if (event->type == SDL_KEYDOWN)
{
switch (event->key.keysym.scancode)
{
case SDL_SCANCODE_ESCAPE: case SDL_SCANCODE_F1:
should_exit = true; screen->decWindowSize();
break; break;
case SDL_SCANCODE_1: case SDL_SCANCODE_F2:
screen->decWindowSize(); screen->incWindowSize();
break; break;
case SDL_SCANCODE_2: case SDL_SCANCODE_F3:
screen->incWindowSize(); ++fx %= fxTotal;
break; break;
case SDL_SCANCODE_F4:
--fireModifier;
fireModifier = max(0, fireModifier);
break;
case SDL_SCANCODE_N: case SDL_SCANCODE_F5:
screen->showNotification("Ejemplo de notificacion", "con 2 lineas de texto", 0); ++fireModifier;
break; fireModifier = min(30, fireModifier);
break;
default: case SDL_SCANCODE_F6:
break; screen->showNotification("Ejemplo de notificacion", "con 2 lineas de texto", 0);
} break;
case SDL_SCANCODE_F7:
if (osk == nullptr)
initOnScreenKeyboard();
else
deleteOnScreenKeyboard();
break;
case SDL_SCANCODE_F8:
break;
default:
break;
} }
} }
string inputPressed = ""; }
if (input->checkInput(INPUT_LEFT)) }
// Comprueba el objeto input
void checkInput()
{
inputPressed = "";
if (input->checkInput(INPUT_LEFT))
{
inputPressed = "Izquierda";
}
if (input->checkInput(INPUT_RIGHT))
{
inputPressed = "Derecha";
}
if (input->checkInput(INPUT_UP))
{
inputPressed = "Arriba";
}
if (input->checkInput(INPUT_DOWN))
{
inputPressed = "Abajo";
}
}
// Actualiza el sprite
void updateSprite()
{
if (sprite->getPosX() + sprite->getWidth() > options->screen.nativeWidth or sprite->getPosX() < 0)
{
sprite->undoMoveX();
int spr_direction = 1;
int spr_force = 1;
if (sprite->getVelX() > 0)
{ {
inputPressed = "LEFT"; spr_direction = -1;
} }
if (input->checkInput(INPUT_RIGHT)) if (SDL_GetTicks() % 2 == 0)
{ {
inputPressed = "RIGHT"; spr_force = 2;
} }
if (input->checkInput(INPUT_UP)) sprite->setVelX(spr_force * spr_direction);
{ JA_PlaySound(sound);
inputPressed = "UP";
}
if (input->checkInput(INPUT_DOWN))
{
inputPressed = "DOWN";
}
// Actualiza la lógica del programa
if (SDL_GetTicks() - ticks > ticksSpeed)
{
// Actualiza la variable
ticks = SDL_GetTicks();
// Incrementa el contador
counter++;
// Actualiza el objeto screen
screen->update();
// Actualiza el sprite
if (sprite->getPosX() + sprite->getWidth() > options->screen.nativeWidth or sprite->getPosX() < 0)
{
sprite->undoMoveX();
int spr_direction = 1;
int spr_force = 1;
if (sprite->getVelX() > 0)
{
spr_direction = -1;
}
if (SDL_GetTicks() % 2 == 0)
{
spr_force = 2;
}
sprite->setVelX(spr_force * spr_direction);
JA_PlaySound(sound);
}
if (sprite->getPosY() + sprite->getHeight() > options->screen.nativeHeight or sprite->getPosY() < 0)
{
sprite->undoMoveY();
int spr_direction = 1;
int spr_force = 1;
if (sprite->getVelY() > 0)
{
spr_direction = -1;
}
if (SDL_GetTicks() % 2 == 0)
{
spr_force = 2;
}
sprite->setVelY(spr_force * spr_direction);
JA_PlaySound(sound);
}
sprite->update();
// Actualiza el degradado
if (counter % 1 == 0)
{
gradBreathDirection == 0 ? gradCurrentColor-- : gradCurrentColor++;
if (gradCurrentColor == gradColorMin)
{
gradBreathDirection = 1;
}
if (gradCurrentColor == gradColorMax)
{
gradBreathDirection = 0;
}
}
}
// Dibuja en pantalla
screen->start();
screen->clean();
// Dibuja un degradado de fondo
const int gradFirstLine = options->screen.nativeHeight / 3;
const int gradLastLine = options->screen.nativeHeight;
const int gradNumLines = gradLastLine - gradFirstLine;
const int gradColorFrom = 0;
for (int i = gradFirstLine; i < gradLastLine; ++i)
{
float step = ((float)(i - gradFirstLine) / gradNumLines);
int color = gradColorFrom + ((gradCurrentColor - gradColorFrom) * step);
SDL_SetRenderDrawColor(renderer, color, 0x00, 0x00, 0xFF);
SDL_RenderDrawLine(renderer, 0, i, options->screen.nativeWidth, i);
}
for (int i = gradLastLine; i > gradFirstLine; --i)
{
float step = ((float)(i - gradFirstLine) / gradNumLines);
int alpha = 0 + ((255 - 0) * step);
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0xFF, alpha);
SDL_RenderDrawLine(renderer, 0, gradLastLine - i, options->screen.nativeWidth, gradLastLine - i);
}
// Dibuja el sprite
sprite->render();
// Escribe el texto
text->setZoom(2);
text->writeDX(TXT_CENTER | TXT_SHADOW, options->screen.nativeWidth / 2, text->getCharacterSize(), "Jail Engine DEMO", 1, {255, 255, 255}, 1, {48, 48, 48});
text->disableZoom();
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE, options->screen.nativeWidth / 2, text->getCharacterSize() * 7, "Pulsa 'N' para mostrar", 1, {240, 240, 240}, 1, {0, 0, 192});
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE, options->screen.nativeWidth / 2, text->getCharacterSize() * 8, "una notificacion", 1, {240, 240, 240}, 1, {0, 0, 192});
debugText->writeCentered(options->screen.nativeWidth / 2, options->screen.nativeHeight / 2, controllerName);
debugText->writeCentered(options->screen.nativeWidth / 2, (options->screen.nativeHeight / 2) + (debugText->getCharacterSize()*2), inputPressed);
// Vuelca el buffer en pantalla
screen->blit();
} }
if (sprite->getPosY() + sprite->getHeight() > options->screen.nativeHeight or sprite->getPosY() < 0)
{
sprite->undoMoveY();
int spr_direction = 1;
int spr_force = 1;
if (sprite->getVelY() > 0)
{
spr_direction = -1;
}
if (SDL_GetTicks() % 2 == 0)
{
spr_force = 2;
}
sprite->setVelY(spr_force * spr_direction);
JA_PlaySound(sound);
}
sprite->update();
}
// Actualiza el degradado
void updateGradient()
{
if (counter % 1 == 0)
{
gradBreathDirection == 0 ? gradCurrentColor-- : gradCurrentColor++;
if (gradCurrentColor == gradColorMin)
{
gradBreathDirection = 1;
}
if (gradCurrentColor == gradColorMax)
{
gradBreathDirection = 0;
}
}
}
// Actualiza el efecto de fuego
void updateFire()
{
const int w = fireWidth;
const int h = fireHeight;
const int mod = 157 - fireModifier;
// Bloquea la textura para poder escribir en los pixeles
SDL_LockTexture(fireTexture, NULL, (void **)&fireBuffer, &pitch);
// Crea la fila inferior del fuego con pixels aleatorios
for (int x = 0; x < w; ++x)
{
fire[h - 1][x] = rand() % 256;
fireBuffer[x + (h - 1) * 320] = palette[fire[h - 1][x]];
}
// Calcula los pixeles del efecto de fuego, desde la fila superior hasta la inferior
for (int y = 0; y < h - 1; ++y)
for (int x = 0; x < w; ++x)
{
fire[y][x] = ((fire[(y + 1) % h][(x - 1 + w) % w] + fire[(y + 1) % h][(x) % w] + fire[(y + 1) % h][(x + 1) % w] + fire[(y + 2) % h][(x) % w]) * 32) / mod;
fireBuffer[x + y * 320] = palette[fire[y][x]];
}
// Desbloquea la textura
SDL_UnlockTexture(fireTexture);
}
// Actualiza el efecto de fondo
void updateFX()
{
switch (fx)
{
case 0: // Degradado
updateGradient();
break;
case 1: // Fuego
updateFire();
break;
default:
break;
}
}
// Actualiza la lógica del programa
void update()
{
// Comprueba el teclado y los eventos
checkEvents();
// Comprueba el objeto input
checkInput();
// Actualiza la lógica del programa
if (SDL_GetTicks() - ticks > ticksSpeed)
{
// Actualiza la variable
ticks = SDL_GetTicks();
// Incrementa el contador
counter++;
// Actualiza los objetos
updateOnScreenKeyboard(); // Actualiza el teclado en pantalla
screen->update(); // Actualiza la pantalla
updateSprite(); // Actualiza el sprite
// Actualiza el efecto de fondo
updateFX();
}
}
// Dibuja un degradado de fondo
void renderGradient()
{
const int gradFirstLine = options->screen.nativeHeight / 3;
const int gradLastLine = options->screen.nativeHeight;
const int gradNumLines = gradLastLine - gradFirstLine;
const int gradColorFrom = 0;
for (int i = gradFirstLine; i < gradLastLine; ++i)
{
float step = ((float)(i - gradFirstLine) / gradNumLines);
int color = gradColorFrom + ((gradCurrentColor - gradColorFrom) * step);
SDL_SetRenderDrawColor(renderer, color, 0x00, 0x00, 0xFF);
SDL_RenderDrawLine(renderer, 0, i, options->screen.nativeWidth, i);
}
for (int i = gradLastLine; i > gradFirstLine; --i)
{
float step = ((float)(i - gradFirstLine) / gradNumLines);
int alpha = 0 + ((255 - 0) * step);
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0xFF, alpha);
SDL_RenderDrawLine(renderer, 0, gradLastLine - i, options->screen.nativeWidth, gradLastLine - i);
}
}
// Dibuja el efecto de fuego
void renderFire()
{
SDL_RenderCopy(renderer, fireTexture, &fireSource, NULL);
}
// Dibuja el efecto de fondo
void renderFX()
{
switch (fx)
{
case 0: // Degradado
renderGradient();
break;
case 1: // Fuego
renderFire();
break;
default:
break;
}
}
// Dibuja el texto
void renderText()
{
const string text1 = "Jail Engine DEMO";
const string text2 = "2023,2024 JailDesigner";
const string text3 = "Pulsa 'F1' o 'F2' para disminuir o aumentar la ventana";
const string text4 = "Pulsa 'F3' para cambiar el efecto de fondo: ";
const string text5 = "Pulsa 'F4' o 'F5' para modificar el efecto de fuego: ";
const string text6 = "Pulsa 'F6' para mostrar una notificacion";
const string text7 = "Pulsa 'F7' para mostrar u ocultar el teclado";
const string text8 = "Pulsa 'F8' para añadir o eliminar sprites";
const string text9 = "Controles:";
const string text10 = "Pulsa 'ESCAPE' para terminar el programa";
const string fxName = fx == 0 ? "Degradado" : "Fuego";
const int center = options->screen.nativeWidth / 2;
text->setZoom(2);
text->writeDX(TXT_CENTER | TXT_STROKE, center, text->getCharacterSize(), text1, 1, {255, 255, 255}, 1, {0, 0, 192});
text->disableZoom();
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE, center, text->getCharacterSize() * 4, text2, 1, {240, 240, 240}, 1, {0, 0, 192});
debugText->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE, center, text->getCharacterSize() * 7, text3, 1, {240, 240, 240}, 1, {0, 0, 192});
debugText->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE, center, text->getCharacterSize() * 9, text4 + fxName, 1, {240, 240, 240}, 1, {0, 0, 192});
if (fxName == "Fuego")
debugText->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE, center, text->getCharacterSize() * 10, text5 + to_string(fireModifier), 1, {240, 240, 240}, 1, {0, 0, 192});
debugText->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE, center, text->getCharacterSize() * 12, text6, 1, {240, 240, 240}, 1, {0, 0, 192});
debugText->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE, center, text->getCharacterSize() * 13, text7, 1, {240, 240, 240}, 1, {0, 0, 192});
debugText->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE, center, text->getCharacterSize() * 14, text8, 1, {240, 240, 240}, 1, {0, 0, 192});
debugText->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE, center, text->getCharacterSize() * 16, text9, 1, {240, 240, 240}, 1, {0, 0, 192});
debugText->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE, center, text->getCharacterSize() * 17, controllerName, 1, {240, 240, 240}, 1, {0, 0, 192});
debugText->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE, center, text->getCharacterSize() * 18, inputPressed, 1, {240, 240, 240}, 1, {0, 0, 192});
debugText->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE, center, options->screen.nativeHeight - (text->getCharacterSize() * 2), text10, 1, {240, 240, 240}, 1, {0, 0, 192});
}
// Dibuja los elementos del programa en pantalla
void render()
{
// Prepara el objeto screen para dibujar
screen->start();
screen->clean();
// Dibuja el efecto de fondo
renderFX();
// Dibuja el sprite
sprite->render();
// Dinuja el texto
renderText();
// Dibuja el teclado en pantalla
renderOnScreenKeyboard();
// Vuelca el buffer en pantalla
screen->blit();
}
// Libera la memoria reservada
void freeAll()
{
// Finaliza el sprite // Finaliza el sprite
if (sprite != nullptr) if (sprite != nullptr)
{
delete sprite; delete sprite;
}
if (texture != nullptr) if (texture != nullptr)
{
delete texture; delete texture;
}
// Finaliza el teclado en pantalla
if (osk != nullptr)
delete osk;
// Finaliza el texto // Finaliza el texto
if (text != nullptr) if (text != nullptr)
{
delete text; delete text;
}
if (debugText != nullptr) if (debugText != nullptr)
{
delete debugText; delete debugText;
}
// Finaliza el objeto input // Finaliza el objeto input
if (input != nullptr) if (input != nullptr)
{
delete input; delete input;
}
// Finaliza el objeto screen // Finaliza el objeto screen
if (screen != nullptr) if (screen != nullptr)
{
delete screen; delete screen;
}
// Finaliza jail_audio // Finaliza jail_audio
JA_DeleteSound(sound); JA_DeleteSound(sound);
@@ -326,24 +728,37 @@ int main(int argc, char *argv[])
// Finaliza el objeto con la lista de recuros // Finaliza el objeto con la lista de recuros
if (asset != nullptr) if (asset != nullptr)
{
delete asset; delete asset;
}
// Finaliza las opciones // Finaliza las opciones
if (options != nullptr) if (options != nullptr)
{
delete options; delete options;
}
// Finaliza SDL y la ventana // Finaliza SDL y la ventana
if (event != nullptr) if (event != nullptr)
{
delete event; delete event;
}
SDL_DestroyRenderer(renderer); SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window); SDL_DestroyWindow(window);
SDL_Quit(); SDL_Quit();
}
int main(int argc, char *argv[])
{
// Inicializa todo
initAll(argv);
// Bucle principal
while (!should_exit)
{
// Actualiza la lógica del programa
update();
// Dibuja los elementos del programa en pantalla
render();
}
// Libera la memoria reservada
freeAll();
return 0; return 0;
} }

428
quickcg.cpp Normal file
View File

@@ -0,0 +1,428 @@
/*
QuickCG 20191227
Copyright (c) 2004-2019, Lode Vandevenne
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
QuickCG is an SDL codebase that wraps some of the SDL functionality.
It' used by Lode's Computer Graphics Tutorial to work with simple C++ calls
to demonstrate graphical programs.
QuickCG can handle some things that standard C++ doesn't but that are commonly useful, such as:
-drawing graphics
-a bitmap font
-simplified saving and loading of files
-reading keyboard and mouse input
-playing sound
-color models
-loading images
*/
#include "quickcg.h"
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <iostream>
#include <fstream>
namespace QuickCG
{
////////////////////////////////////////////////////////////////////////////////
// COLOR STRUCTS/////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
ColorRGB::ColorRGB(Uint8 r, Uint8 g, Uint8 b)
{
this->r = r;
this->g = g;
this->b = b;
}
ColorRGB::ColorRGB(const ColorRGB8bit &color)
{
this->r = color.r;
this->g = color.g;
this->b = color.b;
}
ColorRGB::ColorRGB()
{
this->r = 0;
this->g = 0;
this->b = 0;
}
ColorRGB8bit::ColorRGB8bit(Uint8 r, Uint8 g, Uint8 b)
{
this->r = r;
this->g = g;
this->b = b;
}
ColorRGB8bit::ColorRGB8bit(const ColorRGB &color)
{
this->r = color.r;
this->g = color.g;
this->b = color.b;
}
ColorRGB8bit::ColorRGB8bit()
{
this->r = 0;
this->g = 0;
this->b = 0;
}
// Add two colors
ColorRGB operator+(const ColorRGB &color, const ColorRGB &color2)
{
ColorRGB c;
c.r = color.r + color2.r;
c.g = color.g + color2.g;
c.b = color.b + color2.b;
return c;
}
// Subtract two colors
ColorRGB operator-(const ColorRGB &color, const ColorRGB &color2)
{
ColorRGB c;
c.r = color.r - color2.r;
c.g = color.g - color2.g;
c.b = color.b - color2.b;
return c;
}
// Multiplies a color with an integer
ColorRGB operator*(const ColorRGB &color, int a)
{
ColorRGB c;
c.r = color.r * a;
c.g = color.g * a;
c.b = color.b * a;
return c;
}
// Multiplies a color with an integer
ColorRGB operator*(int a, const ColorRGB &color)
{
ColorRGB c;
c.r = color.r * a;
c.g = color.g * a;
c.b = color.b * a;
return c;
}
// Divides a color through an integer
ColorRGB operator/(const ColorRGB &color, int a)
{
if (a == 0)
return color;
ColorRGB c;
c.r = color.r / a;
c.g = color.g / a;
c.b = color.b / a;
return c;
}
// Are both colors equal?
bool operator==(const ColorRGB &color, const ColorRGB &color2)
{
return (color.r == color2.r && color.g == color2.g && color.b == color2.b);
}
// Are both colors not equal?
bool operator!=(const ColorRGB &color, const ColorRGB &color2)
{
return (!(color.r == color2.r && color.g == color2.g && color.b == color2.b));
}
ColorHSL::ColorHSL(Uint8 h, Uint8 s, Uint8 l)
{
this->h = h;
this->s = s;
this->l = l;
}
ColorHSL::ColorHSL()
{
this->h = 0;
this->s = 0;
this->l = 0;
}
ColorHSV::ColorHSV(Uint8 h, Uint8 s, Uint8 v)
{
this->h = h;
this->s = s;
this->v = v;
}
ColorHSV::ColorHSV()
{
this->h = 0;
this->s = 0;
this->v = 0;
}
////////////////////////////////////////////////////////////////////////////////
// COLOR CONVERSIONS/////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/*
Convert colors from one type to another
r=red g=green b=blue h=hue s=saturation l=lightness v=value
Color components from the color structs are Uint8's between 0 and 255
color components used in the calculations are normalized between 0.0-1.0
*/
// Converts an RGB color to HSL color
ColorHSL RGBtoHSL(const ColorRGB &colorRGB)
{
float r, g, b, h = 0, s = 0, l; // this function works with floats between 0 and 1
r = colorRGB.r / 256.0;
g = colorRGB.g / 256.0;
b = colorRGB.b / 256.0;
float maxColor = std::max(r, std::max(g, b));
float minColor = std::min(r, std::min(g, b));
if (minColor == maxColor) // R = G = B, so it's a shade of grey
{
h = 0; // it doesn't matter what value it has
s = 0;
l = r; // doesn't matter if you pick r, g, or b
}
else
{
l = (minColor + maxColor) / 2;
if (l < 0.5)
s = (maxColor - minColor) / (maxColor + minColor);
if (l >= 0.5)
s = (maxColor - minColor) / (2.0 - maxColor - minColor);
if (r == maxColor)
h = (g - b) / (maxColor - minColor);
if (g == maxColor)
h = 2.0 + (b - r) / (maxColor - minColor);
if (b == maxColor)
h = 4.0 + (r - g) / (maxColor - minColor);
h /= 6; // to bring it to a number between 0 and 1
if (h < 0)
h += 1;
}
ColorHSL colorHSL;
colorHSL.h = int(h * 255.0);
colorHSL.s = int(s * 255.0);
colorHSL.l = int(l * 255.0);
return colorHSL;
}
// Converts an HSL color to RGB color
ColorRGB HSLtoRGB(const ColorHSL &colorHSL)
{
float r, g, b, h, s, l; // this function works with floats between 0 and 1
float temp1, temp2, tempr, tempg, tempb;
h = colorHSL.h / 256.0;
s = colorHSL.s / 256.0;
l = colorHSL.l / 256.0;
// If saturation is 0, the color is a shade of grey
if (s == 0)
r = g = b = l;
// If saturation > 0, more complex calculations are needed
else
{
// set the temporary values
if (l < 0.5)
temp2 = l * (1 + s);
else
temp2 = (l + s) - (l * s);
temp1 = 2 * l - temp2;
tempr = h + 1.0 / 3.0;
if (tempr > 1.0)
tempr--;
tempg = h;
tempb = h - 1.0 / 3.0;
if (tempb < 0.0)
tempb++;
// red
if (tempr < 1.0 / 6.0)
r = temp1 + (temp2 - temp1) * 6.0 * tempr;
else if (tempr < 0.5)
r = temp2;
else if (tempr < 2.0 / 3.0)
r = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempr) * 6.0;
else
r = temp1;
// green
if (tempg < 1.0 / 6.0)
g = temp1 + (temp2 - temp1) * 6.0 * tempg;
else if (tempg < 0.5)
g = temp2;
else if (tempg < 2.0 / 3.0)
g = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempg) * 6.0;
else
g = temp1;
// blue
if (tempb < 1.0 / 6.0)
b = temp1 + (temp2 - temp1) * 6.0 * tempb;
else if (tempb < 0.5)
b = temp2;
else if (tempb < 2.0 / 3.0)
b = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempb) * 6.0;
else
b = temp1;
}
ColorRGB colorRGB;
colorRGB.r = int(r * 255.0);
colorRGB.g = int(g * 255.0);
colorRGB.b = int(b * 255.0);
return colorRGB;
}
// Converts an RGB color to HSV color
ColorHSV RGBtoHSV(const ColorRGB &colorRGB)
{
float r, g, b, h = 0.0, s = 0.0, v; // this function works with floats between 0 and 1
r = colorRGB.r / 256.0;
g = colorRGB.g / 256.0;
b = colorRGB.b / 256.0;
float maxColor = std::max(r, std::max(g, b));
float minColor = std::min(r, std::min(g, b));
v = maxColor;
if (maxColor != 0.0) // avoid division by zero when the color is black
{
s = (maxColor - minColor) / maxColor;
}
if (s == 0.0)
{
h = 0.0; // it doesn't matter what value it has
}
else
{
if (r == maxColor)
h = (g - b) / (maxColor - minColor);
if (g == maxColor)
h = 2.0 + (b - r) / (maxColor - minColor);
if (b == maxColor)
h = 4.0 + (r - g) / (maxColor - minColor);
h /= 6.0; // to bring it to a number between 0 and 1
if (h < 0.0)
h++;
}
ColorHSV colorHSV;
colorHSV.h = int(h * 255.0);
colorHSV.s = int(s * 255.0);
colorHSV.v = int(v * 255.0);
return colorHSV;
}
// Converts an HSV color to RGB color
ColorRGB HSVtoRGB(const ColorHSV &colorHSV)
{
float r, g, b, h, s, v; // this function works with floats between 0 and 1
h = colorHSV.h / 256.0;
s = colorHSV.s / 256.0;
v = colorHSV.v / 256.0;
// if saturation is 0, the color is a shade of grey
if (s == 0.0)
r = g = b = v;
// if saturation > 0, more complex calculations are needed
else
{
float f, p, q, t;
int i;
h *= 6.0; // to bring hue to a number between 0 and 6, better for the calculations
i = int(floor(h)); // e.g. 2.7 becomes 2 and 3.01 becomes 3 or 4.9999 becomes 4
f = h - i; // the fractional part of h
p = v * (1.0 - s);
q = v * (1.0 - (s * f));
t = v * (1.0 - (s * (1.0 - f)));
switch (i)
{
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
case 5:
r = v;
g = p;
b = q;
break;
default:
r = g = b = 0;
break;
}
}
ColorRGB colorRGB;
colorRGB.r = int(r * 255.0);
colorRGB.g = int(g * 255.0);
colorRGB.b = int(b * 255.0);
return colorRGB;
}
Uint32 RGBtoINT(const ColorRGB &colorRGB)
{
return 65536 * colorRGB.r + 256 * colorRGB.g + colorRGB.b;
}
ColorRGB INTtoRGB(Uint32 colorINT)
{
ColorRGB colorRGB;
colorRGB.r = (colorINT / 65536) % 256;
colorRGB.g = (colorINT / 256) % 256;
colorRGB.b = colorINT % 256;
return colorRGB;
}
}

189
quickcg.h Normal file
View File

@@ -0,0 +1,189 @@
/*
QuickCG 20191227
Copyright (c) 2004-2019, Lode Vandevenne
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
QuickCG is an SDL 1.2 codebase that wraps some of the SDL 1.2 functionality.
It's used by Lode's Computer Graphics Tutorial to work with simple function calls
to demonstrate graphical programs. It may or may not be of industrial strength
for games, though I've actually used it for some.
QuickCG can handle some things that standard C++ does not but that are useful, such as:
-drawing graphics
-a bitmap font
-simplified saving and loading of files
-reading keyboard and mouse input
-playing sound
-color models
-loading images
Contact info:
My email address is (puzzle the account and domain together with an @ symbol):
Domain: gmail dot com.
Account: lode dot vandevenne.
*/
#ifndef _quickcg_h_included
#define _quickcg_h_included
#include <SDL2/SDL.h>
#include <string>
#include <sstream>
#include <iomanip>
#include <vector>
#include <algorithm> //std::min and std::max
namespace QuickCG
{
////////////////////////////////////////////////////////////////////////////////
// useful templates//////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// don't know why, but the standard C++ abs sometimes gives cryptic errors? if so use this :D
template <typename T>
const T template_abs(const T &a)
{
return (a < 0) ? -a : a;
}
// usage: std::string str = valtostr(25454.91654654f);
template <typename T>
std::string valtostr(const T &val)
{
std::ostringstream sstream;
sstream << val;
return sstream.str();
}
// usage: double val = strtoval<double>("465498.654");
template <typename T>
T strtoval(const std::string &s)
{
std::istringstream sstream(s);
T val;
sstream >> val;
return val;
}
// length is decimal precision of the floating point number
template <typename T>
std::string valtostr(const T &val, int length, bool fixed = true)
{
std::ostringstream sstream;
if (fixed)
sstream << std::fixed;
sstream << std::setprecision(length) << val;
return sstream.str();
}
////////////////////////////////////////////////////////////////////////////////
// COLOR STRUCTS/////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
struct ColorRGB8bit;
// a color with 3 components: r, g and b
struct ColorRGB
{
int r;
int g;
int b;
ColorRGB(Uint8 r, Uint8 g, Uint8 b);
ColorRGB(const ColorRGB8bit &color);
ColorRGB();
};
ColorRGB operator+(const ColorRGB &color, const ColorRGB &color2);
ColorRGB operator-(const ColorRGB &color, const ColorRGB &color2);
ColorRGB operator*(const ColorRGB &color, int a);
ColorRGB operator*(int a, const ColorRGB &color);
ColorRGB operator/(const ColorRGB &color, int a);
bool operator==(const ColorRGB &color, const ColorRGB &color2);
bool operator!=(const ColorRGB &color, const ColorRGB &color2);
static const ColorRGB RGB_Black(0, 0, 0);
static const ColorRGB RGB_Red(255, 0, 0);
static const ColorRGB RGB_Green(0, 255, 0);
static const ColorRGB RGB_Blue(0, 0, 255);
static const ColorRGB RGB_Cyan(0, 255, 255);
static const ColorRGB RGB_Magenta(255, 0, 255);
static const ColorRGB RGB_Yellow(255, 255, 0);
static const ColorRGB RGB_White(255, 255, 255);
static const ColorRGB RGB_Gray(128, 128, 128);
static const ColorRGB RGB_Grey(192, 192, 192);
static const ColorRGB RGB_Maroon(128, 0, 0);
static const ColorRGB RGB_Darkgreen(0, 128, 0);
static const ColorRGB RGB_Navy(0, 0, 128);
static const ColorRGB RGB_Teal(0, 128, 128);
static const ColorRGB RGB_Purple(128, 0, 128);
static const ColorRGB RGB_Olive(128, 128, 0);
// a color with 3 components: r, g and b
struct ColorRGB8bit
{
Uint8 r;
Uint8 g;
Uint8 b;
ColorRGB8bit(Uint8 r, Uint8 g, Uint8 b);
ColorRGB8bit(const ColorRGB &color);
ColorRGB8bit();
};
// a color with 3 components: h, s and l
struct ColorHSL
{
int h;
int s;
int l;
ColorHSL(Uint8 h, Uint8 s, Uint8 l);
ColorHSL();
};
// a color with 3 components: h, s and v
struct ColorHSV
{
int h;
int s;
int v;
ColorHSV(Uint8 h, Uint8 s, Uint8 v);
ColorHSV();
};
////////////////////////////////////////////////////////////////////////////////
// COLOR CONVERSIONS/////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
ColorHSL RGBtoHSL(const ColorRGB &colorRGB);
ColorRGB HSLtoRGB(const ColorHSL &colorHSL);
ColorHSV RGBtoHSV(const ColorRGB &colorRGB);
ColorRGB HSVtoRGB(const ColorHSV &colorHSV);
Uint32 RGBtoINT(const ColorRGB &colorRGB);
ColorRGB INTtoRGB(Uint32 colorINT);
}
#endif

478
units/gif.c Normal file
View File

@@ -0,0 +1,478 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#define EXTENSION_INTRODUCER 0x21
#define IMAGE_DESCRIPTOR 0x2C
#define TRAILER 0x3B
#define GRAPHIC_CONTROL 0xF9
#define APPLICATION_EXTENSION 0xFF
#define COMMENT_EXTENSION 0xFE
#define PLAINTEXT_EXTENSION 0x01
#define READ(dst, size) memcpy(dst, buffer, size); buffer += size
typedef struct
{
unsigned short width;
unsigned short height;
unsigned char fields;
unsigned char background_color_index;
unsigned char pixel_aspect_ratio;
}
screen_descriptor_t;
typedef struct
{
unsigned char r;
unsigned char g;
unsigned char b;
}
rgb;
typedef struct
{
unsigned short image_left_position;
unsigned short image_top_position;
unsigned short image_width;
unsigned short image_height;
unsigned char fields;
}
image_descriptor_t;
typedef struct
{
unsigned char byte;
int prev;
int len;
}
dictionary_entry_t;
typedef struct
{
unsigned char extension_code;
unsigned char block_size;
}
extension_t;
typedef struct
{
unsigned char fields;
unsigned short delay_time;
unsigned char transparent_color_index;
}
graphic_control_extension_t;
typedef struct
{
unsigned char application_id[ 8 ];
unsigned char version[ 3 ];
}
application_extension_t;
typedef struct
{
unsigned short left;
unsigned short top;
unsigned short width;
unsigned short height;
unsigned char cell_width;
unsigned char cell_height;
unsigned char foreground_color;
unsigned char background_color;
}
plaintext_extension_t;
//static unsigned short width = 0;
//static unsigned short height = 0;
//static unsigned char* uncompressed_data = NULL;
void uncompress( int code_length,
const unsigned char *input,
int input_length,
unsigned char *out )
{
//int maxbits;
int i, bit;
int code, prev = -1;
dictionary_entry_t *dictionary;
int dictionary_ind;
unsigned int mask = 0x01;
int reset_code_length;
int clear_code; // This varies depending on code_length
int stop_code; // one more than clear code
int match_len;
clear_code = 1 << ( code_length );
stop_code = clear_code + 1;
// To handle clear codes
reset_code_length = code_length;
// Create a dictionary large enough to hold "code_length" entries.
// Once the dictionary overflows, code_length increases
dictionary = ( dictionary_entry_t * )
malloc( sizeof( dictionary_entry_t ) * ( 1 << ( code_length + 1 ) ) );
// Initialize the first 2^code_len entries of the dictionary with their
// indices. The rest of the entries will be built up dynamically.
// Technically, it shouldn't be necessary to initialize the
// dictionary. The spec says that the encoder "should output a
// clear code as the first code in the image data stream". It doesn't
// say must, though...
for ( dictionary_ind = 0;
dictionary_ind < ( 1 << code_length );
dictionary_ind++ )
{
dictionary[ dictionary_ind ].byte = dictionary_ind;
// XXX this only works because prev is a 32-bit int (> 12 bits)
dictionary[ dictionary_ind ].prev = -1;
dictionary[ dictionary_ind ].len = 1;
}
// 2^code_len + 1 is the special "end" code; don't give it an entry here
dictionary_ind++;
dictionary_ind++;
// TODO verify that the very last byte is clear_code + 1
while ( input_length )
{
code = 0x0;
// Always read one more bit than the code length
for ( i = 0; i < ( code_length + 1 ); i++ )
{
// This is different than in the file read example; that
// was a call to "next_bit"
bit = ( *input & mask ) ? 1 : 0;
mask <<= 1;
if ( mask == 0x100 )
{
mask = 0x01;
input++;
input_length--;
}
code = code | ( bit << i );
}
if ( code == clear_code )
{
code_length = reset_code_length;
dictionary = ( dictionary_entry_t * ) realloc( dictionary,
sizeof( dictionary_entry_t ) * ( 1 << ( code_length + 1 ) ) );
for ( dictionary_ind = 0;
dictionary_ind < ( 1 << code_length );
dictionary_ind++ )
{
dictionary[ dictionary_ind ].byte = dictionary_ind;
// XXX this only works because prev is a 32-bit int (> 12 bits)
dictionary[ dictionary_ind ].prev = -1;
dictionary[ dictionary_ind ].len = 1;
}
dictionary_ind++;
dictionary_ind++;
prev = -1;
continue;
}
else if ( code == stop_code )
{
/*if ( input_length > 1 )
{
fprintf( stderr, "Malformed GIF (early stop code)\n" );
exit( 0 );
}*/
break;
}
// Update the dictionary with this character plus the _entry_
// (character or string) that came before it
if ( ( prev > -1 ) && ( code_length < 12 ) )
{
if ( code > dictionary_ind )
{
fprintf( stderr, "code = %.02x, but dictionary_ind = %.02x\n",
code, dictionary_ind );
exit( 0 );
}
// Special handling for KwKwK
if ( code == dictionary_ind )
{
int ptr = prev;
while ( dictionary[ ptr ].prev != -1 )
{
ptr = dictionary[ ptr ].prev;
}
dictionary[ dictionary_ind ].byte = dictionary[ ptr ].byte;
}
else
{
int ptr = code;
while ( dictionary[ ptr ].prev != -1 )
{
ptr = dictionary[ ptr ].prev;
}
dictionary[ dictionary_ind ].byte = dictionary[ ptr ].byte;
}
dictionary[ dictionary_ind ].prev = prev;
dictionary[ dictionary_ind ].len = dictionary[ prev ].len + 1;
dictionary_ind++;
// GIF89a mandates that this stops at 12 bits
if ( ( dictionary_ind == ( 1 << ( code_length + 1 ) ) ) &&
( code_length < 11 ) )
{
code_length++;
dictionary = ( dictionary_entry_t * ) realloc( dictionary,
sizeof( dictionary_entry_t ) * ( 1 << ( code_length + 1 ) ) );
}
}
prev = code;
// Now copy the dictionary entry backwards into "out"
match_len = dictionary[ code ].len;
while ( code != -1 )
{
out[ dictionary[ code ].len - 1 ] = dictionary[ code ].byte;
if ( dictionary[ code ].prev == code )
{
fprintf( stderr, "Internal error; self-reference." );
exit( 0 );
}
code = dictionary[ code ].prev;
}
out += match_len;
}
}
static int read_sub_blocks( unsigned char* buffer, unsigned char **data )
{
int data_length;
int index;
unsigned char block_size;
// Everything following are data sub-blocks, until a 0-sized block is
// encountered.
data_length = 0;
*data = NULL;
index = 0;
while ( 1 )
{
READ(&block_size, 1);
if ( block_size == 0 ) // end of sub-blocks
{
break;
}
data_length += block_size;
*data = (unsigned char*)realloc( *data, data_length );
// TODO this could be split across block size boundaries
READ(*data + index, block_size);
index += block_size;
}
return data_length;
}
unsigned char* process_image_descriptor( unsigned char* buffer,
rgb *gct,
int gct_size,
int resolution_bits )
{
image_descriptor_t image_descriptor;
int compressed_data_length;
unsigned char *compressed_data = NULL;
unsigned char lzw_code_size;
int uncompressed_data_length = 0;
unsigned char *uncompressed_data = NULL;
// TODO there could actually be lots of these
READ(&image_descriptor, 9);
// TODO if LCT = true, read the LCT
READ(&lzw_code_size, 1);
compressed_data_length = read_sub_blocks( buffer, &compressed_data );
// width = image_descriptor.image_width;
// height = image_descriptor.image_height;
uncompressed_data_length = image_descriptor.image_width *
image_descriptor.image_height;
uncompressed_data = (unsigned char*)malloc( uncompressed_data_length );
uncompress( lzw_code_size, compressed_data, compressed_data_length,
uncompressed_data );
if ( compressed_data ) free( compressed_data );
//if ( uncompressed_data )
// free( uncompressed_data );
return uncompressed_data;
}
/**
* @param gif_file the file descriptor of a file containing a
* GIF-encoded file. This should point to the first byte in
* the file when invoked.
*/
#define rb (*(buffer++))
uint32_t* LoadPalette(unsigned char *buffer) {
unsigned char header[7];
screen_descriptor_t screen_descriptor;
//int color_resolution_bits;
int global_color_table_size = 0; // number of entries in global_color_table
uint32_t *global_color_table = NULL;
READ(header, 6);
READ(&screen_descriptor, 7);
//color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1;
global_color_table = (uint32_t *)calloc(1, 1024);
if (screen_descriptor.fields & 0x80) {
global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
//global_color_table = (rgb *)malloc(3 * global_color_table_size);
//READ(global_color_table, 3 * global_color_table_size);
for (int i=0; i<global_color_table_size;++i) {
global_color_table[i] = (buffer[0]<<16) + (buffer[1]<<8) + buffer[2];
buffer+=3;
}
}
return global_color_table;
}
static unsigned char* process_gif_stream(unsigned char *buffer, unsigned short* w, unsigned short* h)
{
unsigned char header[ 7 ];
screen_descriptor_t screen_descriptor;
int color_resolution_bits;
int global_color_table_size =0; // number of entries in global_color_table
rgb *global_color_table = NULL;
unsigned char block_type = 0x0;
// A GIF file starts with a Header (section 17)
READ(header, 6);
header[ 6 ] = 0x0;
// XXX there's another format, GIF87a, that you may still find
// floating around.
/*if ( strcmp( "GIF89a", (char*)header ) )
{
fprintf( stderr,
"Invalid GIF file (header is '%s', should be 'GIF89a')\n",
header );
return NULL;
}*/
// Followed by a logical screen descriptor
// Note that this works because GIFs specify little-endian order; on a
// big-endian machine, the height & width would need to be reversed.
// Can't use sizeof here since GCC does byte alignment;
// sizeof( screen_descriptor_t ) = 8!
READ(&screen_descriptor, 7);
*w = screen_descriptor.width;
*h = screen_descriptor.height;
color_resolution_bits = ( ( screen_descriptor.fields & 0x70 ) >> 4 ) + 1;
if ( screen_descriptor.fields & 0x80 )
{
//int i;
// If bit 7 is set, the next block is a global color table; read it
global_color_table_size = 1 <<
( ( ( screen_descriptor.fields & 0x07 ) + 1 ) );
global_color_table = ( rgb * ) malloc( 3 * global_color_table_size );
// XXX this could conceivably return a short count...
READ(global_color_table, 3 * global_color_table_size);
}
while ( block_type != TRAILER )
{
READ(&block_type, 1);
unsigned char size;
switch ( block_type )
{
case IMAGE_DESCRIPTOR:
return process_image_descriptor(buffer,
global_color_table,
global_color_table_size,
color_resolution_bits);
break;
case EXTENSION_INTRODUCER:
buffer++;
size = *(buffer++);
buffer += size;
do {
size = *(buffer++);
buffer += size;
} while (size != 0);
/*if ( !process_extension( buffer ) )
{
return NULL;
}*/
break;
case TRAILER:
break;
default:
fprintf( stderr, "Bailing on unrecognized block type %.02x\n",
block_type );
return NULL;
}
}
return NULL;
}
unsigned char* LoadGif(unsigned char *buffer, unsigned short* w, unsigned short* h) {
return process_gif_stream(buffer, w, h);
}
/*int main( int argc, char *argv[] )
{
FILE* gif_file;
if ( argc < 2 )
{
fprintf( stderr, "Usage: %s <path-to-gif-file>\n", argv[ 0 ] );
exit( 0 );
}
gif_file = fopen( argv[ 1 ], "rb" );
if ( gif_file == NULL )
{
fprintf( stderr, "Unable to open file '%s'", argv[ 1 ] );
perror( ": " );
}
process_gif_stream( gif_file );
fclose( gif_file );
}*/

View File

@@ -2,7 +2,7 @@
#include <iostream> #include <iostream>
// Constructor // Constructor
Input::Input(string file) Input::Input(string file, bool verbose)
{ {
// Fichero gamecontrollerdb.txt // Fichero gamecontrollerdb.txt
dbPath = file; dbPath = file;
@@ -11,17 +11,20 @@ Input::Input(string file)
keyBindings_t kb; keyBindings_t kb;
kb.scancode = 0; kb.scancode = 0;
kb.active = false; kb.active = false;
keyBindings.clear();
keyBindings.resize(INPUT_TOTAL, kb); keyBindings.resize(INPUT_TOTAL, kb);
GameControllerBindings_t gcb; GameControllerBindings_t gcb;
gcb.button = SDL_CONTROLLER_BUTTON_INVALID; gcb.button = SDL_CONTROLLER_BUTTON_INVALID;
gcb.active = false; gcb.active = false;
gameControllerBindings.clear();
gameControllerBindings.resize(INPUT_TOTAL, gcb); gameControllerBindings.resize(INPUT_TOTAL, gcb);
verbose = true; this->verbose = verbose;
enabled = true; enabled = true;
setDefaultBindings(); setDefaultBindings();
discoverGameController();
} }
// Actualiza el estado del objeto // Actualiza el estado del objeto
@@ -221,14 +224,25 @@ bool Input::discoverGameController()
if (verbose) if (verbose)
{ {
cout << "\nChecking for game controllers...\n"; cout << "\n** Checking game controllers" << endl;
cout << nJoysticks << " joysticks found, " << numGamepads << " are gamepads\n"; if (nJoysticks > 0)
{
string stNumJoysticks = nJoysticks == 1 ? "joystick" : "joysticks";
string stNumGamePads = numGamepads == 1 ? "is a gamepad" : "are gamepads";
cout << nJoysticks << " " << stNumJoysticks << " found (" << numGamepads << " " << stNumGamePads << ")" << endl;
}
else
{
cout << ">> No game controllers found" << endl;
}
} }
if (numGamepads > 0) if (numGamepads > 0)
{ {
found = true; found = true;
cout << "\n>> GAMEPAD LIST:" << endl;
for (int i = 0; i < numGamepads; i++) for (int i = 0; i < numGamepads; i++)
{ {
// Abre el mando y lo añade a la lista // Abre el mando y lo añade a la lista
@@ -242,7 +256,8 @@ bool Input::discoverGameController()
// name = name + separator + to_string(i); // name = name + separator + to_string(i);
if (verbose) if (verbose)
{ {
cout << " -" << name << endl; // cout << " - " << name << endl;
cout << "#" << i + 1 << ": " << name << endl;
} }
controllerNames.push_back(name); controllerNames.push_back(name);
} }
@@ -255,6 +270,8 @@ bool Input::discoverGameController()
} }
} }
cout << endl;
SDL_GameControllerEventState(SDL_ENABLE); SDL_GameControllerEventState(SDL_ENABLE);
} }
@@ -321,14 +338,21 @@ void Input::setDefaultBindings()
bindKey(INPUT_DOWN, SDL_SCANCODE_DOWN); bindKey(INPUT_DOWN, SDL_SCANCODE_DOWN);
bindKey(INPUT_LEFT, SDL_SCANCODE_LEFT); bindKey(INPUT_LEFT, SDL_SCANCODE_LEFT);
bindKey(INPUT_RIGHT, SDL_SCANCODE_RIGHT); bindKey(INPUT_RIGHT, SDL_SCANCODE_RIGHT);
bindKey(INPUT_EXIT, SDL_SCANCODE_ESCAPE);
bindKey(INPUT_BUTTON_1, SDL_SCANCODE_Z);
bindKey(INPUT_BUTTON_2, SDL_SCANCODE_X);
bindKey(INPUT_ACCEPT, SDL_SCANCODE_RETURN); bindKey(INPUT_ACCEPT, SDL_SCANCODE_RETURN);
bindKey(INPUT_CANCEL, SDL_SCANCODE_BACKSPACE);
bindKey(INPUT_PAUSE, SDL_SCANCODE_H);
bindKey(INPUT_EXIT, SDL_SCANCODE_ESCAPE);
// Mando // Mando
bindGameControllerButton(INPUT_UP, SDL_CONTROLLER_BUTTON_DPAD_UP); bindGameControllerButton(INPUT_UP, SDL_CONTROLLER_BUTTON_DPAD_UP);
bindGameControllerButton(INPUT_DOWN, SDL_CONTROLLER_BUTTON_DPAD_DOWN); bindGameControllerButton(INPUT_DOWN, SDL_CONTROLLER_BUTTON_DPAD_DOWN);
bindGameControllerButton(INPUT_LEFT, SDL_CONTROLLER_BUTTON_DPAD_LEFT); bindGameControllerButton(INPUT_LEFT, SDL_CONTROLLER_BUTTON_DPAD_LEFT);
bindGameControllerButton(INPUT_RIGHT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); bindGameControllerButton(INPUT_RIGHT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
bindGameControllerButton(INPUT_BUTTON_1, SDL_CONTROLLER_BUTTON_A); bindGameControllerButton(INPUT_BUTTON_1, SDL_CONTROLLER_BUTTON_A);
bindGameControllerButton(INPUT_BUTTON_2, SDL_CONTROLLER_BUTTON_B); bindGameControllerButton(INPUT_BUTTON_2, SDL_CONTROLLER_BUTTON_B);
bindGameControllerButton(INPUT_BUTTON_3, SDL_CONTROLLER_BUTTON_X); bindGameControllerButton(INPUT_BUTTON_3, SDL_CONTROLLER_BUTTON_X);
@@ -337,4 +361,26 @@ void Input::setDefaultBindings()
bindGameControllerButton(INPUT_BUTTON_6, SDL_CONTROLLER_BUTTON_START); bindGameControllerButton(INPUT_BUTTON_6, SDL_CONTROLLER_BUTTON_START);
bindGameControllerButton(INPUT_BUTTON_7, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); bindGameControllerButton(INPUT_BUTTON_7, SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
bindGameControllerButton(INPUT_BUTTON_8, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); bindGameControllerButton(INPUT_BUTTON_8, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
bindGameControllerButton(INPUT_ACCEPT, SDL_CONTROLLER_BUTTON_A);
bindGameControllerButton(INPUT_CANCEL, SDL_CONTROLLER_BUTTON_B);
bindGameControllerButton(INPUT_PAUSE, SDL_CONTROLLER_BUTTON_START);
bindGameControllerButton(INPUT_EXIT, SDL_CONTROLLER_BUTTON_BACK);
}
// Elimina todas las asociaciones de teclado y mando al objeto
void Input::clearBindings()
{
// Inicializa las variables
keyBindings_t kb;
kb.scancode = 0;
kb.active = false;
keyBindings.clear();
keyBindings.resize(INPUT_TOTAL, kb);
GameControllerBindings_t gcb;
gcb.button = SDL_CONTROLLER_BUTTON_INVALID;
gcb.active = false;
gameControllerBindings.clear();
gameControllerBindings.resize(INPUT_TOTAL, gcb);
} }

View File

@@ -89,7 +89,7 @@ private:
public: public:
// Constructor // Constructor
Input(string file); Input(string file, bool verbose=true);
// Actualiza el estado del objeto // Actualiza el estado del objeto
void update(); void update();
@@ -129,6 +129,9 @@ public:
// Establece unas teclas y botones del mando por defectp // Establece unas teclas y botones del mando por defectp
void setDefaultBindings(); void setDefaultBindings();
// Elimina todas las asociaciones de teclado y mando al objeto
void clearBindings();
}; };
#endif #endif

View File

@@ -0,0 +1,300 @@
#include "on_screen_keyboard.h"
// #include <iostream>
// Constructor
OnScreenKeyboard::OnScreenKeyboard(SDL_Renderer *renderer, Input *input, string bitmapFile, string textFile, options_t *options, int width, int height, string output, color_t color)
{
// Inicializa variables
this->renderer = renderer;
this->options = options;
this->input = input;
this->width = width;
this->height = height;
// Crea objetos
text = new Text(textFile, bitmapFile, renderer);
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
textureCursor = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, text->getCharacterSize() * 2, text->getCharacterSize() * 2);
SDL_SetTextureBlendMode(textureCursor, SDL_BLENDMODE_BLEND);
// Inicializa variables
this->output = output;
char_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char_lower = "abcdefghijklmnopqrstuvwxyz";
char_numbers = "0123456789";
char_symbol = " !\"#$%&'()*+,-./:;<=>?@[]";
use_char_upper = true;
use_char_lower = false;
use_char_numbers = false;
use_char_symbol = false;
setLayout();
caption = "";
dest = {(options->screen.nativeWidth - width) / 2, (options->screen.nativeHeight - height) / 2, width, height};
cursor.col = 0;
cursor.row = 0;
cursor.dest = {0, 0, 16, 16};
// Rellena la textura de fondo con el color y el texto
fillTexture();
// Rellena la textura del cursor
SDL_SetRenderTarget(renderer, textureCursor);
color_t c = {192, 0, 0};
SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, 255);
SDL_RenderClear(renderer);
c = lightenColor(c, 20);
SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, 255);
SDL_RenderDrawRect(renderer, NULL);
SDL_SetRenderTarget(renderer, nullptr);
}
// Destructor
OnScreenKeyboard::~OnScreenKeyboard()
{
// Libera la memoria de los objetos
if (text != nullptr)
{
delete text;
}
if (texture != nullptr)
{
SDL_DestroyTexture(texture);
}
}
// Rellena la textura de fondo con el color y el texto
void OnScreenKeyboard::fillTexture()
{
// Destruye la textura si la hubiera
if (texture)
SDL_DestroyTexture(texture);
// Crea la textura
height = (text->getCharacterSize() * 4) + (text->getCharacterSize() * rows * 2);
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
dest = {(options->screen.nativeWidth - width) / 2, (options->screen.nativeHeight - height) / 2, width, height};
// Empieza a dibujar en la textura
SDL_SetRenderTarget(renderer, texture);
// Establece el color de fondo
SDL_SetRenderDrawColor(renderer, bgColor.r, bgColor.g, bgColor.b, 255);
SDL_RenderClear(renderer);
color_t color = lightenColor(bgColor, 20);
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, 255);
SDL_RenderDrawRect(renderer, NULL);
// Escribe el texto que aparece en el objeto
text->write(text->getCharacterSize(), text->getCharacterSize(), caption);
// Dibuja el cuadro donde va el texto que se escribirá
const color_t darkColor = darkenColor(bgColor, 10);
SDL_SetRenderDrawColor(renderer, darkColor.r, darkColor.g, darkColor.b, 255);
const int x_rect = (text->getCharacterSize() * 2) + text->lenght(caption);
const int y_rect = text->getCharacterSize();
const int w_rect = width - text->getCharacterSize() - x_rect;
const int h_rect = text->getCharacterSize();
outputDest = {x_rect, y_rect, w_rect, h_rect};
SDL_RenderFillRect(renderer, &outputDest);
// Dibuja los caracteres que conformaran el teclado
const int dist = text->getCharacterSize() * 2;
const int offset_x = text->getCharacterSize();
const int offset_y = text->getCharacterSize() * 3;
for (int j = 0; j < rows; ++j)
for (int i = 0; i < columns; ++i)
{
const int pos = i + (j * columns);
if (pos < (int)layout.size())
{
// color_t color = randColor();
// SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, 255);
// SDL_Rect rect = {offset_x + (layout.at(pos).col * dist), offset_y + (layout.at(pos).row * dist), dist, dist};
// SDL_RenderFillRect(renderer, &rect);
// text->write(offset_x + (layout.at(pos).col * dist) + (dist / 2), offset_y + (layout.at(pos).row * dist) + (dist / 2), layout.at(pos).caption);
text->writeCentered(offset_x + (layout.at(pos).col * dist) + (dist / 2), offset_y + (layout.at(pos).row * dist) + (dist / 4), layout.at(pos).caption);
}
}
// Deja de dibujar en la textura
SDL_SetRenderTarget(renderer, nullptr);
}
// Calcula cuantos caracteres se utilizaran para crear el teclado
int OnScreenKeyboard::getTotalChars()
{
int total = 0;
total += use_char_upper ? char_upper.length() : 0;
total += use_char_lower ? char_lower.length() : 0;
total += use_char_numbers ? char_numbers.length() : 0;
total += use_char_symbol ? char_symbol.length() : 0;
return total;
}
// Calcula cuantas columnas necesita el teclado
int OnScreenKeyboard::getColumns()
{
const int keyWidth = text->getCharacterSize() * 2;
int rowWidth = width - text->getCharacterSize();
return rowWidth / keyWidth;
}
// Calcula cuantas filas necesita el teclado
int OnScreenKeyboard::getRows()
{
return (totalChars / getColumns()) + 1;
}
// Establece la disposición del teclado
void OnScreenKeyboard::setLayout()
{
totalChars = getTotalChars();
columns = getColumns();
rows = getRows();
int index_col = 0;
int index_row = 0;
// Establece los caracteres a escribir
string allChars = "";
allChars = use_char_upper ? allChars + char_upper : allChars;
allChars = use_char_lower ? allChars + char_lower : allChars;
allChars = use_char_numbers ? allChars + char_numbers : allChars;
allChars = use_char_symbol ? allChars + char_symbol : allChars;
layout.clear();
for (int i = 0; i < (int)allChars.length(); ++i)
{
key_t key;
key.col = index_col;
key.row = index_row;
key.caption = allChars.substr(i, 1);
layout.push_back(key);
++index_col;
if (index_col == columns)
{
index_col = 0;
++index_row;
}
}
}
// Comprueba las entradas
void OnScreenKeyboard::checkInput()
{
if (input->checkInput(INPUT_LEFT, REPEAT_FALSE))
decCursorCol();
if (input->checkInput(INPUT_RIGHT, REPEAT_FALSE))
incCursorCol();
if (input->checkInput(INPUT_UP, REPEAT_FALSE))
decCursorRow();
if (input->checkInput(INPUT_DOWN, REPEAT_FALSE))
incCursorRow();
if (input->checkInput(INPUT_ACCEPT, REPEAT_FALSE))
addChar();
if (input->checkInput(INPUT_CANCEL, REPEAT_FALSE))
removeChar();
}
// Disminuye en una columna la posición del cursor
void OnScreenKeyboard::decCursorCol()
{
cursor.col = cursor.col > 0 ? cursor.col - 1 : columns - 1;
}
// Incrementa en una columna la posición del cursor
void OnScreenKeyboard::incCursorCol()
{
++cursor.col %= columns;
}
// Disminuye en una fila la posición del cursor
void OnScreenKeyboard::decCursorRow()
{
cursor.row = cursor.row > 0 ? cursor.row - 1 : rows - 1;
}
// Aumenta en una fila la posición del cursor
void OnScreenKeyboard::incCursorRow()
{
++cursor.row %= rows;
}
// Actualiza la posición de la textura del cursor
void OnScreenKeyboard::updateCursor()
{
const int offset_x = text->getCharacterSize();
const int offset_y = text->getCharacterSize() * 3;
cursor.dest.x = dest.x + offset_x + (cursor.col * cursor.dest.w);
cursor.dest.y = dest.y + offset_y + (cursor.row * cursor.dest.h);
}
// Añade un caracter a la salida
void OnScreenKeyboard::addChar()
{
const int pos = cursor.col + cursor.row * columns;
if (pos < (int)layout.size())
output = output + layout.at(pos).caption;
}
// Borra el último caracter de la salida
void OnScreenKeyboard::removeChar()
{
if (output.length() > 0)
output = output.substr(0, output.length() - 1);
}
// Actualiza la lógica del objeto
void OnScreenKeyboard::update()
{
// Comprueba las entradas
checkInput();
// Actualiza la posición de la textura del cursor
updateCursor();
}
// Dibuja el objeto en pantalla
void OnScreenKeyboard::render()
{
SDL_RenderCopy(renderer, texture, nullptr, &dest);
SDL_RenderCopy(renderer, textureCursor, nullptr, &cursor.dest);
const int pos = cursor.col + cursor.row * columns;
const string key = pos < (int)layout.size() ? layout.at(pos).caption : "";
text->writeDX(TXT_CENTER | TXT_COLOR, cursor.dest.x + cursor.dest.w / 2, cursor.dest.y + cursor.dest.w / 4, key, 1, {0, 255, 0});
text->write(dest.x + outputDest.x, dest.y + outputDest.y, output);
}
// Establece el color de fondo
void OnScreenKeyboard::setBgColor(color_t color)
{
bgColor = color;
fillTexture();
}
// Establece el texto a mostrar junto al texto a introducir
void OnScreenKeyboard::setCaption(string text)
{
caption = text;
fillTexture();
}
// Establece qué caracteres ofrecerá el objeto
void OnScreenKeyboard::setChars(Uint8 mode)
{
use_char_upper = ((mode & USE_UPPER) == USE_UPPER);
use_char_lower = ((mode & USE_LOWER) == USE_LOWER);
use_char_numbers = ((mode & USE_NUMBER) == USE_NUMBER);
use_char_symbol = ((mode & USE_SYMBOL) == USE_SYMBOL);
setLayout();
fillTexture();
}

128
units/on_screen_keyboard.h Normal file
View File

@@ -0,0 +1,128 @@
#pragma once
#include <SDL2/SDL.h>
#include "utils.h"
#include "text.h"
#include "input.h"
#ifndef ON_SCREEN_KEYBOARD_H
#define ON_SCREEN_KEYBOARD_H
#define USE_UPPER 1
#define USE_LOWER 2
#define USE_NUMBER 4
#define USE_SYMBOL 8
using namespace std;
class OnScreenKeyboard
{
private:
// Objetos y punteros
SDL_Renderer *renderer; // El renderizador de la ventana
Text *text; // Objeto para dibujar texto
Input *input; // Gestor de eventos de entrada de teclado o gamepad
options_t *options; // Variable con todas las opciones del programa
SDL_Texture *texture; // Textura donde dibujar el objeto
SDL_Texture *textureCursor; // Textura para dibujar el cursor
// Estructuras
struct key_t
{
int col; // Posición horizontal de la tecla
int row; // Posición vertical de la tecla
string caption; // Texto de la tecla
};
struct cursor_t
{
int col;
int row;
SDL_Rect dest;
};
// Variables
string char_upper; // Cadena de texto con las letras en mayúscula
string char_lower; // Cadena de texto con las letras en minuscula
string char_numbers; // Cadena de texto con los números
string char_symbol; // Cadena de texto con los símbolos
bool use_char_upper; // Indica si se utilizará ese set de caracteres
bool use_char_lower; // Indica si se utilizará ese set de caracteres
bool use_char_numbers; // Indica si se utilizará ese set de caracteres
bool use_char_symbol; // Indica si se utilizará ese set de caracteres
int totalChars; // Cantidad de caracteres a utilizar
int columns; // Cantidad de caracteres que hay en una fila del teclado
int rows; // Cantidad de filas de caracteres que tendrá el teclado
color_t bgColor; // Color usado para el fondo
string caption; // Texto a mostrar junto al texto a introducir
int width; // Ancho del objeto
int height; // Altura del objeto
SDL_Rect dest; // Coordenadas donde se dibuja el objeto en pantalla
vector<key_t> layout; // Contiene la disposición del teclado
cursor_t cursor; // Variable para gestionar el cursor
string output; // Texto de salida
SDL_Rect outputDest; // Rectangulo con el texto de salida
// Rellena la textura de fondo con el color y el texto
void fillTexture();
// Calcula cuantos caracteres se utilizaran para crear el teclado
int getTotalChars();
// Calcula cuantas columnas necesita el teclado
int getColumns();
// Calcula cuantas filas necesita el teclado
int getRows();
// Establece la disposición del teclado
void setLayout();
// Comprueba las entradas
void checkInput();
// Disminuye en una columna la posición del cursor
void decCursorCol();
// Incrementa en una columna la posición del cursor
void incCursorCol();
// Disminuye en una fila la posición del cursor
void decCursorRow();
// Aumenta en una columna la posición del cursor
void incCursorRow();
// Actualiza la posición de la textura del cursor
void updateCursor();
// Añade un caracter a la salida
void addChar();
// Borra el último caracter de la salida
void removeChar();
public:
// Constructor
OnScreenKeyboard(SDL_Renderer *renderer, Input *input, string bitmapFile, string textFile, options_t *options, int width, int height, string output = "", color_t color = {0, 0, 0});
// Destructor
~OnScreenKeyboard();
// Actualiza la lógica del objeto
void update();
// Dibuja el objeto en pantalla
void render();
// Establece el color de fondo
void setBgColor(color_t color);
// Establece el texto a mostrar junto al texto a introducir
void setCaption(string text);
// Establece qué caracteres ofrecerá el objeto
void setChars(Uint8 mode);
};
#endif

165
units/paleta.cpp Normal file
View File

@@ -0,0 +1,165 @@
#include "paleta.h"
#include "gif.c"
#include <stdio.h>
struct jSurface_s
{
Uint8 *data;
Uint16 w, h;
};
static SDL_Window *jWin = NULL;
static SDL_Renderer *jRen = NULL;
static SDL_Texture *jTex = NULL;
static jSurface jScreen;
static jSurface jDestSurf;
static jSurface jSourceSurf = NULL;
static Uint32 paleta[256];
static int jWidth = 320;
static int jHeight = 240;
static int jZoom = 2;
static int transparentColor = 0;
jSurface jNewSurface(int w, int h)
{
jSurface surf = (jSurface)malloc(sizeof(jSurface_s));
surf->w = w;
surf->h = h;
surf->data = (Uint8 *)malloc(w * h);
return surf;
}
void jDeleteSurface(jSurface surf)
{
if (surf == NULL)
return;
if (surf->data != NULL)
free(surf->data);
free(surf);
}
void jSetDest(jSurface surf)
{
if (surf == NULL)
jDestSurf = jScreen;
else
jDestSurf = surf;
}
void jSetSource(jSurface surf)
{
jSourceSurf = surf;
}
void jBlit(int dx, int dy, int sx, int sy, int w, int h)
{
if (jSourceSurf == NULL)
return;
for (int iy = 0; iy < h; ++iy)
{
for (int ix = 0; ix < w; ++ix)
jPutPixel(dx + ix, dy + iy, jGetPixel(sx + ix, sy + iy));
}
}
jSurface jLoadSurface(const char *filename)
{
FILE *f = fopen(filename, "rb");
if (!f)
return NULL;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
Uint8 *buffer = (Uint8 *)malloc(size);
fread(buffer, size, 1, f);
fclose(f);
Uint16 w, h;
Uint8 *pixels = LoadGif(buffer, &w, &h);
if (pixels == NULL)
{
return NULL;
}
jSurface surf = (jSurface)malloc(sizeof(jSurface_s));
surf->w = w;
surf->h = h;
surf->data = pixels;
free(buffer);
return surf;
}
void jLoadPal(const char *filename)
{
FILE *f = fopen(filename, "rb");
if (!f)
return;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
Uint8 *buffer = (Uint8 *)malloc(size);
fread(buffer, size, 1, f);
fclose(f);
Uint32 *pal = LoadPalette(buffer);
if (pal == NULL)
{
return;
}
free(buffer);
for (int i = 0; i < 256; ++i)
{
paleta[i] = pal[i];
}
}
void jInit(const char *titol, int w, int h, int z)
{
SDL_Init(SDL_INIT_EVERYTHING);
jWidth = w;
jHeight = h;
jZoom = z;
jWin = SDL_CreateWindow(titol, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w * z, h * z, SDL_WINDOW_SHOWN);
jRen = SDL_CreateRenderer(jWin, -1, 0);
SDL_RenderSetLogicalSize(jRen, w, h);
jTex = SDL_CreateTexture(jRen, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, w, h);
jScreen = jNewSurface(w, h);
jDestSurf = jScreen;
}
void jSetPal(int index, Uint32 color)
{
paleta[index] = color;
}
void jCls(Uint8 color)
{
for (int i = 0; i < jDestSurf->w * jDestSurf->h; ++i)
jDestSurf->data[i] = color;
}
void jFlip()
{
Uint32 *pixels;
int pitch;
SDL_LockTexture(jTex, NULL, (void **)&pixels, &pitch);
for (int i = 0; i < jWidth * jHeight; ++i)
pixels[i] = paleta[jScreen->data[i]];
SDL_UnlockTexture(jTex);
SDL_RenderCopy(jRen, jTex, NULL, NULL);
SDL_RenderPresent(jRen);
}
void jPutPixel(int x, int y, Uint8 color)
{
if (x < 0 || y < 0 || x >= jDestSurf->w || y >= jDestSurf->h || color == transparentColor)
return;
jDestSurf->data[x + y * jDestSurf->w] = color;
}
Uint8 jGetPixel(int x, int y)
{
return jSourceSurf->data[x + y * jSourceSurf->w];
}

28
units/paleta.h Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
#include <SDL2/SDL.h>
typedef struct jSurface_s *jSurface;
void init();
void update();
jSurface jNewSurface(int w, int h);
void jDeleteSurface(jSurface surf);
void jSetDest(jSurface surf);
void jSetSource(jSurface surf);
jSurface jLoadSurface(const char* filename);
void jPutPixel(int x, int y, Uint8 color);
Uint8 jGetPixel(int x, int y);
void jBlit(int dx, int dy, int sx, int sy, int w, int h);
void jInit(const char *titol, int w, int h, int z);
void jSetPal(int index, Uint32 color);
void jLoadPal(const char *filename);
void jCls(Uint8 color);
void jFlip();

View File

@@ -1,6 +1,8 @@
#include "utils.h" #include "utils.h"
#include <math.h> #include <math.h>
using namespace std;
// Calcula el cuadrado de la distancia entre dos puntos // Calcula el cuadrado de la distancia entre dos puntos
double distanceSquared(int x1, int y1, int x2, int y2) double distanceSquared(int x1, int y1, int x2, int y2)
{ {
@@ -576,7 +578,37 @@ void initOptions(options_t *options)
options->screen.borderHeight = 0; options->screen.borderHeight = 0;
options->notifications.posV = pos_top; options->notifications.posV = pos_top;
options->notifications.posH = pos_left; options->notifications.posH = pos_left;
options->notifications.sound = true; options->notifications.sound = true;
options->notifications.color = {48, 48, 48}; options->notifications.color = {48, 48, 48};
}
// Oscurece un color
color_t darkenColor(color_t color, int amount)
{
color_t newColor;
newColor.r = max(0, color.r - amount);
newColor.g = max(0, color.g - amount);
newColor.b = max(0, color.b - amount);
return newColor;
}
// Aclara un color
color_t lightenColor(color_t color, int amount)
{
color_t newColor;
newColor.r = min(255, color.r + amount);
newColor.g = min(255, color.g + amount);
newColor.b = min(255, color.b + amount);
return newColor;
}
// Obtiene un color aleatorio
color_t randColor()
{
color_t newColor;
newColor.r = rand() % 256;
newColor.g = rand() % 256;
newColor.b = rand() % 256;
return newColor;
} }

View File

@@ -201,4 +201,13 @@ bool colorAreEqual(color_t color1, color_t color2);
// Inicializa la estructura de opciones // Inicializa la estructura de opciones
void initOptions(options_t *options); void initOptions(options_t *options);
// Oscurece un color
color_t darkenColor(color_t color, int amount);
// Aclara un color
color_t lightenColor(color_t color, int amount);
// Obtiene un color aleatorio
color_t randColor();
#endif #endif