diff --git a/INSTALL.md b/INSTALL.md index d18f38d..b37dd3b 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -32,3 +32,6 @@ To create a package, use the *package* target from cmake invocation: `cmake --bu ## Releasing Update NEWS.yaml to add a new version and the changelog. Then, run `./tools/create_release.sh -v 3.1.5` to update all the files necessary for the release. + +## Audio sounds +The ogg sounds have been created using LMMS DAW. You can install it on your OS using your package manager. To generate the ogg files in command line, run `lmms render input.mmpz -f ogg -b 160 -o output.ogg` diff --git a/NEWS.yaml b/NEWS.yaml index f60c0a5..24c76bc 100644 --- a/NEWS.yaml +++ b/NEWS.yaml @@ -1,4 +1,9 @@ --- +Version: 4.0.0 +Date: 2026-xx-xx +Description: + - New audio theme (menu and in game music) created by zabidenhtf (Mykyta Polishyk) +--- Version: 3.1.6 Date: 2025-12-24 Description: diff --git a/README.md b/README.md index 0b4ccad..6d3fb17 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ You drive a toy wooden train on many levels and you must collect all the wagons German, Italian, Japanese, Korean, Portuguese, Russian, Slovak, Spanish, Swedish, Polish, Turkish, Hungarian, Dutch. - Colorful animated wood engine. -- 50 levels in this first version -- 3 beautiful musics and many sound effects. +- 50 levels in this first version. +- 2 themes with 3 beautiful pieces of music and many sound effects. ## Screenshots ![Main menu](fastlane/metadata/android/en-US/images/sevenInchScreenshots/01.png) @@ -21,7 +21,7 @@ Main menu Gameplay ## Building -Information on how to compile Li-Ri is available in the INSTALL file. +Information on how to compile Li-Ri is available in the INSTALL.md file. ## Thanks * Christian H. and Adrian F. for the German correction. @@ -43,14 +43,16 @@ Information on how to compile Li-Ri is available in the INSTALL file. ------------- -Copyright (c) 2023 +Copyright (c) 2023-2026 Johnny Jazeix: port to SDL2 + android + cmake +Copyright (c) 2026 +Polishyk Mykyta "zabidentwfan@ukr.net": musics ("menu\_zabiden.ogg", "ingame1\_zabiden.ogg", "ingame2\_zabiden.ogg") + Copyright (c) 2006 Dominique Roux-Serret: design & programming & graphics & website. -Maf464: musics -Copyright (c) 2006 (for "menu.mod", "ingame1.xm", "ingame2.xm") MAF464 (email charcosset.b@free.fr; website http://maf464.free.fr). This music licensed under GPL license. See COPYING for details +Copyright (c) 2006 Maf464 (email charcosset.b@free.fr; website http://maf464.free.fr): ("menu\_maf.ogg", "ingame1\_maf.ogg", "ingame2\_maf.ogg") [Get it on F-Droid // for SDL_GetError #include // for SDL_LogInfo, SDL_LOG_CATEGORY_APPLICATION #include +#include #include "audio.h" #include "utils.h" @@ -51,7 +52,7 @@ bool Audio::Init() { char PathFile[512]; - if (Mix_OpenAudio(22050, AUDIO_S16, 1, 1024)) { + if (Mix_OpenAudio(44100, AUDIO_S16, 1, 1024)) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Enable to init Sound card: %s", SDL_GetError()); return false; } @@ -97,10 +98,6 @@ bool Audio::Init() Utils::GetPath(PathFile); Sound[sLive] = Mix_LoadWAV(PathFile); - strcpy(PathFile, "Sounds/menu.mod"); - Utils::GetPath(PathFile); - Music = Mix_LoadMUS(PathFile); - return true; } @@ -108,7 +105,7 @@ bool Audio::Init() /*********************************************************************/ void Audio::LoadMusic(int Num) { - char Provi[512] = "Sounds/ingame1.xm"; + char Provi[512]; if (!N) { return; @@ -118,18 +115,32 @@ void Audio::LoadMusic(int Num) if (Music) { PauseMusic(true); - Mix_HaltMusic(); // Stops the music + Mix_HaltMusic(); Mix_FreeMusic(Music); Music = nullptr; } - if (Num == 0) { // if menu music - strcpy(Provi, "Sounds/menu.mod"); + if (Num == 0) { // menu music + switch (Pref.AudioTheme) { + case mMaf: + strcpy(Provi, "Sounds/menu_maf.mod"); + break; + case mZabiden: + strcpy(Provi, "Sounds/menu_zabiden.ogg"); + break; + } Utils::GetPath(Provi); Music = Mix_LoadMUS(Provi); } - else { - Provi[13] = (char)(Num) + '0'; + else { // in game music + switch (Pref.AudioTheme) { + case mMaf: + sprintf(Provi, "Sounds/ingame%d_maf.xm", Num); + break; + case mZabiden: + sprintf(Provi, "Sounds/ingame%d_zabiden.ogg", Num); + break; + } Utils::GetPath(Provi); Music = Mix_LoadMUS(Provi); } diff --git a/src/main.cc b/src/main.cc index 49d5fab..5d07995 100644 --- a/src/main.cc +++ b/src/main.cc @@ -126,7 +126,9 @@ int main(int narg, char *argv[]) exit(-1); } - audio.PlayMusic(); + // Start background music + audio.LoadMusic(0); + Mouse mouse { audio, screen }; mouse.InitStart(); diff --git a/src/menu.cc b/src/menu.cc index cfc79b1..4338f9c 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -359,7 +359,7 @@ void Menu::InitMain_Options() // Set background image and build display Sprites[background_menu].Draw(400, 300, 0, Sprites[fmenu].Image[0]); Sprites[gmenu].Draw(400, 300, 0, Sprites[fmenu].Image[0]); - Sprites[keys].Draw(610, 455, 0, Sprites[fmenu].Image[0]); + Sprites[keys].Draw(690, 505, 0, Sprites[fmenu].Image[0]); AddButton(0, sound, 140, 110); AddButton(1, music, 160, 200); @@ -372,46 +372,57 @@ void Menu::InitMain_Options() AddButton(3, earth, 180, 400); + // audio theme + Sprites[arrows].Draw(370, 420, 1, Sprites[fmenu].Image[0]); + Sprites[arrows].Draw(620, 420, 4, Sprites[fmenu].Image[0]); + + Menu_Py[4].StartX = 370 - Sprites[arrows].Dim[0].L / 2; + Menu_Py[4].StartY = 420 - Sprites[arrows].Dim[0].H / 2; + Menu_Py[4].EndX = 620 + Sprites[arrows].Dim[0].L / 2; + Menu_Py[4].EndY = 420 + Sprites[arrows].Dim[0].H / 2; + Menu_Py[4].Py = 4; + Menu_Py[4].Valid = true; + // Center text left CenterM = 120 + Sprites[T_menu].Dim[0].L / 2; DrawText(CenterM, 490, T_menu, Sprites[fmenu].Image[0]); - AddButton(4, T_menu, CenterM, 490); + AddButton(5, T_menu, CenterM, 490); // Sound buttons Sprites[arrows].Draw(250, 110, 1, Sprites[fmenu].Image[0]); Sprites[arrows].Draw(700, 110, 4, Sprites[fmenu].Image[0]); - Menu_Py[5].StartX = 230; - Menu_Py[5].StartY = 70; - Menu_Py[5].EndX = 475; - Menu_Py[5].EndY = 145; - Menu_Py[5].Py = 5; - Menu_Py[5].Valid = true; - - Menu_Py[6].StartX = 476; + Menu_Py[6].StartX = 230; Menu_Py[6].StartY = 70; - Menu_Py[6].EndX = 720; + Menu_Py[6].EndX = 475; Menu_Py[6].EndY = 145; Menu_Py[6].Py = 6; Menu_Py[6].Valid = true; - // Music buttons - Sprites[arrows].Draw(250, 200, 1, Sprites[fmenu].Image[0]); - Sprites[arrows].Draw(700, 200, 4, Sprites[fmenu].Image[0]); - Menu_Py[7].StartX = 230; - Menu_Py[7].StartY = 155; - Menu_Py[7].EndX = 475; - Menu_Py[7].EndY = 245; + Menu_Py[7].StartX = 476; + Menu_Py[7].StartY = 70; + Menu_Py[7].EndX = 720; + Menu_Py[7].EndY = 145; Menu_Py[7].Py = 7; Menu_Py[7].Valid = true; - Menu_Py[8].StartX = 476; + // Music buttons + Sprites[arrows].Draw(250, 200, 1, Sprites[fmenu].Image[0]); + Sprites[arrows].Draw(700, 200, 4, Sprites[fmenu].Image[0]); + Menu_Py[8].StartX = 230; Menu_Py[8].StartY = 155; - Menu_Py[8].EndX = 720; + Menu_Py[8].EndX = 475; Menu_Py[8].EndY = 245; Menu_Py[8].Py = 8; Menu_Py[8].Valid = true; - Menu_Py[9].StartX = -1; + Menu_Py[9].StartX = 476; + Menu_Py[9].StartY = 155; + Menu_Py[9].EndX = 720; + Menu_Py[9].EndY = 245; + Menu_Py[9].Py = 9; + Menu_Py[9].Valid = true; + + Menu_Py[10].StartX = -1; } /*** Options menu management ***/ @@ -421,7 +432,7 @@ eMenu Menu::SDLMain_Options() int i, N; int NumSp; - PyE = 4; + PyE = 5; // Fetch events do { m_screen.CleanSpriteAndScreen(fmenu); @@ -451,12 +462,19 @@ eMenu Menu::SDLMain_Options() if (Pref.FullScreen == false) { Pref.FullScreen = true; ChangeVideo(); - PyE = 2; } break; + case 4: + Pref.AudioTheme = eAudioTheme(Pref.AudioTheme - 1); + if (Pref.AudioTheme < 0) { + Pref.AudioTheme = mZabiden; + } + m_audio.LoadMusic(0); + PyE = 4; + break; case 0: - case 5: // Lowers sound effects volume - case 6: + case 6: // lowers sounds effects volume + case 7: Pref.Volume -= SDL_MIX_MAXVOLUME / 10.0; if (Pref.Volume < 0) { Pref.Volume = 0; @@ -465,8 +483,8 @@ eMenu Menu::SDLMain_Options() m_audio.Play(sLive); break; case 1: - case 7: // Lowers music volume - case 8: + case 8: // lowers music volume + case 9: Pref.VolumeM -= SDL_MIX_MAXVOLUME / 10.0; if (Pref.VolumeM < 0) { Pref.VolumeM = 0; @@ -484,9 +502,17 @@ eMenu Menu::SDLMain_Options() PyE = 2; } break; + case 4: + Pref.AudioTheme = eAudioTheme(Pref.AudioTheme + 1); + if (Pref.AudioTheme > mZabiden) { + Pref.AudioTheme = mMaf; + } + m_audio.LoadMusic(0); + PyE = 4; + break; case 0: - case 5: case 6: + case 7: Pref.Volume += SDL_MIX_MAXVOLUME / 10.0; if (Pref.Volume > SDL_MIX_MAXVOLUME) { Pref.Volume = SDL_MIX_MAXVOLUME; @@ -495,8 +521,8 @@ eMenu Menu::SDLMain_Options() m_audio.Play(sLive); break; case 1: - case 7: case 8: + case 9: Pref.VolumeM += SDL_MIX_MAXVOLUME / 10.0; if (Pref.VolumeM > SDL_MIX_MAXVOLUME) { Pref.VolumeM = SDL_MIX_MAXVOLUME; @@ -508,12 +534,12 @@ eMenu Menu::SDLMain_Options() case SDLK_UP: PyE--; if (PyE < 0) { - PyE = 4; + PyE = 5; } break; case SDLK_DOWN: PyE++; - if (PyE >= 5) { + if (PyE >= 6) { PyE = 0; } break; @@ -538,7 +564,15 @@ eMenu Menu::SDLMain_Options() SDLMain_Language(); PyE = 3; break; - case 5: // Lower sound effects volume + case 4: // Audio theme + Pref.AudioTheme = eAudioTheme(Pref.AudioTheme + 1); + if (Pref.AudioTheme > mZabiden) { + Pref.AudioTheme = mMaf; + } + m_audio.LoadMusic(0); + PyE = 4; + break; + case 6: // lower sounds effects volume Pref.Volume -= SDL_MIX_MAXVOLUME / 10.0; if (Pref.Volume < 0) { Pref.Volume = 0; @@ -546,7 +580,7 @@ eMenu Menu::SDLMain_Options() m_audio.DoVolume(); m_audio.Play(sLive); break; - case 6: + case 7: Pref.Volume += SDL_MIX_MAXVOLUME / 10.0; if (Pref.Volume > SDL_MIX_MAXVOLUME) { Pref.Volume = SDL_MIX_MAXVOLUME; @@ -554,14 +588,14 @@ eMenu Menu::SDLMain_Options() m_audio.DoVolume(); m_audio.Play(sLive); break; - case 7: // Lower music volume + case 8: // lower music volume Pref.VolumeM -= SDL_MIX_MAXVOLUME / 10.0; if (Pref.VolumeM < 0) { Pref.VolumeM = 0; } m_audio.DoVolume(); break; - case 8: + case 9: Pref.VolumeM += SDL_MIX_MAXVOLUME / 10.0; if (Pref.VolumeM > SDL_MIX_MAXVOLUME) { Pref.VolumeM = SDL_MIX_MAXVOLUME; @@ -596,6 +630,16 @@ eMenu Menu::SDLMain_Options() m_screen.PrintSprite(arrows, 4, 450, 300); } + switch (Pref.AudioTheme) { + case mMaf: + DrawString(400, 420, "MAF 464", Sprites[fmenu].Image[0]); + break; + case mZabiden: + DrawString(400, 420, "ZABIDEN", Sprites[fmenu].Image[0]); + break; + } + DrawString(350, 370, "Audio Theme", Sprites[fmenu].Image[0]); + NumSp = (currentTime / 30) % 25; m_screen.PrintSprite(sound, NumSp, 150, 110); NumSp = (currentTime / 30) % 25; @@ -629,12 +673,10 @@ eMenu Menu::SDLMain_Options() Print_Main(180); break; case 4: - Print_Main(CenterM); + Print_Main(490); break; case 5: - PyE = 0; - Print_Main(); - PyE = 5; + Print_Main(CenterM); break; case 6: PyE = 0; @@ -642,7 +684,7 @@ eMenu Menu::SDLMain_Options() PyE = 6; break; case 7: - PyE = 1; + PyE = 0; Print_Main(); PyE = 7; break; @@ -651,6 +693,11 @@ eMenu Menu::SDLMain_Options() Print_Main(); PyE = 8; break; + case 9: + PyE = 1; + Print_Main(); + PyE = 9; + break; default: Print_Main(); } diff --git a/src/preference.h b/src/preference.h index 7087b6c..b3253a2 100644 --- a/src/preference.h +++ b/src/preference.h @@ -99,6 +99,11 @@ struct sOldPreference struct sScore Sco[8]; // Store scores }; +enum eAudioTheme { + mMaf = 0, // original Ri-Li soundtrack + mZabiden = 1 // updated soundtrack +}; + struct sNewPreference { e_Difficulty Difficulty { Normal }; // current game difficulty @@ -115,6 +120,7 @@ struct sNewPreference float Volume { (float)SDL_MIX_MAXVOLUME }; // audio volume float VolumeM { (float)SDL_MIX_MAXVOLUME }; // music volume struct sScore Sco[8]; // store scores + eAudioTheme AudioTheme { mZabiden }; // which audio theme to use int HumanRightsQuiz { 1 }; // enable the human rights questions at the end of a level }; diff --git a/src/sprite.cc b/src/sprite.cc index 407cd4e..452fec3 100644 --- a/src/sprite.cc +++ b/src/sprite.cc @@ -285,7 +285,7 @@ void DrawNumber(int x, int y, int Number, SDL_Texture *Background) /*** Display a string ***/ /************************/ -void DrawString(int x, int y, char *Text, SDL_Texture *background) +void DrawString(int x, int y, const char *Text, SDL_Texture *background) { int i = 0; int Le; diff --git a/src/sprite.h b/src/sprite.h index 58ac834..4ffcb86 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -181,7 +181,7 @@ int StringLength(char *Text); // Returns the length in pixels of a string. bool CharExist(char C); // Checks if a character exists void DrawNumber(int x, int y, int Number, SDL_Texture *Background = nullptr); // Displays a number -void DrawString(int x, int y, char *Text, SDL_Texture *Background = nullptr); // Displays a string +void DrawString(int x, int y, const char *Text, SDL_Texture *Background = nullptr); // Displays a string void DrawText(int x, int y, e_Sprite Text, SDL_Texture *Background = nullptr); // Displays a text in the language diff --git a/src/utils.cc b/src/utils.cc index 27b6b65..20250f2 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -274,6 +274,10 @@ bool Utils::LoadPref() if (pv) { Pref.VolumeM = std::stof(pv); } + pv = ini.GetValue("main", "audioTheme"); + if (pv) { + Pref.AudioTheme = eAudioTheme(std::stoi(pv)); + } pv = ini.GetValue("main", "humanRightsQuiz"); if (pv) { Pref.HumanRightsQuiz = std::stoi(pv); @@ -347,6 +351,7 @@ void Utils::SavePref() ini.SetValue("main", "audioVolume", std::to_string(Pref.Volume).c_str()); ini.SetValue("main", "musicVolume", std::to_string(Pref.VolumeM).c_str()); ini.SetValue("main", "humanRightsQuiz", std::to_string(Pref.HumanRightsQuiz).c_str()); + ini.SetValue("main", "audioTheme", std::to_string((int)Pref.AudioTheme).c_str()); ini.SetValue("easy", "maxLevel", std::to_string(Pref.LevelMax[0]).c_str()); ini.SetValue("normal", "maxLevel", std::to_string(Pref.LevelMax[1]).c_str()); ini.SetValue("difficult", "maxLevel", std::to_string(Pref.LevelMax[2]).c_str());