diff --git a/.gitignore b/.gitignore
index 319c11776..060ebab71 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,5 @@ server/lifeLog/
server/categories
server/objects
server/transitions
+
+build/mac/*
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..f96b7aa0a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,187 @@
+# skps2010/OneLife
+## Language - 語言 - 语言 - мова
++ [English](https://github.com/skps2010/OneLife/#English)
++ [正體中文](https://github.com/skps2010/OneLife/#正體中文)
++ [简体中文](https://github.com/skps2010/OneLife/#简体中文)
++ [Українська мова]() *未完工*
+
+
+
+* * *
+### English
+# OneLife
+The purpose of this mod is to translate One Hour One Life into different languages.
+It supports Unicode and has a translation tool.
+It currently support Traditional Chinese, Simplified Chinese and Ukrainian.
+
+## How to use
+
+### Windows
+You need to download it everytime when the game updates.
+1. Go to [release page](https://github.com/skps2010/OneLife/releases) and download `OneLife_Windows_v???.zip` (??? is verison number).
+2. Unzip it and go into the folder.
+3. Run `translator.exe`. It will ask you to input a number, then it will translate the whole game into corresponding languages.
+4. Run `OneLife.exe` to play.
+
+### Mac
+You need to download it everytime when the game updates.
+1. Go to [release page](https://github.com/skps2010/OneLife/releases) and download `OneLife_MacOSX_v???.zip` (??? is verison number).
+2. Unzip it and go into the folder.
+3. Run `translator`. It will ask you to input a number, then it will translate the whole game into corresponding languages.
+4. Run `OneLife_v???.app` to play (??? is verison number).
+
+### Mac, Linux (Source Code)
+This will download the newest version:
+1. Download [skps2010Build.sh](https://github.com/skps2010/OneLife/blob/master/scripts/skps2010Scripts/skps2010Build.sh) and put it into an empty folder, then run it. This will compile the game.
+2. Run `python3 translator.py`. It will ask you to input a number, then it will translate the whole game into corresponding languages.
+3. Run `OneLifeApp` to play.
+
+## How to Login with Steam
+Normal method
+1. Go to [this page](http://onehouronelife.com/steamGate/server.php?action=show_download_link).
+2. This page will ask you to sign in through Steam, and you will see your account and password after you sign in.
+3. Copy your account and password to log in.
+
+Alternative method
+1. Right click on One Hour One Life in your Steam Library, select Manage, then Browse Local Files.
+2. The game folder will pop up, the login account is in `settings/email.ini` and the login password is in `settings/accountKey.ini`.
+3. Copy the account and password to log in.
+
+## Translation
+### Outline
+All translation comes from this [Google sheet](https://docs.google.com/spreadsheets/d/1AH6eZJJ5zkB1zT-iwlomVAUxsa4f7gIgYFS0X265GyM/edit#gid=682688818).
+We'll try to update it every week.
+You can join the [Discord](https://discord.gg/UFZg3WXGrU) to discuss or report bugs.
+You can also join [Kook](https://kook.top/E9cHJL).
+
+### Contribute
+| language | benefactor | step - menu | step - object | step - image |
+| :---: | :----: | :----: | :----: | :----: |
+| zh_tw | skps2010 | ✔ [^PT] | ✔ [^PT] | ✔ |
+| zh_cn | 52Hertz | ✔ [^PT] | ✔ [^PT] | ✔ |
+| ukr | gloompain | ✔ | ✔ [^PT] | ✔ |
+
+*Translations may not be 100% correct.*
+
+* * *
+### 正體中文
+
+# Onelife - 一小時生命
+此模組的目的是將 One Hour One Life 翻譯成不同語言。
+此模組支援 Unicode 並擁有翻譯工具。
+目前支援正體中文、簡體中文和烏克蘭文。
+
+## 如何使用(正體)
+
+### Windows
+每次遊戲更新時,都要重新下載。
+1. 去 [發布頁面](https://github.com/skps2010/OneLife/releases) 並下載 `OneLife_Windows_v???.zip` (??? 是版本號)。
+2. 解壓縮並進入資料夾。
+3. 執行 `translator.exe`。他會要求你輸入一個數字,然後將整個遊戲翻成對應的語言。
+4. 執行 `OneLife.exe` 來玩。
+
+### Mac
+每次遊戲更新時,都要重新下載。
+1. 去 [發布頁面](https://github.com/skps2010/OneLife/releases) 並下載 `OneLife_MacOSX_v???.zip` (??? 是版本號)。
+2. 解壓縮並進入資料夾。
+3. 執行 `translator`。他會要求你輸入一個數字,然後將整個遊戲翻成對應的語言。
+4. 執行 `OneLife_v???.app` 來玩 (??? 是版本號)。
+
+### Mac, Linux(原始碼)
+這會下載最新版本:
+1. 下載 [skps2010Build.sh](https://github.com/skps2010/OneLife/blob/master/scripts/skps2010Scripts/skps2010Build.sh) 並放到空資料夾,然後執行。這會編譯遊戲。
+2. 執行 `python3 translator.py`。他會要求你輸入一個數字,然後將整個遊戲翻成對應的語言。
+3. 執行 `OneLifeApp` 來玩。
+
+## 如何透過 Steam 登入
+正常方法
+1. 去[這個網頁](http://onehouronelife.com/steamGate/server.php?action=show_download_link)。
+2. 此網頁會要求你透過 Steam 登入,登入後即可看到帳號跟密碼。
+3. 複製帳號跟密碼來登入。
+
+另一種方法
+1. 對 Steam 收藏庫中的 One Hour One Life 點右鍵,選 管理,然後選 瀏覽本機檔案。
+2. 此時會跳出遊戲資料夾,登入帳號在 `settings/email.ini`,登入密碼在 `settings/accountKey.ini`。
+3. 複製帳號跟密碼來登入。
+
+## 翻譯
+
+### 概要
+所有翻譯來自此 [Google sheet](https://docs.google.com/spreadsheets/d/1AH6eZJJ5zkB1zT-iwlomVAUxsa4f7gIgYFS0X265GyM/edit#gid=682688818)。
+我們會嘗試每禮拜更新。
+你可加入 [Discord](https://discord.gg/UFZg3WXGrU) 來討論或回報問題。
+你也可以加入 [Kook](https://kook.top/E9cHJL)。
+
+### 貢獻
+| 語言 | 貢獻者 | 進度 - menu | 進度 - object | 進度 - image |
+| :---: | :----: | :----: | :----: | :----: |
+| zh_tw | skps2010 | ✔ [^PT] | ✔ [^PT] | ✔ |
+| zh_cn | 52Hertz | ✔ [^PT] | ✔ [^PT] | ✔ |
+| ukr | gloompain | ✔ | ✔ [^PT] | ✔ |
+
+*翻譯不一定 100% 正確*
+
+* * *
+### 简体中文
+
+# OneLife - 一小时人生
+此模组的目的是将 One Hour One Life 翻译成不同语言。
+此模组支持 Unicode 并拥有翻译工具。
+目前支持简体中文、繁体中文和乌克兰文。
+
+## 如何使用(简体)
+
+### 简体中文版
+功能一致,仅语言预设简体中文,不需要连到 Google(*无法更新翻译*)*~~VPN/翻墙 除外~~*
+1. 去 [发布页面](https://github.com/skps2010/OneLife/releases) 并下载 `OHOL_Simplified_Chinese_v???.exe` (??? 是版本号)。
+2. 执行它,安装程序会帮你装好游戏。
+3. 执行 `OneLife.exe` 来玩。
+
+### Windows
+每次游戏更新时,都要重新下载。
+1. 去 [发布页面](https://github.com/skps2010/OneLife/releases) 并下载 “OneLife_Windows_v???.zip' (??? 是版本号)。
+2. 解压缩并进入文件夹。
+3. 执行 `translator.exe`。 他会要求你输入一个数字,然后将整个游戏翻成对应的语言。
+4. 执行 `OneLife.exe` 来玩。
+
+### Mac
+每次游戏更新时,都要重新下载。
+1. 去 [发布页面](https://github.com/skps2010/OneLife/releases) 并下载 “OneLife_MacOSX_v???.zip' (??? 是版本号)。
+2. 解压缩并进入文件夹。
+3. 执行 `translator`。 他会要求你输入一个数字,然后将整个游戏翻成对应的语言。
+4. 执行 `OneLife_v???.app` 来玩(??? 是版本号)。
+
+### Mac, Linux(源代码)
+这会下载最新版本:
+1. 下载 [skps2010Build.sh](https://github.com/skps2010/OneLife/blob/master/scripts/skps2010Scripts/skps2010Build.sh) 并放到空文件夹,然后执行。这会编译游戏。
+2. 执行 `python3 translator.py`。它会要求你输入一个数字,然后将整个游戏翻成对应的语言。
+3. 执行 `OneLifeApp` 来玩。
+
+## 如何通过 Steam 登录
+正常方法
+1. 去[这个网页](http://onehouronelife.com/steamGate/server.php?action=show_download_link) 。
+2. 此网页会要求你通过 Steam 登入,登入后即可看到帐号跟密码。
+3. 复制帐号跟密码来登入。
+
+另一种方法 (无e-mail而是Steam id则无效)
+1. 对 Steam 收藏库中的 One Hour One Life 点右键,选 管理,然后选 浏览本机文件。
+2. 此时会跳出游戏文件夹,登入帐号在 `settings/email.ini`,登入密码在 `settings/accountKey.ini`。
+3. 复制帐号跟密码来登入。
+
+## 翻译
+### 概要
+所有翻译来自此 [Google sheet](https://docs.google.com/spreadsheets/d/1AH6eZJJ5zkB1zT-iwlomVAUxsa4f7gIgYFS0X265GyM/edit#gid=682688818)。
+我们会尝试每周更新。
+你可加入 [Discord](https://discord.gg/UFZg3WXGrU) 来讨论或回报问题。
+你也可以加入 [Kook(原开黑啦)](https://kook.top/E9cHJL)。
+
+### 贡献
+| 语言 | 贡献者 | 进度 - menu | 进度 - object | 进度 - image |
+| :---: | :----: | :----: | :----: | :----: |
+| zh_tw | skps2010 | ✔ [^PT] | ✔ [^PT] | ✔ |
+| zh_cn | 52Hertz | ✔ [^PT] | ✔ [^PT] | ✔ |
+| ukr | gloompain | ✔ | ✔ [^PT] | ✔ |
+
+*翻译不一定 100% 正确*
+
+[^PT]:partial turnover - 部分機翻 - 部分机翻
diff --git a/build/makeBaseDistributionFolder b/build/makeBaseDistributionFolder
index a47dac50d..7324cb934 100755
--- a/build/makeBaseDistributionFolder
+++ b/build/makeBaseDistributionFolder
@@ -33,6 +33,7 @@ mkdir $baseFolder/ground
cp ../gameSource/graphics/*.tga $baseFolder/graphics
+cp ../gameSource/graphics/font.ttf $baseFolder/graphics
cp ../gameSource/otherSounds/*.aiff $baseFolder/otherSounds
cp ../gameSource/settings/*.ini $baseFolder/settings
cp ../gameSource/languages/*.txt $baseFolder/languages
diff --git a/build/makeDistributionMacOSX b/build/makeDistributionMacOSX
index d46ea81a4..206ef8b44 100755
--- a/build/makeDistributionMacOSX
+++ b/build/makeDistributionMacOSX
@@ -19,7 +19,7 @@ rm -rf mac/OneLife_$1
./makeBaseDistributionFolder $1
-
+mkdir mac
mv base/* mac
@@ -43,14 +43,34 @@ rm */bin_*cache.fcz
cd ../..
+onelife_app=mac/OneLife_$1/OneLife_$1.app/Contents/MacOS/OneLife
cp -r macOSX/OneLife.app mac/OneLife_$1/OneLife_$1.app
-cp ../gameSource/OneLife mac/OneLife_$1/OneLife_$1.app/Contents/MacOS
+cp ../../OneLifeApp $onelife_app
rm -r mac/OneLife_$1/OneLife_$1.app/Contents/MacOS/empty.txt
rm -r mac/OneLife_$1/OneLife_$1.app/Contents/Frameworks/empty.txt
# install SDL framework
-cp -r $3 mac/OneLife_$1/OneLife_$1.app/Contents/Frameworks/
+framework_folder=mac/OneLife_$1/OneLife_$1.app/Contents/Frameworks/
+cp -r $3 $framework_folder
+cp /usr/local/lib/libfreetype.6.dylib $framework_folder
+cp /usr/local/opt/libpng/lib/libpng16.16.dylib $framework_folder
+cp /usr/local/opt/sdl12-compat/lib/libSDL-1.2.0.dylib $framework_folder
+
+# allow to use custom SDL2
+if [ ! -e ../../libSDL2-2.0.0.dylib ]
+then
+ cp /usr/local/Cellar/sdl2/2.28.3/lib/libSDL2-2.0.0.dylib ../../
+fi
+cp ../../libSDL2-2.0.0.dylib $framework_folder
+
+install_name_tool -change /usr/local/lib/libfreetype.6.dylib @executable_path/../Frameworks/libfreetype.6.dylib $onelife_app
+install_name_tool -change /usr/local/opt/libpng/lib/libpng16.16.dylib @executable_path/../Frameworks/libfreetype.6.dylib $framework_folder/libfreetype.6.dylib
+install_name_tool -id @executable_path/libpng16.16.dylib $framework_folder/libpng16.16.dylib
+install_name_tool -change /usr/local/opt/sdl12-compat/lib/libSDL-1.2.0.dylib @executable_path/../Frameworks/libSDL-1.2.0.dylib $onelife_app
+
+cp ../scripts/skps2010Scripts/translator.py mac/OneLife_$1
+cp ../../translator mac/OneLife_$1
cd mac
@@ -59,9 +79,7 @@ d=`date`
echo "$1 built on $d" > OneLife_$1/binary.txt
-
-tar cf "OneLife_$1_$2.tar" OneLife_$1
-gzip "OneLife_$1_$2.tar"
+zip -r -q OneLife_MacOSX_$1.zip OneLife_$1
diff --git a/build/makeDistributionWindows b/build/makeDistributionWindows
index 4280faf88..b7d29ddf0 100755
--- a/build/makeDistributionWindows
+++ b/build/makeDistributionWindows
@@ -25,7 +25,11 @@ mv base/* windows
cd ../gameSource
-sh makeRegenerateCachesWindows
+if [ "$(uname)" = "Linux" ]; then
+ sh makeRegenerateCaches
+else
+ sh makeRegenerateCachesWindows
+fi
cd ../build/windows/OneLife_$1
diff --git a/build/source/runToBuild b/build/source/runToBuild
index cd4509f2d..6e4bb5c91 100755
--- a/build/source/runToBuild
+++ b/build/source/runToBuild
@@ -45,12 +45,23 @@ mkdir -p groundTileCache
echo "Copying items from build into directories"
-cp OneLife/gameSource/OneLife ./OneLifeApp
+if [ "$1" = "5" ] # windows
+then
+ cp OneLife/gameSource/OneLife.exe ./OneLifeApp.exe
+else
+ cp OneLife/gameSource/OneLife ./OneLifeApp
+fi
cp OneLife/documentation/Readme.txt .
cp OneLife/no_copyright.txt .
cp OneLife/gameSource/graphics/* ./graphics
cp OneLife/gameSource/otherSounds/* ./otherSounds
-cp -u OneLife/gameSource/settings/* ./settings
+
+# mac doesn't have "cp -u"
+if [[ "$OSTYPE" == "darwin"* ]]; then
+ rsync -u OneLife/gameSource/settings/* ./settings
+else
+ cp -u OneLife/gameSource/settings/* ./settings
+fi
cp OneLife/gameSource/languages/* ./languages
cp OneLife/gameSource/language.txt ./
cp OneLife/gameSource/us_english_60.txt ./
diff --git a/build/win32/libfreetype-6.dll b/build/win32/libfreetype-6.dll
new file mode 100644
index 000000000..3b2e69dfa
Binary files /dev/null and b/build/win32/libfreetype-6.dll differ
diff --git a/build/win32/zlib1.dll b/build/win32/zlib1.dll
new file mode 100644
index 000000000..5cc713a78
Binary files /dev/null and b/build/win32/zlib1.dll differ
diff --git a/gameSource/ExistingAccountPage.cpp b/gameSource/ExistingAccountPage.cpp
index f28078aff..ae3dfc1fe 100644
--- a/gameSource/ExistingAccountPage.cpp
+++ b/gameSource/ExistingAccountPage.cpp
@@ -21,6 +21,8 @@
#include "minorGems/graphics/openGL/KeyboardHandlerGL.h"
#include "minorGems/util/random/JenkinsRandomSource.h"
+#include
+#include
static JenkinsRandomSource randSource;
@@ -76,6 +78,8 @@ ExistingAccountPage::ExistingAccountPage()
translate( "tutorial" ) ),
mServicesButton( mainFont, -522, 300,
translate( "services" ) ),
+ mTranslateButton( mainFont, -400, -40,
+ translate( "translateButton" ) ),
mPageActiveStartTime( 0 ),
mFramesCounted( 0 ),
mFPSMeasureDone( false ),
@@ -104,6 +108,7 @@ ExistingAccountPage::ExistingAccountPage()
setButtonStyle( &mViewAccountButton );
setButtonStyle( &mTutorialButton );
setButtonStyle( &mServicesButton );
+ setButtonStyle( &mTranslateButton );
setButtonStyle( &mDisableCustomServerButton );
@@ -132,6 +137,7 @@ ExistingAccountPage::ExistingAccountPage()
addComponent( &mViewAccountButton );
addComponent( &mTutorialButton );
addComponent( &mServicesButton );
+ addComponent( &mTranslateButton );
mLoginButton.addActionListener( this );
mFriendsButton.addActionListener( this );
@@ -154,6 +160,7 @@ ExistingAccountPage::ExistingAccountPage()
mViewAccountButton.addActionListener( this );
mTutorialButton.addActionListener( this );
mServicesButton.addActionListener( this );
+ mTranslateButton.addActionListener( this );
mDisableCustomServerButton.addActionListener( this );
@@ -170,6 +177,7 @@ ExistingAccountPage::ExistingAccountPage()
mGenesButton.setMouseOverTip( translate( "genesTip" ) );
mFamilyTreesButton.setMouseOverTip( translate( "familyTreesTip" ) );
mTechTreeButton.setMouseOverTip( translate( "techTreeTip" ) );
+ mTranslateButton.setMouseOverTip( translate( "translateTip" ) );
int reviewPosted = SettingsManager::getIntSetting( "reviewPosted", 0 );
@@ -526,6 +534,55 @@ void ExistingAccountPage::actionPerformed( GUIComponent *inTarget ) {
mDisableCustomServerButton.setVisible( false );
processLogin( true, "done" );
}
+ else if( inTarget == &mTranslateButton ) {
+ struct stat buffer;
+
+#ifdef __mac__
+ if(stat ("translator", &buffer) == 0) {
+ system("open translator; sleep 1.0; while pgrep -f translator >/dev/null; do sleep 1.0; done");
+ }
+ else
+ system("open translator.py -a Terminal; sleep 1.0; while pgrep -f translator >/dev/null; do sleep 1.0; done");
+
+#elif defined(WIN32)
+ char fullscreen = SettingsManager::getIntSetting("fullscreen", 0);
+ if(fullscreen) {
+ char cwd[PATH_MAX];
+ getcwd(cwd, sizeof(cwd));
+ char *call;
+
+ if(stat ("translator.exe", &buffer) == 0) {
+ call = autoSprintf("wmic process call create 'cmd /c start translator.exe', '%s'", cwd );
+ }
+ else
+ call = autoSprintf("wmic process call create 'cmd /c python3 translator.py', '%s'", cwd );
+ system( call );
+ delete [] call;
+ exit(0);
+ }
+ else {
+ if(stat ("translator.exe", &buffer) == 0) {
+ system("translator.exe");
+ }
+ else
+ system("python3 translator.py");
+ }
+
+#else
+ system("x-terminal-emulator -e python3 translator.py");
+#endif
+
+ char relaunched = relaunchGame();
+
+ if( !relaunched ) {
+ printf( "Relaunch failed\n" );
+ setSignal( "relaunchFailed" );
+ }
+ else {
+ printf( "Relaunched... but did not exit?\n" );
+ setSignal( "relaunchFailed" );
+ }
+ }
}
diff --git a/gameSource/ExistingAccountPage.h b/gameSource/ExistingAccountPage.h
index 23167cdb8..11ce22fbd 100644
--- a/gameSource/ExistingAccountPage.h
+++ b/gameSource/ExistingAccountPage.h
@@ -78,6 +78,7 @@ class ExistingAccountPage : public GamePage, public ActionListener {
TextButton mTutorialButton;
TextButton mServicesButton;
+ TextButton mTranslateButton;
double mPageActiveStartTime;
diff --git a/gameSource/LivingLifePage.cpp b/gameSource/LivingLifePage.cpp
index f1680a65c..1ed640b73 100644
--- a/gameSource/LivingLifePage.cpp
+++ b/gameSource/LivingLifePage.cpp
@@ -20056,6 +20056,10 @@ void LivingLifePage::step() {
starPos[0] = '\0';
}
}
+ // translate English here
+ // existing->currentSpeech here is the player's talking
+ // replace keywords in existing->currentSpeech. For example: *HOME SICK*
+ // All words can be found in server.cpp with "PS\n"
}
}
diff --git a/gameSource/LoadingPage.cpp b/gameSource/LoadingPage.cpp
index 79bc69ff8..fc6532c2a 100644
--- a/gameSource/LoadingPage.cpp
+++ b/gameSource/LoadingPage.cpp
@@ -2,6 +2,7 @@
#include "message.h"
#include "minorGems/game/drawUtils.h"
+#include "minorGems/game/game.h"
void LoadingPage::setCurrentPhase( const char *inPhaseName ) {
@@ -21,7 +22,7 @@ void LoadingPage::draw( doublePair inViewCenter,
doublePair labelPos = { 0, 0 };
- drawMessage( "LOADING", labelPos, false );
+ drawMessage( translate( "loading" ), labelPos, false );
labelPos.y = -100;
diff --git a/gameSource/TextField.cpp b/gameSource/TextField.cpp
index faaf64df3..2c6b8cfd0 100644
--- a/gameSource/TextField.cpp
+++ b/gameSource/TextField.cpp
@@ -81,7 +81,7 @@ TextField::TextField( Font *inDisplayFont,
unsigned char widestChar = 0;
double width = 0;
- for( int c=32; c<256; c++ ) {
+ for( int c=32; c<128; c++ ) {
unsigned char pc = processCharacter( c );
if( pc != 0 ) {
diff --git a/gameSource/editor.cpp b/gameSource/editor.cpp
index 095a6b16d..dd2b16b69 100644
--- a/gameSource/editor.cpp
+++ b/gameSource/editor.cpp
@@ -208,7 +208,7 @@ const char *getLinuxAppName() {
const char *getFontTGAFileName() {
- return "font_32_64.tga";
+ return "font_32_32.tga";
}
diff --git a/gameSource/game.cpp b/gameSource/game.cpp
index 7a8015e49..8432e9a7c 100644
--- a/gameSource/game.cpp
+++ b/gameSource/game.cpp
@@ -7,7 +7,7 @@ int binVersionNumber = versionNumber;
// Please use this tag to describe your client honestly and uniquely
// client_official is reserved for the unmodded client
// do not include whitespace in your tag
-const char *clientTag = "client_official";
+const char *clientTag = "client_skps2010";
@@ -288,7 +288,7 @@ const char *getLinuxAppName() {
const char *getFontTGAFileName() {
- return "font_32_64.tga";
+ return "font_32_32.tga";
}
@@ -1813,7 +1813,7 @@ void drawFrame( char inUpdate ) {
startConnecting();
}
- else if( autoUpdatePage->checkSignal( "relaunchFailed" ) ) {
+ else if( existingAccountPage->checkSignal( "relaunchFailed" ) ) {
currentGamePage = finalMessagePage;
finalMessagePage->setMessageKey( "manualRestartMessage" );
diff --git a/gameSource/graphics/font.ttf b/gameSource/graphics/font.ttf
new file mode 100644
index 000000000..da22959dc
Binary files /dev/null and b/gameSource/graphics/font.ttf differ
diff --git a/gameSource/graphics/font_32_32.tga b/gameSource/graphics/font_32_32.tga
new file mode 100644
index 000000000..57892fd86
Binary files /dev/null and b/gameSource/graphics/font_32_32.tga differ
diff --git a/gameSource/graphicsSource/font_32_32.png b/gameSource/graphicsSource/font_32_32.png
new file mode 100644
index 000000000..380907fbc
Binary files /dev/null and b/gameSource/graphicsSource/font_32_32.png differ
diff --git a/gameSource/languages/English.txt b/gameSource/languages/English.txt
index 5e2838ecf..954ced84c 100644
--- a/gameSource/languages/English.txt
+++ b/gameSource/languages/English.txt
@@ -54,6 +54,8 @@ clearAccount "CLEAR ACCOUNT"
settingsButton "SETTINGS"
postReviewButton "POST REVIEW"
updateReviewButton "UPDATE REVIEW"
+translateButton "文/A"
+translateTip "RUN TRANSLATOR AND RESTART GAME"
reviewText "REVIEW TEXT"
reviewName "DISPLAY NAME"
diff --git a/gameSource/makeFileList b/gameSource/makeFileList
index 2b0d947f9..640e02921 100644
--- a/gameSource/makeFileList
+++ b/gameSource/makeFileList
@@ -80,6 +80,7 @@ photoCache.cpp \
GAME_GRAPHICS = \
graphics/font_32_64.tga \
+graphics/font_32_32.tga \
graphics/font_handwriting_32_32.tga \
graphics/font_pencil_32_32.tga \
graphics/font_pencil_erased_32_32.tga \
@@ -130,7 +131,6 @@ graphics/photoDisplay3.tga \
graphics/photoDisplay4.tga \
-
NEEDED_MINOR_GEMS_OBJECTS = \
${SCREEN_GL_SDL_O} \
${SINGLE_TEXTURE_GL_O} \
diff --git a/gameSource/settings/fullscreen.ini b/gameSource/settings/fullscreen.ini
index 56a6051ca..c22708346 100644
--- a/gameSource/settings/fullscreen.ini
+++ b/gameSource/settings/fullscreen.ini
@@ -1 +1 @@
-1
\ No newline at end of file
+0
\ No newline at end of file
diff --git a/gameSource/settings/unicodeOffset.ini b/gameSource/settings/unicodeOffset.ini
new file mode 100644
index 000000000..c67f81a3f
--- /dev/null
+++ b/gameSource/settings/unicodeOffset.ini
@@ -0,0 +1 @@
+-5
\ No newline at end of file
diff --git a/gameSource/settings/unicodeScale.ini b/gameSource/settings/unicodeScale.ini
new file mode 100644
index 000000000..840ca8cbf
--- /dev/null
+++ b/gameSource/settings/unicodeScale.ini
@@ -0,0 +1 @@
+1.4
\ No newline at end of file
diff --git a/gameSource/settings/unicodeWide.ini b/gameSource/settings/unicodeWide.ini
new file mode 100644
index 000000000..8fdd954df
--- /dev/null
+++ b/gameSource/settings/unicodeWide.ini
@@ -0,0 +1 @@
+22
\ No newline at end of file
diff --git a/gameSource/thermalSim/thermalSim.cpp b/gameSource/thermalSim/thermalSim.cpp
index 6cb2e2edd..1da09cc3a 100644
--- a/gameSource/thermalSim/thermalSim.cpp
+++ b/gameSource/thermalSim/thermalSim.cpp
@@ -138,7 +138,7 @@ const char *getLinuxAppName() {
const char *getFontTGAFileName() {
- return "font_32_64.tga";
+ return "font_32_32.tga";
}
diff --git a/scripts/skps2010Scripts/cleanOldBuilds.sh b/scripts/skps2010Scripts/cleanOldBuilds.sh
new file mode 100755
index 000000000..ebace48cc
--- /dev/null
+++ b/scripts/skps2010Scripts/cleanOldBuilds.sh
@@ -0,0 +1,5 @@
+find . -type f -name '*.o' -exec rm {} +
+find . -type f -name '*.dep' -exec rm {} +
+rm OneLife/gameSource/Makefile.minorGems_dependencies
+echo
+echo "Projects cleaned. Ready for compilation."
\ No newline at end of file
diff --git a/scripts/skps2010Scripts/makeMacOSX.sh b/scripts/skps2010Scripts/makeMacOSX.sh
new file mode 100755
index 000000000..af5b8c759
--- /dev/null
+++ b/scripts/skps2010Scripts/makeMacOSX.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+if [ $# -lt 1 ] ; then
+ echo "Usage: $0 release_name"
+ exit 1
+fi
+
+./skps2010Build.sh
+
+# allow to use custom translator
+if [ ! -e translator ]
+then
+ python3 -m PyInstaller --onefile translator.py
+ cp dist/translator .
+fi
+
+cd OneLife/build
+./makeDistributionMacOSX v$1 _ /usr/local/opt/sdl12-compat/lib/libSDL-1.2.0.dylib
+
diff --git a/scripts/skps2010Scripts/makeWindows.sh b/scripts/skps2010Scripts/makeWindows.sh
new file mode 100755
index 000000000..bebc2906d
--- /dev/null
+++ b/scripts/skps2010Scripts/makeWindows.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+if [ $# -lt 1 ] ; then
+ echo "Usage: $0 release_name"
+ exit 1
+fi
+
+# SDL lib for linux-windows cross compile
+if [ ! -e SDL-1.2.15 ]
+then
+ wget https://www.libsdl.org/release/SDL-devel-1.2.15-mingw32.tar.gz
+ tar -xvzf SDL-devel-1.2.15-mingw32.tar.gz
+ rm SDL-devel-1.2.15-mingw32.tar.gz
+fi
+
+if [ ! -e SDL ]
+then
+ ln -s SDL-1.2.15/include/SDL .
+fi
+
+# freetype lib for linux-windows cross compile
+if [ ! -e mingw32 ]
+then
+ wget https://mirror.msys2.org/mingw/mingw32/mingw-w64-i686-freetype-2.13.2-1-any.pkg.tar.zst
+ tar --use-compress-program=unzstd -xvf mingw-w64-i686-freetype-2.13.2-1-any.pkg.tar.zst
+ rm mingw-w64-i686-freetype-2.13.2-1-any.pkg.tar.zst
+fi
+
+if [ ! -e freetype2 ]
+then
+ ln -s mingw32/include/freetype2 .
+fi
+
+if [ ! -e Winsock.h ]
+then
+ ln -s /usr/i686-w64-mingw32/include/winsock.h Winsock.h
+fi
+
+cd OneLife
+
+./configure 5
+cd gameSource
+make
+
+cd ../build
+
+./makeDistributionWindows v$1
+
+cp ../scripts/skps2010Scripts/translator.py "windows/OneLife_v$1"
+cp ../scripts/skps2010Scripts/translator.exe "windows/OneLife_v$1"
+
+mv "windows/OneLife_v$1" "../../"
+
+cd ../../
+
+echo "done building OneLife_v$1"
+
+zip -r -q OneLife_Windows_v$1.zip OneLife_v$1
+
+echo "done zipping OneLife_Windows_v$1.zip"
\ No newline at end of file
diff --git a/scripts/skps2010Scripts/skps2010Build.sh b/scripts/skps2010Scripts/skps2010Build.sh
new file mode 100755
index 000000000..e570befbc
--- /dev/null
+++ b/scripts/skps2010Scripts/skps2010Build.sh
@@ -0,0 +1,125 @@
+#!/bin/sh
+
+if [ ! -e minorGems ]
+then
+ git clone https://github.com/skps2010/minorGems.git
+fi
+
+if [ ! -e OneLife ]
+then
+ git clone https://github.com/skps2010/OneLife.git
+fi
+
+if [ ! -e OneLifeData7 ]
+then
+ git clone https://github.com/jasonrohrer/OneLifeData7.git
+fi
+
+
+# cd minorGems
+# git fetch --tags
+# latestTaggedVersion=`git for-each-ref --sort=-creatordate --format '%(refname:short)' --count=1 refs/tags/OneLife_v* | sed -e 's/OneLife_v//'`
+# git checkout -q OneLife_v$latestTaggedVersion
+
+
+# cd OneLife
+# git fetch --tags
+# latestTaggedVersionA=`git for-each-ref --sort=-creatordate --format '%(refname:short)' --count=1 refs/tags/OneLife_v* | sed -e 's/OneLife_v//'`
+# git checkout -q OneLife_v$latestTaggedVersionA
+
+
+cd OneLifeData7
+git fetch --tags
+# remove translated objects
+git checkout .
+latestTaggedVersionB=`git for-each-ref --sort=-creatordate --format '%(refname:short)' --count=1 refs/tags/OneLife_v* | sed -e 's/OneLife_v//'`
+git checkout -q OneLife_v$latestTaggedVersionB
+
+rm */cache.fcz
+
+
+latestVersion=$latestTaggedVersionB
+
+# if [ "$latestTaggedVersionA" -gt "$latestTaggedVersionB" ]
+# then
+# latestVersion=$latestTaggedVersionA
+# fi
+
+
+
+cd ..
+
+
+if [ ! -h animations ]
+then
+ ln -s OneLifeData7/animations .
+fi
+
+
+if [ ! -h categories ]
+then
+ ln -s OneLifeData7/categories .
+fi
+
+
+if [ ! -h ground ]
+then
+ ln -s OneLifeData7/ground .
+fi
+
+
+if [ ! -h music ]
+then
+ ln -s OneLifeData7/music .
+fi
+
+
+if [ ! -h objects ]
+then
+ ln -s OneLifeData7/objects .
+fi
+
+
+if [ ! -h sounds ]
+then
+ ln -s OneLifeData7/sounds .
+fi
+
+
+if [ ! -h sprites ]
+then
+ ln -s OneLifeData7/sprites .
+fi
+
+
+if [ ! -h transitions ]
+then
+ ln -s OneLifeData7/transitions .
+fi
+
+
+if [ ! -h dataVersionNumber.txt ]
+then
+ ln -s OneLifeData7/dataVersionNumber.txt .
+fi
+
+
+
+
+cp OneLife/build/source/runToBuild .
+cp OneLife/scripts/skps2010Scripts/cleanOldBuilds.sh .
+cp OneLife/scripts/skps2010Scripts/makeWindows.sh .
+cp OneLife/scripts/skps2010Scripts/makeMacOSX.sh .
+cp OneLife/scripts/skps2010Scripts/translator.py .
+
+
+if [ "$(uname)" = "Darwin" ]; then
+ ./runToBuild 2
+else
+ ./runToBuild 1
+fi
+
+
+echo
+echo
+echo "Done building v$latestVersion"
diff --git a/scripts/skps2010Scripts/translator.exe b/scripts/skps2010Scripts/translator.exe
new file mode 100644
index 000000000..db4a96942
Binary files /dev/null and b/scripts/skps2010Scripts/translator.exe differ
diff --git a/scripts/skps2010Scripts/translator.py b/scripts/skps2010Scripts/translator.py
new file mode 100755
index 000000000..bada19760
--- /dev/null
+++ b/scripts/skps2010Scripts/translator.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python3
+import requests
+import os
+from os.path import dirname, realpath
+import sys
+
+
+def main():
+ print('Connect to Google sheet...\n')
+
+ if getattr(sys, 'frozen', False):
+ path = dirname(sys.executable)
+ else:
+ path = dirname(realpath(__file__))
+ os.chdir(path)
+ url = 'https://script.google.com/macros/s/AKfycbx0agAIW99KUpLdLQX1ghFaMu81uopoQ7zNqHe7s3D5gWIZO8cb7tLRTGV8Gb8F4saC/exec'
+
+ try:
+ r = requests.get(f'{url}')
+ except requests.ConnectionError:
+ print('Unable to connect to the Google sheet')
+ return
+
+ if r.status_code != 200:
+ print('Unable to connect to the Google sheet')
+ return
+
+ langs = r.json()
+ for i in range(len(langs)):
+ print(f'{i}: {langs[i]}')
+ print(f'Please input 0~{len(langs)-1}: ')
+ while 1:
+ try:
+ lang = int(input())
+ if lang < 0 or lang > len(langs) - 1:
+ raise ValueError
+ break
+ except ValueError:
+ print(f'Please input 0~{len(langs)-1}: ')
+
+ print('\nAppend English after translated objects?')
+ print('0: No')
+ print('1: Yes')
+ print('Please input 0~1: ')
+ while 1:
+ try:
+ is_append = int(input())
+ if is_append < 0 or is_append > 1:
+ raise ValueError
+ break
+ except ValueError:
+ print('Please input 0~1: ')
+
+ print("Translating Objects...")
+
+ if os.path.isfile('objects/cache.fcz'):
+ os.remove('objects/cache.fcz')
+
+ r = requests.get(f'{url}?lang={lang}&type=0')
+
+ if is_append:
+ r2 = requests.get(f'{url}?lang=0&type=0')
+ data2 = r2.json()
+
+ data = r.json()
+ for i in range(len(data['key'])):
+ translated = data['value'][i]
+ if translated != '':
+ try:
+ with open(f'objects/{data["key"][i]}', encoding='utf-8') as f:
+ content = f.readlines()
+ except FileNotFoundError as e:
+ print(e)
+ continue
+
+ if is_append and data2['value'][i] != '':
+ content[1] = translated.split('#')[0].split(
+ ' $')[0] + data2['value'][i] + '\n'
+ else:
+ content[1] = translated + '\n'
+
+ with open(f'objects/{data["key"][i]}', 'w', encoding='utf-8') as f:
+ f.writelines(content)
+
+ menuItems = {}
+ try:
+ with open('languages/English.txt', encoding='utf-8') as f:
+ datas = f.readlines()
+ for data in datas:
+ if data == '\n':
+ continue
+ name = data.split(' ')[0]
+ value = data[data.index('"') + 1:-2]
+ menuItems[name] = value
+
+ except FileNotFoundError as e:
+ print(e)
+
+ print("Translating Menu...")
+
+ r = requests.get(f'{url}?lang={lang}&type=1')
+ data = r.json()
+
+ for i in range(len(data['key'])):
+ if data['value'][i] != '':
+ menuItems[data['key'][i]] = data['value'][i]
+
+ with open('languages/English.txt', 'w', encoding='utf-8') as f:
+ for key in menuItems:
+ f.write(f'{key} "{menuItems[key]}"\n')
+
+ print("Translating Images...")
+
+ r = requests.get(f'{url}?lang={lang}&type=2')
+ data = r.json()
+ for i in range(len(data['key'])):
+ link = data['value'][i]
+ if link != '':
+ r = requests.get(link)
+ if r.status_code != 200:
+ print(f'File can\'t be found: {link}')
+ continue
+ with open(f'graphics/{data["key"][i]}', 'wb') as f:
+ f.write(r.content)
+
+ print("Apply settings...")
+
+ r = requests.get(f'{url}?lang={lang}&type=3')
+ data = r.json()
+ for i in range(len(data['key'])):
+ value = data['value'][i]
+ if value != '':
+ with open(f'settings/{data["key"][i]}', 'w') as f:
+ f.write(str(value))
+
+ print("Translating done!")
+
+
+if __name__ == '__main__':
+ main()