Add Android controls (#101)
This commit is contained in:
parent
e41a4b8e16
commit
6064a4bc79
|
@ -2,14 +2,23 @@
|
||||||
*.c text eol=lf
|
*.c text eol=lf
|
||||||
*.cc text eol=lf
|
*.cc text eol=lf
|
||||||
*.cmake text eol=lf
|
*.cmake text eol=lf
|
||||||
|
*.gradle text eol=lf
|
||||||
*.h text eol=lf
|
*.h text eol=lf
|
||||||
*.md text eol=lf
|
*.java text eol=lf
|
||||||
*.json text eol=lf
|
*.json text eol=lf
|
||||||
|
*.md text eol=lf
|
||||||
*.plist text eol=lf
|
*.plist text eol=lf
|
||||||
|
*.pro text eol=lf
|
||||||
|
*.properties text eol=lf
|
||||||
|
*.xml text eol=lf
|
||||||
*.yml text eol=lf
|
*.yml text eol=lf
|
||||||
.clang-format text eol=lf
|
.clang-format text eol=lf
|
||||||
.editorconfig text eol=lf
|
.editorconfig text eol=lf
|
||||||
.gitattributes text eol=lf
|
.gitattributes text eol=lf
|
||||||
.gitignore text eol=lf
|
.gitignore text eol=lf
|
||||||
|
gradlew text eol=lf
|
||||||
CMakeLists.txt text eol=lf
|
CMakeLists.txt text eol=lf
|
||||||
LICENSE text eol=lf
|
LICENSE text eol=lf
|
||||||
|
|
||||||
|
# Force CRLF
|
||||||
|
*.bat text eol=crlf
|
||||||
|
|
|
@ -38,6 +38,49 @@ jobs:
|
||||||
- name: cppcheck
|
- name: cppcheck
|
||||||
run: cppcheck --std=c++17 src/
|
run: cppcheck --std=c++17 src/
|
||||||
|
|
||||||
|
android:
|
||||||
|
name: Android
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Clone
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v2
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: 11
|
||||||
|
cache: gradle
|
||||||
|
|
||||||
|
- name: Cache cmake build
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: os/android/app/.cxx
|
||||||
|
key: android-cmake-v1
|
||||||
|
|
||||||
|
- name: Setup signing config
|
||||||
|
run: |
|
||||||
|
cd os/android
|
||||||
|
echo "$KEYSTORE_FILE_BASE64" | base64 --decode > debug.keystore
|
||||||
|
echo "$KEYSTORE_PROPERTIES_FILE_BASE64" | base64 --decode > debug-keystore.properties
|
||||||
|
env:
|
||||||
|
KEYSTORE_FILE_BASE64: ${{ secrets.ANDROID_DEBUG_KEYSTORE_FILE_BASE64 }}
|
||||||
|
KEYSTORE_PROPERTIES_FILE_BASE64: ${{ secrets.ANDROID_DEBUG_KEYSTORE_PROPERTIES_FILE_BASE64 }}
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
cd os/android
|
||||||
|
./gradlew assembleDebug
|
||||||
|
|
||||||
|
- name: Upload
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: fallout2-ce-debug.apk
|
||||||
|
path: os/android/app/build/outputs/apk/debug/app-debug.apk
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
linux:
|
linux:
|
||||||
name: Linux (${{ matrix.arch }})
|
name: Linux (${{ matrix.arch }})
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,51 @@ defaults:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
android:
|
||||||
|
name: Android
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Clone
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v2
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: 11
|
||||||
|
cache: gradle
|
||||||
|
|
||||||
|
- name: Cache cmake build
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: os/android/app/.cxx
|
||||||
|
key: android-cmake-v1
|
||||||
|
|
||||||
|
- name: Setup signing config
|
||||||
|
run: |
|
||||||
|
cd os/android
|
||||||
|
echo "$KEYSTORE_FILE_BASE64" | base64 --decode > release.keystore
|
||||||
|
echo "$KEYSTORE_PROPERTIES_FILE_BASE64" | base64 --decode > release-keystore.properties
|
||||||
|
env:
|
||||||
|
KEYSTORE_FILE_BASE64: ${{ secrets.ANDROID_RELEASE_KEYSTORE_FILE_BASE64 }}
|
||||||
|
KEYSTORE_PROPERTIES_FILE_BASE64: ${{ secrets.ANDROID_RELEASE_KEYSTORE_PROPERTIES_FILE_BASE64 }}
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
cd os/android
|
||||||
|
./gradlew assembleRelease
|
||||||
|
|
||||||
|
- name: Upload
|
||||||
|
run: |
|
||||||
|
cd os/android/app/build/outputs/apk/release
|
||||||
|
cp app-release.apk fallout2-ce-android.apk
|
||||||
|
gh release upload ${{ github.ref_name }} fallout2-ce-android.apk
|
||||||
|
rm fallout2-ce-android.apk
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
linux:
|
linux:
|
||||||
name: Linux (${{ matrix.arch }})
|
name: Linux (${{ matrix.arch }})
|
||||||
|
|
||||||
|
|
14
README.md
14
README.md
|
@ -34,6 +34,20 @@ $ sudo apt install libsdl2-2.0-0
|
||||||
|
|
||||||
- Run `fallout2-ce.app`.
|
- Run `fallout2-ce.app`.
|
||||||
|
|
||||||
|
### Android
|
||||||
|
|
||||||
|
> **NOTE**: Fallout 2 was designed with mouse in mind. There are many controls that require precise cursor positioning, which is not possible with fingers. When playing on Android you'll use fingers to move mouse cursor, not a character, or a map. Double tap to "click" left mouse button in the current cursor position, triple tap to "click" right mouse button. It might feel awkward at first, but it's super handy - you can play with just a thumb. This is not set in stone and might change in the future.
|
||||||
|
|
||||||
|
- Download `fallout2-ce.apk` and copy it to your device. Open it with file explorer, follow instructions (install from unknown source).
|
||||||
|
|
||||||
|
- Run the game once, it will say `Couldn't find/load text fonts` and create a folder for data assets.
|
||||||
|
|
||||||
|
- Open file explorer, navigate to `Android/data/com.alexbatalov.fallout2ce/files`, delete junk folders inside (they will be named as game files, just delete them).
|
||||||
|
|
||||||
|
- Copy `master.dat`, `critter.dat`, `patch000.dat` and `data` folder from your Windows/macOS installation to the folder above.
|
||||||
|
|
||||||
|
- Run the game again.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Integrating Sfall goodies is the top priority. Quality of life updates are OK too. Please no large scale refactorings at this time as we need to reconcile changes from Reference Edition, which will make this process slow and error-prone. In any case open up an issue with your suggestion or to notify other people that something is being worked on.
|
Integrating Sfall goodies is the top priority. Quality of life updates are OK too. Please no large scale refactorings at this time as we need to reconcile changes from Reference Edition, which will make this process slow and error-prone. In any case open up an issue with your suggestion or to notify other people that something is being worked on.
|
||||||
|
|
|
@ -7,3 +7,7 @@
|
||||||
/.idea/navEditor.xml
|
/.idea/navEditor.xml
|
||||||
/.idea/workspace.xml
|
/.idea/workspace.xml
|
||||||
/local.properties
|
/local.properties
|
||||||
|
/debug-keystore.properties
|
||||||
|
/debug.keystore
|
||||||
|
/release-keystore.properties
|
||||||
|
/release.keystore
|
||||||
|
|
|
@ -1,2 +1,5 @@
|
||||||
/.cxx
|
/.cxx
|
||||||
/build
|
/build
|
||||||
|
|
||||||
|
# TODO: Cleanup root .gitignore
|
||||||
|
!/src/debug
|
||||||
|
|
|
@ -26,10 +26,49 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
// Override default debug signing config to make sure every CI runner
|
||||||
|
// uses the same key for painless updates.
|
||||||
|
def debugKeystorePropertiesFile = rootProject.file('debug-keystore.properties')
|
||||||
|
if (debugKeystorePropertiesFile.exists()) {
|
||||||
|
def debugKeystoreProperties = new Properties()
|
||||||
|
debugKeystoreProperties.load(new FileInputStream(debugKeystorePropertiesFile))
|
||||||
|
|
||||||
|
debug {
|
||||||
|
storeFile rootProject.file(debugKeystoreProperties.getProperty('storeFile'))
|
||||||
|
storePassword debugKeystoreProperties.getProperty('storePassword')
|
||||||
|
keyAlias debugKeystoreProperties.getProperty('keyAlias')
|
||||||
|
keyPassword debugKeystoreProperties.getProperty('keyPassword')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def releaseKeystoreProperties = new Properties()
|
||||||
|
def releaseKeystorePropertiesFile = rootProject.file('release-keystore.properties')
|
||||||
|
if (releaseKeystorePropertiesFile.exists()) {
|
||||||
|
releaseKeystoreProperties.load(new FileInputStream(releaseKeystorePropertiesFile))
|
||||||
|
|
||||||
|
release {
|
||||||
|
storeFile rootProject.file(releaseKeystoreProperties.getProperty('storeFile'))
|
||||||
|
storePassword releaseKeystoreProperties.getProperty('storePassword')
|
||||||
|
keyAlias releaseKeystoreProperties.getProperty('keyAlias')
|
||||||
|
keyPassword releaseKeystoreProperties.getProperty('keyPassword')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
debug {
|
||||||
|
// Prevents signing keys clashes between debug and release versions
|
||||||
|
// for painless development.
|
||||||
|
applicationIdSuffix '.debug'
|
||||||
|
}
|
||||||
release {
|
release {
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
|
||||||
|
// Release signing config is optional and might not be present in CI
|
||||||
|
// builds, hence `findByName`.
|
||||||
|
signingConfig signingConfigs.findByName('release')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Fallout 2 (Debug)</string>
|
||||||
|
</resources>
|
|
@ -1,3 +1,3 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Fallout 2 Community Edition</string>
|
<string name="app_name">Fallout 2</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1272,6 +1272,11 @@ void _GNW95_process_message()
|
||||||
// The data is accumulated in SDL itself and will be processed
|
// The data is accumulated in SDL itself and will be processed
|
||||||
// in `_mouse_info`.
|
// in `_mouse_info`.
|
||||||
break;
|
break;
|
||||||
|
case SDL_FINGERDOWN:
|
||||||
|
case SDL_FINGERMOTION:
|
||||||
|
case SDL_FINGERUP:
|
||||||
|
handleTouchFingerEvent(&(e.tfinger));
|
||||||
|
break;
|
||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
case SDL_KEYUP:
|
case SDL_KEYUP:
|
||||||
if (!keyboardIsDisabled()) {
|
if (!keyboardIsDisabled()) {
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
#include "dinput.h"
|
#include "dinput.h"
|
||||||
|
|
||||||
#include <SDL.h>
|
static int gTouchMouseLastX = 0;
|
||||||
|
static int gTouchMouseLastY = 0;
|
||||||
|
static int gTouchMouseDeltaX = 0;
|
||||||
|
static int gTouchMouseDeltaY = 0;
|
||||||
|
|
||||||
|
static int gTouchFingers = 0;
|
||||||
|
static unsigned int gTouchGestureLastTouchDownTimestamp = 0;
|
||||||
|
static unsigned int gTouchGestureLastTouchUpTimestamp = 0;
|
||||||
|
static int gTouchGestureTaps = 0;
|
||||||
|
static bool gTouchGestureHandled = false;
|
||||||
|
|
||||||
|
extern int screenGetWidth();
|
||||||
|
extern int screenGetHeight();
|
||||||
|
|
||||||
// 0x4E0400
|
// 0x4E0400
|
||||||
bool directInputInit()
|
bool directInputInit()
|
||||||
|
@ -47,9 +59,42 @@ bool mouseDeviceUnacquire()
|
||||||
// 0x4E053C
|
// 0x4E053C
|
||||||
bool mouseDeviceGetData(MouseData* mouseState)
|
bool mouseDeviceGetData(MouseData* mouseState)
|
||||||
{
|
{
|
||||||
|
#if __ANDROID__
|
||||||
|
mouseState->x = gTouchMouseDeltaX;
|
||||||
|
mouseState->y = gTouchMouseDeltaY;
|
||||||
|
mouseState->buttons[0] = 0;
|
||||||
|
mouseState->buttons[1] = 0;
|
||||||
|
gTouchMouseDeltaX = 0;
|
||||||
|
gTouchMouseDeltaY = 0;
|
||||||
|
|
||||||
|
if (gTouchFingers == 0) {
|
||||||
|
if (SDL_GetTicks() - gTouchGestureLastTouchUpTimestamp > 150) {
|
||||||
|
if (!gTouchGestureHandled) {
|
||||||
|
if (gTouchGestureTaps == 2) {
|
||||||
|
mouseState->buttons[0] = 1;
|
||||||
|
gTouchGestureHandled = true;
|
||||||
|
} else if (gTouchGestureTaps == 3) {
|
||||||
|
mouseState->buttons[1] = 1;
|
||||||
|
gTouchGestureHandled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (gTouchFingers == 1) {
|
||||||
|
if (SDL_GetTicks() - gTouchGestureLastTouchDownTimestamp > 150) {
|
||||||
|
if (gTouchGestureTaps == 1) {
|
||||||
|
mouseState->buttons[0] = 1;
|
||||||
|
gTouchGestureHandled = true;
|
||||||
|
} else if (gTouchGestureTaps == 2) {
|
||||||
|
mouseState->buttons[1] = 1;
|
||||||
|
gTouchGestureHandled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
Uint32 buttons = SDL_GetRelativeMouseState(&(mouseState->x), &(mouseState->y));
|
Uint32 buttons = SDL_GetRelativeMouseState(&(mouseState->x), &(mouseState->y));
|
||||||
mouseState->buttons[0] = (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0;
|
mouseState->buttons[0] = (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0;
|
||||||
mouseState->buttons[1] = (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
|
mouseState->buttons[1] = (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -100,3 +145,44 @@ bool keyboardDeviceInit()
|
||||||
void keyboardDeviceFree()
|
void keyboardDeviceFree()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleTouchFingerEvent(SDL_TouchFingerEvent* event)
|
||||||
|
{
|
||||||
|
int windowWidth = screenGetWidth();
|
||||||
|
int windowHeight = screenGetHeight();
|
||||||
|
|
||||||
|
if (event->type == SDL_FINGERDOWN) {
|
||||||
|
gTouchFingers++;
|
||||||
|
|
||||||
|
gTouchMouseLastX = (int)(event->x * windowWidth);
|
||||||
|
gTouchMouseLastY = (int)(event->y * windowHeight);
|
||||||
|
gTouchMouseDeltaX = 0;
|
||||||
|
gTouchMouseDeltaY = 0;
|
||||||
|
|
||||||
|
if (event->timestamp - gTouchGestureLastTouchDownTimestamp > 250) {
|
||||||
|
gTouchGestureTaps = 0;
|
||||||
|
gTouchGestureHandled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gTouchGestureLastTouchDownTimestamp = event->timestamp;
|
||||||
|
} else if (event->type == SDL_FINGERMOTION) {
|
||||||
|
int prevX = gTouchMouseLastX;
|
||||||
|
int prevY = gTouchMouseLastY;
|
||||||
|
gTouchMouseLastX = (int)(event->x * windowWidth);
|
||||||
|
gTouchMouseLastY = (int)(event->y * windowHeight);
|
||||||
|
gTouchMouseDeltaX += gTouchMouseLastX - prevX;
|
||||||
|
gTouchMouseDeltaY += gTouchMouseLastY - prevY;
|
||||||
|
} else if (event->type == SDL_FINGERUP) {
|
||||||
|
gTouchFingers--;
|
||||||
|
|
||||||
|
int prevX = gTouchMouseLastX;
|
||||||
|
int prevY = gTouchMouseLastY;
|
||||||
|
gTouchMouseLastX = (int)(event->x * windowWidth);
|
||||||
|
gTouchMouseLastY = (int)(event->y * windowHeight);
|
||||||
|
gTouchMouseDeltaX += gTouchMouseLastX - prevX;
|
||||||
|
gTouchMouseDeltaY += gTouchMouseLastY - prevY;
|
||||||
|
|
||||||
|
gTouchGestureTaps++;
|
||||||
|
gTouchGestureLastTouchUpTimestamp = event->timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef DINPUT_H
|
#ifndef DINPUT_H
|
||||||
#define DINPUT_H
|
#define DINPUT_H
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
typedef struct MouseData {
|
typedef struct MouseData {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
|
@ -26,4 +28,6 @@ void mouseDeviceFree();
|
||||||
bool keyboardDeviceInit();
|
bool keyboardDeviceInit();
|
||||||
void keyboardDeviceFree();
|
void keyboardDeviceFree();
|
||||||
|
|
||||||
|
void handleTouchFingerEvent(SDL_TouchFingerEvent* event);
|
||||||
|
|
||||||
#endif /* DINPUT_H */
|
#endif /* DINPUT_H */
|
||||||
|
|
|
@ -40,6 +40,8 @@ int main(int argc, char* argv[])
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if __ANDROID__
|
#if __ANDROID__
|
||||||
|
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
|
||||||
|
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
|
||||||
chdir(SDL_AndroidGetExternalStoragePath());
|
chdir(SDL_AndroidGetExternalStoragePath());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue