34 Commits
v1.04 ... v1.06

Author SHA1 Message Date
c955543d31 Merge branch 'master' of https://gitea.sustancia.synology.me/JailDesigner/jaildoctors_dilemma 2022-11-22 16:44:27 +01:00
fae8e2d82c Actualizado Makefile 2022-11-22 16:44:23 +01:00
ff7b825cbb Actualizar 'README.md' 2022-11-22 16:39:39 +01:00
09ed0105e9 Cambiada la ubicación del fichero config.txt a la carpeta de sistema 2022-11-22 16:26:58 +01:00
07ae93fd3a Actualizado Makefile 2022-11-22 14:11:17 +01:00
72fb30b927 Actualizado Makefile 2022-11-22 13:59:41 +01:00
8d37f31a08 Cambiada una frase de castellano a ingles en el fichero de estadisticas 2022-11-22 13:35:15 +01:00
e6fb412c4d Fix: La pantalla de Game Over no mostraba Tu peor pesadilla 2022-11-22 13:34:33 +01:00
10c29eb547 Actualizado Makefile 2022-11-22 13:20:13 +01:00
1f9e36dc93 Actualizado info.plist 2022-11-22 13:07:12 +01:00
c114884de1 FIx: El logo se formaba mal despues de haber toqueteado cosas de la clase Sprite 2022-11-22 13:05:34 +01:00
d6a366555d Actualizado Makefile 2022-11-22 12:53:56 +01:00
fd0d8a05fe Actualizado Makefile 2022-11-22 12:45:47 +01:00
79d610a7d9 Actualizado Makefile 2022-11-22 12:42:25 +01:00
9060e1c92a Añadida carpeta de sistema para el modo debug 2022-11-22 12:38:09 +01:00
0d586c0827 Ya funcionan las estadísticas online 2022-11-22 10:42:30 +01:00
fd0bebf533 Trabajando en las estadisticas online 2022-11-18 20:06:07 +01:00
2665b93b70 Añadida Tu peor pesadilla a la pantalla de Game Over 2022-11-18 16:07:33 +01:00
c35be7d21c Empezado a escribir datos de estadisticas en el servidor 2022-11-19 12:07:36 +01:00
bc84968b23 Ya funcionan las notificaciones 2022-11-19 10:28:15 +01:00
1b49097f80 Cambiado el tamaño por defecto del borde 2022-11-19 09:27:55 +01:00
859e3f3b44 Actualizado Makefile 2022-11-19 09:27:24 +01:00
3fa7657c79 Cuendo el jugador entra a la Jail, recupera todas sus vidas 2022-11-19 09:22:10 +01:00
e90c61a416 FIx: El jugador no se moria al caer de alto si se mantenia la tecla de slto pulsada 2022-11-19 09:02:35 +01:00
d003b51de4 Reparada la clase Screen 2022-11-19 07:49:01 +01:00
8d381010c8 Trabajando en la clase Screen 2022-11-19 07:37:03 +01:00
2216c9632e Trabajando en las notificaciones 2022-11-18 23:15:38 +01:00
fdc8797d2b Arreglada la clase Screen. Había un lio de conceptos con varias variables 2022-11-18 20:09:03 +01:00
90bdad7d51 Modificado Makefile 2022-11-18 10:43:54 +01:00
d50b193d9f Cambiado el fichero de estadisticas a stats.csv 2022-11-18 10:43:01 +01:00
ee885d5ca7 Ya crea correctamente la carpeta de sistema en las tres plataformas 2022-11-18 10:40:51 +01:00
bb3a0f263b Ya crea la carpeta de sistema en MacOS y Windows 2022-11-18 10:35:15 +01:00
85d6c48f42 Ya crea la carpeta de sistema en Linux 2022-11-18 09:48:46 +01:00
896dd9daef Trabajando en el guardado de las estadisticas en las carpetas del sistema 2022-11-18 07:32:15 +01:00
33 changed files with 1279 additions and 261 deletions

4
.gitignore vendored
View File

@@ -1,5 +1,5 @@
.vscode
*config.txt
*data/config/config.txt
*stats.txt
*.DS_Store
thumbs.db
@@ -10,4 +10,4 @@ thumbs.db
*.tar.gz
*.zip
*.app
*_debug
*_debug*

View File

@@ -2,7 +2,7 @@ executable = jaildoctors_dilemma
source = source/*.cpp source/common/*.cpp
appName = JailDoctor's Dilemma
releaseFolder = jdd_release
version = v1.04
version = v1.06
# Release names
windowsRelease = $(executable)-$(version)-win32-x64.zip
@@ -12,34 +12,33 @@ linuxRelease = $(executable)-$(version)-linux.tar.gz
windows:
@echo off
g++ $(source) -D DEBUG -std=c++11 -Wall -Os -lmingw32 -lSDL2main -lSDL2 -ffunction-sections -fdata-sections -Wl,--gc-sections -static-libstdc++ -Wl,-subsystem,windows -o $(executable).exe
strip -s -R .comment -R .gnu.version $(executable).exe --strip-unneeded
g++ $(source) -std=c++11 -Wall -Os -lmingw32 -lws2_32 -lSDL2main -lSDL2 -ffunction-sections -fdata-sections -Wl,--gc-sections -static-libstdc++ -Wl,-subsystem,windows -o "$(executable).exe"
strip -s -R .comment -R .gnu.version "$(executable).exe" --strip-unneeded
windows_debug:
@echo off
g++ $(source) -D DEBUG -std=c++11 -Wall -Os -lmingw32 -lws2_32 -lSDL2main -lSDL2 -ffunction-sections -fdata-sections -Wl,--gc-sections -static-libstdc++ -Wl,-subsystem,windows -o "$(executable)_debug.exe"
strip -s -R .comment -R .gnu.version "$(executable)_debug.exe" --strip-unneeded
windows_release:
@echo off
# Remove data
powershell if (Test-Path $(releaseFolder)) {Remove-Item $(releaseFolder) -Recurse -Force}
# Create release folder
powershell if (Test-Path "$(releaseFolder)") {Remove-Item "$(releaseFolder)" -Recurse -Force}
powershell if (-not (Test-Path "$(releaseFolder)")) {New-Item "$(releaseFolder)" -ItemType Directory}
# Create folders
powershell if (-not (Test-Path $(releaseFolder))) {New-Item $(releaseFolder) -ItemType Directory}
# Copy data
# Prepare data folder
powershell Copy-Item -Path "data" -Destination "$(releaseFolder)" -recurse -Force
powershell Copy-Item "LICENSE" -Destination "$(releaseFolder)"
powershell Copy-Item "README.md" -Destination "$(releaseFolder)"
powershell Copy-Item "release/SDL2.dll" -Destination "$(releaseFolder)"
# Remove data
powershell if (Test-Path "$(releaseFolder)\data\room\map.world") {Remove-Item "$(releaseFolder)\data\room\map.world" -Recurse -Force}
powershell if (Test-Path "$(releaseFolder)\data\room\standard.tsx") {Remove-Item "$(releaseFolder)\data\room\standard.tsx" -Recurse -Force}
powershell if (Test-Path "$(releaseFolder)\data\config") {Remove-Item "$(releaseFolder)\data\config" -Recurse -Force}
# Create data
powershell if (-not (Test-Path "$(releaseFolder)\data\config")) {New-Item "$(releaseFolder)\data\config" -ItemType Directory}
# Copy root files
powershell Copy-Item "LICENSE" -Destination "$(releaseFolder)"
powershell Copy-Item "README.md" -Destination "$(releaseFolder)"
powershell Copy-Item "release\SDL2.dll" -Destination "$(releaseFolder)"
# Build
g++ $(source) -std=c++11 -Wall -Os -lmingw32 -lSDL2main -lSDL2 -ffunction-sections -fdata-sections -Wl,--gc-sections -static-libstdc++ -Wl,-subsystem,windows -o "$(releaseFolder)/$(executable).exe"
g++ $(source) -std=c++11 -Wall -Os -lmingw32 -lws2_32 -lSDL2main -lSDL2 -ffunction-sections -fdata-sections -Wl,--gc-sections -static-libstdc++ -Wl,-subsystem,windows -o "$(releaseFolder)/$(executable).exe"
strip -s -R .comment -R .gnu.version "$(releaseFolder)/$(executable).exe" --strip-unneeded
# Create ZIP
@@ -47,13 +46,16 @@ windows_release:
powershell Compress-Archive -Path "$(releaseFolder)"/* -DestinationPath $(windowsRelease)
# Remove folder
powershell if (Test-Path $(releaseFolder)) {Remove-Item $(releaseFolder) -Recurse -Force}
powershell if (Test-Path "$(releaseFolder)") {Remove-Item "$(releaseFolder)" -Recurse -Force}
macos:
clang++ $(source) -D DEBUG -std=c++11 -Wall -Os -lSDL2 -ffunction-sections -fdata-sections -o $(executable)_macos
clang++ $(source) -std=c++11 -Wall -Os -lSDL2 -ffunction-sections -fdata-sections -o "$(executable)"
macos_debug:
clang++ $(source) -D DEBUG -std=c++11 -Wall -Os -lSDL2 -ffunction-sections -fdata-sections -o "$(executable)_debug"
macos_release:
# Remove data and possible data
# Remove data and possible data from previous builds
rm -rdf "$(releaseFolder)"
rm -rdf Frameworks
rm -f tmp.dmg
@@ -74,10 +76,6 @@ macos_release:
# Delete data
rm -f "$(releaseFolder)/$(appName).app/Contents/Resources/data/room/map.world"
rm -f "$(releaseFolder)/$(appName).app/Contents/Resources/data/room/standard.tsx"
rm -rdf "$(releaseFolder)/$(appName).app/Contents/Resources/data/config"
# Create folders
mkdir -p "$(releaseFolder)/$(appName).app/Contents/Resources/data/config"
# Copy files
cp release/*.icns "$(releaseFolder)/$(appName).app/Contents/Resources"
@@ -106,36 +104,36 @@ macos_release:
rm -rdf "$(releaseFolder)"
linux:
g++ $(source) -D DEBUG -std=c++11 -Wall -Os -lSDL2 -ffunction-sections -fdata-sections -Wl,--gc-sections -o $(executable)_linux
strip -s -R .comment -R .gnu.version $(executable)_linux --strip-unneeded
g++ $(source) -std=c++11 -Wall -Os -lSDL2 -ffunction-sections -fdata-sections -Wl,--gc-sections -o "$(executable)"
strip -s -R .comment -R .gnu.version "$(executable)" --strip-unneeded
linux_debug:
g++ $(source) -D DEBUG -std=c++11 -Wall -Os -lSDL2 -ffunction-sections -fdata-sections -Wl,--gc-sections -o "$(executable)_debug"
strip -s -R .comment -R .gnu.version "$(executable)_debug" --strip-unneeded
linux_release:
# Remove data
rm -rdf $(releaseFolder)
rm -rdf "$(releaseFolder)"
# Create folders
mkdir -p $(releaseFolder)
mkdir -p "$(releaseFolder)"
# Copy data
cp -R data $(releaseFolder)
cp LICENSE $(releaseFolder)
cp README.md $(releaseFolder)
cp -R data "$(releaseFolder)"
cp LICENSE "$(releaseFolder)"
cp README.md "$(releaseFolder)"
# Delete data
rm -f "$(releaseFolder)/data/room/map.world"
rm -f "$(releaseFolder)/data/room/standard.tsx"
rm -rdf "$(releaseFolder)/data/config"
# Create folders
mkdir -p "$(releaseFolder)/data/config"
# Build
g++ $(source) -std=c++11 -Wall -Os -lSDL2 -ffunction-sections -fdata-sections -Wl,--gc-sections -o $(releaseFolder)/$(executable)
strip -s -R .comment -R .gnu.version $(releaseFolder)/$(executable) --strip-unneeded
g++ $(source) -std=c++11 -Wall -Os -lSDL2 -ffunction-sections -fdata-sections -Wl,--gc-sections -o "$(releaseFolder)/$(executable)"
strip -s -R .comment -R .gnu.version "$(releaseFolder)/$(executable)" --strip-unneeded
# Pack files
rm -f $(linuxRelease)
cd $(releaseFolder) && tar -czvf ../$(linuxRelease) *
rm -f "$(linuxRelease)"
cd "$(releaseFolder)" && tar -czvf "../$(linuxRelease)" *
# Remove data
rm -rdf $(releaseFolder)
rm -rdf "$(releaseFolder)"

View File

@@ -1,37 +1,85 @@
# JailDoctor's Dilemma
JailDoc es un Jailer. A los Jailers les gusta empezar proyectos. A nadie le gusta terminarlos. Los Jailers viven en la Jail. A la Jail va uno a empezar proyectos. A la Jail va uno a enseñar sus proyectos. A la Jail va uno a aprender como empezar nuevos proyectos. A la Jail va uno a ayudar a sus compañeros a que empiecen nuevos proyectos.
![JailDoctor's Dilemma - Title screen](https://php.sustancia.synology.me/images/jdd_title.png)
JailDoc es un Jailer destacado entre los Jailers. Tiene más proyectos empezados que nadie y es el que más ayuda a que los demas empiecen los suyos.
Un día, ocurrió algo. Alguien terminó un proyecto. Alguien liberó el *Puzzle Jail Facker*. Algún desaprensivo.
Esto hizo que JailDoc decidiera terminar y entregar uno de sus proyectos, pero, ¿cual? ¿JailBattle? ¿Sigmasuá? ¿Calculín Doom? Menudo dilema. JailDoc se arremangó y decidió finalizar y entregar todos sus proyectos inacabados. ¿Lo logrará?
## Jugabilidad
Ayuda a JailDoc a recuperar las partes de su proyecto que estan desperdigadas por cualquier lugar del Universo Jailer. Hay mas de **150 piezas** desperdigadas por **60 pantallas**. Algunas son un paseo, pero en otras tendras que calcular muy bien tus movimientos si no quieres acabar como un arounder del montón.
![JailDoctor's Dilemma - Gameplay](https://php.sustancia.synology.me/images/jdd_game1.png)
Cuando consigas recuperar gran parte de las piezas desperdigadas, dirigete a la Jail a mostrar a los Jailers como se termina un proyecto. Ten en cuenta que Bry no te dejará entrar. Solo aquellos que han realizado un *Fire Effect* o un *Facedor de Tornejos* son dignos de tal privilegio.
## Controles
El juego permite tanto el uso del teclado como de un mando de control. Las teclas para manejar el juego son las siguientes:
- **Cursores**: Para mover a izquierda o derecha a JailDoc y para saltar
- **Tecla M**: Activa o desactiva la música
- **Tecla P**: Pone en pausa el juego
- **Tecla ESC**: Sale del juego si estas jugando. Sale del programa en cualquier otra circunstancia
- **Tecla F**: Cambia a modo de pantalla completa o de ventana
- **Teclas F1 a F4**: Cambian el tamaño de la ventana
- **Tecla B**: Activa o desactiva el borde de colores de la pantalla cuando el programa se ejecuta en modo de ventana
- **Cursores**: Para mover a izquierda o derecha a JailDoc y para saltar
- **Tecla M**: Activa o desactiva la música
- **Tecla P**: Pone en pausa el juego
- **Tecla ESC**: Sale del juego si estas jugando. Sale del programa en cualquier otra circunstancia
- **Tecla F**: Cambia a modo de pantalla completa o de ventana
- **Teclas F1 a F4**: Cambian el tamaño de la ventana
- **Tecla B**: Activa o desactiva el borde de colores de la pantalla cuando el programa se ejecuta en modo de ventana
![JailDoctor's Dilemma - Gameplay](https://php.sustancia.synology.me/images/jdd_game2.png)
## Datos del programa
El programa guarda automáticamente la configuración del modo de video y las estadísticas de juego en tu carpeta personal del sistema. Esta carpeta tiene una ubicación distinta en función del sistema operativo que utilices.
En **Windows** se encuentra en:
`C:\Users\<nombre_de_usuario>\AppData\Roaming\jaildoctors_dilemma`
En **MacOS** se encuentra en:
`~/Library/Application Support/jaildoctors_dilemma`
En **Linux** se encuentra en:
`~/.jaildoctors_dilemma`
En la carpeta está el fichero de configuración `config.txt` donde se puede modificar la configuración para conectarse al servicio online y los ficheros `stats.csv` y `stats_buffer.csv` con información de las estadisticas de juego.
## Agradecimientos
Agradecimientos como siempre a todos los Jailers por motivarme a hacer el juego y ayudarme en los momentos de duda a la hora de escribir el código. Y, como siempre, en especial a JailDoc por su unidad de Jail_Audio y cualquier otro código/ayuda/enseñanzas que haya necesitado para terminar el programa.
Si no me he descontado, este es el cuarto juego que consigo crear.
*13 de noviembre de 2022, JailDesigner*

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleIdentifier</key>
<string>com.apple.xcode.dsym.jaildoctors_dilemma_debug</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>dSYM</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -27,7 +27,7 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
<string>1.0.6</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSMinimumSystemVersion</key>

View File

@@ -10,10 +10,10 @@ Asset::Asset(std::string executablePath)
}
// Añade un elemento a la lista
void Asset::add(std::string file, enum assetType type, bool required)
void Asset::add(std::string file, enum assetType type, bool required, bool absolute)
{
item_t temp;
temp.file = executablePath + file;
temp.file = absolute ? file : executablePath + file;
temp.type = type;
temp.required = required;
fileList.push_back(temp);

View File

@@ -31,6 +31,7 @@ private:
std::string file; // Ruta del fichero desde la raiz del directorio
enum assetType type; // Indica el tipo de recurso
bool required; // Indica si es un fichero que debe de existir
//bool absolute; // Indica si la ruta que se ha proporcionado es una ruta absoluta
};
// Variables
@@ -50,7 +51,7 @@ public:
Asset(std::string path);
// Añade un elemento a la lista
void add(std::string file, enum assetType type, bool required = true);
void add(std::string file, enum assetType type, bool required = true, bool absolute = false);
// Devuelve un elemento de la lista a partir de una cadena
std::string get(std::string text);

154
source/common/jscore.cpp Normal file
View File

@@ -0,0 +1,154 @@
#include "jscore.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#endif
#include <unistd.h>
#include <string>
#include <vector>
namespace jscore {
using namespace std;
struct user {
string name;
int points;
};
vector<user> score;
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
int sock;
struct sockaddr_in client;
int PORT = 9911;
string HOST = "jaildoctor.duckdns.org";
#ifdef WIN32
WSADATA WsaData;
#endif
bool jscore_error = false;
string error_message;
void init(std::string host, const int port) {
PORT = port;
HOST = host;
}
void setErrorMessage(string message) {
jscore_error = true;
error_message = message;
}
string sendRequest(const string request) {
#ifdef WIN32
int ret = WSAStartup(0x101,&WsaData);
if (ret != 0) return 0;
#endif
struct hostent * host = gethostbyname(HOST.c_str());
if ( (host == NULL) || (host->h_addr == NULL) ) {
setErrorMessage("Error retrieving DNS information.\n");
return "";
}
bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons( PORT );
memcpy(&client.sin_addr, host->h_addr, host->h_length);
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
setErrorMessage("Error creating socket.\n");
return "";
}
if ( connect(sock, (struct sockaddr *)&client, sizeof(client)) < 0 ) {
close(sock);
setErrorMessage("Could not connect\n");
return "";
}
string r = request + " HTTP/1.1\r\nHost: "+HOST+"\r\nConnection: close\r\n\r\n\r\n";
if (send(sock, r.c_str(), r.length(), 0) != (int)r.length()) {
setErrorMessage("Error sending request.\n");
return "";
}
char cur;
char start[5]="\r\n\r\n";
int pos = 0;
while ( recv(sock, &cur, 1,0) > 0 ) {
if (cur==start[pos]) { pos++; if (pos == 4) break; } else { pos = 0; }
}
char buffer[1024]; buffer[0]=0; pos=0;
while ( recv(sock, &cur, 1,0) > 0 ) {
buffer[pos] = cur;
pos++;
}
#ifdef WIN32
WSACleanup();
#endif
buffer[pos]=0;
return buffer;
}
const bool initOnlineScore(string game) {
string strbuff = sendRequest("GET /score-list.php?game=" + game);
if (jscore_error) return not jscore_error;
user u;
char buffer[1024];
strcpy(buffer, strbuff.c_str());
char *str = buffer;
char *p = str;
score.clear();
while (*p!=0) {
while (*p!=',') {p++;}
*p=0; u.name = str; p++; str=p;
while (*p!='\n') {p++;}
*p=0; u.points = atoi(str); p++; str=p;
score.push_back(u);
}
return not jscore_error;
}
const int getNumUsers() {
return score.size();
}
string getUserName(const int index) {
return score[index].name;
}
const int getPoints(const int index) {
return score[index].points;
}
const bool updateUserPoints(string game, string user, const int points) {
string strbuff = sendRequest("GET /score-update.php?game=" + game + "&user=" + user + "&points=" + to_string(points));
initOnlineScore(game);
return not jscore_error;
}
const int getUserPoints(string game, std::string user) {
return atoi(sendRequest("GET /getuserpoints.php?game=" + game + "&user=" + user).c_str());
}
string getUserData(string game, string user) {
return sendRequest("GET /getuserdata.php?game=" + game + "&user=" + user);
}
void setUserData(string game, string user, string data) {
sendRequest("GET /setuserdata.php?game=" + game + "&user=" + user + "&data=" + data);
}
};

16
source/common/jscore.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include <string>
namespace jscore {
void init(std::string host, const int port);
const bool initOnlineScore(std::string game);
const int getNumUsers();
std::string getUserName(const int index);
const int getPoints(const int index);
const int getUserPoints(std::string game, std::string user);
const bool updateUserPoints(std::string game, std::string user, const int points);
std::string getUserData(std::string game, std::string user);
void setUserData(std::string game, std::string user, std::string data);
};

157
source/common/notify.cpp Normal file
View File

@@ -0,0 +1,157 @@
#include "notify.h"
#include <string>
#include <stdio.h>
#include <iostream>
// Constructor
Notify::Notify(SDL_Renderer *renderer, std::string bitmapFile, std::string textFile)
{
// Inicializa variables
this->renderer = renderer;
bgColor = {64, 64, 64};
waitTime = 300;
// Crea objetos
texture = new Texture(renderer, bitmapFile);
text = new Text(textFile, texture, renderer);
}
// Destructor
Notify::~Notify()
{
// Libera la memoria de los objetos
delete texture;
delete text;
for (auto notification : notifications)
{
delete notification.sprite;
delete notification.texture;
}
}
// Dibuja las notificaciones por pantalla
void Notify::render()
{
for (int i = (int)notifications.size() - 1; i >= 0; --i)
{
notifications.at(i).sprite->render();
}
}
// Actualiza el estado de las notificaiones
void Notify::update()
{
for (int i = 0; i < (int)notifications.size(); ++i)
{
notifications.at(i).counter++;
// Comprueba los estados
if (notifications.at(i).state == ns_rising)
{
const float step = ((float)notifications.at(i).counter / notifications.at(i).travelDist);
const int alpha = 255 * step;
notifications.at(i).rect.y++;
notifications.at(i).texture->setAlpha(alpha);
if (notifications.at(i).rect.y == notifications.at(i).y)
{
notifications.at(i).state = ns_stay;
notifications.at(i).texture->setAlpha(255);
notifications.at(i).counter = 0;
}
}
else if (notifications.at(i).state == ns_stay)
{
if (notifications.at(i).counter == waitTime)
{
notifications.at(i).state = ns_vanishing;
notifications.at(i).counter = 0;
}
}
else if (notifications.at(i).state == ns_vanishing)
{
const float step = (notifications.at(i).counter / (float)notifications.at(i).travelDist);
const int alpha = 255 * (1 - step);
notifications.at(i).rect.y--;
notifications.at(i).texture->setAlpha(alpha);
if (notifications.at(i).rect.y == notifications.at(i).y - notifications.at(i).travelDist)
{
notifications.at(i).state = ns_finished;
}
}
notifications.at(i).sprite->setRect(notifications.at(i).rect);
}
clearFinishedNotifications();
}
// Elimina las notificaciones finalizadas
void Notify::clearFinishedNotifications()
{
for (int i = (int)notifications.size() - 1; i >= 0; --i)
{
if (notifications.at(i).state == ns_finished)
{
delete notifications.at(i).sprite;
delete notifications.at(i).texture;
notifications.erase(notifications.begin() + i);
}
}
}
// Muestra una notificación de texto por pantalla;
void Notify::showText(std::string text)
{
// Crea constantes
const int width = this->text->lenght(text) + (this->text->getCharacterSize() * 2);
const int height = this->text->getCharacterSize() * 2;
const int despH = this->text->getCharacterSize() / 2;
const int despV = despH;
const int travelDist = height + despV;
const int offset = (int)notifications.size() > 0 ? notifications.back().y + travelDist : despV;
// Crea la notificacion
notification_t n;
// inicializa variables
n.y = offset;
n.travelDist = travelDist;
n.counter = 0;
n.state = ns_rising;
n.text = text;
n.rect = {despH, offset - travelDist, width, height};
// Crea la textura
n.texture = new Texture(renderer);
n.texture->createBlank(renderer, width, height, SDL_TEXTUREACCESS_TARGET);
n.texture->setAsRenderTarget(renderer);
SDL_SetRenderDrawColor(renderer, bgColor.r, bgColor.g, bgColor.b, 255);
SDL_RenderClear(renderer);
n.texture->setBlendMode(SDL_BLENDMODE_BLEND);
this->text->writeDX(TXT_CENTER | TXT_STROKE, width / 2, despV, text, 1, {255, 255, 255}, 1, {0, 0, 0});
SDL_SetRenderTarget(renderer, nullptr);
// Crea el sprite
n.sprite = new Sprite(n.rect, n.texture, renderer);
// Añade la notificación a la lista
notifications.push_back(n);
}
// Indica si hay notificaciones activas
bool Notify::active()
{
if ((int)notifications.size() > 0)
{
return true;
}
return false;
}

82
source/common/notify.h Normal file
View File

@@ -0,0 +1,82 @@
#pragma once
#include <SDL2/SDL.h>
#include "text.h"
#include "texture.h"
#include "sprite.h"
#include "utils.h"
#include <vector>
#ifndef NOTIFY_H
#define NOTIFY_H
class Notify
{
private:
enum notification_state_e
{
ns_rising,
ns_stay,
ns_vanishing,
ns_finished
};
enum notification_position_e
{
upperLeft,
upperCenter,
upperRight,
middleLeft,
middleRight,
bottomLeft,
bottomCenter,
bottomRight
};
struct notification_t
{
std::string text;
int counter;
notification_state_e state;
notification_position_e position;
Texture *texture;
Sprite *sprite;
SDL_Rect rect;
int y;
int travelDist;
};
// Objetos y punteros
SDL_Renderer *renderer; // El renderizador de la ventana
Texture *texture; // Textura para la fuente de las notificaciones
Text *text; // Objeto para dibujar texto
// Variables
color_t bgColor; // Color de fondo de las notificaciones
int waitTime; // Tiempo que se ve la notificación
std::vector<notification_t> notifications; // La lista de notificaciones activas
// Elimina las notificaciones finalizadas
void clearFinishedNotifications();
public:
// Dibuja las notificaciones por pantalla
void render();
// Actualiza el estado de las notificaiones
void update();
// Constructor
Notify(SDL_Renderer *renderer, std::string bitmapFile, std::string textFile);
// Destructor
~Notify();
// Muestra una notificación de texto por pantalla;
void showText(std::string text);
// Indica si hay notificaciones activas
bool active();
};
#endif

View File

@@ -3,15 +3,21 @@
#include <iostream>
// Constructor
Screen::Screen(SDL_Window *window, SDL_Renderer *renderer, options_t *options, int gameInternalResX, int gameInternalResY)
Screen::Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset, options_t *options)
{
// Inicializa variables
this->window = window;
this->renderer = renderer;
this->options = options;
this->asset = asset;
gameCanvasWidth = gameInternalResX;
gameCanvasHeight = gameInternalResY;
// Crea los objetos
notify = new Notify(renderer, asset->get("smb2.png"), asset->get("smb2.txt"));
gameCanvasWidth = options->gameWidth;
gameCanvasHeight = options->gameHeight;
borderWidth = options->gameWidth * options->borderSize;
borderHeight = options->gameHeight * options->borderSize;
iniFade();
iniSpectrumFade();
@@ -27,15 +33,16 @@ Screen::Screen(SDL_Window *window, SDL_Renderer *renderer, options_t *options, i
}
// Establece el modo de video
setVideoMode(options->fullScreenMode);
setVideoMode(options->videoMode);
// Calcula los anclajes
anchor.left = 0;
anchor.right = gameCanvasWidth;
anchor.center = gameCanvasWidth / 2;
anchor.top = 0;
anchor.bottom = gameCanvasHeight;
anchor.middle = gameCanvasHeight / 2;
// Inicializa variables
notifyActive = false;
}
// Destructor
Screen::~Screen()
{
delete notify;
}
// Limpia la pantalla
@@ -64,107 +71,100 @@ void Screen::blit()
// Copia la textura de juego en el renderizador en la posición adecuada
SDL_RenderCopy(renderer, gameCanvas, nullptr, &dest);
// Dibuja las notificaciones
renderNotifications();
// Muestra por pantalla el renderizador
SDL_RenderPresent(renderer);
}
// Establece el modo de video
void Screen::setVideoMode(int fullScreenMode)
void Screen::setVideoMode(int videoMode)
{
// Aplica el modo de video
SDL_SetWindowFullscreen(window, fullScreenMode);
SDL_SetWindowFullscreen(window, videoMode);
// Si está activo el modo ventana quita el borde
if (fullScreenMode == 0)
if (videoMode == 0)
{
if (options->borderEnabled)
{
const int incWidth = gameCanvasWidth * options->borderSize;
const int incHeight = gameCanvasHeight * options->borderSize;
screenWidth = gameCanvasWidth + incWidth;
screenHeight = gameCanvasHeight + incHeight;
dest = {0 + (incWidth / 2), 0 + (incHeight / 2), gameCanvasWidth, gameCanvasHeight};
windowWidth = gameCanvasWidth + borderWidth;
windowHeight = gameCanvasHeight + borderHeight;
dest = {0 + (borderWidth / 2), 0 + (borderHeight / 2), gameCanvasWidth, gameCanvasHeight};
}
else
{
screenWidth = gameCanvasWidth;
screenHeight = gameCanvasHeight;
windowWidth = gameCanvasWidth;
windowHeight = gameCanvasHeight;
dest = {0, 0, gameCanvasWidth, gameCanvasHeight};
}
// Modifica el tamaño del renderizador y de la ventana
SDL_RenderSetLogicalSize(renderer, screenWidth, screenHeight);
SDL_SetWindowSize(window, screenWidth * options->windowSize, screenHeight * options->windowSize);
SDL_RenderSetLogicalSize(renderer, windowWidth, windowHeight);
SDL_SetWindowSize(window, windowWidth * options->windowSize, windowHeight * options->windowSize);
}
// Si está activo el modo de pantalla completa añade el borde
else if (fullScreenMode == SDL_WINDOW_FULLSCREEN_DESKTOP)
else if (videoMode == SDL_WINDOW_FULLSCREEN_DESKTOP)
{
// Obten el alto y el ancho de la ventana
SDL_GetWindowSize(window, &screenWidth, &screenHeight);
SDL_GetWindowSize(window, &windowWidth, &windowHeight);
// Aplica el escalado al rectangulo donde se pinta la textura del juego
if (options->integerScale)
{
// Calcula el tamaño de la escala máxima
int scale = 0;
while (((gameCanvasWidth * (scale + 1)) <= screenWidth) && ((gameCanvasHeight * (scale + 1)) <= screenHeight))
while (((gameCanvasWidth * (scale + 1)) <= windowWidth) && ((gameCanvasHeight * (scale + 1)) <= windowHeight))
{
scale++;
}
dest.w = gameCanvasWidth * scale;
dest.h = gameCanvasHeight * scale;
dest.x = (screenWidth - dest.w) / 2;
dest.y = (screenHeight - dest.h) / 2;
dest.x = (windowWidth - dest.w) / 2;
dest.y = (windowHeight - dest.h) / 2;
}
else if (options->keepAspect)
{
float ratio = (float)gameCanvasWidth / (float)gameCanvasHeight;
if ((screenWidth - gameCanvasWidth) >= (screenHeight - gameCanvasHeight))
if ((windowWidth - gameCanvasWidth) >= (windowHeight - gameCanvasHeight))
{
dest.h = screenHeight;
dest.w = (int)((screenHeight * ratio) + 0.5f);
dest.x = (screenWidth - dest.w) / 2;
dest.y = (screenHeight - dest.h) / 2;
dest.h = windowHeight;
dest.w = (int)((windowHeight * ratio) + 0.5f);
dest.x = (windowWidth - dest.w) / 2;
dest.y = (windowHeight - dest.h) / 2;
}
else
{
dest.w = screenWidth;
dest.h = (int)((screenWidth / ratio) + 0.5f);
dest.x = (screenWidth - dest.w) / 2;
dest.y = (screenHeight - dest.h) / 2;
dest.w = windowWidth;
dest.h = (int)((windowWidth / ratio) + 0.5f);
dest.x = (windowWidth - dest.w) / 2;
dest.y = (windowHeight - dest.h) / 2;
}
}
else
{
dest.w = screenWidth;
dest.h = screenHeight;
dest.w = windowWidth;
dest.h = windowHeight;
dest.x = dest.y = 0;
}
// Modifica el tamaño del renderizador
SDL_RenderSetLogicalSize(renderer, screenWidth, screenHeight);
SDL_RenderSetLogicalSize(renderer, windowWidth, windowHeight);
}
// Actualiza el valor de la variable
options->fullScreenMode = fullScreenMode;
options->videoMode = videoMode;
}
// Camibia entre pantalla completa y ventana
void Screen::switchVideoMode()
{
if (options->fullScreenMode == 0)
{
options->fullScreenMode = SDL_WINDOW_FULLSCREEN_DESKTOP;
}
else
{
options->fullScreenMode = 0;
}
setVideoMode(options->fullScreenMode);
options->videoMode = (options->videoMode == 0) ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0;
setVideoMode(options->videoMode);
}
// Cambia el tamaño de la ventana
@@ -338,4 +338,30 @@ void Screen::renderFX()
{
renderFade();
renderSpectrumFade();
}
// Actualiza el notificador
void Screen::updateNotifier()
{
notify->update();
notifyActive = notify->active();
}
// Muestra una notificación de texto por pantalla;
void Screen::showText(std::string text)
{
notify->showText(text);
}
// Dibuja las notificaciones
void Screen::renderNotifications()
{
if (!notifyActive)
{
return;
}
SDL_RenderSetLogicalSize(renderer, windowWidth * 2, windowHeight * 2);
notify->render();
SDL_RenderSetLogicalSize(renderer, windowWidth, windowHeight);
}

View File

@@ -1,6 +1,8 @@
#pragma once
#include <SDL2/SDL.h>
#include "asset.h"
#include "notify.h"
#include "utils.h"
#include <vector>
@@ -10,42 +12,35 @@
#define FILTER_NEAREST 0
#define FILTER_LINEAL 1
struct anchor_t
{
int left; // Parte izquierda de la pantalla de juego
int right; // Parte drecha de la pantalla de juego
int center; // Parte central horizontal de la pantalla de juego
int top; // Parte superior de la pantalla de juego
int bottom; // Parte infoerior de la pantalla de juego
int middle; // Parte central vertical de la pantalla de juego
};
// Clase Screen
class Screen
{
private:
// Objetos y punteros
SDL_Window *window; // Ventana de la aplicación
SDL_Renderer *renderer; // El renderizador de la ventana
Asset *asset; // Objeto con el listado de recursos
SDL_Texture *gameCanvas; // Textura para completar la ventana de juego hasta la pantalla completa
options_t *options; // Variable con todas las opciones del programa
Notify *notify; // Dibuja notificaciones por pantalla
// Variables
int screenWidth; // Ancho de la pantalla o ventana
int screenHeight; // Alto de la pantalla o ventana
int windowWidth; // Ancho de la pantalla o ventana
int windowHeight; // Alto de la pantalla o ventana
int gameCanvasWidth; // Resolución interna del juego. Es el ancho de la textura donde se dibuja el juego
int gameCanvasHeight; // Resolución interna del juego. Es el alto de la textura donde se dibuja el juego
anchor_t anchor; // Variable con los anclajes de la pantalla
int borderWidth; // Anchura del borde
int borderHeight; // Anltura del borde
SDL_Rect dest; // Coordenadas donde se va a dibujar la textura del juego sobre la pantalla o ventana
color_t borderColor; // Color del borde añadido a la textura de juego para rellenar la pantalla
bool notifyActive; // Indica si hay notificaciones activas
// Variables - Efectos
bool fade; // Indica si esta activo el efecto de fade
int fadeCounter; // Temporizador para el efecto de fade
int fadeLenght; // Duración del fade
bool spectrumFade; // Indica si esta activo el efecto de fade spectrum
int spectrumFadeCounter; // Temporizador para el efecto de fade spectrum
int spectrumFadeLenght; // Duración del fade spectrum
bool fade; // Indica si esta activo el efecto de fade
int fadeCounter; // Temporizador para el efecto de fade
int fadeLenght; // Duración del fade
bool spectrumFade; // Indica si esta activo el efecto de fade spectrum
int spectrumFadeCounter; // Temporizador para el efecto de fade spectrum
int spectrumFadeLenght; // Duración del fade spectrum
std::vector<color_t> spectrumColor; // Colores para el fade spectrum
// Inicializa las variables para el fade
@@ -66,9 +61,15 @@ private:
// Dibuja el spectrum fade
void renderSpectrumFade();
// Dibuja las notificaciones
void renderNotifications();
public:
// Constructor
Screen(SDL_Window *window, SDL_Renderer *renderer, options_t *options, int gameInternalResX, int gameInternalResY);
Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset, options_t *options);
// Destructor
~Screen();
// Limpia la pantalla
void clean(color_t color = {0x00, 0x00, 0x00});
@@ -80,7 +81,7 @@ public:
void blit();
// Establece el modo de video
void setVideoMode(int fullScreenMode);
void setVideoMode(int videoMode);
// Camibia entre pantalla completa y ventana
void switchVideoMode();
@@ -120,6 +121,12 @@ public:
// Dibuja los efectos
void renderFX();
// Actualiza el notificador
void updateNotifier();
// Muestra una notificación de texto por pantalla;
void showText(std::string text);
};
#endif

View File

@@ -18,7 +18,7 @@ Sprite::Sprite(int x, int y, int w, int h, Texture *texture, SDL_Renderer *rende
this->texture = texture;
// Establece el rectangulo de donde coger la imagen
spriteClip = {x, y, w, h};
spriteClip = {0, 0, w, h};
// Inicializa variables
enabled = true;
@@ -41,7 +41,7 @@ Sprite::Sprite(SDL_Rect rect, Texture *texture, SDL_Renderer *renderer)
this->texture = texture;
// Establece el rectangulo de donde coger la imagen
spriteClip = {x, y, w, h};
spriteClip = {0, 0, w, h};
// Inicializa variables
enabled = true;
@@ -83,8 +83,8 @@ int Sprite::getHeight()
// Establece la posición del objeto
void Sprite::setPos(SDL_Rect rect)
{
x = rect.x;
y = rect.y;
this->x = rect.x;
this->y = rect.y;
}
// Establece el valor de la variable
@@ -166,6 +166,12 @@ void Sprite::setRenderer(SDL_Renderer *renderer)
this->renderer = renderer;
}
// Obten el valor de la variable
SDL_Renderer *Sprite::getRenderer()
{
return renderer;
}
// Establece el valor de la variable
void Sprite::setEnabled(bool value)
{

View File

@@ -81,6 +81,9 @@ public:
// Establece el valor de la variable
void setRenderer(SDL_Renderer *renderer);
// Obten el valor de la variable
SDL_Renderer *getRenderer();
// Establece el valor de la variable
void setEnabled(bool value);

View File

@@ -70,24 +70,43 @@ struct cheat_t
bool altSkin; // Indicxa si se usa una skin diferente para el jugador
};
// Estructura para el servicio online
struct online_t
{
bool enabled; // Indica si se quiere usar el modo online o no
std::string server; // Servidor para los servicios online
int port; // Puerto del servidor
std::string gameID; // Identificador del juego para los servicios online
std::string jailerID; // Identificador del jugador para los servicios online
int score; // Puntuación almacenada online
};
// Estructura para almacenar estadísticas
struct op_stats_t
{
int rooms; // Cantidad de habitaciones visitadas
int items; // Cantidad de items obtenidos
std::string worstNightmare; // Habitación con más muertes acumuladas
};
// Estructura con todas las opciones de configuración del programa
struct options_t
{
Uint32 fullScreenMode; // Contiene el valor del modo de pantalla completa
int windowSize; // Contiene el valor por el que se multiplica el tamaño de la ventana
Uint32 filter; // Filtro usado para el escalado de la imagen
bool vSync; // Indica si se quiere usar vsync o no
int screenWidth; // Ancho de la pantalla o ventana
int screenHeight; // Alto de la pantalla o ventana
bool integerScale; // Indica si el escalado de la imagen ha de ser entero en el modo a pantalla completa
bool keepAspect; // Indica si se ha de mantener la relación de aspecto al poner el modo a pantalla completa
bool borderEnabled; // Indica si ha de mostrar el borde en el modo de ventana
float borderSize; // Porcentaje de borde que se añade a lo ventana
palette_e palette; // Paleta de colores a usar en el juego
bool console; // Indica si ha de mostrar información por la consola de texto
cheat_t cheat; // Contiene trucos y ventajas para el juego
int rooms; // Cantidad de habitaciones visitadas
int items; // Cantidad de items obtenidos
Uint32 videoMode; // Contiene el valor del modo de pantalla completa
int windowSize; // Contiene el valor por el que se multiplica el tamaño de la ventana
Uint32 filter; // Filtro usado para el escalado de la imagen
bool vSync; // Indica si se quiere usar vsync o no
int gameWidth; // Ancho de la resolucion nativa del juego
int gameHeight; // Alto de la resolucion nativa del juego
bool integerScale; // Indica si el escalado de la imagen ha de ser entero en el modo a pantalla completa
bool keepAspect; // Indica si se ha de mantener la relación de aspecto al poner el modo a pantalla completa
bool borderEnabled; // Indica si ha de mostrar el borde en el modo de ventana
float borderSize; // Porcentaje de borde que se añade a lo ventana
palette_e palette; // Paleta de colores a usar en el juego
bool console; // Indica si ha de mostrar información por la consola de texto
cheat_t cheat; // Contiene trucos y ventajas para el juego
op_stats_t stats; // Datos con las estadisticas de juego
online_t online; // Datos del servicio online
};
// Calcula el cuadrado de la distancia entre dos puntos

View File

@@ -168,7 +168,7 @@ void Credits::fillTexture()
{
// Inicializa los textos
iniTexts();
// Rellena la textura de texto
SDL_SetRenderTarget(renderer, textTexture);
color_t c = stringToColor(options->palette, "black");
@@ -264,6 +264,9 @@ void Credits::update()
// Actualiza el contador
updateCounter();
// Actualiza las notificaciones
screen->updateNotifier();
// Actualiza el sprite con el brillo
if (counter > 770)
{

View File

@@ -153,6 +153,9 @@ void Demo::update()
scoreboard->update();
screen->updateFX();
checkRoomChange();
// Actualiza las notificaciones
screen->updateNotifier();
}
}
@@ -189,7 +192,7 @@ void Demo::renderRoomName()
// Recarga todas las texturas
void Demo::reLoadTextures()
{
if (options->console)
if (options->console)
{
std::cout << "** RELOAD REQUESTED" << std::endl;
}

View File

@@ -1,7 +1,15 @@
#include "director.h"
#include "common/jscore.h"
#include "common/utils.h"
#include "director.h"
#include <errno.h>
#include <iostream>
#include <string>
#include <sys/stat.h>
#include <unistd.h>
#ifndef _WIN32
#include <pwd.h>
#endif
// Constructor
Director::Director(int argc, char *argv[])
@@ -10,15 +18,18 @@ Director::Director(int argc, char *argv[])
section.subsection = SUBSECTION_LOGO_TO_INTRO;
#ifdef DEBUG
section.name = SECTION_PROG_GAME;
section.name = SECTION_PROG_LOGO;
#endif
// Crea e inicializa las opciones del programa
iniOptions();
initOptions();
// Comprueba los parametros del programa
checkProgramArguments(argc, argv);
// Crea la carpeta del sistema donde guardar datos
createSystemFolder();
// Crea el objeto que controla los ficheros de recursos
asset = new Asset(executablePath);
asset->setVerbose(options->console);
@@ -26,7 +37,7 @@ Director::Director(int argc, char *argv[])
// Si falta algún fichero no inicia el programa
if (!setFileList())
{
section.name = SECTION_PROG_QUIT;
exit(EXIT_FAILURE);
}
// Inicializa variables desde el fichero de configuración
@@ -42,11 +53,13 @@ Director::Director(int argc, char *argv[])
resource = new Resource(renderer, asset, options);
input = new Input(asset->get("gamecontrollerdb.txt"));
initInput();
screen = new Screen(window, renderer, options, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT);
screen = new Screen(window, renderer, asset, options);
screen->setBorderColor(borderColor);
screen->setVideoMode(options->fullScreenMode);
debug = new Debug(renderer, screen, asset);
music = JA_LoadMusic(asset->get("title.ogg").c_str());
// Inicializa los servicios online
initOnline();
}
Director::~Director()
@@ -68,21 +81,61 @@ Director::~Director()
SDL_Quit();
}
// Inicializa los servicios online
void Director::initOnline()
{
if (!options->online.enabled)
{
return;
}
// Obten el Jailer ID
if (options->online.jailerID == "")
{ // Jailer ID no definido
screen->showText("No ha especificado ningun Jailer ID");
std::cout << "No ha especificado ningun Jailer ID" << std::endl;
}
else
{ // Jailer ID iniciado
// Establece el servidor y el puerto
jscore::init(options->online.server, options->online.port);
// Obtiene la información online
if (jscore::initOnlineScore(options->online.gameID))
{
screen->showText(options->online.jailerID + " ha iniciado sesion");
std::cout << options->online.jailerID << " ha iniciado sesion" << std::endl;
}
else
{
screen->showText("Fallo al conectar a " + options->online.server);
std::cout << "Fallo al conectar a " << options->online.server << std::endl;
options->online.enabled = false;
return;
}
}
}
// Crea e inicializa las opciones del programa
void Director::iniOptions()
void Director::initOptions()
{
// Crea el puntero a la estructura de opciones
options = new options_t;
// Inicializa valores
options->fullScreenMode = 0;
options->gameWidth = GAMECANVAS_WIDTH;
options->gameHeight = GAMECANVAS_HEIGHT;
options->videoMode = 0;
options->windowSize = 3;
options->filter = FILTER_NEAREST;
options->vSync = true;
options->integerScale = true;
options->keepAspect = true;
options->borderEnabled = true;
options->borderSize = 0.1f;
options->borderSize = 0.2f;
options->palette = p_zxspectrum;
// Estos valores no se guardan en el fichero de configuraci´ón
@@ -91,8 +144,19 @@ void Director::iniOptions()
options->cheat.invincible = false;
options->cheat.jailEnabled = false;
options->cheat.altSkin = false;
options->rooms = 0;
options->items = 0;
options->stats.rooms = 0;
options->stats.items = 0;
// Online
options->online.enabled = false;
options->online.server = "jaildoctor.duckdns.org";
options->online.port = 9911;
#ifdef DEBUG
options->online.gameID = "jaildoctors_dilemma_test";
#else
options->online.gameID = "jaildoctors_dilemma";
#endif
options->online.jailerID = "";
}
// Comprueba los parametros del programa
@@ -183,19 +247,33 @@ bool Director::loadConfig()
saveConfig();
}
// Normaliza los valores
const bool a = options->videoMode == 0;
const bool b = options->videoMode == SDL_WINDOW_FULLSCREEN;
const bool c = options->videoMode == SDL_WINDOW_FULLSCREEN_DESKTOP;
if (!(a || b || c))
{
options->videoMode = 0;
}
if (options->windowSize < 1 || options->windowSize > 4)
{
options->windowSize = 3;
}
// Aplica opciones
if (options->borderEnabled)
{
const int incWidth = GAMECANVAS_WIDTH * options->borderSize;
const int incHeight = GAMECANVAS_HEIGHT * options->borderSize;
options->screenWidth = (GAMECANVAS_WIDTH + incWidth) * options->windowSize;
options->screenHeight = (GAMECANVAS_HEIGHT + incHeight) * options->windowSize;
}
else
{
options->screenWidth = GAMECANVAS_WIDTH * options->windowSize;
options->screenHeight = GAMECANVAS_HEIGHT * options->windowSize;
}
// if (options->borderEnabled)
//{
// const int incWidth = GAMECANVAS_WIDTH * options->borderSize;
// const int incHeight = GAMECANVAS_HEIGHT * options->borderSize;
// options->gameWidth = GAMECANVAS_WIDTH + incWidth;
// options->gameHeight = GAMECANVAS_HEIGHT + incHeight;
//}
// else
//{
// options->gameWidth = GAMECANVAS_WIDTH;
// options->gameHeight = GAMECANVAS_HEIGHT;
//}
return success;
}
@@ -208,20 +286,36 @@ bool Director::saveConfig()
// Crea y abre el fichero de texto
std::ofstream file(asset->get("config.txt"));
if (file.good())
{
if (options->console)
{
std::cout << asset->get("config.txt") << " open for writing" << std::endl;
}
}
else
{
if (options->console)
{
std::cout << asset->get("config.txt") << " can't be opened" << std::endl;
}
}
// Escribe en el fichero
if (options->fullScreenMode == 0)
file << "## VISUAL OPTIONS\n";
if (options->videoMode == 0)
{
file << "fullScreenMode=0\n";
file << "videoMode=0\n";
}
else if (options->fullScreenMode == SDL_WINDOW_FULLSCREEN)
else if (options->videoMode == SDL_WINDOW_FULLSCREEN)
{
file << "fullScreenMode=SDL_WINDOW_FULLSCREEN\n";
file << "videoMode=SDL_WINDOW_FULLSCREEN\n";
}
else if (options->fullScreenMode == SDL_WINDOW_FULLSCREEN_DESKTOP)
else if (options->videoMode == SDL_WINDOW_FULLSCREEN_DESKTOP)
{
file << "fullScreenMode=SDL_WINDOW_FULLSCREEN_DESKTOP\n";
file << "videoMode=SDL_WINDOW_FULLSCREEN_DESKTOP\n";
}
file << "windowSize=" + std::to_string(options->windowSize) + "\n";
@@ -242,12 +336,73 @@ bool Director::saveConfig()
file << "borderSize=" + std::to_string(options->borderSize) + "\n";
file << "palette=" + std::to_string(options->palette) + "\n";
file << "\n## ONLINE OPTIONS\n";
file << "enabled=" + boolToString(options->online.enabled) + "\n";
file << "server=" + options->online.server + "\n";
file << "port=" + std::to_string(options->online.port) + "\n";
file << "jailerID=" + options->online.jailerID + "\n";
// Cierra el fichero
file.close();
return success;
}
// Crea la carpeta del sistema donde guardar datos
void Director::createSystemFolder()
{
#ifdef DEBUG
const std::string folderName = "jaildoctors_dilemma_debug";
#else
const std::string folderName = "jaildoctors_dilemma";
#endif
#ifdef _WIN32
systemFolder = std::string(getenv("APPDATA")) + "/" + folderName;
#elif __APPLE__
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
systemFolder = std::string(homedir) + "/Library/Application Support/" + folderName;
#elif __linux__
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
systemFolder = std::string(homedir) + "/." + folderName;
#endif
struct stat st = {0};
if (stat(systemFolder.c_str(), &st) == -1)
{
errno = 0;
#ifdef _WIN32
int ret = mkdir(systemFolder.c_str());
#else
int ret = mkdir(systemFolder.c_str(), S_IRWXU);
#endif
if (ret == -1)
{
switch (errno)
{
case EACCES:
printf("the parent directory does not allow write");
exit(EXIT_FAILURE);
case EEXIST:
printf("pathname already exists");
exit(EXIT_FAILURE);
case ENAMETOOLONG:
printf("pathname is too long");
exit(EXIT_FAILURE);
default:
perror("mkdir");
exit(EXIT_FAILURE);
}
}
}
}
// Carga los recursos
void Director::loadResources(section_t section)
{
@@ -812,19 +967,19 @@ bool Director::setOptions(options_t *options, std::string var, std::string value
// Indicador de éxito en la asignación
bool success = true;
if (var == "fullScreenMode")
if (var == "videoMode")
{
if (value == "SDL_WINDOW_FULLSCREEN_DESKTOP")
{
options->fullScreenMode = SDL_WINDOW_FULLSCREEN_DESKTOP;
options->videoMode = SDL_WINDOW_FULLSCREEN_DESKTOP;
}
else if (value == "SDL_WINDOW_FULLSCREEN")
{
options->fullScreenMode = SDL_WINDOW_FULLSCREEN;
options->videoMode = SDL_WINDOW_FULLSCREEN;
}
else
{
options->fullScreenMode = 0;
options->videoMode = 0;
}
}
@@ -893,7 +1048,31 @@ bool Director::setOptions(options_t *options, std::string var, std::string value
}
}
else if (var == "")
else if (var == "enabled")
{
options->online.enabled = stringToBool(value);
}
else if (var == "server")
{
options->online.server = value;
}
else if (var == "port")
{
if (value == "")
{
value = "0";
}
options->online.port = std::stoi(value);
}
else if (var == "jailerID")
{
options->online.jailerID = value;
}
else if (var == "" || var.substr(0, 1) == "#")
{
}
@@ -973,7 +1152,15 @@ bool Director::initSDL()
}
// Crea la ventana
window = SDL_CreateWindow(WINDOW_CAPTION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, options->screenWidth, options->screenHeight, SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI);
int incW = 0;
int incH = 0;
if (options->borderEnabled)
{
incW = options->gameWidth * options->borderSize;
incH = options->gameHeight * options->borderSize;
}
window = SDL_CreateWindow(WINDOW_CAPTION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, (options->gameWidth + incW) * options->windowSize, (options->gameHeight + incH) * options->windowSize, SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI);
if (window == nullptr)
{
if (options->console)
@@ -1008,7 +1195,7 @@ bool Director::initSDL()
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);
// Establece el tamaño del buffer de renderizado
SDL_RenderSetLogicalSize(renderer, options->screenWidth, options->screenHeight);
SDL_RenderSetLogicalSize(renderer, options->gameWidth, options->gameHeight);
// Establece el modo de mezcla
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
@@ -1038,10 +1225,13 @@ bool Director::setFileList()
asset->add(prefix + "/data/font/debug.png", t_font);
asset->add(prefix + "/data/font/debug.txt", t_font);
// Configuración
// Datos
asset->add(prefix + "/data/input/gamecontrollerdb.txt", t_data);
asset->add(prefix + "/data/config/config.txt", t_data, false);
asset->add(prefix + "/data/config/stats.txt", t_data, false);
// Ficheros de sistema
asset->add(systemFolder + "/config.txt", t_data, false, true);
asset->add(systemFolder + "/stats_buffer.csv", t_data, false, true);
asset->add(systemFolder + "/stats.csv", t_data, false, true);
// Habitaciones
asset->add(prefix + "/data/room/01.room", t_room);
@@ -1558,4 +1748,4 @@ void Director::run()
break;
}
}
}
}

View File

@@ -40,7 +40,7 @@ private:
Credits *credits; // Objeto para gestionar los creditos del juego
Demo *demo; // Objeto para gestionar el modo demo, en el que se ven pantallas del juego
Ending *ending; // Objeto para gestionar el final del juego
Ending2 *ending2; // Objeto para gestionar el final del juego
Ending2 *ending2; // Objeto para gestionar el final del juego
GameOver *gameOver; // Objeto para gestionar el final de la partida
Debug *debug; // Objeto para getsionar la información de debug
struct options_t *options; // Variable con todas las opciones del programa
@@ -49,9 +49,13 @@ private:
JA_Music music; // Musica del titulo
std::string executablePath; // Path del ejecutable
section_t section; // Sección y subsección actual del programa;
std::string systemFolder; // Carpeta del sistema donde guardar datos
// Crea e inicializa las opciones del programa
void iniOptions();
void initOptions();
// Inicializa los servicios online
void initOnline();
// Comprueba los parametros del programa
void checkProgramArguments(int argc, char *argv[]);
@@ -62,6 +66,9 @@ private:
// Guarda el fichero de configuración
bool saveConfig();
// Crea la carpeta del sistema donde guardar datos
void createSystemFolder();
// Carga los recursos
void loadResources(section_t section);

View File

@@ -103,6 +103,9 @@ void Ending::update()
// Actualiza el volumen de la musica
updateMusicVolume();
// Actualiza las notificaciones
screen->updateNotifier();
}
}

View File

@@ -102,6 +102,9 @@ void Ending2::update()
// Actualiza el volumen de la musica
updateMusicVolume();
// Actualiza las notificaciones
screen->updateNotifier();
}
}

View File

@@ -21,8 +21,8 @@ Game::Game(SDL_Renderer *renderer, Screen *screen, Resource *resource, Asset *as
this->options = options;
#ifdef DEBUG
currentRoom = "48.room";
const int x1 = 18;
currentRoom = "01.room";
const int x1 = 25;
const int y1 = 13;
spawnPoint = {x1 * 8, y1 * 8, 0, 0, 0, s_standing, SDL_FLIP_HORIZONTAL};
#endif
@@ -40,13 +40,15 @@ Game::Game(SDL_Renderer *renderer, Screen *screen, Resource *resource, Asset *as
text = new Text(resource->getOffset("smb2.txt"), resource->getTexture("smb2.png"), renderer);
music = JA_LoadMusic(asset->get("game.ogg").c_str());
deathSound = JA_LoadSound(asset->get("death.wav").c_str());
stats = new Stats(asset->get("stats.txt"));
stats->addVisit(room->getName());
stats = new Stats(asset->get("stats.csv"), asset->get("stats_buffer.csv"), options);
// Inicializa el resto de variables
ticks = 0;
ticksSpeed = 15;
board.lives = 9;
#ifdef DEBUG
board.lives = 0;
#endif
board.items = 0;
board.rooms = 1;
board.music = true;
@@ -57,6 +59,8 @@ Game::Game(SDL_Renderer *renderer, Screen *screen, Resource *resource, Asset *as
blackScreen = false;
blackScreenCounter = 0;
totalItems = getTotalItems();
initStats();
stats->addVisit(room->getName());
section.name = SECTION_PROG_GAME;
section.subsection = 0;
@@ -131,6 +135,10 @@ void Game::checkEventHandler()
case SDL_SCANCODE_D:
goToRoom(BORDER_RIGHT);
break;
case SDL_SCANCODE_F6:
screen->showText("MAMA MIRA! SIN MANOS!");
break;
#endif
case SDL_SCANCODE_M:
@@ -228,11 +236,15 @@ void Game::update()
checkIfPlayerIsAlive();
checkGameOver();
checkEndGame();
checkRestoringJail();
scoreboard->update();
input->update();
updateBlackScreen();
// Actualiza las notificaciones
screen->updateNotifier();
#ifdef DEBUG
updateDebugInfo();
#endif
@@ -340,7 +352,7 @@ bool Game::changeRoom(std::string file)
{
// Incrementa el contador de habitaciones visitadas
board.rooms++;
options->rooms = board.rooms;
options->stats.rooms = board.rooms;
// Actualiza las estadisticas
stats->addVisit(room->getName());
@@ -593,4 +605,36 @@ void Game::switchPause()
scoreboard->pause();
paused = true;
}
}
// Da vidas al jugador cuando está en la Jail
void Game::checkRestoringJail()
{
if (room->getName() != "THE JAIL" || board.lives == 9)
{
return;
}
static int counter = 0;
counter++;
if (counter == 100)
{
counter = 0;
board.lives++;
JA_PlaySound(deathSound);
}
}
// Inicializa el diccionario de las estadísticas
void Game::initStats()
{
std::vector<res_room_t> *rooms = new std::vector<res_room_t>;
rooms = resource->getAllRooms();
for (auto room : *rooms)
{
stats->addDictionary(room.room->number, room.room->name);
}
stats->init();
}

View File

@@ -127,6 +127,12 @@ private:
// Pone el juego en pausa
void switchPause();
// Da vidas al jugador cuando está en la Jail
void checkRestoringJail();
// Inicializa el diccionario de las estadísticas
void initStats();
public:
// Constructor
Game(SDL_Renderer *renderer, Screen *screen, Resource *resource, Asset *asset, options_t *options, Input *input, Debug *debug);

View File

@@ -28,9 +28,9 @@ GameOver::GameOver(SDL_Renderer *renderer, Screen *screen, Resource *resource, A
iniFade = 310;
fadeLenght = 20;
playerSprite->setPosX(GAMECANVAS_CENTER_X + 10);
playerSprite->setPosY(GAMECANVAS_CENTER_Y - 10);
playerSprite->setPosY(30);
tvSprite->setPosX(GAMECANVAS_CENTER_X - tvSprite->getAnimationClip(0, 0).w - 10);
tvSprite->setPosY(GAMECANVAS_CENTER_Y - 10);
tvSprite->setPosY(30);
// Inicializa el vector de colores
const std::vector<std::string> colorList = {"white", "yellow", "cyan", "green", "magenta", "red", "blue", "black"};
@@ -73,12 +73,17 @@ void GameOver::update()
// Actualiza los dos sprites
playerSprite->update();
tvSprite->update();
// Actualiza las notificaciones
screen->updateNotifier();
}
}
// Dibuja el final en pantalla
void GameOver::render()
{
const int y = 32;
// Prepara para empezar a dibujar en la textura de juego
screen->start();
@@ -86,17 +91,22 @@ void GameOver::render()
screen->clean();
// Escribe el texto de GAME OVER
text->writeDX(TXT_CENTER | TXT_COLOR, GAMECANVAS_CENTER_X, GAMECANVAS_CENTER_Y - 40, "G A M E O V E R", 1, color);
text->writeDX(TXT_CENTER | TXT_COLOR, GAMECANVAS_CENTER_X, y, "G A M E O V E R", 1, color);
// Dibuja los sprites
playerSprite->setPosY(y + 30);
tvSprite->setPosY(y + 30);
renderSprites();
// Escribe el texto con las habitaciones y los items
const std::string itemsTxt = std::to_string(options->items / 100) + std::to_string((options->items % 100) / 10) + std::to_string(options->items % 10);
const std::string roomsTxt = std::to_string(options->rooms / 100) + std::to_string((options->rooms % 100) / 10) + std::to_string(options->rooms % 10);
text->writeDX(TXT_CENTER | TXT_COLOR, GAMECANVAS_CENTER_X, GAMECANVAS_CENTER_Y + 40, "ITEMS: " + itemsTxt, 1, color);
text->writeDX(TXT_CENTER | TXT_COLOR, GAMECANVAS_CENTER_X, GAMECANVAS_CENTER_Y + 55, "ROOMS: " + roomsTxt, 1, color);
const std::string itemsTxt = std::to_string(options->stats.items / 100) + std::to_string((options->stats.items % 100) / 10) + std::to_string(options->stats.items % 10);
const std::string roomsTxt = std::to_string(options->stats.rooms / 100) + std::to_string((options->stats.rooms % 100) / 10) + std::to_string(options->stats.rooms % 10);
text->writeDX(TXT_CENTER | TXT_COLOR, GAMECANVAS_CENTER_X, y + 80, "ITEMS: " + itemsTxt, 1, color);
text->writeDX(TXT_CENTER | TXT_COLOR, GAMECANVAS_CENTER_X, y + 90, "ROOMS: " + roomsTxt, 1, color);
// Escribe el texto con "Tu peor pesadilla"
text->writeDX(TXT_CENTER | TXT_COLOR, GAMECANVAS_CENTER_X, y + 110, "YOUR WORST NIGHTMARE IS", 1, color);
text->writeDX(TXT_CENTER | TXT_COLOR, GAMECANVAS_CENTER_X, y + 120, options->stats.worstNightmare, 1, color);
// Vuelca el contenido del renderizador en pantalla
screen->blit();

View File

@@ -251,6 +251,9 @@ void Intro::update()
// Gestiona el contador de carga
updateLoad();
// Actualiza las notificaciones
screen->updateNotifier();
// Comprueba si ha terminado la intro
if (loadCounter >= 768)
{
@@ -281,7 +284,7 @@ void Intro::render()
section_t Intro::run()
{
// Inicia el sonido de carga
JA_SetVolume(64);
JA_SetVolume(64);
JA_PlayMusic(loadingSound1);
while (section.name == SECTION_PROG_INTRO)
@@ -290,7 +293,7 @@ section_t Intro::run()
render();
}
JA_SetVolume(128);
JA_SetVolume(128);
return section;
}

View File

@@ -19,9 +19,11 @@ Logo::Logo(SDL_Renderer *renderer, Screen *screen, Resource *resource, Asset *as
sprite2->setSpriteClip(0, 0, texture2->getWidth(), texture2->getHeight());
texture2->setColor(0, 0, 0);
// Crea los sprites de cada linea
for (int i = 0; i < texture->getHeight(); ++i)
{
sprite.push_back(new Sprite(0, i, texture->getWidth(), 1, texture, renderer));
sprite.back()->setSpriteClip(0, i, texture->getWidth(), 1);
if (i % 2 == 0)
{
sprite.at(i)->setPosX(256 + (i * 3));
@@ -85,7 +87,7 @@ void Logo::checkEventHandler()
switch (eventHandler->key.keysym.scancode)
{
case SDL_SCANCODE_ESCAPE:
//std::cout << "PULSADO ESCAPE" << std::endl;
// std::cout << "PULSADO ESCAPE" << std::endl;
section.name = SECTION_PROG_QUIT;
break;
@@ -262,6 +264,9 @@ void Logo::update()
// Gestiona el color de las texturas
updateTextureColors();
// Actualiza las notificaciones
screen->updateNotifier();
// Comprueba si ha terminado el logo
if (counter == endLogo + postLogo)
{

View File

@@ -823,6 +823,8 @@ void Player::setState(state_e value)
{
prevState = state;
state = value;
checkState();
}
// Comprueba si el jugador esta vivo

View File

@@ -63,7 +63,9 @@ room_t loadRoomFile(std::string file_path, bool verbose)
room.itemColor2 = "magenta";
room.autoSurfaceDirection = 1;
const std::string filename = file_path.substr(file_path.find_last_of("\\/") + 1);
const std::string fileName = file_path.substr(file_path.find_last_of("\\/") + 1);
room.number = fileName.substr(0, fileName.find_last_of("."));
std::string line;
std::ifstream file(file_path);
@@ -94,7 +96,7 @@ room_t loadRoomFile(std::string file_path, bool verbose)
{
if (verbose)
{
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << line.substr(0, pos).c_str() << "\"" << std::endl;
std::cout << "Warning: file " << fileName.c_str() << "\n, unknown parameter \"" << line.substr(0, pos).c_str() << "\"" << std::endl;
}
}
} while (line != "[/enemy]");
@@ -123,7 +125,7 @@ room_t loadRoomFile(std::string file_path, bool verbose)
{
if (verbose)
{
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << line.substr(0, pos).c_str() << "\"" << std::endl;
std::cout << "Warning: file " << fileName.c_str() << "\n, unknown parameter \"" << line.substr(0, pos).c_str() << "\"" << std::endl;
}
}
@@ -143,7 +145,7 @@ room_t loadRoomFile(std::string file_path, bool verbose)
{
if (verbose)
{
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << line.substr(0, pos).c_str() << "\"" << std::endl;
std::cout << "Warning: file " << fileName.c_str() << "\n, unknown parameter \"" << line.substr(0, pos).c_str() << "\"" << std::endl;
}
}
}
@@ -152,7 +154,7 @@ room_t loadRoomFile(std::string file_path, bool verbose)
// Cierra el fichero
if (verbose)
{
std::cout << "Room loaded: " << filename.c_str() << std::endl;
std::cout << "Room loaded: " << fileName.c_str() << std::endl;
}
file.close();
}
@@ -160,7 +162,7 @@ room_t loadRoomFile(std::string file_path, bool verbose)
else
{
{
std::cout << "Warning: Unable to open " << filename.c_str() << " file" << std::endl;
std::cout << "Warning: Unable to open " << fileName.c_str() << " file" << std::endl;
}
}
@@ -400,6 +402,7 @@ Room::Room(room_t *room, SDL_Renderer *renderer, Screen *screen, Asset *asset, o
this->debug = debug;
this->options = options;
number = room->number;
name = room->name;
bgColor = room->bgColor;
borderColor = room->borderColor;
@@ -824,7 +827,7 @@ bool Room::itemCollision(SDL_Rect &rect)
items.erase(items.begin() + i);
JA_PlaySound(itemSound);
*itemsPicked = *itemsPicked + 1;
options->items = *itemsPicked;
options->stats.items = *itemsPicked;
return true;
}
}

View File

@@ -36,6 +36,7 @@ struct aTile_t
struct room_t
{
std::string number; // Numero de la habitación
std::string name; // Nombre de la habitación
std::string bgColor; // Color de fondo de la habitación
std::string borderColor; // Color del borde de la pantalla
@@ -89,6 +90,7 @@ private:
options_t *options; // Puntero a las opciones del juego
// Variables
std::string number; // Numero de la habitación
std::string name; // Nombre de la habitación
std::string bgColor; // Color de fondo de la habitación
std::string borderColor; // Color del borde de la pantalla

View File

@@ -1,31 +1,66 @@
#include "common/jscore.h"
#include "stats.h"
#include <iostream>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
// Constructor
Stats::Stats(std::string file)
Stats::Stats(std::string file, std::string buffer, options_t *options)
{
this->options = options;
bufferPath = buffer;
filePath = file;
bufferList.clear();
list.clear();
loadFromFile();
dictionary.clear();
}
// Destructor
Stats::~Stats()
{
saveToFile();
// Vuelca los datos del buffer en la lista de estadisticas
updateListFromBuffer();
// Calcula cual es la habitación con más muertes
checkWorstNightmare();
// Guarda las estadísticas
saveToServer();
saveToFile(bufferPath, bufferList);
saveToFile(filePath, list);
bufferList.clear();
list.clear();
dictionary.clear();
}
// Inicializador
// Se debe llamar a este procediiento una vez se haya creado el diccionario numero-nombre
void Stats::init()
{
loadFromFile(bufferPath, bufferList);
loadFromFile(filePath, list);
if (options->online.enabled)
{
loadFromServer();
}
// Vuelca los datos del buffer en la lista de estadisticas
updateListFromBuffer();
}
// Añade una muerte a las estadisticas
void Stats::addDeath(std::string name)
{
// Normaliza el nombre
// std::replace(name.begin(), name.end(), ' ', '_');
// Primero busca si ya hay una entrada con ese nombre
const int index = findByName(name);
const int index = findByName(name, bufferList);
if (index != -1)
{
list.at(index).died++;
bufferList.at(index).died++;
}
// En caso contrario crea la entrada
@@ -35,18 +70,21 @@ void Stats::addDeath(std::string name)
item.name = name;
item.visited = 0;
item.died = 1;
list.push_back(item);
bufferList.push_back(item);
}
}
// Añade una visita a las estadisticas
void Stats::addVisit(std::string name)
{
// Normaliza el nombre
// std::replace(name.begin(), name.end(), ' ', '_');
// Primero busca si ya hay una entrada con ese nombre
const int index = findByName(name);
const int index = findByName(name, bufferList);
if (index != -1)
{
list.at(index).visited++;
bufferList.at(index).visited++;
}
// En caso contrario crea la entrada
@@ -56,12 +94,12 @@ void Stats::addVisit(std::string name)
item.name = name;
item.visited = 1;
item.died = 0;
list.push_back(item);
bufferList.push_back(item);
}
}
// Busca una entrada en la lista por nombre
int Stats::findByName(std::string name)
int Stats::findByName(std::string name, std::vector<stats_t> &list)
{
int i = 0;
@@ -78,8 +116,10 @@ int Stats::findByName(std::string name)
}
// Carga las estadisticas desde un fichero
bool Stats::loadFromFile()
bool Stats::loadFromFile(std::string filePath, std::vector<stats_t> &list)
{
list.clear();
// Indicador de éxito en la carga
bool success = true;
@@ -123,20 +163,68 @@ bool Stats::loadFromFile()
// El fichero no existe
else
{ // Crea el fichero con los valores por defecto
saveToFile();
saveToFile(filePath, list);
}
return success;
}
// Carga las estadisticas desde un servidor
void Stats::loadFromServer()
{
// Limpia los datos del servidor
// eraseServerData();
list.clear();
std::string data;
if (options->online.enabled)
{
data = jscore::getUserData(options->online.gameID, options->online.jailerID);
}
std::stringstream ss(data);
std::string tmp;
int count = 0;
for (int i = 0; i < (int)data.size(); ++i)
{
if (data[i] == ';')
{
count++;
}
}
while (count > 0)
{
stats_t stat;
// Obtiene el nombre
getline(ss, tmp, ';');
stat.name = numberToName(tmp);
// Obtiene las visitas
getline(ss, tmp, ';');
stat.visited = std::stoi(tmp);
// Obtiene las muertes
getline(ss, tmp, ';');
stat.died = std::stoi(tmp);
list.push_back(stat);
count = count - 3;
}
}
// Guarda las estadisticas en un fichero
void Stats::saveToFile()
void Stats::saveToFile(std::string filePath, std::vector<stats_t> &list)
{
// Crea y abre el fichero de texto
std::ofstream file(filePath);
// Escribe en el fichero
file << "# NOMBRE DE LA HABITACION;VISITAS;MUERTES" << std::endl;
file << "# ROOM NAME;VISITS;DEATHS" << std::endl;
for (auto item : list)
{
file << item.name << ";" << item.visited << ";" << item.died << std::endl;
@@ -144,4 +232,104 @@ void Stats::saveToFile()
// Cierra el fichero
file.close();
}
// Guarda las estadisticas en un servidor
void Stats::saveToServer()
{
std::string data = "";
if (options->online.enabled)
{
for (auto item : list)
{
data = data + nameToNumber(item.name) + ";" + std::to_string(item.visited) + ";" + std::to_string(item.died) + ";";
}
jscore::setUserData(options->online.gameID, options->online.jailerID, data);
}
}
// Calcula cual es la habitación con más muertes
void Stats::checkWorstNightmare()
{
int deaths = 0;
for (auto item : list)
{
if (item.died > deaths)
{
deaths = item.died;
options->stats.worstNightmare = item.name;
}
}
}
// Añade una entrada al diccionario
void Stats::addDictionary(std::string number, std::string name)
{
dictionary.push_back({number, name});
}
// Obtiene el nombre de una habitación a partir del número
std::string Stats::numberToName(std::string number)
{
for (auto l : dictionary)
{
if (l.number == number)
{
return l.name;
}
}
return "";
}
// Obtiene el número de una habitación a partir del nombre
std::string Stats::nameToNumber(std::string name)
{
for (auto l : dictionary)
{
if (l.name == name)
{
return l.number;
}
}
return "";
}
// Vuelca los datos del buffer en la lista de estadisticas
void Stats::updateListFromBuffer()
{
// Actualiza list desde bufferList
for (auto buffer : bufferList)
{
int index = findByName(buffer.name, list);
if (index != -1)
{ // Encontrado. Aumenta sus estadisticas
list.at(index).visited += buffer.visited;
list.at(index).died += buffer.died;
}
else
{ // En caso contrario crea la entrada
stats_t item;
item.name = buffer.name;
item.visited = buffer.visited;
item.died = buffer.died;
list.push_back(item);
}
}
// Sube los datos al servidor
if (options->online.enabled)
{
saveToServer();
bufferList.clear();
}
saveToFile(bufferPath, bufferList);
saveToFile(filePath, list);
}
// Limpia los datos del servidor
void Stats::eraseServerData()
{
jscore::setUserData(options->online.gameID, options->online.jailerID, "");
}

View File

@@ -7,41 +7,81 @@
#ifndef STATS_H
#define STATS_H
struct stats_t
{
std::string name; // Nombre de la habitación donde se encuentra el objeto
int visited; // Cuenta las veces que se ha visitado una habitación
int died; // Cuenta las veces que se ha muerto en una habitación
};
class Stats
{
private:
struct stats_t
{
std::string name; // Nombre de la habitación
int visited; // Cuenta las veces que se ha visitado una habitación
int died; // Cuenta las veces que se ha muerto en una habitación
};
struct stats_dictionary_t
{
std::string number; // Numero de la habitación
std::string name; // Nombre de la habitación
};
// Punteros y objetos
options_t *options;
// Variables
std::vector<stats_t> list; // Lista con las estadisticas por habitación
std::string filePath; // Fichero con las estadísticas
std::vector<stats_dictionary_t> dictionary; // Lista con la equivalencia nombre-numero de habitacion
std::vector<stats_t> bufferList; // Lista con las estadisticas temporales por habitación
std::vector<stats_t> list; // Lista con las estadisticas completas por habitación
std::string bufferPath; // Fichero con las estadísticas temporales
std::string filePath; // Fichero con las estadísticas completas
// Busca una entrada en la lista por nombre
int findByName(std::string name);
int findByName(std::string name, std::vector<stats_t> &list);
// Carga las estadisticas desde un fichero
bool loadFromFile();
bool loadFromFile(std::string filePath, std::vector<stats_t> &list);
// Carga las estadisticas desde un servidor
void loadFromServer();
// Guarda las estadisticas en un fichero
void saveToFile();
void saveToFile(std::string filePath, std::vector<stats_t> &list);
// Guarda las estadisticas en un servidor
void saveToServer();
// Calcula cual es la habitación con más muertes
void checkWorstNightmare();
// Obtiene el nombre de una habitación a partir del número
std::string numberToName(std::string number);
// Obtiene el número de una habitación a partir del nombre
std::string nameToNumber(std::string name);
// Vuelca los datos del buffer en la lista de estadisticas
void updateListFromBuffer();
// Limpia los datos del servidor
void eraseServerData();
public:
// Constructor
Stats(std::string file);
Stats(std::string file, std::string buffer, options_t *options);
// Destructor
~Stats();
// Inicializador
// Se debe llamar a este procediiento una vez se haya creado el diccionario numero-nombre
void init();
// Añade una muerte a las estadisticas
void addDeath(std::string name);
// Añade una visita a las estadisticas
void addVisit(std::string name);
// Añade una entrada al diccionario
void addDictionary(std::string number, std::string name);
};
#endif

View File

@@ -115,6 +115,12 @@ void Title::checkEventHandler()
switchPalette();
break;
#ifdef DEBUG
case SDL_SCANCODE_F6:
screen->showText("MAMA MIRA! SIN MANOS!");
break;
#endif
default:
break;
}
@@ -176,6 +182,9 @@ void Title::update()
// Actualiza la marquesina
updateMarquee();
// Actualiza las notificaciones
screen->updateNotifier();
// Comprueba si ha terminado la marquesina y acaba con el titulo
if (letters.at(letters.size() - 1).x < -10)
{