1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2024-10-31 19:44:20 +00:00

new android port

This commit is contained in:
Alula 2021-02-24 09:28:47 +01:00
parent 976281a887
commit 319289ca67
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
74 changed files with 5527 additions and 438 deletions

View file

@ -4,27 +4,8 @@ edition = "2018"
name = "doukutsu-rs" name = "doukutsu-rs"
version = "0.1.0" version = "0.1.0"
#[lib] [lib]
#crate-type = ["lib", "cdylib"] crate-type = ["lib", "cdylib"]
[package.metadata.android]
android_version = 29
target_sdk_version = 28
min_sdk_version = 26
build_targets = ["aarch64-linux-android"]
package_name = "io.github.doukutsu_rs.android"
apk_label = "doukutsu-rs"
opengles_version = [3, 1]
fullscreen = true
orientation = "sensorLandscape"
permission = [
{ name = "android.permission.READ_EXTERNAL_STORAGE" },
{ name = "android.permission.WRITE_EXTERNAL_STORAGE" }
]
application_metadatas = [
{ name = "android:hardwareAccelerated", value = "true" },
{ name = "android:requestLegacyExternalStorage", value = "true" }
]
[profile.release] [profile.release]
lto = 'thin' lto = 'thin'
@ -43,20 +24,22 @@ opt-level = 1
default = ["scripting", "backend-sdl", "ogg-playback"] default = ["scripting", "backend-sdl", "ogg-playback"]
ogg-playback = ["lewton"] ogg-playback = ["lewton"]
backend-sdl = ["sdl2"] backend-sdl = ["sdl2"]
backend-gfx = ["winit", "imgui-gfx-renderer", "imgui-winit-support"] #backend-sokol = ["sokol"]
backend-glutin = ["winit", "glutin"]
scripting = ["lua-ffi"] scripting = ["lua-ffi"]
editor = [] editor = []
[dependencies] [dependencies]
#cpal = { path = "./3rdparty/cpal" }
#glutin = { path = "./3rdparty/glutin/glutin", optional = true }
bitvec = "0.20" bitvec = "0.20"
byteorder = "1.4" byteorder = "1.4"
case_insensitive_hashmap = "1.0.0" case_insensitive_hashmap = "1.0.0"
chrono = "0.4" chrono = "0.4"
cpal = "0.13" cpal = { git = "https://github.com/doukutsu-rs/cpal.git", rev = "e027550be0b93b7e2912c7de28a4944a7d04e070" }
directories = "3" directories = "3"
glutin = { git = "https://github.com/doukutsu-rs/glutin.git", rev = "a34ee3c99b3c999b638ca2bae53cf96df2b94c04", optional = true }
imgui = "0.7.0" imgui = "0.7.0"
imgui-gfx-renderer = { version = "0.7.0", optional = true }
imgui-winit-support = { version = "0.7.0", default-features = false, features = ["winit-24"], optional = true }
image = { version = "0.23", default-features = false, features = ["png", "bmp"] } image = { version = "0.23", default-features = false, features = ["png", "bmp"] }
itertools = "0.10" itertools = "0.10"
lazy_static = "1.4.0" lazy_static = "1.4.0"
@ -69,6 +52,7 @@ num-traits = "0.2.12"
paste = "1.0.0" paste = "1.0.0"
pretty_env_logger = "0.4.0" pretty_env_logger = "0.4.0"
sdl2 = { version = "0.34", optional = true, features = ["unsafe_textures", "bundled", "static-link"] } sdl2 = { version = "0.34", optional = true, features = ["unsafe_textures", "bundled", "static-link"] }
#sokol = { git = "https://github.com/doukutsu-rs/sokol-rs.git", rev = "", optional = true }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_derive = "1" serde_derive = "1"
serde_yaml = "0.8" serde_yaml = "0.8"
@ -77,12 +61,16 @@ strum_macros = "0.20"
# remove and replace when drain_filter is in stable # remove and replace when drain_filter is in stable
vec_mut_scan = "0.4" vec_mut_scan = "0.4"
webbrowser = "0.5.5" webbrowser = "0.5.5"
winit = { version = "0.24", features = ["serde"], optional = true } winit = { version = "0.24", optional = true }
#[build-dependencies]
#gl_generator = { version = "0.14.0", optional = true }
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["winuser"] } winapi = { version = "0.3", features = ["winuser"] }
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]
ndk = "0.3" ndk = "0.2"
ndk-glue = "0.3" ndk-glue = "0.2"
ndk-sys = "0.2"
jni = "0.19" jni = "0.19"

233
app/.gitignore vendored Normal file
View file

@ -0,0 +1,233 @@
# Created by https://www.toptal.com/developers/gitignore/api/androidstudio,gradle,android
# Edit at https://www.toptal.com/developers/gitignore?templates=androidstudio,gradle,android
### Android ###
# Built application files
*.apk
*.aar
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# release/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
### Android Patch ###
gen-external-apklibs
output.json
# Replacement of .externalNativeBuild directories introduced
# with Android Studio 3.5.
### Gradle ###
.gradle
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Cache of project
.gradletasknamecache
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
### Gradle Patch ###
**/build/
### AndroidStudio ###
# Covers files to be ignored for android development using Android Studio.
# Built application files
# Files for the ART/Dalvik VM
# Java class files
# Generated files
# Gradle files
# Signing files
.signing/
# Local configuration file (sdk path, etc)
# Proguard folder generated by Eclipse
# Log Files
# Android Studio
/*/build/
/*/local.properties
/*/out
/*/*/build
/*/*/production
*.ipr
*~
*.swp
# Keystore files
*.jks
*.keystore
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Android Patch
# External native build folder generated in Android Studio 2.2 and later
# NDK
obj/
*.so
# IntelliJ IDEA
*.iws
/out/
# User-specific configurations
.idea/caches/
.idea/libraries/
.idea/shelf/
.idea/.name
.idea/compiler.xml
.idea/copyright/profiles_settings.xml
.idea/encodings.xml
.idea/misc.xml
.idea/scopes/scope_settings.xml
.idea/vcs.xml
.idea/jsLibraryMappings.xml
.idea/datasources.xml
.idea/dataSources.ids
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
.idea/jarRepositories.xml
# OS-specific files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Legacy Eclipse project files
.classpath
.project
.cproject
.settings/
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.war
*.ear
# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
hs_err_pid*
## Plugin-specific files:
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Mongo Explorer plugin
.idea/mongoSettings.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### AndroidStudio Patch ###
!/gradle/wrapper/gradle-wrapper.jar
# End of https://www.toptal.com/developers/gitignore/api/androidstudio,gradle,android

1
app/app/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

99
app/app/build.gradle Normal file
View file

@ -0,0 +1,99 @@
plugins {
id 'com.android.application'
id 'com.github.willir.rust.cargo-ndk-android'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
//ndkVersion "21.3.6528147"
defaultConfig {
applicationId "io.github.doukutsu_rs"
minSdkVersion 24
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
abiFilters 'x86', 'arm64-v8a'
}
externalNativeBuild {
cmake {
arguments "-DANDROID_STL=c++_shared"
}
}
def documentsAuthorityValue = applicationId + ".documents"
manifestPlaceholders =
[documentsAuthority: documentsAuthorityValue]
buildConfigField "String",
"DOCUMENTS_AUTHORITY",
"\"${documentsAuthorityValue}\""
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildFeatures {
viewBinding true
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
}
dependencies {
implementation 'com.android.support:support-annotations:28.0.0'
}
println("cargo target: ${project.buildDir.getAbsolutePath()}/rust-target")
cargoNdk {
targets = [
"x86",
"arm64"
]
librariesNames = ["libdoukutsu_rs.so"]
//targetDirectory = "${project.buildDir.getAbsolutePath()}/rust-target"
module = "../"
extraCargoEnv = ["ANDROID_NDK_HOME": android.ndkDirectory]
extraCargoBuildArguments = ["--no-default-features", "--features", "backend-glutin ogg-playback scripting"]
verbose = true
buildTypes {
release {
buildType = "release"
}
debug {
buildType = "debug"
}
}
}
tasks.whenTaskAdded { task ->
if (task.name == 'javaPreCompileDebug') {
task.dependsOn 'buildCargoNdkDebug'
}
if (task.name == 'javaPreCompileRelease') {
task.dependsOn 'buildCargoNdkRelease'
}
}

21
app/app/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.github.doukutsu_rs">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Doukutsurs">
<provider
android:name=".DoukutsuDocumentsProvider"
android:authorities="${documentsAuthority}"
android:exported="true"
android:grantUriPermissions="true"
android:permission="android.permission.MANAGE_DOCUMENTS">
<intent-filter>
<action
android:name="android.content.action.DOCUMENTS_PROVIDER"/>
</intent-filter>
</provider>
<activity
android:name="io.github.doukutsu_rs.MainActivity"
android:label="doukutsu-rs"
android:screenOrientation="sensorLandscape"
android:launchMode="standard"
android:configChanges="orientation|keyboardHidden|screenSize">
<meta-data android:name="android.app.lib_name" android:value="doukutsu_rs" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,53 @@
# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.
cmake_minimum_required(VERSION 3.10)
# Copy shared STL files to Android Studio output directory so they can be
# packaged in the APK.
# Usage:
#
# find_package(ndk-stl REQUIRED)
#
# or
#
# find_package(ndk-stl REQUIRED PATHS ".")
if(NOT ${ANDROID_STL} MATCHES "_shared")
return()
endif()
function(configure_shared_stl lib_path so_base)
message("Configuring STL ${so_base} for ${ANDROID_ABI}")
configure_file(
"${ANDROID_NDK}/sources/cxx-stl/${lib_path}/libs/${ANDROID_ABI}/lib${so_base}.so"
"${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/lib${so_base}.so"
COPYONLY)
endfunction()
if("${ANDROID_STL}" STREQUAL "libstdc++")
# The default minimal system C++ runtime library.
elseif("${ANDROID_STL}" STREQUAL "gabi++_shared")
# The GAbi++ runtime (shared).
message(FATAL_ERROR "gabi++_shared was not configured by ndk-stl package")
elseif("${ANDROID_STL}" STREQUAL "stlport_shared")
# The STLport runtime (shared).
configure_shared_stl("stlport" "stlport_shared")
elseif("${ANDROID_STL}" STREQUAL "gnustl_shared")
# The GNU STL (shared).
configure_shared_stl("gnu-libstdc++/4.9" "gnustl_shared")
elseif("${ANDROID_STL}" STREQUAL "c++_shared")
# The LLVM libc++ runtime (static).
configure_shared_stl("llvm-libc++" "c++_shared")
else()
message(FATAL_ERROR "STL configuration ANDROID_STL=${ANDROID_STL} is not supported")
endif()
# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add_library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.
#add_library(dummy SHARED dummy.cpp)

View file

@ -0,0 +1,2 @@
void drs_dummy_export() {
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View file

@ -0,0 +1,279 @@
package io.github.doukutsu_rs;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsProvider;
import android.util.Log;
import android.webkit.MimeTypeMap;
import androidx.annotation.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
public class DoukutsuDocumentsProvider extends DocumentsProvider {
private final static String[] DEFAULT_ROOT_PROJECTION =
new String[]{
Root.COLUMN_DOCUMENT_ID,
Root.COLUMN_ROOT_ID,
Root.COLUMN_ICON,
Root.COLUMN_TITLE,
Root.COLUMN_MIME_TYPES,
Root.COLUMN_AVAILABLE_BYTES,
Root.COLUMN_FLAGS
};
private final static String[] DEFAULT_DOCUMENT_PROJECTION =
new String[]{
Document.COLUMN_DOCUMENT_ID,
Document.COLUMN_DISPLAY_NAME,
Document.COLUMN_SIZE,
Document.COLUMN_LAST_MODIFIED,
Document.COLUMN_MIME_TYPE,
Document.COLUMN_FLAGS
};
@Override
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
File file = getContext().getFilesDir();
String id = file.getAbsolutePath();
Log.d(DoukutsuDocumentsProvider.class.getName(), "files dir location: " + id);
MatrixCursor result = new MatrixCursor(projection != null ?
projection : DEFAULT_ROOT_PROJECTION);
RowBuilder row = result.newRow();
row.add(Root.COLUMN_DOCUMENT_ID, id);
row.add(Root.COLUMN_ROOT_ID, id);
row.add(Root.COLUMN_ICON, R.mipmap.ic_launcher);
row.add(Root.COLUMN_TITLE,
getContext().getString(R.string.document_provider_name));
row.add(Root.COLUMN_MIME_TYPES, "*/*");
row.add(Root.COLUMN_AVAILABLE_BYTES, file.getFreeSpace());
row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
return result;
}
@Override
public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException {
MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
Log.d("dupa", "queryDocument: " + documentId);
pushFile(result, new File(documentId));
return result;
}
@Override
public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException {
MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
File root = new File(parentDocumentId);
Log.d("dupa", "doc id:" + parentDocumentId);
if (!root.exists()) {
Log.d("dupa", "no such file");
throw new FileNotFoundException("No such file: " + root.getAbsolutePath());
}
if (!root.isDirectory()) {
Log.d("dupa", "not a directory");
return null;
}
File[] files = root.listFiles();
if (files != null) {
for (File file : files) {
pushFile(result, file);
}
}
return result;
}
@Override
public ParcelFileDescriptor openDocument(String documentId, String mode, @Nullable CancellationSignal signal) throws FileNotFoundException {
File file = new File(documentId);
int imode = ParcelFileDescriptor.parseMode(mode);
return ParcelFileDescriptor.open(file, imode);
}
@Override
public String createDocument(String parentDocumentId, String mimeType, String displayName) throws FileNotFoundException {
File file = new File(parentDocumentId, displayName);
if (file.exists()) {
int nextId = 1;
while (file.exists()) {
// maybe let's put the id before extension?
file = new File(parentDocumentId, String.format("%s (%d)", displayName, nextId));
++nextId;
}
}
try {
if (mimeType != null && mimeType.equals(Document.MIME_TYPE_DIR)) {
if (!file.mkdir()) {
throw new FileNotFoundException("Couldn't create directory: " + file.getAbsolutePath());
}
} else {
if (!file.createNewFile()) {
throw new FileNotFoundException("Couldn't create file: " + file.getAbsolutePath());
}
}
} catch (IOException e) {
throw new FileNotFoundException("Couldn't create file: " + e.getMessage());
}
return file.getAbsolutePath();
}
@Override
public void deleteDocument(String documentId) throws FileNotFoundException {
File file = new File(documentId);
if (!file.exists()) {
throw new FileNotFoundException("Couldn't find file: " + file.getAbsolutePath());
}
deleteRecursive(file);
// todo refresh this shit
// getContext().getContentResolver().refresh()
}
@Override
public String getDocumentType(String documentId) throws FileNotFoundException {
File file = new File(documentId);
if (!file.exists()) {
throw new FileNotFoundException("Couldn't find file: " + file.getAbsolutePath());
} else if (file.isDirectory()) {
return Document.MIME_TYPE_DIR;
} else if (file.isFile()) {
return getMimeType(file.getAbsolutePath());
}
return null;
}
@Override
public boolean onCreate() {
return true;
}
@Override
public boolean isChildDocument(String parentDocumentId, String documentId) {
return documentId.startsWith(parentDocumentId);
}
@Override
public String renameDocument(String documentId, String displayName) throws FileNotFoundException {
File file = new File(documentId);
if (!file.exists()) {
throw new FileNotFoundException("Couldn't find file: " + file.getAbsolutePath());
}
File newPath = new File(file.getParentFile().getAbsolutePath() + "/" + displayName);
try {
Files.move(file.toPath(), newPath.toPath());
} catch (IOException e) {
throw new FileNotFoundException(e.getMessage());
}
return newPath.getAbsolutePath();
}
@Override
public void removeDocument(String documentId, String parentDocumentId) throws FileNotFoundException {
deleteDocument(documentId);
}
private static void deleteRecursive(File file) {
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
if (!Files.isSymbolicLink(f.toPath())) {
deleteRecursive(f);
}
}
}
}
file.delete();
}
private static String getMimeType(String url) {
String type = null;
String extension = MimeTypeMap.getFileExtensionFromUrl(url.toLowerCase());
if (extension != null) {
switch (extension) {
case "pbm":
type = "image/bmp";
break;
case "yml":
type = "text/x-yaml";
break;
default:
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
break;
}
}
if (type == null) {
type = "application/octet-stream";
}
return type;
}
private void pushFile(MatrixCursor result, File file) throws FileNotFoundException {
if (!file.exists()) {
throw new FileNotFoundException("Couldn't find file: " + file.getAbsolutePath());
}
String mimeType = "application/octet-stream";
int flags = 0;
if (file.isDirectory()) {
mimeType = Document.MIME_TYPE_DIR;
if (file.canWrite()) {
flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
}
} else if (file.isFile()) {
mimeType = getMimeType(file.getAbsolutePath());
if (file.canWrite()) {
flags |= Document.FLAG_SUPPORTS_WRITE;
}
}
if (file.getParentFile().canWrite()) {
flags |= Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_RENAME;
}
RowBuilder row = result.newRow();
row.add(Document.COLUMN_DOCUMENT_ID, file.getAbsolutePath());
row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
row.add(Document.COLUMN_SIZE, file.length());
row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
row.add(Document.COLUMN_FLAGS, flags);
row.add(Document.COLUMN_MIME_TYPE, mimeType);
row.add(Document.COLUMN_ICON, R.mipmap.ic_launcher);
}
}

View file

@ -0,0 +1,76 @@
package io.github.doukutsu_rs;
import android.app.NativeActivity;
import android.content.res.Configuration;
import android.hardware.SensorManager;
import android.os.Build;
import android.os.Bundle;
import android.view.OrientationEventListener;
import android.view.WindowInsets;
import static android.os.Build.VERSION.SDK_INT;
public class MainActivity extends NativeActivity {
private int[] displayInsets = new int[]{0, 0, 0, 0};
private OrientationEventListener listener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
listener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_UI) {
@Override
public void onOrientationChanged(int orientation) {
MainActivity.this.updateCutouts();
}
};
if (listener.canDetectOrientation()) {
listener.enable();
} else {
listener = null;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (listener != null) {
listener.disable();
listener = null;
}
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
this.updateCutouts();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
this.updateCutouts();
}
private void updateCutouts() {
if (SDK_INT >= Build.VERSION_CODES.P) {
WindowInsets insets = getWindow().getDecorView().getRootWindowInsets();
if (insets != null) {
android.view.DisplayCutout cutout = insets.getDisplayCutout();
if (cutout != null) {
this.displayInsets[0] = cutout.getSafeInsetLeft();
this.displayInsets[1] = cutout.getSafeInsetTop();
this.displayInsets[2] = cutout.getSafeInsetRight();
this.displayInsets[3] = cutout.getSafeInsetBottom();
}
}
}
}
}

View file

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View file

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Doukutsurs" parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
<item name="android:windowLayoutInDisplayCutoutMode">
shortEdges
</item>
</style>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#8DA5F8</color>
</resources>

View file

@ -0,0 +1,4 @@
<resources>
<string name="app_name">doukutsu-rs</string>
<string name="document_provider_name">doukutsu-rs game data</string>
</resources>

View file

@ -0,0 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.Doukutsurs" parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
</style>
</resources>

28
app/build.gradle Normal file
View file

@ -0,0 +1,28 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.2"
classpath "gradle.plugin.com.github.willir.rust:plugin:0.3.4"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

17
app/gradle.properties Normal file
View file

@ -0,0 +1,17 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true

BIN
app/gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,6 @@
#Wed Feb 17 23:16:31 CET 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

172
app/gradlew vendored Executable file
View file

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
app/gradlew.bat vendored Normal file
View file

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

2
app/settings.gradle Normal file
View file

@ -0,0 +1,2 @@
rootProject.name = "doukutsu-rs"
include ':app'

28
build.rs Normal file
View file

@ -0,0 +1,28 @@
use std::env;
use std::fs::File;
use std::path::PathBuf;
// #[cfg(feature = "generate-gl")]
// use gl_generator::{Api, Fallbacks, Profile, Registry};
fn main() {
// let dest = PathBuf::from(&env::var("OUT_DIR").unwrap());
let target = env::var("TARGET").unwrap_or_else(|e| panic!(e));
let is_android = cfg!(target_os = "android") || (cfg!(target_os = "linux") && target.contains("android")); // hack
println!("cargo:rerun-if-changed=build.rs");
//
// #[cfg(feature = "generate-gl")]
// {
// let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap();
//
// Registry::new(Api::Gles2, (3, 0), Profile::Core, Fallbacks::All, [])
// .write_bindings(gl_generator::StructGenerator, &mut file)
// .unwrap();
// }
if is_android {
println!("cargo:rustc-link-lib=dylib=GLESv2");
println!("cargo:rustc-link-lib=dylib=EGL");
}
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,6 @@
#Thu Feb 18 11:02:22 CET 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

172
gradlew vendored Normal file
View file

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored Normal file
View file

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View file

@ -0,0 +1,8 @@
#version 330
varying vec4 color;
varying vec4 frag_color;
void main() {
frag_color = color;
}

View file

@ -1,27 +0,0 @@
#version 150 core
in vec2 a_Pos;
in vec2 a_Uv;
in vec4 a_Src;
in vec4 a_TCol1;
in vec4 a_TCol2;
in vec4 a_TCol3;
in vec4 a_TCol4;
in vec4 a_Color;
layout (std140) uniform Globals {
mat4 u_MVP;
};
out vec2 v_Uv;
out vec4 v_Color;
void main() {
v_Uv = a_Uv * a_Src.zw + a_Src.xy;
v_Color = a_Color;
mat4 instance_transform = mat4(a_TCol1, a_TCol2, a_TCol3, a_TCol4);
vec4 position = instance_transform * vec4(a_Pos, 0.0, 1.0);
gl_Position = u_MVP * position;
}

View file

@ -1,13 +0,0 @@
#version 300 es
uniform mediump sampler2D t_Texture;
varying mediump vec2 v_Uv;
varying mediump vec4 v_Color;
//uniform mediump mat4 u_MVP;
mediump vec4 Target0;
void main() {
gl_FragColor = texture2D(t_Texture, v_Uv) * v_Color;
}

View file

@ -1,26 +0,0 @@
#version 300 es
attribute mediump vec2 a_Pos;
attribute mediump vec2 a_Uv;
attribute mediump vec4 a_VertColor;
attribute mediump vec4 a_Src;
attribute mediump vec4 a_TCol1;
attribute mediump vec4 a_TCol2;
attribute mediump vec4 a_TCol3;
attribute mediump vec4 a_TCol4;
attribute mediump vec4 a_Color;
uniform mediump mat4 u_MVP;
varying mediump vec2 v_Uv;
varying mediump vec4 v_Color;
void main() {
v_Uv = a_Uv * a_Src.zw + a_Src.xy;
v_Color = a_Color * a_VertColor;
mat4 instance_transform = mat4(a_TCol1, a_TCol2, a_TCol3, a_TCol4);
vec4 position = instance_transform * vec4(a_Pos, 0.0, 1.0);
gl_Position = u_MVP * position;
}

View file

@ -1,27 +0,0 @@
#version 300 es
in vec2 a_Pos;
in vec2 a_Uv;
in vec4 a_Src;
in vec4 a_TCol1;
in vec4 a_TCol2;
in vec4 a_TCol3;
in vec4 a_TCol4;
in vec4 a_Color;
layout (std140) uniform Globals {
mat4 u_MVP;
};
out vec2 v_Uv;
out vec4 v_Color;
void main() {
v_Uv = a_Uv * a_Src.zw + a_Src.xy;
v_Color = a_Color;
mat4 instance_transform = mat4(a_TCol1, a_TCol2, a_TCol3, a_TCol4);
vec4 position = instance_transform * vec4(a_Pos, 0.0, 1.0);
gl_Position = u_MVP * position;
}

View file

@ -113,10 +113,10 @@ impl BuiltinFS {
FSNode::File("organya-wavetable-doukutsu.bin", include_bytes!("builtin/organya-wavetable-doukutsu.bin")), FSNode::File("organya-wavetable-doukutsu.bin", include_bytes!("builtin/organya-wavetable-doukutsu.bin")),
FSNode::File("touch.png", include_bytes!("builtin/touch.png")), FSNode::File("touch.png", include_bytes!("builtin/touch.png")),
FSNode::Directory("shaders", vec![ FSNode::Directory("shaders", vec![
FSNode::File("basic_150.vert.glsl", include_bytes!("builtin/shaders/basic_150.vert.glsl")), // FSNode::File("basic_150.vert.glsl", include_bytes!("builtin/shaders/basic_150.vert.glsl")),
FSNode::File("water_150.frag.glsl", include_bytes!("builtin/shaders/water_150.frag.glsl")), // FSNode::File("water_150.frag.glsl", include_bytes!("builtin/shaders/water_150.frag.glsl")),
FSNode::File("basic_es300.vert.glsl", include_bytes!("builtin/shaders/basic_es300.vert.glsl")), // FSNode::File("basic_es300.vert.glsl", include_bytes!("builtin/shaders/basic_es300.vert.glsl")),
FSNode::File("water_es300.frag.glsl", include_bytes!("builtin/shaders/water_es300.frag.glsl")), // FSNode::File("water_es300.frag.glsl", include_bytes!("builtin/shaders/water_es300.frag.glsl")),
]), ]),
FSNode::Directory("lightmap", vec![ FSNode::Directory("lightmap", vec![
FSNode::File("spot.png", include_bytes!("builtin/lightmap/spot.png")), FSNode::File("spot.png", include_bytes!("builtin/lightmap/spot.png")),

View file

@ -1,10 +1,10 @@
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::common::Rect; use crate::common::Rect;
use crate::components::draw_common::{Alignment, draw_number}; use crate::components::draw_common::{draw_number, Alignment};
use crate::entity::GameEntity; use crate::entity::GameEntity;
use crate::frame::Frame; use crate::frame::Frame;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::graphics::screen_insets_scaled;
use crate::inventory::Inventory; use crate::inventory::Inventory;
use crate::player::Player; use crate::player::Player;
use crate::shared_game_state::SharedGameState; use crate::shared_game_state::SharedGameState;
@ -78,11 +78,7 @@ impl GameEntity<(&Player, &mut Inventory)> for HUD {
self.current_level = inventory.get_current_level() as usize; self.current_level = inventory.get_current_level() as usize;
for (a, slot) in self.weapon_types.iter_mut().enumerate() { for (a, slot) in self.weapon_types.iter_mut().enumerate() {
*slot = if let Some(weapon) = inventory.get_weapon(a) { *slot = if let Some(weapon) = inventory.get_weapon(a) { weapon.wtype as u8 } else { 0 };
weapon.wtype as u8
} else {
0
};
} }
// update health bar // update health bar
@ -163,13 +159,19 @@ impl GameEntity<(&Player, &mut Inventory)> for HUD {
return Ok(()); return Ok(());
} }
let (left, top, right, bottom) = screen_insets_scaled(ctx, state.scale);
// none // none
let weap_x = self.weapon_x_pos as f32; let weap_x = self.weapon_x_pos as f32;
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?; let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?;
let (bar_offset, num_offset, weapon_offset) = match self.alignment { let (bar_offset, num_offset, weapon_offset) = match self.alignment {
Alignment::Left => (0.0, 0.0, 0.0), Alignment::Left => (left, left, left),
Alignment::Right => (state.canvas_size.0 - 112.0, state.canvas_size.0 - 48.0, state.canvas_size.0 - 40.0), Alignment::Right => (
state.canvas_size.0 - 112.0 - right,
state.canvas_size.0 - 48.0 - right,
state.canvas_size.0 - 40.0 - right,
),
}; };
let air_offset = if self.has_player2 { let air_offset = if self.has_player2 {
50.0 * match self.alignment { 50.0 * match self.alignment {
@ -181,31 +183,24 @@ impl GameEntity<(&Player, &mut Inventory)> for HUD {
}; };
if self.max_ammo == 0 { if self.max_ammo == 0 {
batch.add_rect(bar_offset + weap_x + 48.0, 16.0, batch.add_rect(bar_offset + weap_x + 48.0, 16.0 + top, &Rect::new_size(80, 48, 16, 8));
&Rect::new_size(80, 48, 16, 8)); batch.add_rect(bar_offset + weap_x + 48.0, 24.0 + top, &Rect::new_size(80, 48, 16, 8));
batch.add_rect(bar_offset + weap_x + 48.0, 24.0,
&Rect::new_size(80, 48, 16, 8));
} }
// per // per
batch.add_rect(bar_offset + weap_x + 32.0, 24.0, batch.add_rect(bar_offset + weap_x + 32.0, 24.0 + top, &Rect::new_size(72, 48, 8, 8));
&Rect::new_size(72, 48, 8, 8));
// lv // lv
batch.add_rect(num_offset + weap_x, 32.0, batch.add_rect(num_offset + weap_x, 32.0 + top, &Rect::new_size(80, 80, 16, 8));
&Rect::new_size(80, 80, 16, 8));
// xp box // xp box
batch.add_rect(bar_offset + weap_x + 24.0, 32.0, batch.add_rect(bar_offset + weap_x + 24.0, 32.0 + top, &Rect::new_size(0, 72, 40, 8));
&Rect::new_size(0, 72, 40, 8));
if self.max_level { if self.max_level {
batch.add_rect(bar_offset + weap_x + 24.0, 32.0, batch.add_rect(bar_offset + weap_x + 24.0, 32.0 + top, &Rect::new_size(40, 72, 40, 8));
&Rect::new_size(40, 72, 40, 8));
} else if self.max_xp > 0 { } else if self.max_xp > 0 {
// xp bar // xp bar
let bar_width = (self.xp as f32 / self.max_xp as f32 * 40.0) as u16; let bar_width = (self.xp as f32 / self.max_xp as f32 * 40.0) as u16;
batch.add_rect(bar_offset + weap_x + 24.0, 32.0, batch.add_rect(bar_offset + weap_x + 24.0, 32.0 + top, &Rect::new_size(0, 80, bar_width, 8));
&Rect::new_size(0, 80, bar_width, 8));
} }
if self.max_life != 0 { if self.max_life != 0 {
@ -213,28 +208,23 @@ impl GameEntity<(&Player, &mut Inventory)> for HUD {
let bar_width = (self.life as f32 / self.max_life as f32 * 39.0) as u16; let bar_width = (self.life as f32 / self.max_life as f32 * 39.0) as u16;
// heart/hp number box // heart/hp number box
batch.add_rect(num_offset + 16.0, 40.0, batch.add_rect(num_offset + 16.0, 40.0 + top, &Rect::new_size(0, 40, 24, 8));
&Rect::new_size(0, 40, 24, 8));
// life box // life box
batch.add_rect(bar_offset + 40.0, 40.0, batch.add_rect(bar_offset + 40.0, 40.0 + top, &Rect::new_size(24, 40, 40, 8));
&Rect::new_size(24, 40, 40, 8));
// yellow bar // yellow bar
batch.add_rect(bar_offset + 40.0, 40.0, batch.add_rect(bar_offset + 40.0, 40.0 + top, &Rect::new_size(0, 32, yellow_bar_width, 8));
&Rect::new_size(0, 32, yellow_bar_width, 8));
// life // life
batch.add_rect(bar_offset + 40.0, 40.0, batch.add_rect(bar_offset + 40.0, 40.0 + top, &Rect::new_size(0, 24, bar_width, 8));
&Rect::new_size(0, 24, bar_width, 8));
} }
if self.air_counter > 0 { if self.air_counter > 0 {
let rect = if self.air % 30 > 10 { let rect = if self.air % 30 > 10 { Rect::new_size(112, 72, 32, 8) } else { Rect::new_size(112, 80, 32, 8) };
Rect::new_size(112, 72, 32, 8)
} else {
Rect::new_size(112, 80, 32, 8)
};
batch.add_rect((state.canvas_size.0 / 2.0).floor() - 40.0 + air_offset, batch.add_rect(
(state.canvas_size.1 / 2.0).floor(), &rect); left + ((state.canvas_size.0 - left - right) / 2.0).floor() - 40.0 + air_offset,
top + ((state.canvas_size.1 - top - bottom) / 2.0).floor(),
&rect,
);
} }
batch.draw(ctx)?; batch.draw(ctx)?;
@ -267,7 +257,7 @@ impl GameEntity<(&Player, &mut Inventory)> for HUD {
if wtype != 0 { if wtype != 0 {
rect.left = wtype as u16 * 16; rect.left = wtype as u16 * 16;
rect.right = rect.left + 16; rect.right = rect.left + 16;
batch.add_rect(pos_x + weapon_offset, 16.0, &rect); batch.add_rect(pos_x + weapon_offset, 16.0 + top, &rect);
} }
} }
} }
@ -275,17 +265,22 @@ impl GameEntity<(&Player, &mut Inventory)> for HUD {
batch.draw(ctx)?; batch.draw(ctx)?;
if self.air_counter > 0 && self.air_counter % 6 < 4 { if self.air_counter > 0 && self.air_counter % 6 < 4 {
draw_number((state.canvas_size.0 / 2.0).floor() + 8.0 + air_offset, draw_number(
(state.canvas_size.1 / 2.0).floor(), left + ((state.canvas_size.0 - left - right) / 2.0).floor() + 8.0 + air_offset,
(self.air / 10) as usize, Alignment::Left, state, ctx)?; top + ((state.canvas_size.1 - top - bottom) / 2.0).floor(),
(self.air / 10) as usize,
Alignment::Left,
state,
ctx,
)?;
} }
if self.max_ammo != 0 { if self.max_ammo != 0 {
draw_number(num_offset + weap_x + 64.0, 16.0, self.ammo as usize, Alignment::Right, state, ctx)?; draw_number(num_offset + weap_x + 64.0, 16.0 + top, self.ammo as usize, Alignment::Right, state, ctx)?;
draw_number(num_offset + weap_x + 64.0, 24.0, self.max_ammo as usize, Alignment::Right, state, ctx)?; draw_number(num_offset + weap_x + 64.0, 24.0 + top, self.max_ammo as usize, Alignment::Right, state, ctx)?;
} }
draw_number(num_offset + weap_x + 24.0, 32.0, self.current_level, Alignment::Right, state, ctx)?; draw_number(num_offset + weap_x + 24.0, 32.0 + top, self.current_level, Alignment::Right, state, ctx)?;
draw_number(num_offset + 40.0, 40.0, self.life_bar as usize, Alignment::Right, state, ctx)?; draw_number(num_offset + 40.0, 40.0 + top, self.life_bar as usize, Alignment::Right, state, ctx)?;
Ok(()) Ok(())
} }

View file

@ -21,6 +21,10 @@ pub trait BackendRenderer {
fn present(&mut self) -> GameResult; fn present(&mut self) -> GameResult;
fn prepare_draw(&mut self, width: f32, height: f32) -> GameResult {
Ok(())
}
fn create_texture_mutable(&mut self, width: u16, height: u16) -> GameResult<Box<dyn BackendTexture>>; fn create_texture_mutable(&mut self, width: u16, height: u16) -> GameResult<Box<dyn BackendTexture>>;
fn create_texture(&mut self, width: u16, height: u16, data: &[u8]) -> GameResult<Box<dyn BackendTexture>>; fn create_texture(&mut self, width: u16, height: u16, data: &[u8]) -> GameResult<Box<dyn BackendTexture>>;
@ -36,8 +40,6 @@ pub trait BackendRenderer {
fn imgui(&self) -> GameResult<&mut imgui::Context>; fn imgui(&self) -> GameResult<&mut imgui::Context>;
fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult; fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult;
fn prepare_frame(&self, ui: &imgui::Ui) -> GameResult;
} }
pub trait BackendTexture { pub trait BackendTexture {
@ -51,10 +53,20 @@ pub trait BackendTexture {
} }
pub fn init_backend() -> GameResult<Box<dyn Backend>> { pub fn init_backend() -> GameResult<Box<dyn Backend>> {
#[cfg(feature = "backend_sdl")] #[cfg(all(feature = "backend-glutin"))]
{ {
return crate::framework::backend_sdl2::SDL2Backend::new() return crate::framework::backend_opengl::GlutinBackend::new();
} }
#[cfg(feature = "backend-sokol")]
{
return crate::framework::backend_sokol::SokolBackend::new();
}
#[cfg(feature = "backend-sdl")]
{
return crate::framework::backend_sdl2::SDL2Backend::new();
}
log::warn!("No backend compiled in, using null backend instead."); log::warn!("No backend compiled in, using null backend instead.");
crate::framework::backend_null::NullBackend::new() crate::framework::backend_null::NullBackend::new()

View file

@ -29,6 +29,9 @@ impl BackendEventLoop for NullEventLoop {
fn run(&mut self, game: &mut Game, ctx: &mut Context) { fn run(&mut self, game: &mut Game, ctx: &mut Context) {
let state_ref = unsafe { &mut *game.state.get() }; let state_ref = unsafe { &mut *game.state.get() };
ctx.screen_size = (640.0, 480.0);
state_ref.handle_resize(ctx).unwrap();
loop { loop {
game.update(ctx).unwrap(); game.update(ctx).unwrap();
if state_ref.shutdown { if state_ref.shutdown {
@ -118,8 +121,4 @@ impl BackendRenderer for NullRenderer {
fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult { fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult {
Ok(()) Ok(())
} }
fn prepare_frame<'ui>(&self, ui: &Ui<'ui>) -> GameResult {
Ok(())
}
} }

File diff suppressed because it is too large Load diff

View file

@ -564,14 +564,8 @@ impl BackendRenderer for SDL2Renderer {
Ok(()) Ok(())
} }
fn prepare_frame<'ui>(&self, _ui: &Ui<'ui>) -> GameResult {
Ok(())
}
} }
impl SDL2Renderer {}
struct SDL2Texture { struct SDL2Texture {
refs: Rc<RefCell<SDL2Context>>, refs: Rc<RefCell<SDL2Context>>,
texture: Option<Texture>, texture: Option<Texture>,

View file

@ -0,0 +1,358 @@
use std::cell::RefCell;
use std::mem;
use std::ops::DerefMut;
use std::time::Duration;
use imgui::{DrawData, Ui};
use ndk::input_queue::InputQueue;
use sokol::app::{SApp, SAppDesc, SAppEvent, SAppEventType, SAppKeycode};
use sokol::gfx::{sg_isvalid, sg_query_backend, sg_shutdown};
use crate::common::{Color, Rect};
use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand};
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::graphics::BlendMode;
use crate::framework::keyboard::ScanCode;
use crate::Game;
pub struct SokolBackend;
impl SokolBackend {
pub fn new() -> GameResult<Box<dyn Backend>> {
Ok(Box::new(SokolBackend))
}
}
impl Backend for SokolBackend {
fn create_event_loop(&self) -> GameResult<Box<dyn BackendEventLoop>> {
Ok(Box::new(SokolEventLoop))
}
}
pub struct SokolEventLoop;
#[cfg(target_os = "android")]
extern "C" {
fn sapp_android_on_create(
activity: *mut ndk_sys::ANativeActivity,
window: *mut ndk_sys::ANativeWindow,
input_queue: *mut ndk_sys::AInputQueue,
);
}
impl BackendEventLoop for SokolEventLoop {
fn run(&mut self, game: &mut Game, ctx: &mut Context) {
#[cfg(target_os = "android")]
unsafe {
let activity = ndk_glue::native_activity().ptr().as_ptr();
let window = match ndk_glue::native_window().as_ref() {
None => std::ptr::null_mut(),
Some(p) => p.ptr().as_ptr(),
};
let input_queue = match ndk_glue::input_queue().as_ref() {
None => std::ptr::null_mut(),
Some(p) => p.ptr().as_ptr(),
};
println!("activity = {:?} window = {:?} input_queue = {:?}", activity, window, input_queue);
sapp_android_on_create(activity, window, input_queue);
}
struct Callbacks<'a, 'b> {
ctx: &'a mut Context,
game: &'b mut Game,
};
impl<'a, 'b> SApp for Callbacks<'a, 'b> {
fn sapp_init(&mut self) {
let state_ref = unsafe { &mut *self.game.state.get() };
self.ctx.screen_size = (640.0, 480.0);
state_ref.handle_resize(self.ctx).unwrap();
}
fn sapp_frame(&mut self) {
let state_ref = unsafe { &mut *self.game.state.get() };
self.game.update(self.ctx).unwrap();
// todo: not really supported on iOS/consoles
if state_ref.shutdown {
log::info!("Shutting down...");
std::process::exit(0);
return;
}
if state_ref.next_scene.is_some() {
mem::swap(&mut self.game.scene, &mut state_ref.next_scene);
state_ref.next_scene = None;
self.game.scene.as_mut().unwrap().init(state_ref, self.ctx).unwrap();
self.game.loops = 0;
state_ref.frame_time = 0.0;
}
self.game.draw(self.ctx).unwrap();
}
fn sapp_cleanup(&mut self) {
if sg_isvalid() {
sg_shutdown();
}
}
fn sapp_event(&mut self, event: SAppEvent) {
let state_ref = unsafe { &mut *self.game.state.get() };
println!("event: {:?}", event.event_type);
match event.event_type {
SAppEventType::Invalid => {}
SAppEventType::KeyDown => {
if let Some(drs_scan) = conv_scancode(event.key_code) {
state_ref.process_debug_keys(drs_scan);
self.ctx.keyboard_context.set_key(drs_scan, true);
}
}
SAppEventType::KeyUp => {
if let Some(drs_scan) = conv_scancode(event.key_code) {
self.ctx.keyboard_context.set_key(drs_scan, false);
}
}
SAppEventType::Char => {}
SAppEventType::MouseDown => {}
SAppEventType::MouseUp => {}
SAppEventType::MouseScroll => {}
SAppEventType::MouseMove => {}
SAppEventType::MouseEnter => {}
SAppEventType::MouseLeave => {}
SAppEventType::TouchesBegan => {}
SAppEventType::TouchesMoved => {}
SAppEventType::TouchesEnded => {}
SAppEventType::TouchesCancelled => {}
SAppEventType::Resized => {}
SAppEventType::Iconified => {}
SAppEventType::Restored => {}
SAppEventType::Suspended => {}
SAppEventType::Resumed => {}
SAppEventType::UpdateCursor => {}
SAppEventType::QuitRequested => {
state_ref.shutdown();
}
}
}
}
sokol::app::sapp_run(
Callbacks { ctx: unsafe { std::mem::transmute(ctx) }, game: unsafe { std::mem::transmute(game) } },
SAppDesc {
width: 640,
height: 480,
window_title: "doukutsu-rs".to_string(),
ios_keyboard_resizes_canvas: false,
..Default::default()
},
);
loop {
std::thread::sleep(Duration::from_millis(10))
}
}
fn new_renderer(&self) -> GameResult<Box<dyn BackendRenderer>> {
let mut imgui = imgui::Context::create();
imgui.io_mut().display_size = [640.0, 480.0];
imgui.fonts().build_alpha8_texture();
log::info!("Using Sokol backend: {:?}", sg_query_backend());
Ok(Box::new(SokolRenderer(RefCell::new(imgui))))
}
}
pub struct NullTexture(u16, u16);
impl BackendTexture for NullTexture {
fn dimensions(&self) -> (u16, u16) {
(self.0, self.1)
}
fn add(&mut self, command: SpriteBatchCommand) {}
fn clear(&mut self) {}
fn draw(&mut self) -> GameResult<()> {
Ok(())
}
}
pub struct SokolRenderer(RefCell<imgui::Context>);
impl BackendRenderer for SokolRenderer {
fn clear(&mut self, color: Color) {}
fn present(&mut self) -> GameResult {
Ok(())
}
fn create_texture_mutable(&mut self, width: u16, height: u16) -> GameResult<Box<dyn BackendTexture>> {
Ok(Box::new(NullTexture(width, height)))
}
fn create_texture(&mut self, width: u16, height: u16, data: &[u8]) -> GameResult<Box<dyn BackendTexture>> {
Ok(Box::new(NullTexture(width, height)))
}
fn set_blend_mode(&mut self, blend: BlendMode) -> GameResult {
Ok(())
}
fn set_render_target(&mut self, texture: Option<&Box<dyn BackendTexture>>) -> GameResult {
Ok(())
}
fn draw_rect(&mut self, rect: Rect<isize>, color: Color) -> GameResult {
Ok(())
}
fn draw_outline_rect(&mut self, rect: Rect<isize>, line_width: usize, color: Color) -> GameResult {
Ok(())
}
fn imgui(&self) -> GameResult<&mut imgui::Context> {
unsafe { Ok(&mut *self.0.as_ptr()) }
}
fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult {
Ok(())
}
fn prepare_frame<'ui>(&self, ui: &Ui<'ui>) -> GameResult {
Ok(())
}
}
fn conv_scancode(code: SAppKeycode) -> Option<ScanCode> {
match code {
SAppKeycode::KeySpace => Some(ScanCode::Space),
SAppKeycode::KeyApostrophe => Some(ScanCode::Apostrophe),
SAppKeycode::KeyComma => Some(ScanCode::Comma),
SAppKeycode::KeyMinus => Some(ScanCode::Minus),
SAppKeycode::KeyPeriod => Some(ScanCode::Period),
SAppKeycode::KeySlash => Some(ScanCode::Slash),
SAppKeycode::Key0 => Some(ScanCode::Key0),
SAppKeycode::Key1 => Some(ScanCode::Key1),
SAppKeycode::Key2 => Some(ScanCode::Key2),
SAppKeycode::Key3 => Some(ScanCode::Key3),
SAppKeycode::Key4 => Some(ScanCode::Key4),
SAppKeycode::Key5 => Some(ScanCode::Key5),
SAppKeycode::Key6 => Some(ScanCode::Key6),
SAppKeycode::Key7 => Some(ScanCode::Key7),
SAppKeycode::Key8 => Some(ScanCode::Key8),
SAppKeycode::Key9 => Some(ScanCode::Key9),
SAppKeycode::KeySemicolon => Some(ScanCode::Semicolon),
SAppKeycode::KeyEqual => Some(ScanCode::Equals),
SAppKeycode::KeyA => Some(ScanCode::A),
SAppKeycode::KeyB => Some(ScanCode::B),
SAppKeycode::KeyC => Some(ScanCode::C),
SAppKeycode::KeyD => Some(ScanCode::D),
SAppKeycode::KeyE => Some(ScanCode::E),
SAppKeycode::KeyF => Some(ScanCode::F),
SAppKeycode::KeyG => Some(ScanCode::G),
SAppKeycode::KeyH => Some(ScanCode::H),
SAppKeycode::KeyI => Some(ScanCode::I),
SAppKeycode::KeyJ => Some(ScanCode::J),
SAppKeycode::KeyK => Some(ScanCode::K),
SAppKeycode::KeyL => Some(ScanCode::L),
SAppKeycode::KeyM => Some(ScanCode::M),
SAppKeycode::KeyN => Some(ScanCode::N),
SAppKeycode::KeyO => Some(ScanCode::O),
SAppKeycode::KeyP => Some(ScanCode::P),
SAppKeycode::KeyQ => Some(ScanCode::Q),
SAppKeycode::KeyR => Some(ScanCode::R),
SAppKeycode::KeyS => Some(ScanCode::S),
SAppKeycode::KeyT => Some(ScanCode::T),
SAppKeycode::KeyU => Some(ScanCode::U),
SAppKeycode::KeyV => Some(ScanCode::V),
SAppKeycode::KeyW => Some(ScanCode::W),
SAppKeycode::KeyX => Some(ScanCode::X),
SAppKeycode::KeyY => Some(ScanCode::Y),
SAppKeycode::KeyZ => Some(ScanCode::Z),
SAppKeycode::KeyLeftBracket => Some(ScanCode::LBracket),
SAppKeycode::KeyBackslash => Some(ScanCode::Backslash),
SAppKeycode::KeyRightBracket => Some(ScanCode::RBracket),
SAppKeycode::KeyGraveAccent => Some(ScanCode::Grave),
SAppKeycode::KeyWorld1 => Some(ScanCode::AbntC1),
SAppKeycode::KeyWorld2 => Some(ScanCode::AbntC2),
SAppKeycode::KeyEscape => Some(ScanCode::Escape),
SAppKeycode::KeyEnter => Some(ScanCode::Return),
SAppKeycode::KeyTab => Some(ScanCode::Tab),
SAppKeycode::KeyBackspace => Some(ScanCode::Backspace),
SAppKeycode::KeyInsert => Some(ScanCode::Insert),
SAppKeycode::KeyDelete => Some(ScanCode::Delete),
SAppKeycode::KeyRight => Some(ScanCode::Right),
SAppKeycode::KeyLeft => Some(ScanCode::Left),
SAppKeycode::KeyDown => Some(ScanCode::Down),
SAppKeycode::KeyUp => Some(ScanCode::Up),
SAppKeycode::KeyPageUp => Some(ScanCode::PageUp),
SAppKeycode::KeyPageDown => Some(ScanCode::PageDown),
SAppKeycode::KeyHome => Some(ScanCode::Home),
SAppKeycode::KeyEnd => Some(ScanCode::End),
SAppKeycode::KeyCapsLock => Some(ScanCode::Capslock),
SAppKeycode::KeyScrollLock => Some(ScanCode::Scrolllock),
SAppKeycode::KeyNumLock => Some(ScanCode::Numlock),
SAppKeycode::KeyPrintScreen => Some(ScanCode::Sysrq),
SAppKeycode::KeyPause => Some(ScanCode::Pause),
SAppKeycode::KeyF1 => Some(ScanCode::F1),
SAppKeycode::KeyF2 => Some(ScanCode::F2),
SAppKeycode::KeyF3 => Some(ScanCode::F3),
SAppKeycode::KeyF4 => Some(ScanCode::F4),
SAppKeycode::KeyF5 => Some(ScanCode::F5),
SAppKeycode::KeyF6 => Some(ScanCode::F6),
SAppKeycode::KeyF7 => Some(ScanCode::F7),
SAppKeycode::KeyF8 => Some(ScanCode::F8),
SAppKeycode::KeyF9 => Some(ScanCode::F9),
SAppKeycode::KeyF10 => Some(ScanCode::F10),
SAppKeycode::KeyF11 => Some(ScanCode::F11),
SAppKeycode::KeyF12 => Some(ScanCode::F12),
SAppKeycode::KeyF13 => Some(ScanCode::F13),
SAppKeycode::KeyF14 => Some(ScanCode::F14),
SAppKeycode::KeyF15 => Some(ScanCode::F15),
SAppKeycode::KeyF16 => Some(ScanCode::F16),
SAppKeycode::KeyF17 => Some(ScanCode::F17),
SAppKeycode::KeyF18 => Some(ScanCode::F18),
SAppKeycode::KeyF19 => Some(ScanCode::F19),
SAppKeycode::KeyF20 => Some(ScanCode::F20),
SAppKeycode::KeyF21 => Some(ScanCode::F21),
SAppKeycode::KeyF22 => Some(ScanCode::F22),
SAppKeycode::KeyF23 => Some(ScanCode::F23),
SAppKeycode::KeyF24 => Some(ScanCode::F24),
SAppKeycode::KeyKP0 => Some(ScanCode::Numpad0),
SAppKeycode::KeyKP1 => Some(ScanCode::Numpad1),
SAppKeycode::KeyKP2 => Some(ScanCode::Numpad2),
SAppKeycode::KeyKP3 => Some(ScanCode::Numpad3),
SAppKeycode::KeyKP4 => Some(ScanCode::Numpad4),
SAppKeycode::KeyKP5 => Some(ScanCode::Numpad5),
SAppKeycode::KeyKP6 => Some(ScanCode::Numpad6),
SAppKeycode::KeyKP7 => Some(ScanCode::Numpad7),
SAppKeycode::KeyKP8 => Some(ScanCode::Numpad8),
SAppKeycode::KeyKP9 => Some(ScanCode::Numpad9),
SAppKeycode::KeyKPDecimal => Some(ScanCode::NumpadDecimal),
SAppKeycode::KeyKPDivide => Some(ScanCode::NumpadDivide),
SAppKeycode::KeyKPMultiply => Some(ScanCode::NumpadMultiply),
SAppKeycode::KeyKPSubtract => Some(ScanCode::NumpadSubtract),
SAppKeycode::KeyKPAdd => Some(ScanCode::NumpadAdd),
SAppKeycode::KeyKPEnter => Some(ScanCode::NumpadEnter),
SAppKeycode::KeyKPEqual => Some(ScanCode::NumpadEquals),
SAppKeycode::KeyLeftShift => Some(ScanCode::LShift),
SAppKeycode::KeyLeftControl => Some(ScanCode::LControl),
SAppKeycode::KeyLeftAlt => Some(ScanCode::LAlt),
SAppKeycode::KeyLeftSuper => Some(ScanCode::LWin),
SAppKeycode::KeyRightShift => Some(ScanCode::RShift),
SAppKeycode::KeyRightControl => Some(ScanCode::RControl),
SAppKeycode::KeyRightAlt => Some(ScanCode::RAlt),
SAppKeycode::KeyRightSuper => Some(ScanCode::RWin),
SAppKeycode::KeyMenu => Some(ScanCode::Menu),
_ => None,
}
}

View file

@ -1,14 +1,15 @@
use crate::framework::backend::{init_backend, BackendRenderer}; use crate::framework::backend::{init_backend, BackendRenderer};
use crate::framework::error::GameResult; use crate::framework::error::GameResult;
use crate::framework::filesystem::Filesystem; use crate::framework::filesystem::Filesystem;
use crate::Game;
use crate::framework::keyboard::KeyboardContext; use crate::framework::keyboard::KeyboardContext;
use crate::Game;
pub struct Context { pub struct Context {
pub(crate) filesystem: Filesystem, pub(crate) filesystem: Filesystem,
pub(crate) renderer: Option<Box<dyn BackendRenderer>>, pub(crate) renderer: Option<Box<dyn BackendRenderer>>,
pub(crate) keyboard_context: KeyboardContext, pub(crate) keyboard_context: KeyboardContext,
pub(crate) screen_size: (f32, f32), pub(crate) screen_size: (f32, f32),
pub(crate) screen_insets: (f32, f32, f32, f32),
} }
impl Context { impl Context {
@ -18,6 +19,7 @@ impl Context {
renderer: None, renderer: None,
keyboard_context: KeyboardContext::new(), keyboard_context: KeyboardContext::new(),
screen_size: (320.0, 240.0), screen_size: (320.0, 240.0),
screen_insets: (0.0, 0.0, 0.0, 0.0),
} }
} }

1965
src/framework/gl.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -60,6 +60,14 @@ pub fn screen_size(ctx: &mut Context) -> (f32, f32) {
ctx.screen_size ctx.screen_size
} }
pub fn screen_insets(ctx: &mut Context) -> (f32, f32, f32, f32) {
ctx.screen_insets
}
pub fn screen_insets_scaled(ctx: &mut Context, scale: f32) -> (f32, f32, f32, f32) {
(ctx.screen_insets.0 / scale, ctx.screen_insets.1 / scale, ctx.screen_insets.2 / scale, ctx.screen_insets.3 / scale)
}
pub fn set_render_target(ctx: &mut Context, texture: Option<&Box<dyn BackendTexture>>) -> GameResult { pub fn set_render_target(ctx: &mut Context, texture: Option<&Box<dyn BackendTexture>>) -> GameResult {
if let Some(renderer) = ctx.renderer.as_mut() { if let Some(renderer) = ctx.renderer.as_mut() {
return renderer.set_render_target(texture); return renderer.set_render_target(texture);
@ -108,9 +116,9 @@ pub fn render_imgui(ctx: &mut Context, draw_data: &imgui::DrawData) -> GameResul
Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string())) Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string()))
} }
pub fn imgui_prepare_frame(ctx: &Context, ui: &imgui::Ui) -> GameResult { pub fn prepare_draw(ctx: &mut Context) -> GameResult {
if let Some(renderer) = ctx.renderer.as_ref() { if let Some(renderer) = ctx.renderer.as_mut() {
return renderer.prepare_frame(ui); return renderer.prepare_draw(ctx.screen_size.0, ctx.screen_size.1);
} }
Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string())) Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string()))

View file

@ -165,6 +165,7 @@ pub enum ScanCode {
Mail, Mail,
MediaSelect, MediaSelect,
MediaStop, MediaStop,
Menu,
Minus, Minus,
Mute, Mute,
MyComputer, MyComputer,

View file

@ -1,7 +1,13 @@
pub mod backend; pub mod backend;
#[cfg(feature = "backend_sdl2")]
pub mod backend_sdl2;
pub mod backend_null; pub mod backend_null;
#[cfg(feature = "backend-glutin")]
pub mod backend_opengl;
#[cfg(feature = "backend-glutin")]
mod gl;
#[cfg(feature = "backend-sdl")]
pub mod backend_sdl2;
#[cfg(feature = "backend-sokol")]
pub mod backend_sokol;
pub mod context; pub mod context;
pub mod error; pub mod error;
pub mod filesystem; pub mod filesystem;

View file

@ -1,8 +1,8 @@
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::common::Rect; use crate::common::Rect;
use crate::engine_constants::EngineConstants; use crate::engine_constants::EngineConstants;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::graphics::screen_insets_scaled;
use crate::texture_set::TextureSet; use crate::texture_set::TextureSet;
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
@ -14,67 +14,38 @@ pub enum TouchControlType {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct TouchPoint { pub struct TouchPoint {
id: u64, pub id: u64,
touch_id: u64, pub touch_id: u64,
position: (f64, f64), pub position: (f64, f64),
last_position: (f64, f64), pub last_position: (f64, f64),
} }
pub struct TouchControls { pub struct TouchControls {
pub control_type: TouchControlType, pub control_type: TouchControlType,
pub points: Vec<TouchPoint>, pub points: Vec<TouchPoint>,
pub interact_icon: bool, pub interact_icon: bool,
touch_id_counter: u64, pub touch_id_counter: u64,
clicks: Vec<TouchPoint>, pub clicks: Vec<TouchPoint>,
} }
impl TouchControls { impl TouchControls {
pub fn new() -> TouchControls { pub fn new() -> TouchControls {
TouchControls { TouchControls {
control_type: TouchControlType::None, control_type: TouchControlType::None,
touch_id_counter: 0,
interact_icon: false,
points: Vec::with_capacity(8), points: Vec::with_capacity(8),
interact_icon: false,
touch_id_counter: 0,
clicks: Vec::with_capacity(8), clicks: Vec::with_capacity(8),
} }
} }
/*
pub fn process_winit_event(&mut self, scale: f32, touch: winit::event::Touch) {
match touch.phase {
TouchPhase::Started | TouchPhase::Moved => {
if let Some(point) = self.points.iter_mut().find(|p| p.id == touch.id) {
point.last_position = point.position;
point.position = (touch.location.x / scale as f64, touch.location.y / scale as f64);
} else {
self.touch_id_counter = self.touch_id_counter.wrapping_add(1);
let point = TouchPoint {
id: touch.id,
touch_id: self.touch_id_counter,
position: (touch.location.x / scale as f64, touch.location.y / scale as f64),
last_position: (0.0, 0.0),
};
self.points.push(point);
if touch.phase == TouchPhase::Started {
self.clicks.push(point);
}
}
}
TouchPhase::Ended | TouchPhase::Cancelled => {
self.points.retain(|p| p.id != touch.id);
self.clicks.retain(|p| p.id != touch.id);
}
}
}*/
pub fn point_in(&self, bounds: Rect) -> Option<u64> { pub fn point_in(&self, bounds: Rect) -> Option<u64> {
for point in self.points.iter() { for point in self.points.iter() {
if (point.position.0 as isize) > bounds.left if (point.position.0 as isize) > bounds.left
&& (point.position.0 as isize) < bounds.right && (point.position.0 as isize) < bounds.right
&& (point.position.1 as isize) > bounds.top && (point.position.1 as isize) > bounds.top
&& (point.position.1 as isize) < bounds.bottom { && (point.position.1 as isize) < bounds.bottom
{
return Some(point.touch_id); return Some(point.touch_id);
} }
} }
@ -89,7 +60,8 @@ impl TouchControls {
if (point.position.0 as isize) > bounds.left if (point.position.0 as isize) > bounds.left
&& (point.position.0 as isize) < bounds.right && (point.position.0 as isize) < bounds.right
&& (point.position.1 as isize) > bounds.top && (point.position.1 as isize) > bounds.top
&& (point.position.1 as isize) < bounds.bottom { && (point.position.1 as isize) < bounds.bottom
{
point.touch_id = 0; point.touch_id = 0;
return true; return true;
@ -99,11 +71,20 @@ impl TouchControls {
false false
} }
pub fn draw(&self, canvas_size: (f32, f32), constants: &EngineConstants, texture_set: &mut TextureSet, ctx: &mut Context) -> GameResult { pub fn draw(
&self,
canvas_size: (f32, f32),
scale: f32,
constants: &EngineConstants,
texture_set: &mut TextureSet,
ctx: &mut Context,
) -> GameResult {
if self.control_type == TouchControlType::Controls { if self.control_type == TouchControlType::Controls {
let batch = texture_set.get_or_load_batch(ctx, constants, "builtin/touch")?; let batch = texture_set.get_or_load_batch(ctx, constants, "builtin/touch")?;
let color = (255, 255, 255, 160); let color = (255, 255, 255, 160);
let (left, _, right, bottom) = screen_insets_scaled(ctx, scale);
for x in 0..3 { for x in 0..3 {
for y in 0..3 { for y in 0..3 {
let mut icon_x = x; let mut icon_x = x;
@ -113,21 +94,28 @@ impl TouchControls {
icon_x = 3; icon_x = 3;
} }
batch.add_rect_tinted(4.0 + 48.0 * x as f32 + 8.0, batch.add_rect_tinted(
(canvas_size.1 - 4.0 - 48.0 * 3.0) + 48.0 * y as f32 + 8.0, 4.0 + 48.0 * x as f32 + 8.0 + left,
color, (canvas_size.1 - 4.0 - 48.0 * 3.0) + 48.0 * y as f32 + 8.0 - bottom,
&Rect::new_size(icon_x * 32, icon_y * 32, 32, 32)); color,
&Rect::new_size(icon_x * 32, icon_y * 32, 32, 32),
);
} }
} }
batch.add_rect_tinted(
canvas_size.0 - (4.0 + 48.0) + 8.0 - right,
canvas_size.1 - (4.0 + 48.0) + 8.0 - bottom,
color,
&Rect::new_size(3 * 32, 32, 32, 32),
);
batch.add_rect_tinted(canvas_size.0 - (4.0 + 48.0) + 8.0, canvas_size.1 - (4.0 + 48.0) + 8.0, batch.add_rect_tinted(
color, canvas_size.0 - (4.0 + 48.0) + 8.0 - right,
&Rect::new_size(3 * 32, 32, 32, 32)); canvas_size.1 - (4.0 + 48.0) * 2.0 + 8.0 - bottom,
color,
batch.add_rect_tinted(canvas_size.0 - (4.0 + 48.0) + 8.0, canvas_size.1 - (4.0 + 48.0) * 2.0 + 8.0, &Rect::new_size(3 * 32, 0, 32, 32),
color, );
&Rect::new_size(3 * 32, 0, 32, 32));
batch.draw(ctx)?; batch.draw(ctx)?;
} }

View file

@ -1,9 +1,8 @@
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::bitfield; use crate::bitfield;
use crate::common::Rect; use crate::common::Rect;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::graphics::screen_insets_scaled;
use crate::input::player_controller::PlayerController; use crate::input::player_controller::PlayerController;
use crate::input::touch_controls::TouchControlType; use crate::input::touch_controls::TouchControlType;
use crate::shared_game_state::SharedGameState; use crate::shared_game_state::SharedGameState;
@ -37,21 +36,21 @@ bitfield! {
impl TouchPlayerController { impl TouchPlayerController {
pub fn new() -> TouchPlayerController { pub fn new() -> TouchPlayerController {
TouchPlayerController { TouchPlayerController { state: KeyState(0), old_state: KeyState(0), trigger: KeyState(0), prev_touch_len: 0 }
state: KeyState(0),
old_state: KeyState(0),
trigger: KeyState(0),
prev_touch_len: 0,
}
} }
} }
impl PlayerController for TouchPlayerController { impl PlayerController for TouchPlayerController {
fn update(&mut self, state: &mut SharedGameState, _ctx: &mut Context) -> GameResult { fn update(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
match state.touch_controls.control_type { match state.touch_controls.control_type {
TouchControlType::None => {} TouchControlType::None => {}
TouchControlType::Dialog => { TouchControlType::Dialog => {
self.state.set_jump(state.touch_controls.point_in(Rect::new_size(0, 0, state.canvas_size.0 as isize, state.canvas_size.1 as isize)).is_some()); self.state.set_jump(
state
.touch_controls
.point_in(Rect::new_size(0, 0, state.canvas_size.0 as isize, state.canvas_size.1 as isize))
.is_some(),
);
if state.touch_controls.points.len() > 1 && self.prev_touch_len != state.touch_controls.points.len() { if state.touch_controls.points.len() > 1 && self.prev_touch_len != state.touch_controls.points.len() {
self.prev_touch_len = state.touch_controls.points.len(); self.prev_touch_len = state.touch_controls.points.len();
@ -59,37 +58,152 @@ impl PlayerController for TouchPlayerController {
} }
} }
TouchControlType::Controls => { TouchControlType::Controls => {
let (left, _, right, bottom) = screen_insets_scaled(ctx, state.scale);
let left = 4 + left as isize;
let bottom = 4 + bottom as isize;
let right = 4 + right as isize;
self.state.0 = 0; self.state.0 = 0;
// left // left
self.state.set_left(self.state.left() || state.touch_controls.point_in(Rect::new_size(4, state.canvas_size.1 as isize - 4 - 48 * 2, 48, 48)).is_some()); self.state.set_left(
self.state.left()
|| state
.touch_controls
.point_in(Rect::new_size(left, state.canvas_size.1 as isize - bottom - 48 * 2, 48, 48))
.is_some(),
);
// up // up
self.state.set_up(self.state.up() || state.touch_controls.point_in(Rect::new_size(48 + 4, state.canvas_size.1 as isize - 4 - 48 * 3, 48, 48)).is_some()); self.state.set_up(
self.state.up()
|| state
.touch_controls
.point_in(Rect::new_size(48 + left, state.canvas_size.1 as isize - bottom - 48 * 3, 48, 48))
.is_some(),
);
// right // right
self.state.set_right(self.state.right() || state.touch_controls.point_in(Rect::new_size(4 + 48 * 2, state.canvas_size.1 as isize - 4 - 48 * 2, 48, 48)).is_some()); self.state.set_right(
self.state.right()
|| state
.touch_controls
.point_in(Rect::new_size(
48 * 2 + left,
state.canvas_size.1 as isize - bottom - 48 * 2,
48,
48,
))
.is_some(),
);
// down // down
self.state.set_down(self.state.down() || state.touch_controls.point_in(Rect::new_size(48 + 4, state.canvas_size.1 as isize - 4 - 48, 48, 48)).is_some()); self.state.set_down(
self.state.down()
|| state
.touch_controls
.point_in(Rect::new_size(48 + left, state.canvas_size.1 as isize - bottom - 48, 48, 48))
.is_some(),
);
// left+up // left+up
self.state.set_left(self.state.left() || state.touch_controls.point_in(Rect::new_size(4, state.canvas_size.1 as isize - 4 - 48 * 3, 48, 48)).is_some()); self.state.set_left(
self.state.set_up(self.state.up() || state.touch_controls.point_in(Rect::new_size(4, state.canvas_size.1 as isize - 4 - 48 * 3, 48, 48)).is_some()); self.state.left()
|| state
.touch_controls
.point_in(Rect::new_size(left, state.canvas_size.1 as isize - bottom - 48 * 3, 48, 48))
.is_some(),
);
self.state.set_up(
self.state.up()
|| state
.touch_controls
.point_in(Rect::new_size(left, state.canvas_size.1 as isize - bottom - 48 * 3, 48, 48))
.is_some(),
);
// right+up // right+up
self.state.set_right(self.state.right() || state.touch_controls.point_in(Rect::new_size(4 + 48 * 2, state.canvas_size.1 as isize - 4 - 48 * 3, 48, 48)).is_some()); self.state.set_right(
self.state.set_up(self.state.up() || state.touch_controls.point_in(Rect::new_size(4 + 48 * 2, state.canvas_size.1 as isize - 4 - 48 * 3, 48, 48)).is_some()); self.state.right()
|| state
.touch_controls
.point_in(Rect::new_size(
48 * 2 + left,
state.canvas_size.1 as isize - bottom - 48 * 3,
48,
48,
))
.is_some(),
);
self.state.set_up(
self.state.up()
|| state
.touch_controls
.point_in(Rect::new_size(
48 * 2 + left,
state.canvas_size.1 as isize - bottom - 48 * 3,
48,
48,
))
.is_some(),
);
// left+down // left+down
self.state.set_left(self.state.left() || state.touch_controls.point_in(Rect::new_size(4, state.canvas_size.1 as isize - 48 - 4, 48, 48)).is_some()); self.state.set_left(
self.state.set_down(self.state.down() || state.touch_controls.point_in(Rect::new_size(4, state.canvas_size.1 as isize - 48 - 4, 48, 48)).is_some()); self.state.left()
|| state
.touch_controls
.point_in(Rect::new_size(left, state.canvas_size.1 as isize - 48 - bottom, 48, 48))
.is_some(),
);
self.state.set_down(
self.state.down()
|| state
.touch_controls
.point_in(Rect::new_size(left, state.canvas_size.1 as isize - 48 - bottom, 48, 48))
.is_some(),
);
// right+down // right+down
self.state.set_right(self.state.right() || state.touch_controls.point_in(Rect::new_size(4 + 48 * 2, state.canvas_size.1 as isize - 48 - 4, 48, 48)).is_some()); self.state.set_right(
self.state.set_down(self.state.down() || state.touch_controls.point_in(Rect::new_size(4 + 48 * 2, state.canvas_size.1 as isize - 48 - 4, 48, 48)).is_some()); self.state.right()
|| state
.touch_controls
.point_in(Rect::new_size(48 * 2 + left, state.canvas_size.1 as isize - 48 - bottom, 48, 48))
.is_some(),
);
self.state.set_down(
self.state.down()
|| state
.touch_controls
.point_in(Rect::new_size(48 * 2 + left, state.canvas_size.1 as isize - 48 - bottom, 48, 48))
.is_some(),
);
self.state.set_jump(self.state.jump() || state.touch_controls.point_in(Rect::new_size(state.canvas_size.0 as isize - 48 - 4, state.canvas_size.1 as isize - 48 - 4, 48, 48)).is_some()); self.state.set_jump(
self.state.set_shoot(self.state.shoot() || state.touch_controls.point_in(Rect::new_size(state.canvas_size.0 as isize - 48 - 4, state.canvas_size.1 as isize - (48 - 4) * 2, 48, 48)).is_some()); self.state.jump()
|| state
.touch_controls
.point_in(Rect::new_size(
state.canvas_size.0 as isize - 48 - right,
state.canvas_size.1 as isize - (48 + 4) - bottom,
48,
48,
))
.is_some(),
);
self.state.set_shoot(
self.state.shoot()
|| state
.touch_controls
.point_in(Rect::new_size(
state.canvas_size.0 as isize - 48 - right,
state.canvas_size.1 as isize - (48 + 4) * 2 - bottom,
48,
48,
))
.is_some(),
);
} }
} }

View file

@ -10,9 +10,11 @@ extern crate strum_macros;
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
use std::env; use std::env;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Mutex;
use std::time::Instant; use std::time::Instant;
use directories::ProjectDirs; use directories::ProjectDirs;
use lazy_static::lazy_static;
use log::*; use log::*;
use pretty_env_logger::env_logger::Env; use pretty_env_logger::env_logger::Env;
@ -65,6 +67,10 @@ mod text_script;
mod texture_set; mod texture_set;
mod weapon; mod weapon;
lazy_static! {
pub static ref GAME_SUSPENDED: Mutex<bool> = Mutex::new(false);
}
pub struct Game { pub struct Game {
scene: Option<Box<dyn Scene>>, scene: Option<Box<dyn Scene>>,
state: UnsafeCell<SharedGameState>, state: UnsafeCell<SharedGameState>,
@ -98,14 +104,12 @@ impl Game {
TimingMode::_50Hz | TimingMode::_60Hz => { TimingMode::_50Hz | TimingMode::_60Hz => {
let last_tick = self.next_tick; let last_tick = self.next_tick;
while self.start_time.elapsed().as_nanos() >= self.next_tick && self.loops < 10 while self.start_time.elapsed().as_nanos() >= self.next_tick && self.loops < 10 {
{
if (state_ref.settings.speed - 1.0).abs() < 0.01 { if (state_ref.settings.speed - 1.0).abs() < 0.01 {
self.next_tick += state_ref.timing_mode.get_delta() as u128; self.next_tick += state_ref.timing_mode.get_delta() as u128;
} else { } else {
self.next_tick += (state_ref.timing_mode.get_delta() as f64 self.next_tick +=
/ state_ref.settings.speed) (state_ref.timing_mode.get_delta() as f64 / state_ref.settings.speed) as u128;
as u128;
} }
self.loops += 1; self.loops += 1;
} }
@ -114,8 +118,7 @@ impl Game {
log::warn!("Frame skip is way too high, a long system lag occurred?"); log::warn!("Frame skip is way too high, a long system lag occurred?");
self.last_tick = self.start_time.elapsed().as_nanos(); self.last_tick = self.start_time.elapsed().as_nanos();
self.next_tick = self.last_tick self.next_tick = self.last_tick
+ (state_ref.timing_mode.get_delta() as f64 / state_ref.settings.speed) + (state_ref.timing_mode.get_delta() as f64 / state_ref.settings.speed) as u128;
as u128;
self.loops = 0; self.loops = 0;
} }
@ -141,152 +144,59 @@ impl Game {
if state_ref.timing_mode != TimingMode::FrameSynchronized { if state_ref.timing_mode != TimingMode::FrameSynchronized {
let mut elapsed = self.start_time.elapsed().as_nanos(); let mut elapsed = self.start_time.elapsed().as_nanos();
#[cfg(target_os = "windows")]
{ // Even with the non-monotonic Instant mitigation at the start of the event loop, there's still a chance of it not working.
// Even with the non-monotonic Instant mitigation at the start of the event loop, there's still a chance of it not working. // This check here should trigger if that happens and makes sure there's no panic from an underflow.
// This check here should trigger if that happens and makes sure there's no panic from an underflow. if elapsed < self.last_tick {
if elapsed < self.last_tick { elapsed = self.last_tick;
elapsed = self.last_tick;
}
} }
let n1 = (elapsed - self.last_tick) as f64; let n1 = (elapsed - self.last_tick) as f64;
let n2 = (self.next_tick - self.last_tick) as f64; let n2 = (self.next_tick - self.last_tick) as f64;
state_ref.frame_time = if state_ref.settings.motion_interpolation { state_ref.frame_time = if state_ref.settings.motion_interpolation { n1 / n2 } else { 1.0 };
n1 / n2
} else {
1.0
};
} }
unsafe { unsafe {
G_MAG = if state_ref.settings.subpixel_coords { G_MAG = if state_ref.settings.subpixel_coords { state_ref.scale } else { 1.0 };
state_ref.scale
} else {
1.0
};
I_MAG = state_ref.scale; I_MAG = state_ref.scale;
} }
self.loops = 0; self.loops = 0;
graphics::prepare_draw(ctx)?;
graphics::clear(ctx, [0.0, 0.0, 0.0, 1.0].into()); graphics::clear(ctx, [0.0, 0.0, 0.0, 1.0].into());
/*graphics::set_projection(ctx, DrawParam::new()
.scale(Vec2::new(state_ref.scale, state_ref.scale))
.to_matrix());*/
if let Some(scene) = self.scene.as_mut() { if let Some(scene) = self.scene.as_mut() {
scene.draw(state_ref, ctx)?; scene.draw(state_ref, ctx)?;
if state_ref.settings.touch_controls { if state_ref.settings.touch_controls {
state_ref.touch_controls.draw( state_ref.touch_controls.draw(
state_ref.canvas_size, state_ref.canvas_size,
state_ref.scale,
&state_ref.constants, &state_ref.constants,
&mut state_ref.texture_set, &mut state_ref.texture_set,
ctx, ctx,
)?; )?;
} }
//graphics::set_projection(ctx, self.def_matrix);
self.ui.draw(state_ref, ctx, scene)?; self.ui.draw(state_ref, ctx, scene)?;
} }
graphics::present(ctx)?; graphics::present(ctx)?;
Ok(()) Ok(())
} }
} }
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
fn request_perms() -> GameResult { #[cfg_attr(target_os = "android", ndk_glue::main())]
use jni::objects::JObject;
use jni::objects::JValue;
let native_activity = ndk_glue::native_activity();
let vm_ptr = native_activity.vm();
let vm = unsafe { jni::JavaVM::from_raw(vm_ptr) }?;
let vm_env = vm.attach_current_thread()?;
fn perm_name<'a, 'b, 'c>(
vm_env: &'b jni::AttachGuard<'a>,
name: &'c str,
) -> GameResult<jni::objects::JValue<'a>> {
let class = vm_env.find_class("android/Manifest$permission")?;
Ok(vm_env.get_static_field(class, name.to_owned(), "Ljava/lang/String;")?)
}
fn has_permission(
vm_env: &jni::AttachGuard,
activity: &jni::sys::jobject,
name: &str,
) -> GameResult<bool> {
let perm_granted = {
let class = vm_env.find_class("android/content/pm/PackageManager")?;
vm_env
.get_static_field(class, "PERMISSION_GRANTED", "I")?
.i()?
};
let perm = perm_name(vm_env, name)?;
let activity_obj = JObject::from(*activity);
let result = vm_env
.call_method(
activity_obj,
"checkSelfPermission",
"(Ljava/lang/String;)I",
&[perm],
)?
.i()?;
Ok(result == perm_granted)
}
let str_class = vm_env.find_class("java/lang/String")?;
let array = vm_env.new_object_array(2, str_class, JObject::null())?;
vm_env.set_object_array_element(array, 0, perm_name(&vm_env, "READ_EXTERNAL_STORAGE")?.l()?)?;
vm_env.set_object_array_element(
array,
1,
perm_name(&vm_env, "WRITE_EXTERNAL_STORAGE")?.l()?,
)?;
let activity_obj = JObject::from(native_activity.activity());
loop {
if has_permission(
&vm_env,
&native_activity.activity(),
"READ_EXTERNAL_STORAGE",
)? && has_permission(
&vm_env,
&native_activity.activity(),
"WRITE_EXTERNAL_STORAGE",
)? {
break;
}
vm_env.call_method(
activity_obj,
"requestPermissions",
"([Ljava/lang/String;I)V",
&[JValue::from(array), JValue::from(0)],
)?;
}
Ok(())
}
#[cfg(target_os = "android")]
#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))]
pub fn android_main() { pub fn android_main() {
println!("main invoked.");
request_perms().expect("Failed to attach to the JVM and request storage permissions.");
env::set_var("CAVESTORY_DATA_DIR", "/sdcard/doukutsu");
let _ = std::fs::create_dir("/sdcard/doukutsu/");
let _ = std::fs::write("/sdcard/doukutsu/.nomedia", b"");
init().unwrap(); init().unwrap();
} }
pub fn init() -> GameResult { pub fn init() -> GameResult {
pretty_env_logger::env_logger::from_env(Env::default().default_filter_or("info")).init(); pretty_env_logger::env_logger::from_env(Env::default().default_filter_or("info"))
//.filter(Some("ndk_glue"), LevelFilter::Trace)
.init();
#[cfg(not(target_os = "android"))]
let resource_dir = if let Ok(data_dir) = env::var("CAVESTORY_DATA_DIR") { let resource_dir = if let Ok(data_dir) = env::var("CAVESTORY_DATA_DIR") {
PathBuf::from(data_dir) PathBuf::from(data_dir)
} else { } else {
@ -298,51 +208,53 @@ pub fn init() -> GameResult {
resource_dir resource_dir
}; };
#[cfg(not(target_os = "android"))]
info!("Resource directory: {:?}", resource_dir); info!("Resource directory: {:?}", resource_dir);
info!("Initializing engine..."); info!("Initializing engine...");
let mut context = Context::new(); let mut context = Context::new();
mount_vfs(&mut context, Box::new(BuiltinFS::new())); mount_vfs(&mut context, Box::new(BuiltinFS::new()));
#[cfg(not(target_os = "android"))]
mount_vfs(&mut context, Box::new(PhysicalFS::new(&resource_dir, true))); mount_vfs(&mut context, Box::new(PhysicalFS::new(&resource_dir, true)));
#[cfg(not(target_os = "android"))] #[cfg(not(target_os = "android"))]
let project_dirs = match ProjectDirs::from("", "", "doukutsu-rs") { let project_dirs = match ProjectDirs::from("", "", "doukutsu-rs") {
Some(dirs) => dirs, Some(dirs) => dirs,
None => { None => {
return Err(GameError::FilesystemError(String::from( return Err(GameError::FilesystemError(String::from("No valid home directory path could be retrieved.")));
"No valid home directory path could be retrieved.",
)));
} }
}; };
mount_user_vfs(
&mut context,
Box::new(PhysicalFS::new(project_dirs.data_local_dir(), false)),
);
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
{ {
loop { let mut data_path =
match ndk_glue::native_window().as_ref() { PathBuf::from(ndk_glue::native_activity().internal_data_path().to_string_lossy().to_string());
Some(_) => { let mut user_path = data_path.clone();
println!("NativeScreen Found:{:?}", ndk_glue::native_window());
break; data_path.push("data");
} user_path.push("saves");
None => (),
} let _ = std::fs::create_dir_all(&data_path);
} let _ = std::fs::create_dir_all(&user_path);
log::info!("Android data directories: data_path={:?} user_path={:?}", &data_path, &user_path);
mount_vfs(&mut context, Box::new(PhysicalFS::new(&data_path, true)));
mount_user_vfs(&mut context, Box::new(PhysicalFS::new(&user_path, false)));
} }
let mut game = Game::new(&mut context)?; #[cfg(not(target_os = "android"))]
let state_ref = unsafe { &mut *game.state.get() }; mount_user_vfs(&mut context, Box::new(PhysicalFS::new(project_dirs.data_local_dir(), false)));
let game = UnsafeCell::new(Game::new(&mut context)?);
let state_ref = unsafe { &mut *((&mut *game.get()).state.get()) };
#[cfg(feature = "scripting")] #[cfg(feature = "scripting")]
{ {
state_ref state_ref.lua.update_refs(unsafe { (&*game.get()).state.get() }, &mut context as *mut Context);
.lua
.update_refs(game.state.get(), &mut context as *mut Context);
} }
state_ref.next_scene = Some(Box::new(LoadingScene::new())); state_ref.next_scene = Some(Box::new(LoadingScene::new()));
context.run(&mut game)?; context.run(unsafe { &mut *game.get() })?;
Ok(()) Ok(())
} }

View file

@ -45,6 +45,7 @@ impl LoadingScene {
impl Scene for LoadingScene { impl Scene for LoadingScene {
fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
println!("**TICK**");
// deferred to let the loading image draw // deferred to let the loading image draw
if self.tick == 1 { if self.tick == 1 {
if let Err(err) = self.load_stuff(state, ctx) { if let Err(err) = self.load_stuff(state, ctx) {
@ -66,6 +67,8 @@ impl Scene for LoadingScene {
batch.draw(ctx)?; batch.draw(ctx)?;
} }
Err(err) => { Err(err) => {
log::error!("Failed to load game data: {}", err);
state.next_scene = Some(Box::new(NoDataScene::new(err))); state.next_scene = Some(Box::new(NoDataScene::new(err)));
} }
} }

View file

@ -182,7 +182,7 @@ impl Scene for TitleScene {
TimingMode::_60Hz => state.timing_mode = TimingMode::_50Hz, TimingMode::_60Hz => state.timing_mode = TimingMode::_50Hz,
_ => {} _ => {}
} }
state.settings.save(ctx); let _ = state.settings.save(ctx);
*value = state.timing_mode == TimingMode::_50Hz; *value = state.timing_mode == TimingMode::_50Hz;
} }
@ -190,7 +190,7 @@ impl Scene for TitleScene {
MenuSelectionResult::Selected(1, toggle) => { MenuSelectionResult::Selected(1, toggle) => {
if let MenuEntry::Toggle(_, value) = toggle { if let MenuEntry::Toggle(_, value) = toggle {
state.settings.shader_effects = !state.settings.shader_effects; state.settings.shader_effects = !state.settings.shader_effects;
state.settings.save(ctx); let _ = state.settings.save(ctx);
*value = state.settings.shader_effects; *value = state.settings.shader_effects;
} }
@ -199,7 +199,7 @@ impl Scene for TitleScene {
if let MenuEntry::Toggle(_, value) = toggle { if let MenuEntry::Toggle(_, value) = toggle {
state.settings.original_textures = !state.settings.original_textures; state.settings.original_textures = !state.settings.original_textures;
state.reload_textures(); state.reload_textures();
state.settings.save(ctx); let _ = state.settings.save(ctx);
*value = state.settings.original_textures; *value = state.settings.original_textures;
} }
@ -208,7 +208,7 @@ impl Scene for TitleScene {
if let MenuEntry::Toggle(_, value) = toggle { if let MenuEntry::Toggle(_, value) = toggle {
state.settings.seasonal_textures = !state.settings.seasonal_textures; state.settings.seasonal_textures = !state.settings.seasonal_textures;
state.reload_textures(); state.reload_textures();
state.settings.save(ctx); let _ = state.settings.save(ctx);
*value = state.settings.seasonal_textures; *value = state.settings.seasonal_textures;
} }

View file

@ -1,7 +1,7 @@
declare type EventHandler<T> = (this: void, param: T) => void; declare type EventHandler<T> = (this: void, param: T) => void;
/** /**
* Represents a * Represents an in-game player.
*/ */
declare interface DoukutsuPlayer { declare interface DoukutsuPlayer {
/** /**
@ -28,28 +28,40 @@ declare interface DoukutsuPlayer {
* Current velocity of player in Y axis (as floating point, not internal fixed point representation). * Current velocity of player in Y axis (as floating point, not internal fixed point representation).
*/ */
velY(): number; velY(): number;
/**
* Sets the position of player in X axis (as floating point, not internal fixed point representation).
*/
setX(value: number): void;
/**
* Sets the position of player in Y axis (as floating point, not internal fixed point representation).
*/
setY(value: number): void;
/**
* Sets the velocity of player in X axis (as floating point, not internal fixed point representation).
*/
setVelX(value: number): void;
/**
* Sets the velocity of player in Y axis (as floating point, not internal fixed point representation).
*/
setVelY(value: number): void;
} }
declare interface DoukutsuScene { declare interface DoukutsuStage {
/** /**
* Returns the tick of current scene. * Returns the tick of current stage.
*/ */
tick(): number; tick(): number;
/**
* Returns a list of players connected to current game.
*/
onlinePlayers(): DoukutsuPlayer[];
/** /**
* Returns a list of players on current map. * Returns a list of players on current map.
*/ */
mapPlayers(): DoukutsuPlayer[]; players(): DoukutsuPlayer[];
/**
* Returns the id of local player.
*/
localPlayerId(): number;
/** /**
* Returns player with specified id. * Returns player with specified id.
@ -69,6 +81,22 @@ declare namespace doukutsu {
*/ */
function playMusic(id: number): void; function playMusic(id: number): void;
/**
* Returns the value of a certain TSC flag.
* @param id the flag number
*/
function getFlag(id: number): boolean;
/**
* Returns a list of players connected to current game.
*/
function onlinePlayers(): DoukutsuPlayer[];
/**
* Returns the id of local player.
*/
function localPlayerId(): number;
/** /**
* Sets an implementation-defined game setting. * Sets an implementation-defined game setting.
* @param name * @param name
@ -88,7 +116,7 @@ declare namespace doukutsu {
* @param event event name * @param event event name
* @param handler event handler procedure * @param handler event handler procedure
*/ */
function on(event: "tick", handler: EventHandler<DoukutsuScene>): EventHandler<DoukutsuScene>; function on(event: "tick", handler: EventHandler<DoukutsuStage>): EventHandler<DoukutsuStage>;
function on<T>(event: string, handler: EventHandler<T>): EventHandler<T>; function on<T>(event: string, handler: EventHandler<T>): EventHandler<T>;
} }

View file

@ -35,6 +35,18 @@ impl Doukutsu {
0 0
} }
unsafe fn lua_set_setting(&self, state: &mut State) -> c_int {
if let Some(index) = state.to_int(2) {
let game_state = &mut (*(*self.ptr).state_ptr);
state.push(game_state.get_flag(index.max(0) as usize));
} else {
state.push_nil();
}
1
}
unsafe fn lua_get_flag(&self, state: &mut State) -> c_int { unsafe fn lua_get_flag(&self, state: &mut State) -> c_int {
if let Some(index) = state.to_int(2) { if let Some(index) = state.to_int(2) {
let game_state = &mut (*(*self.ptr).state_ptr); let game_state = &mut (*(*self.ptr).state_ptr);
@ -55,9 +67,9 @@ impl LuaObject for Doukutsu {
fn lua_fns() -> Vec<luaL_Reg> { fn lua_fns() -> Vec<luaL_Reg> {
vec![ vec![
lua_method!("play_sfx", Doukutsu, Doukutsu::lua_play_sfx), lua_method!("playSfx", Doukutsu, Doukutsu::lua_play_sfx),
lua_method!("play_song", Doukutsu, Doukutsu::lua_play_song), lua_method!("playSong", Doukutsu, Doukutsu::lua_play_song),
lua_method!("get_flag", Doukutsu, Doukutsu::lua_get_flag), lua_method!("getFlag", Doukutsu, Doukutsu::lua_get_flag),
] ]
} }
} }

View file

@ -222,6 +222,10 @@ impl SharedGameState {
self.texture_set = texture_set; self.texture_set = texture_set;
} }
pub fn graphics_reset(&mut self) {
self.reload_textures();
}
pub fn start_new_game(&mut self, ctx: &mut Context) -> GameResult { pub fn start_new_game(&mut self, ctx: &mut Context) -> GameResult {
let mut next_scene = GameScene::new(self, ctx, 13)?; let mut next_scene = GameScene::new(self, ctx, 13)?;
next_scene.player1.cond.set_alive(true); next_scene.player1.cond.set_alive(true);

View file

@ -526,9 +526,29 @@ where
}, },
err_fn, err_fn,
)?; )?;
stream.play()?; stream.play()?;
let mut saved_state = true;
loop { loop {
std::thread::sleep(Duration::from_millis(10)); std::thread::sleep(Duration::from_millis(10));
{
let mutex = crate::GAME_SUSPENDED.lock().unwrap();
let state = *mutex;
if saved_state != state {
saved_state = state;
if state {
if let Err(e) = stream.pause() {
log::error!("Failed to pause the stream: {}", e);
}
} else {
if let Err(e) = stream.play() {
log::error!("Failed to unpause the stream: {}", e);
}
}
}
}
} }
} }

View file

@ -729,7 +729,7 @@ impl TextScriptVM {
FromPrimitive::from_i32(read_cur_varint(&mut cursor).unwrap_or_else(|_| OpCode::END as i32)); FromPrimitive::from_i32(read_cur_varint(&mut cursor).unwrap_or_else(|_| OpCode::END as i32));
if let Some(op) = op_maybe { if let Some(op) = op_maybe {
println!("opcode: {:?}", op); log::info!("opcode: {:?}", op);
match op { match op {
OpCode::_NOP => { OpCode::_NOP => {
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);