Compare commits

...

14 Commits

Author SHA1 Message Date
aabf90351c - [FIX] Variable pública innecesaria
- [NEW] Cheat mode
2025-10-20 19:07:27 +02:00
13e0e89aa8 - [NEW] L'explosió puja més alta, que ara tots els pixels anaven cap avall
- [NEW] Comentaris i pijades en el modul play
2025-10-20 18:11:09 +02:00
eb0f5e24bd - [NEW] Simplificada la búsqueda de si hiha un arounder bloquejant el pas
- [FIX] Arreglat un include
2025-10-20 13:58:00 +02:00
81c5011bc7 - [NEW] Mogut tot el codi de lloc 2025-07-02 13:36:48 +02:00
4670b52378 - [NEW] Afegides funcions pa guardar i carregar del arxiu de configuració enters, floats i bools, a més de cadenes que ja estaven.
- [NEW] Ja guarda i recupera la configuració de zoom, fullscreen i shader.
2025-07-02 10:26:08 +02:00
b403dbad52 - [FIX] Ja se pot amagar i mostrar el cursor.
- [FIX] Arreglat error al carregar arxius zero_terminated.
- [ONGOING] Un poc de treball en shaders més avançats.
2025-07-01 19:18:03 +02:00
8cc347f639 - [FIX] El ratolí ja va correctament en pantalla completa.
- [FIX] Els shaders ja van correctament a pantalla completa.
2025-07-01 17:24:53 +02:00
bc59b74f15 - [NEW] jshader (shader fan cosa rara en fullscreen)
- [NEW] jfile convertit
- [NEW] jinput te en compte el ratio per a les coordenades en pantalla (falla en fullscreen, falta afegir offset)
- [NEW] F1 escala avall la finestra
- [NEW] F2 escala amunt la finestra
- [NEW] F3 togglecha la pantalla completa
- [NEW] F4 togglecha el shader
2025-07-01 13:46:58 +02:00
063016cf55 - A que huelen los commits? 2025-07-01 06:28:54 +02:00
28f4da9b26 - Preparant la nova API 2025-06-30 14:03:42 +02:00
becc33b88c - Makefile updatat pa SDL3 2025-06-29 17:11:12 +02:00
737e18fd96 - [NEW] Passat a SDL3
- [NEW] Ara usa JailAudio
- [NEW] Usant última versió de Respak
- [NEW] Afegit lagueirtofile
2025-06-29 16:56:28 +02:00
d5286d8abe Update README.md 2025-02-17 18:34:55 +01:00
7622045e36 Canvi de versió a v1.2.1 2023-10-23 18:23:18 +02:00
44 changed files with 7435 additions and 1036 deletions

3
.gitignore vendored
View File

@@ -6,4 +6,5 @@ arounders_debug
./*.dll
release/*
respak2
*.dSYM/*
*.dSYM/*
build/*

View File

@@ -13,12 +13,12 @@ linuxRelease = $(executable)-$(version)-linux.tar.gz
windows:
@echo off
windres icon.rc -O coff icon.res
g++ $(source) icon.res -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lmingw32 -lSDL2main -lSDL2 -lSDL2_mixer -mwindows -o "$(executable).exe"
g++ $(source) icon.res -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lmingw32 -lSDL3 -mwindows -o "$(executable).exe"
strip -s -R .comment -R .gnu.version --strip-unneeded "$(executable).exe"
windows_debug:
@echo off
g++ $(source) -D DEBUG -D VERSION=\"$(version)\" -g -Wall -fvar-tracking -lmingw32 -lSDL2main -lSDL2 -lSDL2_mixer -o "$(executable)_debug.exe"
g++ $(source) -D DEBUG -D VERSION=\"$(version)\" -g -Wall -fvar-tracking -lmingw32 -lSDL3 -o "$(executable)_debug.exe"
windows_release:
@echo off
@@ -38,7 +38,7 @@ windows_release:
# Build
windres icon.rc -O coff icon.res
g++ $(source) icon.res -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lmingw32 -lSDL2main -lSDL2 -lSDL2_mixer -mwindows -o "$(releaseFolder)/$(executable).exe"
g++ $(source) icon.res -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lmingw32 -lSDL3 -mwindows -o "$(releaseFolder)/$(executable).exe"
strip -s -R .comment -R .gnu.version --strip-unneeded "$(releaseFolder)/$(executable).exe"
# Create ZIP
@@ -49,10 +49,10 @@ windows_release:
powershell if (Test-Path "$(releaseFolder)") {Remove-Item "$(releaseFolder)" -Recurse -Force}
macos:
clang++ $(source) -D VERSION=\"$(version)\" -Wall -Os -std=c++11 -ffunction-sections -fdata-sections -lSDL2 -lSDL2_mixer -o "$(executable)"
clang++ $(source) -D VERSION=\"$(version)\" -Wall -Os -std=c++11 -ffunction-sections -fdata-sections -lSDL3 -o "$(executable)"
macos_debug:
clang++ $(source) -D DEBUG -D VERSION=\"$(version)\" -g -Wall -std=c++11 -lSDL2 -lSDL2_mixer -o "$(executable)_debug"
clang++ $(source) -D DEBUG -D VERSION=\"$(version)\" -g -Wall -std=c++11 -lSDL3 -o "$(executable)_debug"
macos_release:
# Remove data and possible data from previous builds
@@ -86,7 +86,7 @@ macos_release:
ln -s /Applications "$(releaseFolder)"/Applications
# Build INTEL
clang++ $(source) -D MACOS_BUNDLE -D VERSION=\"$(version)\" -Wall -Os -std=c++11 -framework SDL2 -framework SDL2_mixer -F ./Frameworks -ffunction-sections -fdata-sections -o "$(releaseFolder)/$(appName).app/Contents/MacOS/$(executable)" -rpath @executable_path/../Frameworks/ -target x86_64-apple-macos10.12
clang++ $(source) -D MACOS_BUNDLE -D VERSION=\"$(version)\" -Wall -Os -std=c++11 -framework SDL3 -F ./Frameworks -ffunction-sections -fdata-sections -o "$(releaseFolder)/$(appName).app/Contents/MacOS/$(executable)" -rpath @executable_path/../Frameworks/ -target x86_64-apple-macos10.12
# Build INTEL DMG
hdiutil create tmp.dmg -ov -volname "$(appName)" -fs HFS+ -srcfolder "$(releaseFolder)"
@@ -94,7 +94,7 @@ macos_release:
rm -f tmp.dmg
# Build APPLE SILICON
#clang++ $(source) -D MACOS_BUNDLE -D VERSION=\"$(version)\" -std=c++11 -Wall -Os -framework SDL2 -framework SDL2_mixer -F ./Frameworks -ffunction-sections -fdata-sections -o "$(releaseFolder)/$(appName).app/Contents/MacOS/$(executable)" -rpath @executable_path/../Frameworks/ -target arm64-apple-macos11
#clang++ $(source) -D MACOS_BUNDLE -D VERSION=\"$(version)\" -std=c++11 -Wall -Os -framework SDL3 -F ./Frameworks -ffunction-sections -fdata-sections -o "$(releaseFolder)/$(appName).app/Contents/MacOS/$(executable)" -rpath @executable_path/../Frameworks/ -target arm64-apple-macos11
# Build APPLE SILICON DMG
#hdiutil create tmp.dmg -ov -volname "$(appName)" -fs HFS+ -srcfolder "$(releaseFolder)"
@@ -107,11 +107,11 @@ macos_release:
linux:
g++ $(source) -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lSDL2 -lSDL2_mixer -o "$(executable)"
g++ $(source) -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lSDL3 -o "$(executable)"
strip -s -R .comment -R .gnu.version --strip-unneeded "$(executable)"
linux_debug:
g++ $(source) -D DEBUG -D VERSION=\"$(version)\" -g -Wall -fvar-tracking -lSDL2 -lSDL2_mixer -o "$(executable)_debug"
g++ $(source) -D DEBUG -D VERSION=\"$(version)\" -g -Wall -fvar-tracking -lSDL3 -o "$(executable)_debug"
linux_release:
# Remove data
@@ -129,7 +129,7 @@ linux_release:
cp README.md "$(releaseFolder)"
# Build
g++ $(source) -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lSDL2 -lSDL2_mixer -o "$(releaseFolder)/$(executable)"
g++ $(source) -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lSDL3 -o "$(releaseFolder)/$(executable)"
strip -s -R .comment -R .gnu.version --strip-unneeded "$(releaseFolder)/$(executable)"
# Pack files

View File

@@ -1,7 +1,7 @@
# AROUNDERS
versió 1.2
versió 1.2.1
![Menú del Arounders](https://php.sustancia.synology.me/images/arounders1.png)
![Menú del Arounders](https://php.sustancia.synology.me/images/arounders/arounders1.png)
## HISTÒRIA
@@ -27,7 +27,7 @@ Teòricament, deuria funcionar amb comps molt més xungos, pero no tinc temps ar
- Impresora (¿¿??)
OPCIONAL: Scanner, scanner!
![Una fase del Arounders](https://php.sustancia.synology.me/images/arounders2.png)
![Una fase del Arounders](https://php.sustancia.synology.me/images/arounders/arounders2.png)
## INSTRUCCIONS

141
data/crtpi.glsl Normal file
View File

@@ -0,0 +1,141 @@
/*
crt-pi - A Raspberry Pi friendly CRT shader.
Copyright (C) 2015-2016 davej
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
Notes:
This shader is designed to work well on Raspberry Pi GPUs (i.e. 1080P @ 60Hz on a game with a 4:3 aspect ratio). It pushes the Pi's GPU hard and enabling some features will slow it down so that it is no longer able to match 1080P @ 60Hz. You will need to overclock your Pi to the fastest setting in raspi-config to get the best results from this shader: 'Pi2' for Pi2 and 'Turbo' for original Pi and Pi Zero. Note: Pi2s are slower at running the shader than other Pis, this seems to be down to Pi2s lower maximum memory speed. Pi2s don't quite manage 1080P @ 60Hz - they drop about 1 in 1000 frames. You probably won't notice this, but if you do, try enabling FAKE_GAMMA.
SCANLINES enables scanlines. You'll almost certainly want to use it with MULTISAMPLE to reduce moire effects. SCANLINE_WEIGHT defines how wide scanlines are (it is an inverse value so a higher number = thinner lines). SCANLINE_GAP_BRIGHTNESS defines how dark the gaps between the scan lines are. Darker gaps between scan lines make moire effects more likely.
GAMMA enables gamma correction using the values in INPUT_GAMMA and OUTPUT_GAMMA. FAKE_GAMMA causes it to ignore the values in INPUT_GAMMA and OUTPUT_GAMMA and approximate gamma correction in a way which is faster than true gamma whilst still looking better than having none. You must have GAMMA defined to enable FAKE_GAMMA.
CURVATURE distorts the screen by CURVATURE_X and CURVATURE_Y. Curvature slows things down a lot.
By default the shader uses linear blending horizontally. If you find this too blury, enable SHARPER.
BLOOM_FACTOR controls the increase in width for bright scanlines.
MASK_TYPE defines what, if any, shadow mask to use. MASK_BRIGHTNESS defines how much the mask type darkens the screen.
*/
// Haven't put these as parameters as it would slow the code down.
#define SCANLINES
#define MULTISAMPLE
#define GAMMA
// MASK_TYPE: 0 = none, 1 = green/magenta, 2 = trinitron(ish)
#define MASK_TYPE 2
#define COMPAT_PRECISION
#define MASK_BRIGHTNESS 0.80
#define SCANLINE_WEIGHT 6.0
#define SCANLINE_GAP_BRIGHTNESS 0.12
#define BLOOM_FACTOR 3.5
#define INPUT_GAMMA 2.4
#define OUTPUT_GAMMA 2.2
/* COMPATIBILITY
- GLSL compilers
*/
uniform vec2 TextureSize;
varying vec2 TEX0;
varying float filterWidth;
#if defined(VERTEX)
void main()
{
filterWidth = (768.0 / TextureSize.x) / 3.0;
TEX0 = vec2(gl_MultiTexCoord0.x, 1.0-gl_MultiTexCoord0.y)*1.0001;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
#elif defined(FRAGMENT)
uniform sampler2D Texture;
float CalcScanLineWeight(float dist)
{
return max(1.0-dist*dist*SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS);
}
float CalcScanLine(float dy)
{
float scanLineWeight = CalcScanLineWeight(dy);
#if defined(MULTISAMPLE)
scanLineWeight += CalcScanLineWeight(dy-filterWidth);
scanLineWeight += CalcScanLineWeight(dy+filterWidth);
scanLineWeight *= 0.3333333;
#endif
return scanLineWeight;
}
void main()
{
//vec2 TextureSize = vec2(320.0, 256.0);
vec2 texcoord = TEX0;
{
vec2 texcoordInPixels = texcoord * TextureSize;
float tempY = floor(texcoordInPixels.y) + 0.5;
float yCoord = tempY / TextureSize.y;
float dy = texcoordInPixels.y - tempY;
float scanLineWeight = CalcScanLine(dy);
float signY = sign(dy);
dy = dy * dy;
dy = dy * dy;
dy *= 8.0;
dy /= TextureSize.y;
dy *= signY;
vec2 tc = vec2(texcoord.x, yCoord + dy);
vec3 colour = texture2D(Texture, tc).rgb;
#if defined(SCANLINES)
#if defined(GAMMA)
colour = pow(colour, vec3(INPUT_GAMMA));
#endif
scanLineWeight *= BLOOM_FACTOR;
colour *= scanLineWeight;
#if defined(GAMMA)
colour = pow(colour, vec3(1.0/OUTPUT_GAMMA));
#endif
#endif
#if MASK_TYPE == 0
gl_FragColor = vec4(colour, 1.0);
#else
#if MASK_TYPE == 1
float whichMask = fract((gl_FragCoord.x*1.0001) * 0.5);
vec3 mask;
if (whichMask < 0.5)
mask = vec3(MASK_BRIGHTNESS, 1.0, MASK_BRIGHTNESS);
else
mask = vec3(1.0, MASK_BRIGHTNESS, 1.0);
#elif MASK_TYPE == 2
float whichMask = fract((gl_FragCoord.x*1.0001) * 0.3333333);
vec3 mask = vec3(MASK_BRIGHTNESS, MASK_BRIGHTNESS, MASK_BRIGHTNESS);
if (whichMask < 0.3333333)
mask.x = 1.0;
else if (whichMask < 0.6666666)
mask.y = 1.0;
else
mask.z = 1.0;
#endif
gl_FragColor = vec4(colour * mask, 1.0);
#endif
}
}
#endif

38
data/gbc.glsl Normal file
View File

@@ -0,0 +1,38 @@
varying vec2 tex_coord;
varying vec2 pix_coord;
#if defined(VERTEX)
void main()
{
pix_coord = vec2(gl_MultiTexCoord0.x, 1.0-gl_MultiTexCoord0.y)*1.0001;
tex_coord = vec2((gl_Vertex.x+1.0)*0.5, (-gl_Vertex.y+1.0)*0.5);
vec4 pos = vec4(gl_Vertex.x * 2.0, gl_Vertex.y * 2.0, gl_Vertex.z, gl_Vertex.w);
gl_Position = gl_Vertex; //(gl_Vertex*2)-vec3(1.0, 1.0, 1.0);//gl_ModelViewProjectionMatrix * gl_Vertex;
}
#elif defined(FRAGMENT)
uniform sampler2D Texture;
void main()
{
float x = sign(pix_coord.x)*floor(abs(pix_coord.x)+0.5);
float y = sign(pix_coord.y)*floor(abs(pix_coord.y)+0.5);
float column = mod(x,4.0);
float row = mod(y,4.0);
vec4 color = texture2D(Texture, tex_coord);
vec4 newcolor;
if ((column == 0.0) || (row == 0.0) ) {
newcolor = color * vec4(0.4, 0.4, 0.4, 1.0);
} else if ((column == 1.0) || (row == 1.0) ) {
newcolor = color * vec4(0.6, 0.7, 0.8, 1.0);
} else if ((column == 3.0) || (row == 3.0) ) {
newcolor = color * vec4(0.8, 0.7, 0.6, 1.0);
} else {
newcolor = color;
}
gl_FragColor = newcolor;
}
#endif

41
data/lynx.glsl Normal file
View File

@@ -0,0 +1,41 @@
varying vec2 tex_coord;
varying vec2 pix_coord;
#if defined(VERTEX)
void main()
{
pix_coord = vec2(gl_MultiTexCoord0.x, 1.0-gl_MultiTexCoord0.y)*1.0001;
tex_coord = vec2((gl_Vertex.x+1.0)*0.5, (-gl_Vertex.y+1.0)*0.5);
vec4 pos = vec4(gl_Vertex.x * 2.0, gl_Vertex.y * 2.0, gl_Vertex.z, gl_Vertex.w);
gl_Position = gl_Vertex; //(gl_Vertex*2)-vec3(1.0, 1.0, 1.0);//gl_ModelViewProjectionMatrix * gl_Vertex;
}
#elif defined(FRAGMENT)
uniform sampler2D Texture;
void main()
{
float x = sign(pix_coord.x)*floor(abs(pix_coord.x)+0.5);
float column = mod(x,4.0);
vec4 color = texture2D(Texture, tex_coord);
float xfade = abs((tex_coord.s * 2.0) - 1.0);
xfade = xfade * xfade * xfade * xfade * xfade;
float yfade = abs((tex_coord.t * 2.0) - 1.0);
yfade = yfade * yfade * yfade * yfade * yfade;
color = color + vec4(0.7, 0.7, 0.7, 0.0) * (1.0-tex_coord.t) * (1.0-xfade) * (1.0-yfade);
vec4 newcolor;
if (column == 0.0) {
newcolor = color * vec4(1.0, 0.4, 0.6, 1.0);
} else if (column == 1.0) {
newcolor = color * vec4(0.4, 1.0, 0.4, 1.0);
} else if (column == 2.0) {
newcolor = color * vec4(0.6, 0.4, 1.0, 1.0);
} else {
newcolor = color * vec4(0.2, 0.2, 0.2, 1.0);
}
gl_FragColor = newcolor;
}
#endif

5
lagueirtofile Normal file
View File

@@ -0,0 +1,5 @@
libs = -lSDL3 -lGL
cppflags = -D DEBUG -D VERBOSE -g -Wall
executable = arounders
sourcepath = source source/aux source/japi source/gamestates source/entities
buildpath = build

View File

@@ -1,5 +1,5 @@
#include "aux_font.h"
#include "jgame.h"
#include "font.h"
#include "../japi/game.h"
namespace font
{

View File

@@ -1,6 +1,6 @@
#include "aux_textfile.h"
#include "textfile.h"
#include "jgame.h"
#include "../japi/game.h"
#include <stdlib.h>
namespace textfile
@@ -22,7 +22,7 @@ namespace textfile
const bool open(std::string filename)
{
buffer = file::getFileBuffer(filename.c_str(), &fsize);
buffer = file::getFileBuffer(filename.c_str(), fsize);
p = 0;
return true;

View File

@@ -1,7 +1,7 @@
#include "proc_arounders.h"
#include "jgame.h"
#include "proc_mapa.h"
#include "proc_explosio.h"
#include "arounders.h"
#include "../japi/game.h"
#include "mapa.h"
#include "explosio.h"
namespace arounders
{
@@ -55,9 +55,6 @@ namespace arounders
bool pujarEscalo(arounder *a);
bool baixarEscalo(arounder *a);
bool checkArounderAnt(arounder *a, int x, int y, int o);
bool checkArounderSig(arounder *a, int x, int y, int o);
void doCavar(arounder *a);
void doPerforar(arounder *a);
void doEscalera(arounder *a, int desfase = 0);
@@ -681,11 +678,12 @@ namespace arounders
bool blockArounder(arounder *a)
{
bool resultado = false;
if (a->anterior) resultado = checkArounderAnt(a->anterior, a->x, a->y, a->orientacio);
if (a->siguiente) resultado = resultado || checkArounderSig(a->siguiente, a->x, a->y, a->orientacio);
return resultado;
arounder *b = arounders::first;
while (b) {
if ( (a != b) && ( b->accio == arounders::accions::parar ) && ( a->y >= b->y-8 ) && ( a->y <= b->y+8 ) && ( b->x == a->x+8-(a->orientacio*2) ) ) return true;
b = b->siguiente;
}
return false;
}
bool blockParet(arounder *a, int desfase)
@@ -835,33 +833,6 @@ namespace arounders
);
}
bool checkArounderAnt(arounder *a, int x, int y, int o)
{
if ( a->accio == arounders::accions::parar && (y >= a->y-8) && (y <= a->y+8) && ( (o == arounders::orientacions::dreta && a->x == x+8) || (o == arounders::orientacions::esquerra && a->x == x-8) ) ) {
return true;
} else {
if (a->anterior) {
return checkArounderAnt(a->anterior, x, y, o);
} else {
return false;
}
}
}
bool checkArounderSig(arounder *a, int x, int y, int o)
{
if ( a->accio == arounders::accions::parar && (y >= a->y-8) && (y <= a->y+8) && ( (o == arounders::orientacions::dreta && a->x == x+8) || (o == arounders::orientacions::esquerra && a->x == x-8) ) ) {
return true;
} else {
if (a->siguiente) {
return checkArounderSig(a->siguiente, x, y, o);
} else {
return false;
}
}
}
void doCavar(arounder *a)
{
if (a->orientacio == arounders::orientacions::dreta) {

View File

@@ -36,7 +36,6 @@ namespace arounders
arounder *siguiente;
};
extern arounder *first;
extern arounder *seleccionat;
void init();

View File

@@ -1,6 +1,6 @@
#include "proc_explosio.h"
#include "explosio.h"
#include <stdlib.h>
#include "jgame.h"
#include "../japi/game.h"
namespace explosio
{
@@ -34,7 +34,7 @@ namespace explosio
exp[numexp].pix[i].x = x;
exp[numexp].pix[i].y = y;
exp[numexp].pix[i].xa = float((rand()%expansio) - (expansio >> 1))/1000.0f;
exp[numexp].pix[i].ya = float((rand()%expansio) - (expansio >> 1))/1000.0f;
exp[numexp].pix[i].ya = -float((rand()%expansio))/1000.0f;
exp[numexp].pix[i].g = 0.05;
exp[numexp].pix[i].c = rand()%60;
}

View File

@@ -1,7 +1,7 @@
#include "proc_mapa.h"
#include "jgame.h"
#include "aux_textfile.h"
#include "aux_font.h"
#include "mapa.h"
#include "../japi/game.h"
#include "../aux/textfile.h"
#include "../aux/font.h"
namespace mapa
{
namespace botons

View File

@@ -1,5 +1,5 @@
#pragma once
#include "jgame.h"
#include "../japi/game.h"
#include <stdint.h>
namespace mapa
{

View File

@@ -1,6 +1,6 @@
#include "gamestates.h"
#include "jgame.h"
#include "aux_font.h"
#include "../japi/game.h"
#include "../aux/font.h"
namespace gamestate
{
@@ -25,9 +25,7 @@ namespace gamestate
menu::fondo = draw::loadSurface("menuprin.gif", true);
menu::cursor = draw::loadSurface("cursor.gif");
if (audio::getMusicState() != audio::music_state::MUSIC_PLAYING || audio::whichMusic() != "mus3.ogg") {
audio::playMusic(audio::loadMusic("mus3.ogg"));
}
audio::loadAndPlayMusic("mus3.ogg");
font::selectFont(font::type::colored);
font::setColor(font::color::white);

View File

@@ -1,5 +1,5 @@
#include "gamestates.h"
#include "jgame.h"
#include "../japi/game.h"
namespace gamestate
{
@@ -24,7 +24,7 @@ namespace gamestate
fondo = draw::loadSurface("mort.gif", true);
cursor = draw::loadSurface("cursor.gif");
audio::playMusic(audio::loadMusic("mus5.ogg"));
audio::loadAndPlayMusic("mus5.ogg");
draw::fadein();

View File

@@ -1,9 +1,9 @@
#include "gamestates.h"
#include "jgame.h"
#include "../japi/game.h"
#include <string>
#include "aux_font.h"
#include "proc_mapa.h"
#include <SDL2/SDL.h>
#include "../aux/font.h"
#include "../entities/mapa.h"
#include <SDL3/SDL.h>
namespace gamestate
{
@@ -120,7 +120,7 @@ namespace gamestate
{
password[10] = 0;
int filesize = 0;
const char *buffer = file::getFileBuffer("offsets.bal", &filesize);
const char *buffer = file::getFileBuffer("offsets.bal", filesize);
int punter = 0;

View File

@@ -1,10 +1,10 @@
#include "gamestates.h"
#include "jgame.h"
#include <SDL2/SDL.h>
#include "../japi/game.h"
#include <SDL3/SDL.h>
#include <string>
#include "aux_font.h"
#include "proc_mapa.h"
#include "proc_arounders.h"
#include "../aux/font.h"
#include "../entities/mapa.h"
#include "../entities/arounders.h"
namespace gamestate
{
@@ -15,6 +15,7 @@ namespace gamestate
const int no = 0;
const int postfase = 1;
const int mort = 2;
const int cheat = 3;
}
draw::surface *faded = nullptr;
@@ -31,7 +32,7 @@ namespace gamestate
int aigua_frame1 = 0;
int aigua_frame2 = 5;
int exit = 0;
int exit = play::eixir::no;
bool loop();
@@ -72,8 +73,7 @@ namespace gamestate
// arounder_seleccionat = primerArounders
// Enxufa el arradio
audio::loadMusic((game::getConfig("fase")+1) % 5 == 0 ? "mus6.ogg" : "mus4.ogg");
audio::playMusic();
audio::loadAndPlayMusic((game::getConfig("fase")+1) % 5 == 0 ? "mus6.ogg" : "mus4.ogg");
// Fiquem a contar el cronómetre de arounders
play::arounderCount = play::startTicks = game::getTicks();
@@ -95,6 +95,7 @@ namespace gamestate
// I anem on toque
if (play::exit==play::eixir::postfase) gamestate::postfase::init();
else if (play::exit==play::eixir::mort) gamestate::mort::init();
else if (play::exit==play::eixir::cheat) gamestate::prefase::init();
}
// En qualsevol cas, renderitzem i eixim ja del bucle (fins que acabe el fadeout)
draw::render();
@@ -183,68 +184,99 @@ namespace gamestate
arounders::abortarAccio();
}
// Si pulsem el botó de l'esquerra... pos es més complicat! Anem pas per pas...
if (input::mouseClk(input::mouse::button::left))
{
// Si al pulsar no havem seleccionat cap arounder (o siga, no havem fet click sobre un arounder)...
if (!arounders::seleccionar())
{
// Si no havem pulsat en la zona de baix dels botons, i hi ha un arounder seleccionat, i està caminant...
if (input::mouseY()<165 && arounders::seleccionat != nullptr && arounders::seleccionat->accio == arounders::accions::caminar)
{
// Fem que camine en la direcció en que està el cursor
arounders::seleccionat->orientacio = input::mouseX() > arounders::seleccionat->x ? arounders::orientacions::dreta : arounders::orientacions::esquerra;
}
}
// Mirem si havem pulsat un botó... (si el botó està a 0, no conta com a pulsat)
const int botoPulsat = mapa::procesar();
// Si havem pulsat un botó i tenim un arounder seleccionat...
if (botoPulsat != -1 && arounders::seleccionat != nullptr)
{
// Si el botó pulsat se correspon en la accio encolada
if (botoPulsat == arounders::seleccionat->prevista) {
// La desencolem
arounders::seleccionat->prevista = arounders::seleccionat->accio;
} else {
// Pero si no, la encolem (atenció, moltes accions comencen instantàneament)
arounders::seleccionat->prevista = botoPulsat;
}
}
}
// Si ha passat el temps suficient, actualitzem els arounders (i les ones)
// Açò pot ser mes tard o mes prompte depenent de si havem accelerat el temps
currentTicks = game::getTicks() - startTicks;
if( currentTicks >= mapa::velocitat ) {
startTicks = game::getTicks();
aigua_frame1 = (aigua_frame1+1)%10;
aigua_frame2 = (aigua_frame2+1)%10;
// Ací passa quasi tot lo interessant!!! No t'ho perdes!!
arounders::procesar();
}
// Si ha passat el temps suficient...
// (també depen de si havem pulsat el botó de passar rapid el temps)
if ( (game::getTicks() - arounderCount) >= mapa::velocitat*58) {
// I encara queden arounders per eixir...
if (mapa::arounders::eixits < mapa::arounders::totals) {
// Traguem un nou arounder per la porta
arounders::afegir();
mapa::arounders::eixits++;
arounderCount = game::getTicks();
}
}
// Si tots els arounders ja han arribat o han mort...
if (mapa::arounders::arrivats + mapa::arounders::morts == mapa::arounders::totals) {
// Si han arribat els necessaris...
if (mapa::arounders::arrivats >= mapa::arounders::necessaris) {
// Pujem el numero de fase en el config i anem a la postfase
game::setConfig("fase", game::getConfig("fase")+1);
draw::fadeout();
audio::fadeoutMusic();
play::exit = 1;
audio::fadeOutMusic();
play::exit = play::eixir::postfase;
} else {
// Sino, anem a la pantalla de mort
draw::fadeout();
audio::fadeoutMusic();
play::exit = 2;
audio::fadeOutMusic();
play::exit = play::eixir::mort;
}
}
if (input::keyPressed(SDL_SCANCODE_F12)) {
game::setConfig("fase", game::getConfig("fase")+1);
draw::fadeout();
audio::fadeOutMusic();
play::exit = play::eixir::cheat;
}
return true;
}
// Mentres estem en pausa, este es el bucle principal
bool loop_pause()
{
// Consisteix en pintar la surface sobre la que havem pintat la foto del que hi havia en pantalla
draw::draw(faded);
// ...I el cursor...
draw::setSource(cursor);
draw::draw(input::mouseX(), input::mouseY());
draw::render();
// I mirem si s'ha polsat P o ESC, per a tornat al bucle principal del mòdul de joc
if (input::keyPressed(SDL_SCANCODE_P) || input::keyPressed(SDL_SCANCODE_ESCAPE))
{
play::backToLoop();
@@ -252,10 +284,14 @@ namespace gamestate
return true;
}
// Mentres estem en el menú, este es el bucle principal
bool loop_menu()
{
// Si havem triat eixir...
if (play::exit) {
// esperem a que acabe el fade...
if (!draw::isfading()) {
// I quan acabe
play::finalize();
if (exit==1) {
mapa::carregar();
@@ -287,13 +323,13 @@ namespace gamestate
if (input::mouseY() >= 71 && input::mouseY() <= 76) {
free(play::original_palette);
draw::fadeout();
audio::fadeoutMusic();
audio::fadeOutMusic();
play::exit = 1;
}
if (input::mouseY() >= 82 && input::mouseY() <= 87) {
free(play::original_palette);
draw::fadeout();
audio::fadeoutMusic();
audio::fadeOutMusic();
play::exit = 2;
}
if (input::mouseY() >= 93 && input::mouseY() <= 98) {

View File

@@ -1,7 +1,7 @@
#include "gamestates.h"
#include "jgame.h"
#include "../japi/game.h"
#include <string>
#include "aux_font.h"
#include "../aux/font.h"
namespace gamestate
{
@@ -31,8 +31,7 @@ namespace gamestate
gamestate::sequence::init();
return;
} else {
audio::loadMusic("mus3.ogg");
audio::playMusic();
audio::loadAndPlayMusic("mus3.ogg");
}
if (game::getConfig("fase") % 5 == 0) {
@@ -104,7 +103,8 @@ namespace gamestate
std::string getPassword()
{
char *buffer = file::getFileBuffer("offsets.bal");
int size;
char *buffer = file::getFileBuffer("offsets.bal", size);
int punter = (game::getConfig("fase")-1)*10;
char passFile[11];

View File

@@ -1,8 +1,8 @@
#include "gamestates.h"
#include "jgame.h"
#include "../japi/game.h"
#include <string>
#include "aux_font.h"
#include "proc_mapa.h"
#include "../aux/font.h"
#include "../entities/mapa.h"
namespace gamestate
{

View File

@@ -1,8 +1,8 @@
#include "gamestates.h"
#include "jgame.h"
#include "../japi/game.h"
#include <string>
#include <SDL2/SDL.h>
#include "aux_font.h"
#include <SDL3/SDL.h>
#include "../aux/font.h"
namespace gamestate
{
@@ -33,7 +33,8 @@ namespace gamestate
default: gamestate::prefase::init(); return; break;
}
sequence_file = file::getFilePointer(filename);
int size;
sequence_file = file::getFilePointer(filename.c_str(), size);
game::setState(&gamestate::sequence::loop);
}
@@ -98,15 +99,14 @@ namespace gamestate
} else if (command=="PLAYMUSIC") {
fscanf(sequence_file, " '%[^']'", text);
audio::loadMusic(text);
audio::playMusic();
audio::loadAndPlayMusic(text);
} else if (command=="FADEOUT") {
draw::fadeout();
} else if (command=="FADEOUTMUSIC") {
draw::fadeout();
audio::fadeoutMusic();
audio::fadeOutMusic();
} else if (command=="END") {
fclose(sequence_file);
@@ -125,7 +125,7 @@ namespace gamestate
void drawPic(std::string filename)
{
draw::surface *pic = draw::loadSurface(filename, true);
draw::surface *pic = draw::loadSurface(filename.c_str(), true);
draw::draw(pic);
draw::freeSurface(pic);
}

493
source/japi/audio.cpp Normal file
View File

@@ -0,0 +1,493 @@
#include "audio.h"
#include "file.h"
#include "stb_vorbis.h"
#include <SDL3/SDL.h>
#include <stdio.h>
namespace audio {
#define MAX_CHANNELS 5
struct sound
{
SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 };
Uint32 length { 0 };
Uint8 *buffer { NULL };
};
struct channel
{
audio::sound *sound { nullptr };
int pos { 0 };
int times { 0 };
SDL_AudioStream *stream { nullptr };
audio::state state { audio::state::available };
};
struct music
{
SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 };
Uint32 length { 0 };
Uint8 *buffer { nullptr };
char *filename { nullptr };
int pos { 0 };
int times { 0 };
SDL_AudioStream *stream { nullptr };
audio::state state { audio::state::invalid };
};
audio::music *current_music { nullptr };
audio::channel channels[MAX_CHANNELS];
SDL_AudioSpec audio_spec { SDL_AUDIO_S16, 2, 48000 };
float music_volume { 1.0f };
float sound_volume { 0.5f };
bool music_enabled { true };
bool sound_enabled { true };
SDL_AudioDeviceID audio_device { 0 };
SDL_TimerID timer_id { 0 };
bool fading { false };
int fade_start_time;
int fade_duration;
int fade_initial_volume;
Uint32 updateCallback(void *userdata, SDL_TimerID timer_id, Uint32 interval)
{
if (music_enabled && current_music && current_music->state == audio::state::playing)
{
if (fading)
{
const int time = SDL_GetTicks();
if (time > (fade_start_time+fade_duration)) {
fading = false;
stopMusic();
return 30;
} else {
const int time_passed = time - fade_start_time;
const float percent = (float)time_passed / (float)fade_duration;
SDL_SetAudioStreamGain(current_music->stream, music_volume*(1.0 - percent));
}
}
if (current_music->times != 0)
{
if (SDL_GetAudioStreamAvailable(current_music->stream) < int(current_music->length/2))
{
SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length);
}
if (current_music->times>0) current_music->times--;
}
else
{
if (SDL_GetAudioStreamAvailable(current_music->stream) == 0) stopMusic();
}
}
if (sound_enabled)
{
for (int i=0; i < MAX_CHANNELS; ++i)
if (channels[i].state == audio::state::playing)
{
if (channels[i].times != 0)
{
if (SDL_GetAudioStreamAvailable(channels[i].stream) < int(channels[i].sound->length/2))
{
SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer, channels[i].sound->length);
if (channels[i].times>0) channels[i].times--;
}
}
else
{
if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) stopChannel(i);
}
}
}
return 30;
}
void init(const int freq, const SDL_AudioFormat format, const int num_channels)
{
#ifdef DEBUG
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG);
#endif
SDL_Log("Iniciant JailAudio...");
audio_spec = {format, num_channels, freq };
if (!audio_device) SDL_CloseAudioDevice(audio_device);
audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio_spec);
SDL_Log( (audio_device==0) ? "Failed to initialize SDL audio!\n" : "OK!\n");
for (int i=0;i<MAX_CHANNELS;++i) channels[i].state = audio::state::available;
timer_id = SDL_AddTimer(30, updateCallback, nullptr);
}
void quit()
{
if (timer_id) SDL_RemoveTimer(timer_id);
if (!audio_device) SDL_CloseAudioDevice(audio_device);
audio_device = 0;
}
audio::music *loadMusic(Uint8* buffer, Uint32 length)
{
audio::music *music = new audio::music();
int chan, samplerate;
short *output;
music->length = stb_vorbis_decode_memory(buffer, length, &chan, &samplerate, &output) * chan * 2;
music->spec.channels = chan;
music->spec.freq = samplerate;
music->spec.format = SDL_AUDIO_S16;
music->buffer = (Uint8*)SDL_malloc(music->length);
SDL_memcpy(music->buffer, output, music->length);
free(output);
music->pos = 0;
music->state = audio::state::stopped;
return music;
}
audio::music *loadMusic(const char* filename)
{
int size;
char *buffer = file::getFileBuffer(filename, size);
if (buffer == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::loadMusic): No s'ha trobat l'arxiu %s\n", filename);
exit(1);
}
audio::music *music = loadMusic((Uint8*)buffer, size);
music->filename = (char*)malloc(strlen(filename)+1);
strcpy(music->filename, filename);
free(buffer);
return music;
}
void playMusic(audio::music *music, const int loop)
{
if (!music_enabled) return;
stopMusic();
current_music = music;
current_music->pos = 0;
current_music->state = audio::state::playing;
current_music->times = loop;
current_music->stream = SDL_CreateAudioStream(&current_music->spec, &audio_spec);
if (!current_music->stream) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::playMusic): SDL_CreateAudioStream: %s\n", SDL_GetError());
exit(1);
}
if (!SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length)) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::playMusic): SDL_PutAudioStreamData: %s\n", SDL_GetError());
exit(1);
}
SDL_SetAudioStreamGain(current_music->stream, music_volume);
if (!SDL_BindAudioStream(audio_device, current_music->stream)) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::playMusic): SDL_BindAudioStream: %s\n", SDL_GetError());
exit(1);
}
}
void loadAndPlayMusic(const char* filename, const bool force_reinit)
{
if ( (getMusicState() != audio::state::playing) || (strcmp(getMusicFilename(), filename)!=0) || force_reinit )
{
playMusic(loadMusic(filename));
}
}
char *getMusicFilename(audio::music *music)
{
if (!music) music = current_music;
return music->filename;
}
void pauseMusic()
{
if (!music_enabled) return;
if (!current_music || current_music->state == audio::state::invalid) return;
current_music->state = audio::state::paused;
SDL_UnbindAudioStream(current_music->stream);
}
void resumeMusic()
{
if (!music_enabled) return;
if (!current_music || current_music->state == audio::state::invalid) return;
current_music->state = audio::state::playing;
SDL_BindAudioStream(audio_device, current_music->stream);
}
void stopMusic()
{
if (!music_enabled) return;
if (!current_music || current_music->state == audio::state::invalid) return;
current_music->pos = 0;
current_music->state = audio::state::stopped;
SDL_DestroyAudioStream(current_music->stream);
current_music->stream = nullptr;
free(current_music->filename);
current_music->filename = nullptr;
}
void fadeOutMusic(const int milliseconds)
{
if (!music_enabled) return;
if (current_music == NULL || current_music->state == audio::state::invalid) return;
fading = true;
fade_start_time = SDL_GetTicks();
fade_duration = milliseconds;
fade_initial_volume = music_volume;
}
audio::state getMusicState()
{
if (!music_enabled) return audio::state::disabled;
if (!current_music) return audio::state::invalid;
return current_music->state;
}
void deleteMusic(audio::music *music)
{
if (current_music == music) current_music = nullptr;
SDL_free(music->buffer);
if (music->stream) SDL_DestroyAudioStream(music->stream);
delete music;
}
float setMusicVolume(float volume)
{
music_volume = SDL_clamp( volume, 0.0f, 1.0f );
if (current_music) SDL_SetAudioStreamGain(current_music->stream, music_volume);
return music_volume;
}
void setMusicPosition(float value)
{
if (!current_music) return;
current_music->pos = value * current_music->spec.freq;
}
float getMusicPosition()
{
if (!current_music) return 0;
return float(current_music->pos)/float(current_music->spec.freq);
}
void enableMusic(const bool value)
{
if ( !value && current_music && (current_music->state==audio::state::playing) ) stopMusic();
music_enabled = value;
}
audio::sound *newSound(Uint8* buffer, Uint32 length)
{
audio::sound *sound = new audio::sound();
sound->buffer = buffer;
sound->length = length;
return sound;
}
audio::sound *loadSound(uint8_t* buffer, uint32_t size)
{
audio::sound *sound = new audio::sound();
SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size),1, &sound->spec, &sound->buffer, &sound->length);
return sound;
}
audio::sound *loadSound(const char* filename)
{
int size;
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size);
if (buffer == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::loadSound): No s'ha trobat l'arxiu %s\n", filename);
exit(1);
}
audio::sound *sound = loadSound(buffer, size);
free(buffer);
return sound;
}
int playSound(audio::sound *sound, const int loop)
{
if (!sound_enabled) return -1;
int channel = 0;
while (channel < MAX_CHANNELS && channels[channel].state != audio::state::available) { channel++; }
if (channel == MAX_CHANNELS) channel = 0;
stopChannel(channel);
channels[channel].sound = sound;
channels[channel].times = loop;
channels[channel].pos = 0;
channels[channel].state = audio::state::playing;
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &audio_spec);
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
SDL_SetAudioStreamGain(channels[channel].stream, sound_volume);
SDL_BindAudioStream(audio_device, channels[channel].stream);
return channel;
}
int playSoundOnChannel(audio::sound *sound, const int channel, const int loop)
{
if (!sound_enabled) return -1;
if (channel < 0 || channel >= MAX_CHANNELS) return -1;
stopChannel(channel);
channels[channel].sound = sound;
channels[channel].times = loop;
channels[channel].pos = 0;
channels[channel].state = audio::state::playing;
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &audio_spec);
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
SDL_SetAudioStreamGain(channels[channel].stream, sound_volume);
SDL_BindAudioStream(audio_device, channels[channel].stream);
return channel;
}
void deleteSound(audio::sound *sound)
{
for (int i = 0; i < MAX_CHANNELS; i++) {
if (channels[i].sound == sound) stopChannel(i);
}
SDL_free(sound->buffer);
delete sound;
}
void pauseChannel(const int channel)
{
if (!sound_enabled) return;
if (channel == -1)
{
for (int i = 0; i < MAX_CHANNELS; i++)
if (channels[i].state == audio::state::playing)
{
channels[i].state = audio::state::paused;
SDL_UnbindAudioStream(channels[i].stream);
}
}
else if (channel >= 0 && channel < MAX_CHANNELS)
{
if (channels[channel].state == audio::state::playing)
{
channels[channel].state = audio::state::paused;
SDL_UnbindAudioStream(channels[channel].stream);
}
}
}
void resumeChannel(const int channel)
{
if (!sound_enabled) return;
if (channel == -1)
{
for (int i = 0; i < MAX_CHANNELS; i++)
if (channels[i].state == audio::state::paused)
{
channels[i].state = audio::state::playing;
SDL_BindAudioStream(audio_device, channels[i].stream);
}
}
else if (channel >= 0 && channel < MAX_CHANNELS)
{
if (channels[channel].state == audio::state::paused)
{
channels[channel].state = audio::state::playing;
SDL_BindAudioStream(audio_device, channels[channel].stream);
}
}
}
void stopChannel(const int channel)
{
if (!sound_enabled) return;
if (channel == -1)
{
for (int i = 0; i < MAX_CHANNELS; i++) {
if (channels[i].state != audio::state::available) SDL_DestroyAudioStream(channels[i].stream);
channels[i].stream = nullptr;
channels[i].state = audio::state::available;
channels[i].pos = 0;
channels[i].sound = NULL;
}
}
else if (channel >= 0 && channel < MAX_CHANNELS)
{
if (channels[channel].state != audio::state::available) SDL_DestroyAudioStream(channels[channel].stream);
channels[channel].stream = nullptr;
channels[channel].state = audio::state::available;
channels[channel].pos = 0;
channels[channel].sound = NULL;
}
}
audio::state getChannelState(const int channel)
{
if (!sound_enabled) return audio::state::disabled;
if (channel < 0 || channel >= MAX_CHANNELS) return audio::state::invalid;
return channels[channel].state;
}
float setSoundVolume(float volume)
{
sound_volume = SDL_clamp( volume, 0.0f, 1.0f );
for (int i = 0; i < MAX_CHANNELS; i++)
if ( (channels[i].state == audio::state::playing) || (channels[i].state == audio::state::paused) )
SDL_SetAudioStreamGain(channels[i].stream, sound_volume);
return sound_volume;
}
void enableSound(const bool value)
{
for (int i = 0; i < MAX_CHANNELS; i++)
{
if (channels[i].state == audio::state::playing) stopChannel(i);
}
sound_enabled = value;
}
float setVolume(float volume)
{
setSoundVolume(setMusicVolume(volume) / 2.0f);
return music_volume;
}
#undef MAX_CHANNELS
}

44
source/japi/audio.h Normal file
View File

@@ -0,0 +1,44 @@
#pragma once
#include <SDL3/SDL.h>
namespace audio
{
enum state { invalid, available, playing, paused, stopped, disabled };
struct sound;
struct music;
void init(const int freq, const SDL_AudioFormat format, const int num_channels);
void quit();
audio::music *loadMusic(const char* filename);
audio::music *loadMusic(Uint8* buffer, Uint32 length);
void playMusic(audio::music *music, const int loop = -1);
void loadAndPlayMusic(const char* filename, const bool force_reinit=false);
char *getMusicFilename(audio::music *music = nullptr);
void pauseMusic();
void resumeMusic();
void stopMusic();
void fadeOutMusic(const int milliseconds=250);
audio::state getMusicState();
void deleteMusic(audio::music *music);
float setMusicVolume(float volume);
void setMusicPosition(float value);
float getMusicPosition();
void enableMusic(const bool value);
audio::sound *newSound(Uint8* buffer, Uint32 length);
audio::sound *loadSound(Uint8* buffer, Uint32 length);
audio::sound *loadSound(const char* filename);
int playSound(audio::sound *sound, const int loop = 0);
int playSoundOnChannel(audio::sound *sound, const int channel, const int loop = 0);
void pauseChannel(const int channel);
void resumeChannel(const int channel);
void stopChannel(const int channel);
audio::state getChannelState(const int channel);
void deleteSound(audio::sound *sound);
float setSoundVolume(float volume);
void enableSound(const bool value);
float setVolume(float volume);
}

View File

@@ -1,7 +1,8 @@
#include "jdraw.h"
#include "SDL2/SDL.h"
#include "gif.c"
#include "jfile.h"
#include "draw.h"
#include <SDL3/SDL.h>
#include "gif.h"
#include "file.h"
#include "shader.h"
namespace draw
{
@@ -10,22 +11,37 @@ namespace draw
// Aleshores, en "render" el contingut de screen se volca a la textura SDL que crearem,
// i eixa textura se pintarà a pantalla com se sol fer amb SDL. Ho anirem veient en el codi.
SDL_Window *sdl_window = nullptr; // La finestra de SDL
SDL_Renderer *sdl_renderer = nullptr; // El renderer de SDL
SDL_Texture *sdl_texture = nullptr; // La textura de SDL a la que pintarem la nostra superficie "screen" i que despres volcarem a pantalla
SDL_Window *sdl_window {nullptr}; // La finestra de SDL
SDL_Renderer *sdl_renderer {nullptr}; // El renderer de SDL
SDL_Texture *sdl_texture {nullptr}; // La textura de SDL a la que pintarem la nostra superficie "screen" i que despres volcarem a pantalla
SDL_Texture *sdl_shadertex {nullptr}; // La textura de SDL per al shader
static int screen_zoom = 1;
static int screen_zoom = 1;
static bool screen_fullscreen = false;
static bool screen_cursor = true;
static char* screen_shader = nullptr;
static bool shader_enabled = false;
static float window_ratio = 1;
static int canvas_width;
static int canvas_height;
static int desktop_width;
static int desktop_height;
static int window_width;
static int window_height;
static int offset_x = 0;
static int offset_y = 0;
char window_title[256];
surface *screen = nullptr; // La superficie screen, que representa la pantalla. Se crea i destrueix internament
surface *destination = nullptr; // Punter a la actual superficie de destí
surface *source = nullptr; // Punter a la actual superficie d'oritge
surface *pushedSource = nullptr; // Punter a la superficie d'oritge que s'ha pushat
surface *screen {nullptr}; // La superficie screen, que representa la pantalla. Se crea i destrueix internament
surface *destination {nullptr}; // Punter a la actual superficie de destí
surface *source {nullptr}; // Punter a la actual superficie d'oritge
surface *pushed_source {nullptr}; // Punter a la superficie d'oritge que s'ha pushat
uint32_t palette[256]; // La paleta de colors
uint32_t aux_palette[256]; // La paleta de colors
uint8_t color_indices[256]; // Indices dels colors per defecte
uint8_t sel_color = 0; // Color seleccionat per defecte
uint8_t transparent = 0; // El color transparent
uint32_t palette[256]; // La paleta de colors
uint32_t aux_palette[256]; // La paleta de colors, para els fadein
uint8_t color_indices[256]; // Indices dels colors per defecte
uint8_t sel_color {0}; // Color seleccionat per defecte
uint8_t transparent {0}; // El color transparent
//surface *textsurf = nullptr; // Surface on guardar el gif amb la font
@@ -34,20 +50,112 @@ namespace draw
bool fading_out = false;
bool fading_in = false;
// Inicialització de tot el que fa falta per a carregar gràfics i pintar en pantalla
void init(const std::string &titol, const uint16_t width, const uint16_t height, const int zoom)
void createDisplay()
{
screen_zoom = zoom;
// Ajustem el zoom i el ratio, per si tenen valors locs
if (screen_zoom <= 0) screen_zoom = 1;
if (window_ratio <= 0) window_ratio = 1;
// [TODO] Incloure gestió de pantalla completa
// Ajustem el tamany de la finestra, segons el zoom i el ratio
window_width = canvas_width*screen_zoom;
window_height = window_ratio != 1 ? int(float(canvas_width)*window_ratio*float(screen_zoom)) : canvas_height*screen_zoom;
// Mentres no càpiga en la pantalla, reduïm el zoom
while (window_width > desktop_width || window_height > desktop_height) {
screen_zoom--;
window_width = canvas_width*screen_zoom;
window_height = window_ratio != 1 ? int(float(canvas_width)*window_ratio*float(screen_zoom)) : canvas_height*screen_zoom;
}
if (screen_fullscreen) {
if (desktop_width * window_ratio > desktop_height) {
offset_y = 0;
window_height = desktop_height;
window_width = desktop_height/window_ratio;
offset_x = (desktop_width - window_width)/2;
} else {
offset_x = 0;
window_width = desktop_width;
window_height = desktop_width*window_ratio;
offset_y = (desktop_height - window_height)/2;
}
} else {
offset_x = offset_y = 0;
}
sdl_window = SDL_CreateWindow(window_title, window_width, window_height, SDL_WINDOW_OPENGL|(screen_fullscreen?SDL_WINDOW_FULLSCREEN:0));
if (!sdl_window) {
SDL_LogCritical(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize window!\n");
exit(1);
}
sdl_renderer = SDL_CreateRenderer(sdl_window, NULL);
if (!sdl_renderer) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize renderer!\n");
exit(1);
}
if (screen_cursor) SDL_ShowCursor(); else SDL_HideCursor();
sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, canvas_width, canvas_height);
SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_NEAREST);
if (!sdl_texture) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize texture!\n");
exit(1);
}
sdl_shadertex = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, window_width, window_height);
SDL_SetTextureScaleMode(sdl_shadertex, SDL_SCALEMODE_NEAREST);
loadShader();
}
void destroyDisplay()
{
SDL_DestroyTexture(sdl_texture);
SDL_DestroyRenderer(sdl_renderer);
SDL_DestroyWindow(sdl_window);
}
// Inicialització de tot el que fa falta per a carregar gràfics i pintar en pantalla
void init(const char *titol, const uint16_t width, const uint16_t height, const int zoom, const bool fullscreen, const float ratio)
{
screen_zoom = file::getConfigValueInteger("zoom", zoom);
screen_fullscreen = file::getConfigValueBool("fullscreen", fullscreen);
window_ratio = ratio;
canvas_width = width;
canvas_height = height;
strcpy(window_title, titol);
const SDL_DisplayMode *dm = SDL_GetDesktopDisplayMode(SDL_GetPrimaryDisplay());
if (!dm)
{
SDL_Log("SDL_GetDesktopDisplayMode failed: %s", SDL_GetError());
exit(1);
}
desktop_width = dm->w;
desktop_height = dm->h;
createDisplay();
// Inicialització de les estructures de SDL
sdl_window = SDL_CreateWindow(titol.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width * zoom, height * zoom, SDL_WINDOW_SHOWN);
sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
/*
sdl_window = SDL_CreateWindow(titol, width * zoom, height * zoom, 0);
if (!sdl_window) {
SDL_LogCritical(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize window!\n");
exit(1);
}
sdl_renderer = SDL_CreateRenderer(sdl_window, NULL);
if (!sdl_renderer) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize renderer!\n");
exit(1);
}
sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
// Establim el tamany "logic", indepndent del tamany de finestra
SDL_RenderSetLogicalSize(sdl_renderer, width, height);
SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_NEAREST);
if (!sdl_texture) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize texture!\n");
exit(1);
}
*/
// Creem la superficie "screen" i la establim com a superficie destinació
screen = createSurface(width, height);
@@ -58,7 +166,7 @@ namespace draw
sel_color = transparent = 0;
for (int i=0;i<256;++i) color_indices[i] = i;
SDL_ShowCursor(SDL_DISABLE);
//SDL_HideCursor();
//textsurf = loadSurface("font.gif");
}
@@ -74,9 +182,7 @@ namespace draw
}
// Destruim tot el relacionat amb SDL
SDL_DestroyTexture(sdl_texture);
SDL_DestroyRenderer(sdl_renderer);
SDL_DestroyWindow(sdl_window);
destroyDisplay();
// Fiquem tots els punters a nullptr, per si de cas no estem eixint del programa
// i anem a tornar a inicialitzar el sistema
@@ -86,11 +192,105 @@ namespace draw
screen = destination = source = nullptr;
}
void setZoom(const int value) {
screen_zoom = value;
destroyDisplay();
createDisplay();
file::setConfigValueInteger("zoom", screen_zoom);
}
const int getZoom()
{
return screen_zoom;
}
const float getScaleX()
{
return float(window_width) / float(canvas_width);
}
const float getScaleY()
{
return float(window_height) / float(canvas_height);
}
const int getOffsetX()
{
return offset_x;
}
const int getOffsetY()
{
return offset_y;
}
bool getFullscreen() {
return screen_fullscreen;
}
void setFullscreen(const bool value) {
screen_fullscreen=value;
destroyDisplay();
createDisplay();
file::setConfigValueBool("fullscreen", screen_fullscreen);
}
void loadShader()
{
char *buffer = nullptr;
if (screen_shader) {
int size;
buffer = file::getFileBuffer(screen_shader, size, true);
}
shader::setAspectRatio(3.0f/4.0f);
shader::init(sdl_window, sdl_shadertex, buffer);
if (buffer) free(buffer);
}
void setShader(const char* shader_file)
{
if (screen_shader) free(screen_shader);
screen_shader = (char*)malloc(strlen(shader_file)+1);
strcpy(screen_shader, shader_file);
loadShader();
if (file::getConfigValueBool("shader_enabled", false)) enableShader();
}
void enableShader()
{
shader_enabled = true;
shader::enable();
//destroyDisplay();
//createDisplay();
file::setConfigValueBool("shader_enabled", shader_enabled);
}
void disableShader()
{
shader_enabled = false;
shader::disable();
//destroyDisplay();
//createDisplay();
file::setConfigValueBool("shader_enabled", shader_enabled);
}
void toggleShader()
{
shader_enabled ? disableShader() : enableShader();
}
void hideCursor()
{
screen_cursor = false;
SDL_HideCursor();
}
void showCursor()
{
screen_cursor = true;
SDL_ShowCursor();
}
// Crea una superficie i torna un punter a ella
surface *createSurface(const uint16_t w, const uint16_t h)
{
@@ -109,12 +309,12 @@ namespace draw
}
// Carrega un gràfic d'un arxiu (en format GIF) a una nova superficie, i torna un punter a ella
surface *loadSurface(const std::string &filename, const bool loadPalette)
surface *loadSurface(const char *filename, const bool loadPalette)
{
// Agafem un buffer de bytes de l'arxiu especificat
// getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char
int size;
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, &size);
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size);
// Si ens ha tornat nullptr, es que no l'ha trobat, tornem nosaltres també nullptr ja que no s'ha pogut crear la superficie
if (buffer == nullptr)
@@ -191,12 +391,12 @@ namespace draw
void pushSource()
{
pushedSource = source;
pushed_source = source;
}
void popSource()
{
source = pushedSource;
source = pushed_source;
}
void setViewport(const int x, const int y, const int w, const int h)
@@ -225,12 +425,12 @@ namespace draw
}
// Carrega la paleta d'un GIF i la torna en un array de uint32_t
uint32_t *loadPalette(const std::string &filename, int *paletteSize)
uint32_t *loadPalette(const char *filename, int *paletteSize)
{
// Agafem un buffer de bytes de l'arxiu especificat
// getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char
int size;
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, &size);
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size);
// Li passem el array del arxiu a LoadPalette. Ell ens torna un array de uint32_t amb la paleta
// Van a ser 256 entrades de 32 bits, cada entrada es un color, amb el format 0xAARRGGBB
@@ -321,7 +521,7 @@ namespace draw
}
// Pinta un troç de la superficie "source" en la superficie "destination".
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const int flip)
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const draw::flip flip)
{
// Si no hi ha superficie d'oritge especificada, no fem res, o petarà el mame
if (source == nullptr)
@@ -333,14 +533,14 @@ namespace draw
int sdx = 1, sdy = 1, ssx = sx, ssy = sy;
// Però si s'ha especificat que fem flip en horitzontal...
if (flip & DRAW_FLIP_HORIZONTAL)
if (flip & draw::flip::horizontal)
{
sdx = -1; // Avançarem 1 pixel en negatiu
ssx = sx + w - 1; // I començarem al final, o siga, sumarem a sx el ample
}
// De igual forma per al flip en vertical, per a la y
if (flip & DRAW_FLIP_VERTICAL)
if (flip & draw::flip::vertical)
{
sdy = -1;
ssy = sy + h - 1;
@@ -497,17 +697,19 @@ namespace draw
// i el enviem a la textura SDL
for (uint32_t i = 0; i < size; ++i)
{
sdl_pixels[i] = palette[screen->pixels[i]];
sdl_pixels[i] = palette[screen->pixels[i]] | 0xff000000;
}
// Desbloquejem la textura
SDL_UnlockTexture(sdl_texture);
SDL_SetRenderTarget(sdl_renderer, sdl_shadertex);
// Pintem la textura a pantalla
SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
SDL_RenderTexture(sdl_renderer, sdl_texture, NULL, NULL);
// I ho presentem
SDL_RenderPresent(sdl_renderer);
shader::render();
//SDL_RenderPresent(sdl_renderer);
}
}

View File

@@ -1,15 +1,16 @@
#pragma once
#include <cstdint>
#include <string>
#define DRAW_FLIP_NONE 0
#define DRAW_FLIP_HORIZONTAL 1
#define DRAW_FLIP_VERTICAL 2
#define DRAW_FLIP_BOTH 3
//#define DRAW_FLIP_NONE 0
//#define DRAW_FLIP_HORIZONTAL 1
//#define DRAW_FLIP_VERTICAL 2
//#define DRAW_FLIP_BOTH 3
// Unitat per a la gestió dels recursos gràfics i dibuixat en pantalla
namespace draw
{
enum flip { none, horizontal, vertical, both };
// Estructura per a mantindre una superficie de pintat, la "pantalla virtual" de tota la vida
struct surface
{
@@ -24,12 +25,29 @@ namespace draw
/// @param width es el ample de la finestra "virtual"
/// @param height es el alt de la finestra "virtual"
/// @param zoom es com de grans son els pixels.
void init(const std::string &titol, const uint16_t width, const uint16_t height, const int zoom);
void init(const char *titol, const uint16_t width, const uint16_t height, const int zoom, const bool fullscreen=false, const float ratio=1.0);
/// @brief Finalització del sistema (tancar coses de SDL, superficies fixes, etc...)
void quit();
void setZoom(const int value);
const int getZoom();
const float getScaleX();
const float getScaleY();
const int getOffsetX();
const int getOffsetY();
bool getFullscreen();
void setFullscreen(const bool value);
void loadShader();
void setShader(const char* shader_file);
void enableShader();
void disableShader();
void toggleShader();
void hideCursor();
void showCursor();
/// @brief Crea una superficie i torna un punter a ella
/// @param w ample de la superficie
@@ -41,7 +59,7 @@ namespace draw
/// @param filename nom de l'arxiu GIF d'on carregar la superficie
/// @param loadPalette si es true també se carrega la paleta del GIF
/// @return un punter a una nova superficie
surface *loadSurface(const std::string &filename, const bool loadPalette = false);
surface *loadSurface(const char* filename, const bool loadPalette = false);
/// @brief Allibera la memòria d'una superficie, els seus pixels inclosos
/// @param surf punter a la superficie a alliberar
@@ -67,7 +85,7 @@ namespace draw
/// @brief Carrega la paleta d'un GIF i la torna en un array de uint32_t
/// @param filename nom de l'arxiu GIF d'on carregar la paleta
/// @param paletteSize si no es NULL ens torna el tamany de la paleta carregada
uint32_t *loadPalette(const std::string &filename, int *paletteSize = nullptr);
uint32_t *loadPalette(const char *filename, int *paletteSize = nullptr);
/// @brief Estableix la paleta del sistema, o part de ella, des d'un array especificat
/// @param pal un array de uint32_t
@@ -104,7 +122,7 @@ namespace draw
/// @param sx coordenada x de l'oritge
/// @param sy coordenada y de l'oritge
/// @param flip si s'ha de fer flip en hortizontal o vertical (o ambdos)
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const int flip = DRAW_FLIP_NONE);
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const draw::flip flip = draw::flip::none);
/// @brief Pinta tota la superficie "source" en la superficie "destination", posició (x,y).
/// @param x coordenada x de la destinació

275
source/japi/file.cpp Normal file
View File

@@ -0,0 +1,275 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include "file.h"
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <filesystem>
#include <string>
#include <vector>
#ifndef _WIN32
#include <pwd.h>
#endif
#define DEFAULT_FILENAME "data.jf2"
#define DEFAULT_FOLDER "data/"
#define CONFIG_FILENAME "config.txt"
namespace file
{
struct file_t
{
std::string path;
uint32_t size;
uint32_t offset;
};
std::vector<file_t> toc;
/* El std::map me fa coses rares, vaig a usar un good old std::vector amb una estructura key,value propia i au, que sempre funciona */
struct keyvalue_t {
std::string key, value;
};
char *resource_filename = NULL;
char *resource_folder = NULL;
int file_source = SOURCE_FILE;
char scratch[255];
static std::string config_folder;
std::vector<keyvalue_t> config;
void setResourceFilename(const char *str) {
if (resource_filename != NULL) free(resource_filename);
resource_filename = (char*)malloc(strlen(str)+1);
strcpy(resource_filename, str);
}
void setResourceFolder(const char *str) {
if (resource_folder != NULL) free(resource_folder);
resource_folder = (char*)malloc(strlen(str)+1);
strcpy(resource_folder, str);
}
void setSource(const int src) {
file_source = src%2; // mod 2 so it always is a valid value, 0 (file) or 1 (folder)
if (src==SOURCE_FOLDER && resource_folder==NULL) setResourceFolder(DEFAULT_FOLDER);
}
bool getDictionary() {
if (resource_filename == NULL) setResourceFilename(DEFAULT_FILENAME);
std::ifstream fi (resource_filename, std::ios::binary);
if (!fi.is_open()) return false;
char header[4];
fi.read(header, 4);
uint32_t num_files, toc_offset;
fi.read((char*)&num_files, 4);
fi.read((char*)&toc_offset, 4);
fi.seekg(toc_offset);
for (uint i=0; i<num_files; ++i)
{
uint32_t file_offset, file_size;
fi.read( (char*)&file_offset, 4 );
fi.read( (char*)&file_size, 4 );
uint8_t path_size;
fi.read( (char*)&path_size, 1 );
char file_name[path_size+1];
fi.read( file_name, path_size );
file_name[path_size] = 0;
std::string filename = file_name;
toc.push_back({filename, file_size, file_offset});
}
fi.close();
return true;
}
char *getFilenameWithFolder(const char* filename) {
strcpy(scratch, resource_folder);
strcat(scratch, filename);
return scratch;
}
FILE *getFilePointer(const char *resourcename, int& filesize, const bool binary) {
if (file_source==SOURCE_FILE and toc.size()==0) {
if (not getDictionary()) setSource(SOURCE_FOLDER);
}
FILE *f;
if (file_source==SOURCE_FILE) {
bool found = false;
uint32_t count = 0;
while( !found && count < toc.size() ) {
found = ( std::string(resourcename) == toc[count].path );
if( !found ) count++;
}
if( !found ) {
perror("El recurs no s'ha trobat en l'arxiu de recursos");
exit(1);
}
filesize = toc[count].size;
f = fopen(resource_filename, binary?"rb":"r");
if (not f) {
perror("No s'ha pogut obrir l'arxiu de recursos");
exit(1);
}
fseek(f, toc[count].offset, SEEK_SET);
} else {
f = fopen(getFilenameWithFolder(resourcename), binary?"rb":"r");
fseek(f, 0, SEEK_END);
filesize = ftell(f);
fseek(f, 0, SEEK_SET);
}
return f;
}
char *getFileBuffer(const char *resourcename, int& filesize, const bool zero_terminate) {
FILE *f = getFilePointer(resourcename, filesize, true);
char* buffer = (char*)malloc(zero_terminate?filesize+1:filesize);
fread(buffer, filesize, 1, f);
if (zero_terminate) buffer[filesize]=0;
fclose(f);
return buffer;
}
// Crea la carpeta del sistema donde guardar datos
void setConfigFolder(const char *foldername)
{
#ifdef _WIN32
config_folder = std::string(getenv("APPDATA")) + "/" + foldername;
#elif __APPLE__
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
config_folder = std::string(homedir) + "/Library/Application Support/" + foldername;
#elif __linux__
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
config_folder = std::string(homedir) + "/." + foldername;
#endif
struct stat st = {0};
if (stat(config_folder.c_str(), &st) == -1)
{
#ifdef _WIN32
int ret = mkdir(config_folder.c_str());
#else
int ret = mkdir(config_folder.c_str(), S_IRWXU);
#endif
if (ret == -1)
{
printf("ERROR CREATING CONFIG FOLDER.");
exit(EXIT_FAILURE);
}
}
}
const char *getConfigFolder() {
std::string folder = config_folder + "/";
return folder.c_str();
}
void loadConfigValues() {
config.clear();
std::string config_file = config_folder + "/config.txt";
FILE *f = fopen(config_file.c_str(), "r");
if (!f) return;
char line[1024];
while (fgets(line, sizeof(line), f)) {
char *value = strchr(line, '=');
if (value) {
*value='\0'; value++;
value[strlen(value)-1] = '\0';
config.push_back({line, value});
}
}
fclose(f);
}
void saveConfigValues() {
std::string config_file = config_folder + "/config.txt";
FILE *f = fopen(config_file.c_str(), "w");
if (f) {
for (auto pair : config) {
fprintf(f, "%s=%s\n", pair.key.c_str(), pair.value.c_str());
}
fclose(f);
}
}
const char* getConfigValueString(const char *key) {
if (config.empty()) loadConfigValues();
for (auto pair : config) {
if (pair.key == std::string(key)) {
strcpy(scratch, pair.value.c_str());
return scratch;
}
}
return NULL;
}
const int getConfigValueInteger(const char *key, const int default_value)
{
const char* value = getConfigValueString(key);
if (!value) return default_value;
return atoi(value);
}
const float getConfigValueFloat(const char *key, const float default_value)
{
const char* value = getConfigValueString(key);
if (!value) return default_value;
return atof(value);
}
const bool getConfigValueBool(const char *key, const bool default_value)
{
const char* value = getConfigValueString(key);
if (!value) return default_value;
return strcmp(value, "true")==0?true:false;
}
void setConfigValueString(const char* key, const char* value) {
if (config.empty()) loadConfigValues();
for (auto &pair : config) {
if (pair.key == std::string(key)) {
pair.value = value;
saveConfigValues();
return;
}
}
config.push_back({key, value});
saveConfigValues();
return;
}
void setConfigValueInteger(const char* key, const int value)
{
char tmp[256];
sprintf(tmp, "%i", value);
setConfigValueString(key, tmp);
}
void setConfigValueFloat(const char* key, const float value)
{
char tmp[256];
sprintf(tmp, "%.2f", value);
setConfigValueString(key, tmp);
}
void setConfigValueBool(const char* key, const bool value)
{
setConfigValueString(key, value?"true":"false");
}
}

27
source/japi/file.h Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
#include <stdio.h>
#define SOURCE_FILE 0
#define SOURCE_FOLDER 1
namespace file
{
void setConfigFolder(const char *foldername);
const char *getConfigFolder();
void setResourceFilename(const char *str);
void setResourceFolder(const char *str);
void setSource(const int src);
FILE *getFilePointer(const char *resourcename, int& filesize, const bool binary=false);
char *getFileBuffer(const char *resourcename, int& filesize, const bool zero_terminate=false);
const char* getConfigValueString(const char *key);
const int getConfigValueInteger(const char *key, const int default_value=0);
const float getConfigValueFloat(const char *key, const float default_value=0.0f);
const bool getConfigValueBool(const char *key, const bool default_value=false);
void setConfigValueString(const char* key, const char* value);
void setConfigValueInteger(const char* key, const int value);
void setConfigValueFloat(const char* key, const float value);
void setConfigValueBool(const char* key, const bool value);
}

View File

@@ -1,8 +1,9 @@
#include "jgame.h"
#include "jdraw.h"
#include "jinput.h"
#include <SDL2/SDL.h>
#include "game.h"
#include "draw.h"
#include "input.h"
#include <SDL3/SDL.h>
#include <map>
#include <string>
#ifdef MACOS_BUNDLE
#include <libgen.h>
@@ -49,13 +50,21 @@ int main(int argc, char *argv[])
char res_file[255] = "";
strcpy(res_file, dirname(argv[0]));
strcat(res_file, "/../Resources/data.jf2");
file::setResourceFilename(res_file);
file_setresourcefilename(res_file);
#endif
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
#ifdef DEBUG
SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG);
#endif
SDL_LogInfo(SDL_LOG_CATEGORY_SYSTEM, "JAPI v%s\n", JAPI_VERSION);
game::windowHasFocus = true;
game::init();
input::init(draw::getZoom());
input::init();
static unsigned int current_ticks = SDL_GetTicks();
bool should_exit=false;
@@ -64,26 +73,49 @@ int main(int argc, char *argv[])
{
while(SDL_PollEvent(&e))
{
if (e.type==SDL_QUIT) { should_exit = true; break; }
if (e.type==SDL_KEYDOWN)
if (e.type==SDL_EVENT_QUIT)
{
input::updateKey(e.key.keysym.scancode);
should_exit = true;
break;
}
if (e.type==SDL_KEYUP)
if (e.type==SDL_EVENT_KEY_DOWN)
{
input::updateKeypressed(e.key.keysym.scancode);
input::updateKey(e.key.scancode);
}
if (e.type==SDL_MOUSEBUTTONUP)
if (e.type==SDL_EVENT_KEY_UP)
{
switch (e.key.scancode) {
case SDL_SCANCODE_F1:
draw::setZoom(draw::getZoom()-1);
break;
case SDL_SCANCODE_F2:
draw::setZoom(draw::getZoom()+1);
break;
case SDL_SCANCODE_F3:
draw::setFullscreen(!draw::getFullscreen());
break;
case SDL_SCANCODE_F4:
draw::toggleShader();
break;
default:
input::updateKeypressed(e.key.scancode);
}
}
if (e.type==SDL_EVENT_MOUSE_BUTTON_UP)
{
input::updateClk(e.button.button);
}
if (e.type==SDL_MOUSEWHEEL)
if (e.type==SDL_EVENT_MOUSE_WHEEL)
{
input::updateWheel(e.wheel.y);
}
if ( e.type == SDL_WINDOWEVENT ) {
if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) game::windowHasFocus = true;
if (e.window.event == SDL_WINDOWEVENT_FOCUS_LOST) game::windowHasFocus = false;
if ( e.type == SDL_EVENT_WINDOW_FOCUS_GAINED )
{
game::windowHasFocus = true;
}
if ( e.type == SDL_EVENT_WINDOW_FOCUS_LOST )
{
game::windowHasFocus = false;
}
}

View File

@@ -1,9 +1,12 @@
#pragma once
#include "jdraw.h"
#include "jinput.h"
#include "jaudio.h"
#include "jfile.h"
#include "draw.h"
#include "input.h"
#include "audio.h"
#include "file.h"
#define JAPI_VERSION "0.8"
namespace game
{
extern bool windowHasFocus;

View File

@@ -2,6 +2,7 @@
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stdint.h>
#define EXTENSION_INTRODUCER 0x21
#define IMAGE_DESCRIPTOR 0x2C

View File

@@ -1,20 +1,18 @@
#include "jinput.h"
#include <SDL2/SDL.h>
#include "jdraw.h"
#include "input.h"
#include <SDL3/SDL.h>
#include "draw.h"
namespace input
{
static const uint8_t *keys = nullptr;
static const bool *keys = nullptr;
static uint8_t keypressed = 0;
static uint8_t keydown = 0;
static uint8_t btnClicked = 0;
static int wheel = 0;
static int screen_zoom = 1;
void init(const int zoom)
void init()
{
keys = SDL_GetKeyboardState(NULL);
screen_zoom = zoom;
}
// Determina si la tecla especificada està sent polsada ara mateix
@@ -79,23 +77,23 @@ namespace input
// Torna la posició X actual del ratolí
const int mouseX()
{
int x;
float x;
SDL_GetMouseState(&x, NULL);
return x/screen_zoom;
return (x-draw::getOffsetX())/draw::getScaleX();
}
// Torna la posició Y actual del ratolí
const int mouseY()
{
int y;
float y;
SDL_GetMouseState(NULL, &y);
return y/screen_zoom;
return (y-draw::getOffsetY())/draw::getScaleY();
}
// Determina si el botó del ratolí especificat està sent polsada ara mateix
const bool mouseBtn(const int btn)
{
return (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(btn));
return (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MASK(btn));
}
// Determina si el botó especificat ha sigut polsat, pero no tornarà a ser true fins

View File

@@ -14,7 +14,7 @@ namespace input
}
/// @brief Inicialitza els sistemes de teclat, ratolí i gamepad
void init(const int zoom);
void init();
/// @brief Determina si la tecla especificada està sent polsada ara mateix
/// @param key tecla a consultar

277
source/japi/shader.cpp Normal file
View File

@@ -0,0 +1,277 @@
#include "shader.h"
#include <iostream>
#ifdef __APPLE__
#include "CoreFoundation/CoreFoundation.h"
#include <OpenGL/OpenGL.h>
#if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3
#include <OpenGL/gl3.h>
#else
#include <OpenGL/gl.h>
#endif //!ESSENTIAL_GL_PRACTICES_SUPPORT_GL3
#else
#include <SDL3/SDL_opengl.h>
#include <SDL3/SDL_opengl_glext.h>
#endif
namespace shader
{
SDL_Window *win = nullptr;
SDL_Renderer *renderer = nullptr;
GLuint programId = 0;
SDL_Texture* backBuffer = nullptr;
SDL_FRect window = {0, 0, 640, 480};
SDL_FPoint tex_size = {320, 240};
float aspect_ratio = 1;
bool can_use_opengl = false;
bool using_opengl = false;
GLuint texture_number;
GLuint nose;
#ifndef __APPLE__
// I'm avoiding the use of GLEW or some extensions handler, but that
// doesn't mean you should...
PFNGLCREATESHADERPROC glCreateShader;
PFNGLSHADERSOURCEPROC glShaderSource;
PFNGLCOMPILESHADERPROC glCompileShader;
PFNGLGETSHADERIVPROC glGetShaderiv;
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
PFNGLDELETESHADERPROC glDeleteShader;
PFNGLATTACHSHADERPROC glAttachShader;
PFNGLCREATEPROGRAMPROC glCreateProgram;
PFNGLDELETEPROGRAMPROC glDeleteProgram;
PFNGLLINKPROGRAMPROC glLinkProgram;
PFNGLVALIDATEPROGRAMPROC glValidateProgram;
PFNGLGETPROGRAMIVPROC glGetProgramiv;
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
PFNGLUSEPROGRAMPROC glUseProgram;
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
PFNGLUNIFORM2FPROC glUniform2f;
bool initGLExtensions() {
glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader");
glShaderSource = (PFNGLSHADERSOURCEPROC)SDL_GL_GetProcAddress("glShaderSource");
glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader");
glGetShaderiv = (PFNGLGETSHADERIVPROC)SDL_GL_GetProcAddress("glGetShaderiv");
glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)SDL_GL_GetProcAddress("glGetShaderInfoLog");
glDeleteShader = (PFNGLDELETESHADERPROC)SDL_GL_GetProcAddress("glDeleteShader");
glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader");
glCreateProgram = (PFNGLCREATEPROGRAMPROC)SDL_GL_GetProcAddress("glCreateProgram");
glDeleteProgram = (PFNGLDELETEPROGRAMPROC)SDL_GL_GetProcAddress("glDeleteProgram");
glLinkProgram = (PFNGLLINKPROGRAMPROC)SDL_GL_GetProcAddress("glLinkProgram");
glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)SDL_GL_GetProcAddress("glValidateProgram");
glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv");
glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog");
glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram");
glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)SDL_GL_GetProcAddress("glGetUniformLocation");
glUniform2f = (PFNGLUNIFORM2FPROC)SDL_GL_GetProcAddress("glUniform2f");
return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv &&
glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram &&
glDeleteProgram && glLinkProgram && glValidateProgram && glGetProgramiv &&
glGetProgramInfoLog && glUseProgram && glGetUniformLocation && glUniform2f;
}
#endif
GLuint compileShader(const char* source, GLuint shaderType) {
// Create ID for shader
GLuint result = glCreateShader(shaderType);
// Add define depending on shader type
const char *sources[2] = { shaderType==GL_VERTEX_SHADER?"#define VERTEX\n":"#define FRAGMENT\n", source };
// Define shader text
glShaderSource(result, 2, sources, NULL);
// Compile shader
glCompileShader(result);
//Check vertex shader for errors
GLint shaderCompiled = GL_FALSE;
glGetShaderiv( result, GL_COMPILE_STATUS, &shaderCompiled );
if (shaderCompiled != GL_TRUE)
{
std::cout << "Error en la compilación: " << result << "!" << std::endl;
GLint logLength;
glGetShaderiv(result, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0)
{
GLchar *log = (GLchar*)malloc(logLength);
glGetShaderInfoLog(result, logLength, &logLength, log);
std::cout << "Shader compile log:" << log << std::endl;
//std::cout << source << std::endl;
free(log);
}
glDeleteShader(result);
result = 0;
}
return result;
}
GLuint compileProgram(const char* vertexShaderSource, const char* fragmentShaderSource)
{
GLuint programId = 0;
GLuint vtxShaderId, fragShaderId;
if (programId != 0) glDeleteProgram(programId);
programId = glCreateProgram();
vtxShaderId = compileShader(vertexShaderSource, GL_VERTEX_SHADER);
fragShaderId = compileShader(fragmentShaderSource?fragmentShaderSource:vertexShaderSource, GL_FRAGMENT_SHADER);
if(vtxShaderId && fragShaderId)
{
// Associate shader with program
glAttachShader(programId, vtxShaderId);
glAttachShader(programId, fragShaderId);
glLinkProgram(programId);
glValidateProgram(programId);
// Check the status of the compile/link
GLint logLen;
glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &logLen);
if (logLen > 0)
{
char* log = (char*) malloc(logLen * sizeof(char));
// Show any errors as appropriate
glGetProgramInfoLog(programId, logLen, &logLen, log);
std::cout << "Prog Info Log: " << std::endl << log << std::endl;
free(log);
}
}
if (vtxShaderId) glDeleteShader(vtxShaderId);
if (fragShaderId) glDeleteShader(fragShaderId);
return programId;
}
const bool init(SDL_Window* win, SDL_Texture* backBuffer, const char* vertexShader, const char* fragmentShader)
{
shader::win = win;
shader::renderer = SDL_GetRenderer(win);
shader::backBuffer = backBuffer;
int w, h;
SDL_GetWindowSize(win, &w, &h);
if (w * aspect_ratio > h) {
window.y = 0;
window.h = h;
window.w = h/aspect_ratio;
window.x = (w - window.w)/2;
} else {
window.x = 0;
window.w = w;
window.h = w*aspect_ratio;
window.y = (h - window.h)/2;
}
SDL_GetTextureSize(backBuffer, &tex_size.x, &tex_size.y);
printf("tex size: %fx%f\n", tex_size.x, tex_size.y);
SDL_PropertiesID props = SDL_GetTextureProperties(backBuffer);
texture_number = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER, -1);
printf("texture number: %i\n", texture_number);
int access = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_ACCESS_NUMBER, -1);
nose = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_TARGET_NUMBER, -1);
printf("texture target number: %i\n", nose);
if (access != SDL_TEXTUREACCESS_TARGET)
{
std::cout << "ERROR FATAL: La textura per al render ha de tindre SDL_TEXTUREACCESS_TARGET definit." << std::endl;
exit(1);
}
const char * renderer_name = SDL_GetRendererName(renderer);
printf("rendererInfo.name: %s\n", renderer_name);
if(!strncmp(renderer_name, "opengl", 6)) {
#ifndef __APPLE__
static bool gl_extensions_initialized = false;
if (!gl_extensions_initialized) {
if (!initGLExtensions()) {
std::cout << "WARNING: No s'han pogut inicialitzar les extensions d'OpenGL!" << std::endl;
can_use_opengl = false;
return false;
}
gl_extensions_initialized = true;
}
#endif
// Compilar el shader y dejarlo listo para usar.
if (!vertexShader) {
can_use_opengl = false;
return false;
}
programId = compileProgram(vertexShader, fragmentShader);
} else {
std::cout << "WARNING: El driver del renderer no es OpenGL." << std::endl;
can_use_opengl = false;
return false;
}
can_use_opengl = true;
return true;
}
unsigned char pixels[512*240*4];
void enable() { if (can_use_opengl) using_opengl = true; }
void disable() { using_opengl = false; }
void setAspectRatio(const float ratio)
{
aspect_ratio = ratio;
}
void render()
{
SDL_FlushRenderer(renderer);
SDL_SetRenderTarget(renderer, NULL);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_FlushRenderer(renderer);
if (using_opengl)
{
GLint oldProgramId;
if (programId != 0)
{
glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId);
glUseProgram(programId);
}
//GLint loc = glGetUniformLocation(programId, "TextureSize");
//glUniform2f(loc, 320, 256);
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 1);
//glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, pixels);
//if (glGetError()) { printf("GLGETERROR!\n"); exit(1);}
//GLint param;
//glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &param);
//printf("tex width: %i\n", param);
glViewport(window.x, window.y, window.w, window.h);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f, 0.0f);
glVertex2f(-1.0f, -1.0f);
glTexCoord2f(tex_size.x, 0.0f);
glVertex2f(1.0f, -1.0f);
glTexCoord2f(0.0f, tex_size.y);
glVertex2f(-1.0f, 1.0f);
glTexCoord2f(tex_size.x, tex_size.y);
glVertex2f(1.0f, 1.0f);
glEnd();
SDL_GL_SwapWindow(win);
if (programId != 0) glUseProgram(oldProgramId);
} else {
SDL_RenderTexture(renderer, backBuffer, NULL, &window);
SDL_RenderPresent(renderer);
}
int glerror = glGetError();
if (glerror) { printf("GLERROR: %i\n", glerror); exit(1); }
}
}

48
source/japi/shader.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <SDL3/SDL.h>
// TIPS:
// =======================================================================
// Abans de crear el renderer, cridar a la següent funció:
//
// SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
//
// Aixó li diu que volem un renderer que use especificament opengl. A més,
// al crear el renderer li tenim que dir que el volem que use acceeració
// per hardware, i que soporte render a textura. Per exemple:
//
// SDL_Renderer *ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED |
// SDL_RENDERER_TARGETTEXTURE);
//
// Per altra part, al crear la textura tenim que definir que puga ser target
// de renderitzat (SDL_TEXTUREACCESS_TARGET), per exemple:
//
// SDL_Texture *tex = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
// SDL_TEXTUREACCESS_TARGET, 320, 240);
//
// Els shaders li'ls passem com una cadena, som nosaltres els que s'encarreguem
// de carregarlos de disc, amb fopen, ifstream, jfile o el que vullgues.
// Si els tens en un std::string, passa-li-la com "cadena.c_str()".
//
// Poden ser els dos el mateix arxiu, com fa libRetro, jo desde dins ja fique
// els defines necessaris. Si es el mateix arxiu, pots no ficar el quart paràmetre.
//
// Els shaders de libRetro no funcionen directament, hi ha que fer algunes modificacions.
//
// El pintat final de la teua escena l'has de fer com si "backBuffer" fora la pantalla.
//
// Ah! una cosa mes: al compilar, en Linux afegir "-lGL", en Windows afegir "-lopengl32".
// En Mac ni idea
namespace shader
{
const bool init(SDL_Window* win, SDL_Texture* backBuffer,
const char* vertexShader, const char* fragmentShader=nullptr);
void setAspectRatio(const float ratio);
void enable();
void disable();
void render();
}

5584
source/japi/stb_vorbis.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,168 +0,0 @@
#include "jaudio.h"
#include "jfile.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_mixer.h>
#include <stdio.h>
namespace audio
{
// Açò son estructures de mentires, per a usar estructures de SDL_Mixer de forma opaca.
// Al final es un punter, així que és irrelevant el tipus del punter,
// però si amague que son estructures de SDL_Mixer, no fa falta ficar el include a SDL_mixer fora de este arxiu
struct sound
{
}; // Dummy structs
static char *buffer = nullptr;
static Mix_Music *music = nullptr;
static std::string music_filename = "";
// Inicialitza el sistema de só
void init()
{
// Al final he ficat la configuració automàtica i au. Si en el futur necesitem canviar-ho pos se canvia
if (Mix_Init(MIX_INIT_OGG)==0) {
printf("Failed Mix_Init()\n");
}
if (Mix_OpenAudio(48000, AUDIO_S16, 2, 1024)!=0) {
printf("Failed Mix_OpenAudio()\n");
}
}
// Tanca el sistema de só (no shit, sherlock)
void quit()
{
Mix_CloseAudio();
}
// Carrega un arxiu de música en format OGG
const bool loadMusic(const std::string filename)
{
if (music != nullptr) {
Mix_FreeMusic(music);
free(buffer);
}
int filesize=0;
buffer = file::getFileBuffer(filename, &filesize);
music = Mix_LoadMUS_RW(SDL_RWFromMem(buffer, filesize), 1);
if (music==nullptr) return false;
music_filename = filename;
return true;
}
// Comença a reproduïr la música en questió
void playMusic(const int loop)
{
if (Mix_PlayMusic(music, loop) == -1) {
printf("Failed Mix_PlayMusic()\n");
}
}
// Pausa la música que està sonant ara
void pauseMusic()
{
Mix_PauseMusic();
}
// Continua la música pausada
void resumeMusic()
{
Mix_ResumeMusic();
}
// Para la música que estava sonant
void stopMusic()
{
Mix_HaltMusic();
}
// Para la música que estava sonant fent un fade
void fadeoutMusic()
{
Mix_FadeOutMusic(500);
}
// Obté el estat actual de la música
const music_state getMusicState()
{
if (Mix_PausedMusic())
{
return MUSIC_PAUSED;
}
else if (Mix_PlayingMusic())
{
return MUSIC_PLAYING;
}
else
{
return MUSIC_STOPPED;
}
}
// Obté el nom de l'arxiu de música actual
std::string whichMusic()
{
return music_filename;
}
// Carrega un só des d'un arxiu WAV
const sound *loadSound(const std::string filename)
{
return (sound *)Mix_LoadWAV(filename.c_str());
}
// Comença a reproduïr el só especificat
const int playSound(sound *snd, const int loop)
{
return Mix_PlayChannel(-1, (Mix_Chunk *)snd, loop);
}
// Allibera un só
void deleteSound(sound *snd)
{
Mix_FreeChunk((Mix_Chunk *)snd);
}
// Pausa un canal en el que s'estava reproduïnt un só
void pauseChannel(const int channel)
{
Mix_Pause(channel);
}
// Continua un canal pausat
void resumeChannel(const int channel)
{
Mix_Resume(channel);
}
// Para un canal que estava reproduïnt un só
void stopChannel(const int channel)
{
Mix_HaltChannel(channel);
}
// Obté l'estat d'un canal
const channel_state getChannelState(const int channel)
{
if (Mix_Paused(channel))
{
return CHANNEL_PAUSED;
}
else if (Mix_Playing(channel))
{
return CHANNEL_PLAYING;
}
else
{
return CHANNEL_FREE;
}
}
// Estableix el volum general
const int setVolume(int volume)
{
return Mix_Volume(-1, volume);
}
}

View File

@@ -1,98 +0,0 @@
#pragma once
#include <string>
namespace audio
{
// Enumeració per a representar el estat de un canal de sò
enum channel_state
{
CHANNEL_INVALID,
CHANNEL_FREE,
CHANNEL_PLAYING,
CHANNEL_PAUSED
};
// Enumeració per a representar el estat de la música
enum music_state
{
MUSIC_INVALID,
MUSIC_PLAYING,
MUSIC_PAUSED,
MUSIC_STOPPED
};
// Estructures per a gestionar música i só
struct sound;
/// @brief Inicialitza el sistema de só
void init();
/// @brief Tanca el sistema de só
void quit();
/// @brief Carrega un arxiu de música en format OGG
/// @param filename nom de l'arxiu
/// @return true si tot be, false si ha fallat
const bool loadMusic(const std::string filename);
/// @brief Comença a reproduïr la música en questió
/// @param loop quants bucles farà (-1=infinit, 0=no repeteix, 1=repeteix 1 vegada...)
void playMusic(const int loop = -1);
/// @brief Pausa la música que està sonant ara
void pauseMusic();
/// @brief Continua la música pausada
void resumeMusic();
/// @brief Para la música que estava sonant
void stopMusic();
/// @brief Para la música que estava sonant fent un fade
void fadeoutMusic();
/// @brief Obté el estat actual de la música
/// @return estat actual de la música (MUSIC_INVALID, MUSIC_PLAYING, MUSIC_PAUSED o MUSIC_STOPPED)
const music_state getMusicState();
/// @brief Obté el nom de l'arxiu de música actual
/// @return el nom de l'arxiu
std::string whichMusic();
/// @brief Carrega un só des d'un arxiu WAV
/// @param filename nom de l'arxiu
/// @return un punter al só
const sound *loadSound(const std::string filename);
/// @brief Comença a reproduïr el só especificat
/// @param snd punter al só a reproduïr
/// @param loop si es fa bucle (-1=infinit, 0=no repeteix, 1=repeteix 1 vegada...)
/// @return número del canal en que està sonant el só
const int playSound(sound *snd, const int loop = 0);
/// @brief Pausa un canal en el que s'estava reproduïnt un só
/// @param channel número del canal a pausar
void pauseChannel(const int channel);
/// @brief Continua un canal pausat
/// @param channel número del canal pausat
void resumeChannel(const int channel);
/// @brief Para un canal que estava reproduïnt un só
/// @param channel número del canal a parar
void stopChannel(const int channel);
/// @brief Obté l'estat d'un canal
/// @param channel canal del que es vol obtindre l'estat
/// @return estat del canal (CHANNEL_INVALID, CHANNEL_FREE, CHANNEL_PLAYING o CHANNEL_PAUSED)
const channel_state getChannelState(const int channel);
/// @brief Allibera un só
/// @param snd punter al só
void deleteSound(sound *snd);
/// @brief Estableix el volum general
/// @param volume valor a establir com a volum (128 màxim)
/// @return el volum anterior
const int setVolume(int volume);
}

View File

@@ -1,500 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include "jfile.h"
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <filesystem>
#include <string>
#include <vector>
#ifndef _WIN32
#include <pwd.h>
#endif
#define DEFAULT_FILENAME "data.jf2"
#define DEFAULT_FOLDER "data/"
#define CONFIG_FILENAME "config.txt"
/* FORMAT DEL ARXIU .JF2
4 bytes header capçalera "PK2" (caracter 0 de final de cadena al final) (en realitat passe de ella, pero be...)
4 bytes num_files nombre d'arxius inclosos
4 bytes toc_offset en quina posició de l'arxiu està la taula de continguts
... Ara venen tots els arxius inclosos, uno darrere de l'altre.
... Quan acaben ve la taula de continguts. La taula te tantes entrades com arxius inclosos. Cada entrada te el següent format:
(per cert, toc_offset apunta ací)
4 bytes offset en quina posició del arxiu de recursos comença este arxiu
4 bytes size tamany d'este arxiu
1 byte path_size tamany de la cadena amb la ruta de l'arxiu
path_size bytes path ruta de l'arxiu original. La usem per a trobar el arxiu que ens demanen.
EXEMPLE SIMPLE:
- Imaginem que volem incloure un arxiu "data/hola.txt" amb el contingut "HOLA", i un arxiu "data/adios.txt" amb el contingut "ADIOS":
OFFSET CONTINGUT TAMANY DESCRIPCIÓ
0 "PK2"+0 4 La capçalera
4 2 4 nombre d'arxius
8 21 4 offset a la taula de continguts
--- COMENCEN ELS ARXIUS INCLOSOS ---
12 HOLA 4 el contingut del primer arxiu
16 ADIOS 5 el contingut del segon arxiu
--- COMENÇA LA TAULA DE CONTINGUTS ---
21 12 4 offset al primer arxiu
25 4 4 tamany del primer axiu
29 13 1 tamany de la ruta al primer arxiu
30 "data/hola.txt" 13 la ruta al primer arxiu
43 16 4 offset al primer arxiu
47 4 4 tamany del primer axiu
51 13 1 tamany de la ruta al primer arxiu
52 "data/adios.txt" 14 la ruta al primer arxiu
- Es un exemple raro, perque ocupa mes la ruta al arxiu que l'arxiu en si, pero espere que la idea quede clara!
Al principi se carrega la tabla de continguts en memòria, així el acces als arxius es ràpid.
Y com funciona tot açò? pos per defecte va a intentar llegir tots els arxius de "data.jf2". Si no troba e l'arxiu, automaticament passa a
buscar-ho tot en la carpeta "data" en arxius independents. En principi, si no se tenen requeriments diferents, no fa falta configurar res.
Respecte al tema de l'arxiu de configuració, està montat de forma que se pot escriure i llegir valors asociats a una clau sense calfar-se el cap.
En l'arxiu de configuració els valor se guarden de la forma:
CLAU=VALOR
Cada un en una linea. Si llegim i no existeix, torna cadena buida. Si escrivim i ja exisita, se reemplaça.
Tot son valors de cadena. Si en realitat son números, tindràs que encarregar-te tu de la caonversió de cadena a numero o al reves.
*/
namespace file
{
// Estructures
// ===============================================================================================================================
// Estructura que representa un arxiu en la tabla de continguts del arxiu de recursos
struct file_t
{
std::string path; // Ruta relativa de l'arxiu
uint32_t size; // Tamany de l'arxiu
uint32_t offset; // Offset a l'arxiu dins de l'arxiu de recursos
};
std::vector<file_t> toc; // vector que conté la taula de continguts de l'arxiu de recursos
/* El std::map me fa coses rares, vaig a usar un good old std::vector amb una estructura key,value propia i au, que sempre funciona */
struct keyvalue_t
{
std::string key, value;
};
// Variables
// ===============================================================================================================================
static std::string resource_filename = DEFAULT_FILENAME; // Nom de l'arxiu de recursos
static std::string resource_folder = DEFAULT_FOLDER; // Nom de la carpeta de recursos
static int file_source = SOURCE_FILE; // D'on anem a pillar els recursos, arxiu o carpeta
static std::string config_folder; // Nom de la carpeta on guardar la configuració
std::vector<keyvalue_t> config; // Vector amb els valors guardats a l'arxiu de configuració
// Funcions
// ===============================================================================================================================
// Estableix el nom de l'arxiu on estàn guardats els recursos (es "data.jf2" per defecte)
void setResourceFilename(const std::string str)
{
resource_filename = str;
}
// Estableix el nom de la carpeta on estàn guardats els recursos (es "data" per defecte)
void setResourceFolder(const std::string str)
{
resource_folder = str;
}
// Estableix d'on s'han de obtenir els recursos (arxius individuals dins d'una carpeta o arxiu de recursos)
void setSource(const int src)
{
file_source = src % 2; // mod 2 de forma que sempre es un valor vàlid, 0 (arxiu) o 1 (carpeta)
// Si volem que busque en carpeta i encara no haviem especificat una carpeta, usem la per defecte
if (/*src == SOURCE_FOLDER && */resource_folder == "")
{
setResourceFolder(DEFAULT_FOLDER);
}
}
// Carreguem la taula de continguts de l'arxiu de recursos
bool getTableOfContents()
{
// Si encara no haviem especificat un arxiu de recursos, usem el arxiu de recursos per defecte
if (resource_filename == "")
{
setResourceFilename(DEFAULT_FILENAME);
}
// Si l'arxiu de recursos existeix...
std::ifstream fi(resource_filename, std::ios::binary);
if (fi.is_open())
{
// Llegim la capçalera (controlar que siga correcta?)
char header[4];
fi.read(header, 4);
// LLegim el nombre d'arxius i la posició de la taula de continguts
uint32_t num_files, toc_offset;
fi.read((char *)&num_files, 4);
fi.read((char *)&toc_offset, 4);
// Anem a la taula de continguts
fi.seekg(toc_offset);
// Per a cada arxiu inclos en l'arxiu de recursos...
for (unsigned int i = 0; i < num_files; ++i)
{
// Llegim en quina posició està i quant ocupa
uint32_t file_offset, file_size;
fi.read((char *)&file_offset, 4);
fi.read((char *)&file_size, 4);
// Llegim la seua ruta
uint8_t path_size;
fi.read((char *)&path_size, 1);
char file_name[path_size + 1];
fi.read(file_name, path_size);
file_name[path_size] = 0;
std::string filename = file_name;
// Y afegim les dades a la taula de continguts en memòria
toc.push_back({filename, file_size, file_offset});
}
// Tanquem la paradeta i tornem true
fi.close();
return true;
}
else // Si no s'ha pogut llegir el arxiu de recursos, tornem false
{
return false;
}
}
// Obté un "std::ifstream" al arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos)
std::ifstream getFileStream(const std::string resourcename, int *filesize, const bool binary)
{
// Si tenim configurat agafar els recursos de arxiu, pero encara no tenim la taula de continguts carregada...
if (file_source == SOURCE_FILE and toc.size() == 0)
{
// Si fallem al intentar carregar la taula de continguts de l'arxiu de recursos, canviem a pillar els recursos de carpeta
if (not getTableOfContents())
{
setSource(SOURCE_FOLDER);
}
}
std::ifstream f;
// Si estem pillant els recursos de un arxiu de recursos...
if (file_source == SOURCE_FILE)
{
// Busquem el recurs en la taula de continguts usant la ruta
bool found = false;
uint32_t count = 0;
while (!found && count < toc.size())
{
found = (resource_folder + resourcename == toc[count].path);
if (!found)
{
count++;
}
}
// Si no trobem el recurs, petem el mame
if (!found)
{
printf("ERROR FATAL: No s'ha trobat el recurs '%s' a l'arxiu de recursos '%s'\n", resourcename.c_str(), resource_filename.c_str());
exit(1);
}
// Agafem el tamany del recurs de la taula de continguts
if (filesize) *filesize = toc[count].size;
// obrim l'arxiu de recursos
f.open(resource_filename.c_str(), binary ? std::ios::binary : std::ios::in);
if (!f.is_open()) // En el raruno cas de que a este altures pete al obrir el arxiu de recursos, petem el mame
{
printf("ERROR FATAL: No s'ha pogut obrir l'arxiu de recursos '%s'\n", resource_filename.c_str());
exit(1);
}
// Anem a la posició on està el recurs que volem. Amb açò "f" ja està preparat per a ser tornar.
// Ojo, realment estic tornant un FILE* al arxiu de recursos, pero ja apuntant al moment en que comença el recurs que volem.
// Ho dic perque si fem fseek(f, 0, SEEK_SET) tornarà al principi de l'arxiu de recursos, no del recurs. Tindre-ho en compte.
f.seekg(toc[count].offset);
}
else
{
// Si estem pillant els recursos de carpeta, simplement obrim el arxiu en questió i tornem el FILE* associat.
f.open((resource_folder + resourcename), binary ? std::ios::binary : std::ios::in);
if (f.rdstate() & std::ios_base::failbit)
{
printf("ERROR FATAL: No s'ha pogut obrir l'arxiu '%s/%s'\n", resource_folder.c_str(), resourcename.c_str());
exit(1);
}
f.seekg(0, std::ios_base::end);
if (filesize) *filesize = f.tellg();
f.seekg(0, std::ios_base::beg);
}
// Tornar el punter FILE* al arxiu. OJO! Tenim que tancar-lo quan acabem amb ell
return f;
}
// Obté un "FILE*" al arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos)
FILE *getFilePointer(const std::string resourcename, int *filesize, const bool binary)
{
// Si tenim configurat agafar els recursos de arxiu, pero encara no tenim la taula de continguts carregada...
if (file_source == SOURCE_FILE and toc.size() == 0)
{
// Si fallem al intentar carregar la taula de continguts de l'arxiu de recursos, canviem a pillar els recursos de carpeta
if (not getTableOfContents())
{
setSource(SOURCE_FOLDER);
}
}
FILE *f;
// Si estem pillant els recursos de un arxiu de recursos...
if (file_source == SOURCE_FILE)
{
// Busquem el recurs en la taula de continguts usant la ruta
bool found = false;
uint32_t count = 0;
while (!found && count < toc.size())
{
found = (resource_folder + resourcename == toc[count].path);
if (!found)
{
count++;
}
}
// Si no trobem el recurs, petem el mame
if (!found)
{
printf("ERROR FATAL: No s'ha trobat el recurs '%s' a l'arxiu de recursos '%s'\n", resourcename.c_str(), resource_filename.c_str());
exit(1);
}
// Agafem el tamany del recurs de la taula de continguts
if (filesize) *filesize = toc[count].size;
// obrim l'arxiu de recursos
f = fopen(resource_filename.c_str(), binary ? "rb" : "r");
if (!f) // En el raruno cas de que a este altures pete al obrir el arxiu de recursos, petem el mame
{
printf("ERROR FATAL: No s'ha pogut obrir l'arxiu de recursos '%s'\n", resource_filename.c_str());
exit(1);
}
// Anem a la posició on està el recurs que volem. Amb açò "f" ja està preparat per a ser tornar.
// Ojo, realment estic tornant un FILE* al arxiu de recursos, pero ja apuntant al moment en que comença el recurs que volem.
// Ho dic perque si fem fseek(f, 0, SEEK_SET) tornarà al principi de l'arxiu de recursos, no del recurs. Tindre-ho en compte.
fseek(f, toc[count].offset, SEEK_SET);
}
else
{
// Si estem pillant els recursos de carpeta, simplement obrim el arxiu en questió i tornem el FILE* associat.
f = fopen((resource_folder + resourcename).c_str(), binary ? "rb" : "r");
if(!f)
{
printf("ERROR FATAL: No s'ha pogut obrir l'arxiu '%s/%s'\n", resource_folder.c_str(), resourcename.c_str());
exit(1);
}
fseek(f, 0, SEEK_END);
if (filesize) *filesize = ftell(f);
fseek(f, 0, SEEK_SET);
}
// Tornar el punter FILE* al arxiu. OJO! Tenim que tancar-lo quan acabem amb ell
return f;
}
// Obté un buffer de memòria en format "char*" del arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos)
char *getFileBuffer(const std::string resourcename, int *filesize)
{
int size;
// Usem la funció anterior per a obtinde un FILE*, independentment de on pillem els recursos
FILE *f = getFilePointer(resourcename, &size, true);
// Reservem memòria per al buffer
char *buffer = (char *)malloc(size);
// llegim el contingut del arxiu i el fiquem en el buffer
fread(buffer, size, 1, f);
// Tanquem l'arxiu
fclose(f);
if (filesize) *filesize = size;
// Tornem el buffer. OJO! recordar alliberar-lo amb free(buffer) quan acabem amb ell.
return buffer;
}
// Estableix el nom de la carpeta on es guardarà la configuració
// Adaptat del codi que va escriure JailDesigner en el JailDoctor's Dilemma
// Vull revisar-la tranquilament algun dia
void setConfigFolder(const std::string foldername)
{
#ifdef _WIN32
config_folder = std::string(getenv("APPDATA")) + "/" + foldername;
#elif __APPLE__
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
config_folder = std::string(homedir) + "/Library/Application Support/" + foldername;
#elif __linux__
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
config_folder = std::string(homedir) + "/." + foldername;
#endif
struct stat st = {0};
if (stat(config_folder.c_str(), &st) == -1)
{
#ifdef _WIN32
int ret = mkdir(config_folder.c_str());
#else
int ret = mkdir(config_folder.c_str(), S_IRWXU);
#endif
if (ret == -1)
{
printf("ERROR CREATING CONFIG FOLDER.");
exit(EXIT_FAILURE);
}
}
}
// Obté el nom de la carpeta on es guardarà la configuració
const std::string getConfigFolder()
{
return config_folder + "/";
}
// Carrega tots els valors guardats a l'arxiu de recursos
void loadConfigValues()
{
// Buidem la taula de claus-valors en memòria
config.clear();
// Obrim l'arxiu de configuració
std::string config_file = config_folder + "/config.txt";
FILE *f = fopen(config_file.c_str(), "r");
if (f)
{
// Agafem linea a linea
char line[1024];
while (fgets(line, sizeof(line), f))
{
// Separem la clau del valor
char *value = strchr(line, '=');
if (value)
{
*value = '\0';
value++;
value[strlen(value) - 1] = '\0';
// i els afegim a la taula de claus-valors en memòria
config.push_back({line, value});
}
}
// tanquem la paradeta
fclose(f);
}
}
// Guardem tots els valors en la taula de claus-valors a l'arxiu de configuració
void saveConfigValues()
{
// Obrim l'arxiu de configuració
std::string config_file = config_folder + "/config.txt";
FILE *f = fopen(config_file.c_str(), "w");
if (f)
{
// Guardem cada parella clau/valor de la taula en memòria en una linea en format "clau=valor\n" en l'arxiu de configuració
for (auto pair : config)
{
fprintf(f, "%s=%s\n", pair.key.c_str(), pair.value.c_str());
}
// tanquem la paradeta
fclose(f);
}
}
// Obté un valor de l'arxiu de configuració per a la clau donada (o cadena buida si no existeix)
const std::string getConfigValue(const std::string key)
{
// Si la taula de claus-valors esta buida, la carreguem de l'arxiu de configuració
if (config.empty())
{
loadConfigValues();
}
// busquem la clau en la taula
for (auto pair : config)
{
if (pair.key == std::string(key))
{
// Si la trobem, tornem el seu valor
return pair.value;
}
}
// Si no la trobem, tornem cadena buida
return "";
}
// Estableix un valor en l'arxiu de configuració per a la clau donada
void setConfigValue(const std::string key, const std::string value)
{
// Si la taula de claus-valors esta buida, la carreguem de l'arxiu de configuració
if (config.empty())
{
loadConfigValues();
}
// busquem la clau en la taula
for (auto &pair : config)
{
if (pair.key == std::string(key))
{
// Si la trobem, actualitzem el seu valor i guardem els canvis a l'arxiu de configuració
pair.value = value;
saveConfigValues();
return;
}
}
// Si NO la trobem, afegim la nova clau i el seu valor, i guardem els canvis a l'arxiu de configuració
config.push_back({key, value});
saveConfigValues();
}
}

View File

@@ -1,68 +0,0 @@
#pragma once
#include <stdio.h>
#include <fstream>
#include <string>
#define SOURCE_FILE 0
#define SOURCE_FOLDER 1
// Unitat per a la gestió de l'acces a arxius
namespace file
{
// Funcions d'acces als recursos (gràfics, musiques...)
// -----------------------------------------------------------------------------------------------------------
/// @brief Estableix el nom de l'arxiu on estàn guardats els recursos (es "data.jf2" per defecte)
/// @param str nom de l'arxiu de recursos
void setResourceFilename(const std::string str);
/// @brief Estableix el nom de la carpeta on estàn guardats els recursos (es "data" per defecte)
/// @param str nom de la carpeta de recursos
void setResourceFolder(const std::string str);
/// @brief Estableix d'on s'han de obtenir els recursos (arxius individuals dins d'una carpeta o arxiu de recursos)
/// @param src SOURCE_FILE o SOURCE_FOLDER, si es vol que se pillen recursos de arxiu o de carpeta
void setSource(const int src);
/// @brief Obté un "std::ifstream" al arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos). Recordar tancar-lo al acabar amb ell.
/// @param resourcename el nom de l'arxiu que volem obrir
/// @param filesize paràmetre de retorn. Ací es torna el tamany de l'arxiu
/// @param binary si volem obrir el arxiu en format binary
/// @return un std::ifstream al arxiu
std::ifstream getFileStream(const std::string resourcename, int *filesize = nullptr, const bool binary = false);
/// @brief Obté un "FILE*" al arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos). Recordar tancar-lo al acabar amb ell.
/// @param resourcename el nom de l'arxiu que volem obrir
/// @param filesize paràmetre de retorn. Ací es torna el tamany de l'arxiu
/// @param binary si volem obrir el arxiu en format binary
/// @return un punter FILE* al arxiu
FILE *getFilePointer(const std::string resourcename, int *filesize = nullptr, const bool binary = false);
/// @brief Obté un buffer de memòria en format "char*" del arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos).
/// @brief Recordar alliberar la memoria del buffer amb free(buffer) al acabar amb ell.
/// @param resourcename el nom de l'arxiu del que volem obrindre el buffer
/// @param filesize paràmetre de retorn. Ací es torna el tamany de l'arxiu
/// @return un array de "filesize" bytes amb el contingut del arxiu
char *getFileBuffer(const std::string resourcename, int *filesize = nullptr);
// Funcions d'access a la configuració (clau = valor)
// -----------------------------------------------------------------------------------------------------------
/// @brief Estableix el nom de la carpeta on es guardarà la configuració
/// @param foldername nom de la carpeta
void setConfigFolder(const std::string foldername);
/// @brief Obté el nom de la carpeta on es guardarà la configuració
/// @return nom de la carpeta
const std::string getConfigFolder();
/// @brief Obté un valor de l'arxiu de configuració per a la clau donada (o cadena buida si no existeix)
/// @param key clau de la que obtindre el valor
/// @return el valor de la clau especificada
const std::string getConfigValue(const std::string key);
/// @brief Estableix un valor en l'arxiu de configuració per a la clau donada
/// @param key clau a la que establir un valor
/// @param value valor a establir per a la clau
void setConfigValue(const std::string key, const std::string value);
}

View File

@@ -1,14 +1,15 @@
#include "jgame.h"
#include "gamestates.h"
#include "aux_font.h"
#include "japi/game.h"
#include "gamestates/gamestates.h"
#include "aux/font.h"
void game::init()
{
draw::init("Arounders", 320, 200, 3);
file::setConfigFolder("arounders");
draw::init("Arounders", 320, 200, 3, false, 3.0f/4.0f);
draw::setShader("gbc.glsl");
draw::setTrans(0);
input::init(3);
audio::init();
audio::init(48000, SDL_AUDIO_S16, 2);
draw::hideCursor();
font::init();