Files
jail_engine/main.cpp

764 lines
18 KiB
C++

/*
Ejemplo de uso de las unidades incluídas en jail_engine
Código fuente creado por JailDesigner
*/
#include <SDL2/SDL.h>
#include <iostream>
#include "units/jail_audio.h"
#include "units/text.h"
#include "units/utils.h"
#include "units/asset.h"
#include "units/movingsprite.h"
#include "units/texture.h"
#include "units/screen.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_Window *window;
SDL_Renderer *renderer;
Asset *asset;
JA_Music_t *music;
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 gradColorMax = 192; // Minimo color más alto del degradado
int gradCurrentColor = 192; // Color actual más alto del degradado
int gradBreathDirection = 0; // Indica si gradCurrentColor crece o decrece
// 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()
{
options = new options_t;
initOptions(options);
options->screen.nativeWidth = 320;
options->screen.nativeHeight = 240;
options->screen.nativeZoom = 2;
options->screen.windowZoom = 1;
options->console = true;
}
// Inicializa la lista de recursos
void initAsset(char *argv[])
{
asset = new Asset(argv[0]);
asset->add("/data/music.ogg", t_music);
asset->add("/data/sound.wav", t_sound);
asset->add("/data/smb2.txt", t_font);
asset->add("/data/smb2.png", t_bitmap);
asset->add("/data/debug.txt", t_font);
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/notify.png", t_bitmap);
asset->add("/data/notify.wav", t_sound);
asset->add("/data/gamecontrollerdb.txt", t_data);
asset->setVerbose(options->console);
if (!asset->check())
{
exit(EXIT_FAILURE);
}
}
// Inicializa SDL
void initSDL()
{
SDL_Init(SDL_INIT_EVERYTHING);
}
// 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)
{
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (renderer != nullptr)
{
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
}
else
{
exit(EXIT_FAILURE);
}
}
else
{
exit(EXIT_FAILURE);
}
}
// Inicializa el gestor de eventos
void initEvent()
{
event = new SDL_Event();
}
// Inicializa jail_audio
void initJailAudio()
{
JA_Init(48000, AUDIO_S16, 2);
music = JA_LoadMusic(asset->get("music.ogg").c_str());
sound = JA_LoadSound(asset->get("sound.wav").c_str());
// JA_PlayMusic(music, true);
}
// Inicializa el objeto screen
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"));
}
// Inicializa el objeto input
void initInput()
{
input = new Input(asset->get("gamecontrollerdb.txt"), options->console);
controllerName = input->getNumControllers() > 0 ? input->getControllerName(0) : "No se ha encontrado ningun mando";
}
// Inicializa el texto
void initText()
{
text = new Text(asset->get("smb2.txt"), asset->get("smb2.png"), renderer);
debugText = new Text(asset->get("debug.txt"), asset->get("debug.png"), renderer);
}
// 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)
{
delete osk;
osk = nullptr;
}
}
// Dibuja el teclado en pantalla
void renderOnScreenKeyboard()
{
if (osk != nullptr)
osk->render();
}
// Actualiza el teclado en pantalla
void updateOnScreenKeyboard()
{
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->setTexture(texture);
sprite->setPosX(140);
sprite->setPosY(100);
sprite->setWidth(16);
sprite->setHeight(32);
sprite->setSpriteClip({0, 0, 16, 32});
sprite->setVelX(1);
sprite->setVelY(2);
}
// Inicializa el efecto de fuego
void initFire()
{
// 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)));
// Pon en la paleta el color calculado
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)
{
should_exit = true;
break;
}
if (event->type == SDL_KEYDOWN)
{
switch (event->key.keysym.scancode)
{
case SDL_SCANCODE_ESCAPE:
should_exit = true;
break;
case SDL_SCANCODE_F1:
screen->decWindowSize();
break;
case SDL_SCANCODE_F2:
screen->incWindowSize();
break;
case SDL_SCANCODE_F3:
++fx %= fxTotal;
break;
case SDL_SCANCODE_F4:
--fireModifier;
fireModifier = max(0, fireModifier);
break;
case SDL_SCANCODE_F5:
++fireModifier;
fireModifier = min(30, fireModifier);
break;
case SDL_SCANCODE_F6:
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;
}
}
}
}
// 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)
{
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
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
if (sprite != nullptr)
delete sprite;
if (texture != nullptr)
delete texture;
// Finaliza el teclado en pantalla
if (osk != nullptr)
delete osk;
// Finaliza el texto
if (text != nullptr)
delete text;
if (debugText != nullptr)
delete debugText;
// Finaliza el objeto input
if (input != nullptr)
delete input;
// Finaliza el objeto screen
if (screen != nullptr)
delete screen;
// Finaliza jail_audio
JA_DeleteSound(sound);
JA_DeleteMusic(music);
// Finaliza el objeto con la lista de recuros
if (asset != nullptr)
delete asset;
// Finaliza las opciones
if (options != nullptr)
delete options;
// Finaliza SDL y la ventana
if (event != nullptr)
delete event;
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
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;
}