diff --git a/README.md b/README.md index b37e45e..7eea527 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # DungeonRush ![](screenshot.gif) +English | [简体中文](README.zh-CN.md) >A game inspired by Snake, in pure C with SDL2. >My piece of work from C assignment. Hope you like it :) @@ -111,7 +112,7 @@ $ cmake -B build && cmake --build build ## Known issues [Game speed too fast with some NVIDIA graphic cards on Linux](https://github.com/Rapiz1/DungeonRush/issues/4) ## License and Credits -DungeonRush has mixed meida with +DungeonRush has mixed media with various licenses. Unfortunately I failed to track them all. In other word, there are many stuff excluding code that comes with unknown license. You should not reuse any of audio, bitmaps, font in this project. If you insist, use at your own risk. ### Code GPL @@ -127,3 +128,7 @@ GPL |BOMB By Azureflux|CC BY-NC-SA 4.0| |Unknown BGM|Unknown| |The Essential Retro Video Game Sound Effects Collection By Juhani Junkala |CC BY 3.0| +### Font +|Name|License| +|----|-------| +|Unifont|GPLv2| \ No newline at end of file diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 0000000..d0d2f31 --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,132 @@ +# DungeonRush +![](screenshot.gif) +[English](README.md) | 简体中文 +> 受贪吃蛇启发的游戏,纯 C + SDL 图形库。 +> 它是我的 C 作业。希望你喜欢 :) + +*这个项目由我自己编写,所以注释比较少* + +## 下载 +[Windows(64 位)](https://github.com/Rapiz1/DungeonRush/releases/download/v1.1-beta/DungeonRush-v1.1-beta-Windows-x86_64.zip) + +[Windows(32 位)](https://github.com/Rapiz1/DungeonRush/releases/download/v1.1-beta/DungeonRush-v1.1-beta-Windows-i686.zip) + +[Linux](https://github.com/Rapiz1/DungeonRush/releases/download/v1.1-beta/DungeonRush-v1.1-beta-linux.zip) + +[有人把它移植到了安卓!](https://github.com/imrunning/DungeonRush_Android) + +在 release 页里给 Linux 的 AppImage 也可用。 + +[Archlinux AUR](https://aur.archlinux.org/packages/dungeonrush/) + +```yay -S dungeonrush``` + +[Archlinux AUR(git version)](https://aur.archlinux.org/packages/dungeonrush-git/) + +```yay -S dungeonrush-git``` + +*游戏支持 macOS,但是我没有一台 Mac 来编译它。* + +可执行文件叫 `dungeon_rush` + +## 发行说明 + +### v1.1-beta +- 修复 bug + +### v1.0-beta +- 修复 bug +- 添加 多人 / 局域网(Multiplayer / LAN)模式 + +**通过互联网和你的好朋友玩耍吧!** +*你们要在同一个网络里,用IP直接连接* + +### v1.0-alpha + +初次发行 + +## 玩法 + +### 单人模式 + +使用 WASD 来移动。 + +收集英雄来扩大你的军队,并避免遭受怪物的攻击。每个关卡都有一个军队的目标长度。如果到达目标长度,你则会在下一级别重新开始。有很多东西会根据你的等级进行调整,比如生命值、伤害系数、怪物的数量和力量等。 + +### 多人模式 +使用 WASD 和箭头(↑↓←→)来移动。 + +这种模式很有竞争力。防止遭受怪物和朋友的攻击! + +### 武器 + +怪物们会随机扔下威力强大的武器。不同种类的英雄可以装备不同种类的武器。 + +*我最爱雷霆塔夫。一根很酷的~~搅屎棍~~棍子,可以让你的巫师召唤雷霆攻击周围的恩美人。* + +### Buff/Debuff + +Buff/Debuff 会让一个拥有武器的人攻击时触发他自己的某些增益或者对敌人的减益。 + +- 冰剑可以冰冻敌人,敌人将无法移动。 +- 圣剑可以给你一个盾牌,吸收伤害并使你对 Debuff 免疫。 +- 大弓可以增加你所有的英雄的攻击伤害。 +- 还有很多。 + +当然,有些怪物有武器,可以给你施加 Debuff! *(就比如烦人的泥泞怪可以减缓你的移动速度)* + +## 依赖关系 +该项目仅依赖 SDL 库。 +`SDL2, SDL2-image, SDL2-mixer, SDL2-net, SDL2-ttf` +### Arch +``` +# pacman -S sdl2 sdl2_image sdl2_mixer sdl2_net sdl2_ttf --needed +``` +### Debian + +``` +# apt install libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-net-dev libsdl2-ttf-dev +``` + +### openSUSE + +``` +# zypper in libSDL2-devel libSDL2_image-devel libSDL2_mixer-devel libSDL2_net-devel libSDL2_ttf-devel +``` + +### MacOS + +``` +# brew install sdl2 sdl2_image sdl2_mixer sdl2_net sdl2_ttf +``` + +### 其他系统 + +包名大概差不多。您可以查找 SDL 教程来设置环境。 +## 编译 +**您应该确保在编译之前安装了所有依赖项** +``` +$ cmake -B build && cmake --build build +``` +## 已知问题 +[在一些装载 NVIDIA 图形卡的 Linux 系统上,游戏速度太快](https://github.com/Rapiz1/DungeonRush/issues/4) +## 版权和许可 +DungeonRush 已经把各种许可证的媒体混杂在一起。不幸的是我没找到他们。换句话说,有许多不包括代码在内的未知许可证。你不该在这个项目中重复使用任何音频、位图、字体。如果你坚持使用,风险自负。 +### 代码 +GPL +### 位图 +|名称|版权协议| +|----|--------| +|来自 0x72 的 DungeonTilesetII_v1.3|CC 0| +|其他来自 rapiz 的位图|CC BY-NC-SA 4.0| +### 音频 +|名称|版权协议| +|----|--------| +|来自 Starbox 的 Digital_Dream_Azureflux_Remix|CC BY-NC-SA 4.0| +|来自 Azureflux 的 BOMB|CC BY-NC-SA 4.0| +|未知音频|未知| +|来自 Juhani Junkala 的 The Essential Retro Video Game Sound Effects Collection|CC BY 3.0| +### 字体 +|名称|版权协议| +|----|--------| +|Unifont|GPLv2| \ No newline at end of file diff --git a/res/font/Unifont.ttf b/res/font/Unifont.ttf new file mode 100644 index 0000000..d4f0376 Binary files /dev/null and b/res/font/Unifont.ttf differ diff --git a/res/messages.txt b/res/messages.txt new file mode 100644 index 0000000..c597702 --- /dev/null +++ b/res/messages.txt @@ -0,0 +1,16 @@ +Stage Clear +Game Over +Paused +Score: %-6.0lf Got: %-6d Kill: %-6d Damage: %-6d Stand: %-6d +Player%d:%5d +Stage:%3d +Find %d more heros! +Listening +Listening. +Listening.. +Listening... +Connecting +Connecting. +Connecting.. +Connecting... +Enter IP \ No newline at end of file diff --git a/res/messages.zh-CN.txt b/res/messages.zh-CN.txt new file mode 100644 index 0000000..7d4cbe5 --- /dev/null +++ b/res/messages.zh-CN.txt @@ -0,0 +1,16 @@ +目标达成 +游戏结束 +暂停 +分数: %-6.0lf 得到英雄: %-6d 击杀怪物: %-6d 造成伤害: %-6d 受到伤害: %-6d +玩家%d:%5d +阶段:%3d +再找到 %d 个英雄! +等待中 +等待中. +等待中.. +等待中... +连接中 +连接中. +连接中.. +连接中... +输入 IP \ No newline at end of file diff --git a/res/messages.zh-TW.txt b/res/messages.zh-TW.txt new file mode 100644 index 0000000..b92533d --- /dev/null +++ b/res/messages.zh-TW.txt @@ -0,0 +1,16 @@ +目標達成 +遊戲結束 +暫停 +分數: %-6.0lf 得到英雄: %-6d 擊殺怪物: %-6d 造成傷害: %-6d 受到傷害: %-6d +玩家%d:%5d +階段:%3d +再找到 %d 個英雄! +等待中 +等待中. +等待中.. +等待中... +連接中 +連接中. +連接中.. +連接中... +輸入 IP \ No newline at end of file diff --git a/res/text.txt b/res/text.txt index 05d5215..37abe4f 100644 --- a/res/text.txt +++ b/res/text.txt @@ -7,6 +7,7 @@ Player 2 Singleplayer Multiplayers Ranklist +Languages Exit Normal Hard @@ -15,3 +16,6 @@ Local Lan Host a game Join a game +English +繁體中文 +简体中文 \ No newline at end of file diff --git a/res/text.zh-CN.txt b/res/text.zh-CN.txt new file mode 100644 index 0000000..e6a87b9 --- /dev/null +++ b/res/text.zh-CN.txt @@ -0,0 +1,21 @@ +DungeonRush +作者 Rapiz +PLACEHOLDER +PLACEHOLDER +玩家1 +玩家2 +单人游戏 +多人游戏 +历史排名 +界面语言 +退出 +正常 +困难 +疯狂 +本地 +局域网 +创建房间 +加入房间 +English +简体中文 +繁體中文 \ No newline at end of file diff --git a/res/text.zh-TW.txt b/res/text.zh-TW.txt new file mode 100644 index 0000000..670e150 --- /dev/null +++ b/res/text.zh-TW.txt @@ -0,0 +1,21 @@ +DungeonRush +作者 Rapiz +PLACEHOLDER +PLACEHOLDER +玩家1 +玩家2 +單人遊戲 +多人遊戲 +歷史排名 +界面語言 +退出 +正常 +困難 +癲狂 +本地 +局域網 +創建房間 +加入房間 +English +简体中文 +繁體中文 \ No newline at end of file diff --git a/src/game.c b/src/game.c index acf8f1d..54fb91b 100644 --- a/src/game.c +++ b/src/game.c @@ -36,6 +36,8 @@ extern Weapon weapons[WEAPONS_SIZE]; extern Sprite commonSprites[COMMON_SPRITE_SIZE]; extern unsigned int renderFrames; +extern Text messages[]; + // Map Block map[MAP_SIZE][MAP_SIZE]; Item itemMap[MAP_SIZE][MAP_SIZE]; @@ -515,15 +517,8 @@ void destroyGame(int status) { bullets = NULL; blackout(); - char* msg; - if (status == 0) - msg = "Stage Clear"; - else - msg = "Game Over"; - extern SDL_Color WHITE; - Text* text = createText(msg, WHITE); + Text* text = (status ? &messages[MSG_GAME_OVER] : &messages[MSG_STAGE_CLEAR]); renderCenteredText(text, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 2); - destroyText(text); SDL_RenderPresent(renderer); sleep(RENDER_GAMEOVER_DURATION); clearRenderer(); @@ -979,12 +974,9 @@ void pauseGame() { pauseSound(); playAudio(AUDIO_BUTTON1); dim(); - const char msg[] = "Paused"; - extern SDL_Color WHITE; - Text* text = createText(msg, WHITE); + Text* text = &messages[MSG_PAUSED]; renderCenteredText(text, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 1); SDL_RenderPresent(renderer); - destroyText(text); SDL_Event e; for (bool quit = 0; !quit;) { while (SDL_PollEvent(&e)) { diff --git a/src/net.c b/src/net.c index a905e55..eb2f32f 100644 --- a/src/net.c +++ b/src/net.c @@ -14,6 +14,7 @@ TCPsocket lanServerSocket; TCPsocket lanClientSocket; SDLNet_SocketSet socketSet; +extern Text messages[MSG_COUNT]; static void CHECK() { CASSERT(sizeof(LanPacket) == 4); @@ -76,10 +77,10 @@ void hostGame() { extern SDL_Color WHITE; Text* listening[4] = { - createText("Listening", WHITE), - createText("Listening.", WHITE), - createText("Listening..", WHITE), - createText("Listening...", WHITE), + &messages[MSG_LAN_LISTENING0], + &messages[MSG_LAN_LISTENING1], + &messages[MSG_LAN_LISTENING2], + &messages[MSG_LAN_LISTENING3], }; SDL_Event e; @@ -102,7 +103,6 @@ void hostGame() { // Clean up after connecting SDLNet_TCP_Close(lanServerSocket); lanServerSocket = NULL; - for (unsigned i = 0; i < 4; i++) destroyText(listening[i]); blackout(); @@ -150,10 +150,10 @@ void joinGame(const char* hostname, Uint16 port) { extern SDL_Color WHITE; Text* connecting[4] = { - createText("Connecting", WHITE), - createText("Connecting.", WHITE), - createText("Connecting..", WHITE), - createText("Connecting...", WHITE), + &messages[MSG_LAN_CONNECTING0], + &messages[MSG_LAN_CONNECTING1], + &messages[MSG_LAN_CONNECTING2], + &messages[MSG_LAN_CONNECTING3], }; bool quit = false; diff --git a/src/render.c b/src/render.c index 523f81c..ae3ea52 100644 --- a/src/render.c +++ b/src/render.c @@ -27,6 +27,7 @@ extern int texturesCount; extern Texture textures[TEXTURES_SIZE]; extern int textsCount; extern Text texts[TEXTSET_SIZE]; +extern Text messages[MSG_COUNT]; Text* stageText; Text* taskText; Text* scoresText[MAX_PALYERS_NUM]; @@ -58,7 +59,7 @@ void initCountDownBar() { void initInfo() { extern int stage; char buf[1 << 8]; - sprintf(buf, "Stage:%3d", stage); + sprintf(buf, messages[MSG_INFO_STAGE_FORMAT].text, stage); if (stageText) setText(stageText, buf); else @@ -374,7 +375,7 @@ void renderInfo() { for (int i = 0; i < playersCount; i++) { char buf[1 << 8]; calcScore(spriteSnake[i]->score); - sprintf(buf, "Player%d:%5d", i + 1, + sprintf(buf, messages[MSG_INFO_PLAYER_FORMAT].text, i + 1, (int)(spriteSnake[i]->score->rank + 0.5)); setText(scoresText[i], buf); renderText(scoresText[i], startX, startY, 1); @@ -383,7 +384,7 @@ void renderInfo() { if (playersCount == 1) { extern int GAME_WIN_NUM; char buf[1 << 8]; - sprintf(buf, "Find %d more heros!", + sprintf(buf, messages[MSG_INFO_FIND_HEROS].text, GAME_WIN_NUM > spriteSnake[0]->num ? GAME_WIN_NUM - spriteSnake[0]->num : 0); diff --git a/src/res.c b/src/res.c index ef4945a..eeb5e65 100644 --- a/src/res.c +++ b/src/res.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include "types.h" #include "render.h" #include "weapon.h" @@ -54,7 +54,15 @@ const char tilesetPath[TILESET_SIZE][PATH_LEN] = { "res/drawable/attack_up", "res/drawable/powerful_bow"}; const char fontPath[] = "res/font/m5x7.ttf"; +const char unifontPath[] = "res/font/Unifont.ttf"; const char textsetPath[] = "res/text.txt"; +const char simplifiedChineseTextsetPath[] = "res/text.zh-CN.txt"; +const char traditionalChineseTextsetPath[] = "res/text.zh-TW.txt"; +const char messagePath[] = "res/messages.txt"; +const char simplifiedChineseMessagePath[] = "res/messages.zh-CN.txt"; +const char traditionalChineseMessagePath[] = "res/messages.zh-TW.txt"; + +Text messages[MSG_COUNT]; const int bgmNums = 4; const char bgmsPath[AUDIO_BGM_SIZE][PATH_LEN] = { @@ -79,6 +87,7 @@ extern Weapon weapons[WEAPONS_SIZE]; SDL_Window* window; SDL_Texture* originTextures[TILESET_SIZE]; TTF_Font* font; +TTF_Font* unifont; Effect effects[EFFECTS_SIZE]; @@ -89,6 +98,8 @@ Mix_Music *bgms[AUDIO_BGM_SIZE]; int soundsCount; Mix_Chunk *sounds[AUDIO_SOUND_SIZE]; +int language = LANG_ENGLISH; + bool init() { // Initialization flag bool success = true; @@ -174,14 +185,33 @@ SDL_Texture* loadSDLTexture(const char* path) { } bool loadTextset() { bool success = true; - FILE* file = fopen(textsetPath, "r"); + FILE* file; + switch (language) { + case LANG_SIMPLIFIED_CHINESE: + file = fopen(simplifiedChineseTextsetPath, "r"); + break; + case LANG_TRADITIONAL_CHINESE: + file = fopen(traditionalChineseTextsetPath, "r"); + break; + default: + file = fopen(textsetPath, "r"); + break; + } char str[TEXT_LEN]; + // Reset the "textsCount" every time you switch languages + textsCount = 0; while (fgets(str, TEXT_LEN, file)) { int n = strlen(str); - while (n - 1 >= 0 && !isprint(str[n - 1])) str[--n] = 0; + while (n - 1 >= 0 && !iswprint(str[n - 1])) str[--n] = 0; if (!n) continue; - if (!initText(&texts[textsCount++], str, WHITE)) { - success = false; + if (textsCount < 19) { + if (!initText(&texts[textsCount++], str, WHITE)) { + success = false; + } + } else { + if (!initUnicodeText(&texts[textsCount++], str, WHITE)) { + success = false; + } } #ifdef DBG printf("Texts #%d: %s loaded\n", textsCount - 1, str); @@ -236,6 +266,33 @@ bool loadAudio() { fclose(f); return success; } +bool loadMessage() { + bool success = true; + FILE* file; + switch (language) { + case LANG_SIMPLIFIED_CHINESE: + file = fopen(simplifiedChineseMessagePath, "r"); + break; + case LANG_TRADITIONAL_CHINESE: + file = fopen(traditionalChineseMessagePath, "r"); + break; + default: + file = fopen(messagePath, "r"); + break; + } + char str[MSG_LEN]; + int count = 0; + while (fgets(str, MSG_LEN, file)) { + int n = strlen(str); + while (n - 1 >= 0 && !iswprint(str[n - 1])) str[--n] = 0; + if (!n) continue; + if (!initText(&messages[count++], str, WHITE)) { + success = false; + } + } + fclose(file); + return success; +} bool loadMedia() { // Loading success flag bool success = true; @@ -251,27 +308,48 @@ bool loadMedia() { success &= (bool)originTextures[i]; } // Open the font - font = TTF_OpenFont(fontPath, FONT_SIZE); - if (font == NULL) { - printf("Failed to load lazy font! SDL_ttf Error: %s\n", TTF_GetError()); + if (!loadFont()) { + fputs("Failed to load font! SDL_ttf Error: ", stdout); + puts(TTF_GetError()); + success = false; + } else if (!loadUnifont()) { + fputs("Failed to load unifont! SDL_ttf Error: ", stderr); + fputs(TTF_GetError(), stderr); + fputchar('\n', stderr); + success = false; + } else if (!loadTextset()) { + printf("Failed to load textset!\n"); + success = false; + } else if (!loadMessage()) { + puts("Failde to load messages!"); success = false; - } else { - if (!loadTextset()) { - printf("Failed to load textset!\n"); - success = false; - } } // Init common sprites initWeapons(); initCommonSprites(); if (!loadAudio()) { - printf("Failed to load audio!\n"); + puts("Failed to load audio!"); success = false; } return success; } +bool loadFont() { + switch (language) { + case LANG_SIMPLIFIED_CHINESE: + case LANG_TRADITIONAL_CHINESE: + font = TTF_OpenFont(unifontPath, UNICODE_FONT_SIZE); + break; + default: + font = TTF_OpenFont(fontPath, FONT_SIZE); + break; + } + return font != NULL; +} +bool loadUnifont() { + return (unifont = TTF_OpenFont(unifontPath, UNICODE_FONT_SIZE)) != NULL; +} void cleanup() { // Deallocate surface for (int i = 0; i < TILESET_SIZE; i++) { @@ -362,3 +440,56 @@ void initCommonSprites() { initCommonSprite(now=&commonSprites[SPRITE_BIG_DEMON], &weapons[WEAPON_THUNDER], RES_BIG_DEMON, 2500); now->dropRate = 100; } +void nextLanguage() { + language = (language + 1 > LANG_COUNT ? 1 : language + 1); +} +void lastLanguage() { + language = (language - 1 == 0 ? LANG_COUNT : language - 1); +} +bool changeToNextLanguage() { + nextLanguage(); + if (!loadFont()) { + fputs("Failed to load font! SDL_ttf Error: ", stdout); + puts(TTF_GetError()); + return false; + } else if (!loadTextset()) { + puts("Failed to load textset!"); + return false; + } else if (!loadMessage()) { + puts("Failde to load messages!"); + return false; + } + return true; +} +bool changeToLastLanguage() { + lastLanguage(); + if (!loadFont()) { + fputs("Failed to load font! SDL_ttf Error: ", stdout); + puts(TTF_GetError()); + return false; + } else if (!loadTextset()) { + puts("Failed to load textset!"); + return false; + } else if (!loadMessage()) { + puts("Failde to load messages!"); + return false; + } + return true; +} +bool setLanguage(int lang) { + language = lang; + if (!loadFont()) { + fputs("Failed to load font! SDL_ttf Error: ", stdout); + puts(TTF_GetError()); + return false; + } + else if (!loadTextset()) { + puts("Failed to load textset!"); + return false; + } + else if (!loadMessage()) { + puts("Failde to load messages!"); + return false; + } + return true; +} diff --git a/src/res.h b/src/res.h index 20b73f8..ac254b2 100644 --- a/src/res.h +++ b/src/res.h @@ -14,6 +14,7 @@ #define SCREEN_WIDTH 1440 #define SCREEN_HEIGHT 960 #define FONT_SIZE 32 +#define UNICODE_FONT_SIZE 20 // Resource ID // Map Resource #define RES_WALL_TOP_LEFT 0 @@ -169,16 +170,48 @@ #define AUDIO_CLAW_HIT_HEAVY 15 #define AUDIO_COIN 16 #define AUDIO_MED 17 +// Language +#define LANG_COUNT 3 +#define LANG_ENGLISH 1 +#define LANG_TRADITIONAL_CHINESE 2 +#define LANG_SIMPLIFIED_CHINESE 3 +// Message +#define MSG_LEN 1024 +#define MSG_COUNT 16 +#define MSG_STAGE_CLEAR 0 +#define MSG_GAME_OVER 1 +#define MSG_PAUSED 2 +#define MSG_RANKLISH_FORMAT 3 +#define MSG_INFO_PLAYER_FORMAT 4 +#define MSG_INFO_STAGE_FORMAT 5 +#define MSG_INFO_FIND_HEROS 6 +#define MSG_LAN_LISTENING0 7 +#define MSG_LAN_LISTENING1 8 +#define MSG_LAN_LISTENING2 9 +#define MSG_LAN_LISTENING3 10 +#define MSG_LAN_CONNECTING0 11 +#define MSG_LAN_CONNECTING1 12 +#define MSG_LAN_CONNECTING2 13 +#define MSG_LAN_CONNECTING3 14 +#define MSG_LAN_INPUT_IP 15 bool init(); void cleanup(); bool loadMedia(); +bool loadFont(); +bool loadUnifont(); SDL_Texture* loadSDLTexture(const char* path); bool loadTileset(const char* path, SDL_Texture* origin); bool loadTextset(); +bool loadMessage(); void initCommonEffects(); void initCommonAnimations(); void initCommonSprites(); +void nextLanguage(); +void lastLanguage(); +bool changeToNextLanguage(); +bool changeToLastLanguage(); +bool setLanguage(int language); extern SDL_Renderer* renderer; #endif diff --git a/src/text.h b/src/text.h index 42e0946..f6ce86c 100644 --- a/src/text.h +++ b/src/text.h @@ -1,9 +1,9 @@ #ifndef SNAKE_TEXT_H_ #define SNAKE_TEXT_H_ -#define MULTIPLAYER_LOCAL 13 -#define MULTIPLAYER_LAN 14 -#define LAN_HOSTGAME 15 -#define LAN_JOINGAME 16 +#define MULTIPLAYER_LOCAL 14 +#define MULTIPLAYER_LAN 15 +#define LAN_HOSTGAME 16 +#define LAN_JOINGAME 17 #endif diff --git a/src/types.c b/src/types.c index 9966956..65bb1fb 100644 --- a/src/types.c +++ b/src/types.c @@ -8,6 +8,7 @@ #include "helper.h" #include "render.h" +#include "res.h" #ifdef DBG #include @@ -15,6 +16,7 @@ extern LinkList animationsList[]; extern TTF_Font* font; +extern TTF_Font* unifont; extern SDL_Renderer* renderer; SDL_Color BLACK = {0, 0, 0, 255}; SDL_Color WHITE = {255, 255, 255, 255}; @@ -38,7 +40,7 @@ bool initText(Text* self, const char* str, SDL_Color color) { self->color = color; strcpy(self->text, str); // Render text surface - SDL_Surface* textSurface = TTF_RenderText_Solid(font, str, color); + SDL_Surface* textSurface = TTF_RenderUTF8_Solid(font, str, color); if (textSurface == NULL) { printf("Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError()); @@ -58,6 +60,31 @@ bool initText(Text* self, const char* str, SDL_Color color) { } return false; } +bool initUnicodeText(Text* self, const char* str, SDL_Color color) { + self->color = color; + strcpy(self->text, str); + // Render text surface + SDL_Surface* textSurface = TTF_RenderUTF8_Solid(unifont, str, color); + if (textSurface == NULL) { + printf("Unable to render text surface! SDL_ttf Error: %s\n", + TTF_GetError()); + } else { + // Create texture from surface pixels + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, textSurface); + self->width = textSurface->w; + self->height = textSurface->h; + SDL_FreeSurface(textSurface); + if (texture == NULL) { + printf("Unable to create texture from rendered text! SDL Error: %s\n", + SDL_GetError()); + } + else { + self->origin = texture; + return true; + } + } + return false; +} Text* createText(const char* str, SDL_Color color) { Text* self = malloc(sizeof(Text)); initText(self, str, color); diff --git a/src/types.h b/src/types.h index bf57551..59714a2 100644 --- a/src/types.h +++ b/src/types.h @@ -57,6 +57,7 @@ void initTexture(Texture* self, SDL_Texture* origin, int width, int height, void destroyTexture(Texture* self); bool initText(Text* self, const char* str, SDL_Color color); +bool initUnicodeText(Text* self, const char* str, SDL_Color color); Text* createText(const char* str, SDL_Color color); void destroyText(Text* self); diff --git a/src/ui.c b/src/ui.c index 473ceeb..a581c42 100644 --- a/src/ui.c +++ b/src/ui.c @@ -25,6 +25,7 @@ extern int renderFrames; extern SDL_Color WHITE; extern Texture textures[]; extern Effect effects[]; +extern Text messages[]; extern LinkList animationsList[ANIMATION_LINK_LIST_NUM]; int cursorPos; @@ -56,6 +57,18 @@ bool moveCursor(int optsNum) { playAudio(AUDIO_BUTTON1); return quit; break; + case SDLK_LEFT: + if (cursorPos == 3) { + changeToLastLanguage(); + playAudio(AUDIO_BUTTON1); + } + break; + case SDLK_RIGHT: + if (cursorPos == 3) { + changeToNextLanguage(); + playAudio(AUDIO_BUTTON1); + } + break; } } } @@ -99,13 +112,26 @@ bool chooseLevelUi() { baseUi(30, 12); int optsNum = 3; Text** opts = malloc(sizeof(Text*) * optsNum); - for (int i = 0; i < optsNum; i++) opts[i] = texts + i + 10; + for (int i = 0; i < optsNum; i++) opts[i] = texts + i + 11; int opt = chooseOptions(optsNum, opts); if (opt != optsNum) setLevel(opt); clearRenderer(); return opt != optsNum; } - +int chooseLanguageUi() { + baseUi(30, 12); + int optsNum = 3; + Text** opts = malloc(sizeof(Text*) * optsNum); + if (opts == NULL) { + fputs("malloc failed.\n", stderr); + return false; + } + for (int i = 0; i < optsNum; i++) opts[i] = texts + i + 18; + int opt = chooseOptions(optsNum, opts) + 1; + clearRenderer(); + extern int language; + return (opt != (optsNum + 1) ? opt : -1); +} void launchLocalGame(int localPlayerNum) { Score** scores = startGame(localPlayerNum, 0, true); rankListUi(localPlayerNum, scores); @@ -130,9 +156,8 @@ char* inputUi() { int retLen = 0; memset(ret, 0, MAX_LEN); - extern SDL_Color WHITE; Text* text = NULL; - Text* placeholder = createText("Enter IP", WHITE); + Text* placeholder = &messages[MSG_LAN_INPUT_IP]; SDL_StartTextInput(); SDL_Event e; @@ -173,7 +198,6 @@ char* inputUi() { } SDL_StopTextInput(); - destroyText(placeholder); destroyText(text); if (quit) { @@ -307,7 +331,7 @@ void mainUi() { AT_BOTTOM_CENTER); } */ - int optsNum = 4; + int optsNum = 5; Text** opts = malloc(sizeof(Text*) * optsNum); for (int i = 0; i < optsNum; i++) opts[i] = texts + i + 6; int opt = chooseOptions(optsNum, opts); @@ -334,10 +358,15 @@ void mainUi() { localRankListUi(); break; case 3: + setLanguage(chooseLanguageUi()); + break; + case 4: + break; + default: break; } if (opt == optsNum) return; - if (opt != 3) { + if (opt != 4) { mainUi(); } } @@ -347,7 +376,7 @@ void rankListUi(int count, Score** scores) { Text** opts = malloc(sizeof(Text*) * count); char buf[1 << 8]; for (int i = 0; i < count; i++) { - sprintf(buf, "Score: %-6.0lf Got: %-6d Kill: %-6d Damage: %-6d Stand: %-6d", + sprintf(buf, messages[MSG_RANKLISH_FORMAT].text, scores[i]->rank, scores[i]->got, scores[i]->killed, scores[i]->damage, scores[i]->stand); opts[i] = createText(buf, WHITE);