From 1d09af729e924474f93dca8b8cee3ccf3d57d21d Mon Sep 17 00:00:00 2001 From: alistair Date: Mon, 17 May 2021 20:56:29 +1000 Subject: [PATCH] android build --- build-android/CMakeLists.txt | 25 + build-android/LICENSE | 20 + build-android/README.md | 56 + build-android/android/.gitignore | 3 + build-android/android/SDL2/.gitignore | 1 + build-android/android/SDL2/build.gradle | 38 + build-android/android/SDL2/proguard-rules.pro | 25 + build-android/android/app/.gitignore | 1 + build-android/android/app/build.gradle | 45 + build-android/android/app/proguard-rules.pro | 25 + build-android/android/build.gradle | 31 + build-android/android/gradle.properties | 17 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + build-android/android/gradlew | 160 ++ build-android/android/gradlew.bat | 90 + build-android/android/settings.gradle | 4 + build-android/get_dependencies.sh | 17 + build-android/res/TerminusTTF.ttf | Bin 0 -> 453248 bytes build-android/res/hello.png | Bin 0 -> 9887 bytes build-android/src/audio.c | 226 ++ build-android/src/audio.h | 37 + build-android/src/basic-lib.c | 97 + build-android/src/colours.c | 238 +++ build-android/src/colours.h | 43 + build-android/src/controlscheme.c | 106 + build-android/src/controlscheme.h | 35 + build-android/src/datatypes.c | 159 ++ build-android/src/datatypes.h | 61 + build-android/src/debuginfo.h | 2 + build-android/src/draw.c | 881 ++++++++ build-android/src/draw.h | 42 + build-android/src/environment.c | 244 +++ build-android/src/environment.h | 12 + build-android/src/game.c | 1869 +++++++++++++++++ build-android/src/game.h | 82 + build-android/src/main.c | 185 ++ build-android/src/main.cpp | 61 + build-android/src/physics.c | 2 + build-android/src/physics.h | 0 build-android/src/types.h | 236 +++ build-android/src/vect.c | 81 + build-android/src/vect.h | 45 + 43 files changed, 5308 insertions(+) create mode 100644 build-android/CMakeLists.txt create mode 100644 build-android/LICENSE create mode 100644 build-android/README.md create mode 100644 build-android/android/.gitignore create mode 100644 build-android/android/SDL2/.gitignore create mode 100644 build-android/android/SDL2/build.gradle create mode 100644 build-android/android/SDL2/proguard-rules.pro create mode 100644 build-android/android/app/.gitignore create mode 100644 build-android/android/app/build.gradle create mode 100644 build-android/android/app/proguard-rules.pro create mode 100644 build-android/android/build.gradle create mode 100644 build-android/android/gradle.properties create mode 100644 build-android/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 build-android/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 build-android/android/gradlew create mode 100644 build-android/android/gradlew.bat create mode 100644 build-android/android/settings.gradle create mode 100755 build-android/get_dependencies.sh create mode 100644 build-android/res/TerminusTTF.ttf create mode 100644 build-android/res/hello.png create mode 100644 build-android/src/audio.c create mode 100644 build-android/src/audio.h create mode 100644 build-android/src/basic-lib.c create mode 100644 build-android/src/colours.c create mode 100644 build-android/src/colours.h create mode 100644 build-android/src/controlscheme.c create mode 100644 build-android/src/controlscheme.h create mode 100644 build-android/src/datatypes.c create mode 100644 build-android/src/datatypes.h create mode 100644 build-android/src/debuginfo.h create mode 100644 build-android/src/draw.c create mode 100644 build-android/src/draw.h create mode 100644 build-android/src/environment.c create mode 100644 build-android/src/environment.h create mode 100644 build-android/src/game.c create mode 100644 build-android/src/game.h create mode 100644 build-android/src/main.c create mode 100644 build-android/src/main.cpp create mode 100644 build-android/src/physics.c create mode 100644 build-android/src/physics.h create mode 100644 build-android/src/types.h create mode 100644 build-android/src/vect.c create mode 100644 build-android/src/vect.h diff --git a/build-android/CMakeLists.txt b/build-android/CMakeLists.txt new file mode 100644 index 0000000..f96ec53 --- /dev/null +++ b/build-android/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.4.1) + +string(TOLOWER ${CMAKE_BUILD_TYPE} ANDROID_BUILD_DIR) +set(DISTRIBUTION_DIR ${CMAKE_SOURCE_DIR}/android/distribution/android/SDL2/intermediates/ndkBuild) +set(SOURCE_FILES src/audio.c src/colours.c src/datatypes.c src/environment.c src/main.c src/vect.c src/basic-lib.c src/controlscheme.c src/draw.c src/game.c src/physics.c) +set(SDL_LOCATION ${CMAKE_SOURCE_DIR}/external/SDL2) + +add_library( SDL2 SHARED IMPORTED ) +add_library( SDL2_mixer SHARED IMPORTED ) +add_library( SDL2_ttf SHARED IMPORTED ) +set_target_properties(SDL2 PROPERTIES IMPORTED_LOCATION +${DISTRIBUTION_DIR}/${ANDROID_BUILD_DIR}/obj/local/${ANDROID_ABI}/libSDL2.so) +set_target_properties(SDL2_mixer PROPERTIES IMPORTED_LOCATION +${DISTRIBUTION_DIR}/${ANDROID_BUILD_DIR}/obj/local/${ANDROID_ABI}/libSDL2_mixer.so) +set_target_properties(SDL2_ttf PROPERTIES IMPORTED_LOCATION +${DISTRIBUTION_DIR}/${ANDROID_BUILD_DIR}/obj/local/${ANDROID_ABI}/libSDL2_ttf.so) + +include_directories(${SDL_LOCATION}/SDL2/SDL2/incl2) +include_directories(${SDL_LOCATION}/SDL2/include) +include_directories(${SDL_LOCATION}/SDL2_mixer) +include_directories(${SDL_LOCATION}/SDL2_ttf) + +add_library( main SHARED ${SDL_LOCATION}/SDL2/src/main/android/SDL_android_main.c ${SOURCE_FILES} ) + +target_link_libraries( main SDL2 SDL2_mixer SDL2_ttf) diff --git a/build-android/LICENSE b/build-android/LICENSE new file mode 100644 index 0000000..03cbdbe --- /dev/null +++ b/build-android/LICENSE @@ -0,0 +1,20 @@ + +hello-sdl2-android +Copyright (C) 2017 Paul Vallet + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + diff --git a/build-android/README.md b/build-android/README.md new file mode 100644 index 0000000..8a1c708 --- /dev/null +++ b/build-android/README.md @@ -0,0 +1,56 @@ +# hello-sdl2-android + +## Synopsis + +This project is an update of the hello world project here https://github.com/stephen47/android-sdl2-gradle-template, as I couldn't find a good way to integrate a CMake built C++ project to an android application. +It features a sample hello world using SDL and SDL_image. + +## Requirements +- JDK and JRE 8 +- Android SDK and NDK (with Android Build-tools 28.0.3 and Android Platform API 28, though these are configurable) +- ANDROID_HOME and ANDROID_NDK_HOME environment variables set (I did this in /etc/environment) + +## Compiling the sample program (command line) + +You can download the dependencies (SDL2 and SDL2_image), compile your program and then install it on a connected device with the following commands: +``` +./get_dependencies +cd android +./gradlew assembleDebug +./gradlew installDebug +``` + +## Compiling the sample program (Android Studio 3.2.1) + +First download the dependencies (SDL2 and SDL2_image) as above or manually like below. Then open the ./android folder as an existing project in Android Studio. + +## Downloading dependencies manually + +Download the latest source release from SDL and SDL_image websites: + +https://www.libsdl.org/download-2.0.php +https://www.libsdl.org/projects/SDL_image/ + +Unzip it, put the SDL2-x.x.x and SDL2_image-x.x.x folders in `external/SDL2` and rename them to SDL2 and SDL2_image so your project folder looks like this: +``` ++ android ++ external +| + SDL2 +| | + Android.mk +| | | SDL2 +| | | SDL2_image +``` + +There, it's done! + +## Thanks + +Most of the code here is inspired by these repositories: + +https://github.com/stephen47/android-sdl2-gradle-template (Android + gradle) + +https://github.com/suikki/simpleSDL/ (Android + CMake) + +The example libSDL2 program which draws the square on screen was found at https://stackoverflow.com/questions/21890627/drawing-a-rectangle-with-sdl2/21903973#21903973. + +The Google NDK example projects were very helpful: https://github.com/googlesamples/android-ndk diff --git a/build-android/android/.gitignore b/build-android/android/.gitignore new file mode 100644 index 0000000..b16bce7 --- /dev/null +++ b/build-android/android/.gitignore @@ -0,0 +1,3 @@ +distribution/ +.gradle/ +.idea/ diff --git a/build-android/android/SDL2/.gitignore b/build-android/android/SDL2/.gitignore new file mode 100644 index 0000000..8375cf7 --- /dev/null +++ b/build-android/android/SDL2/.gitignore @@ -0,0 +1 @@ +.externalNativeBuild/ diff --git a/build-android/android/SDL2/build.gradle b/build-android/android/SDL2/build.gradle new file mode 100644 index 0000000..d54e243 --- /dev/null +++ b/build-android/android/SDL2/build.gradle @@ -0,0 +1,38 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 28 + buildToolsVersion "28.0.3" + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + ndk { + abiFilters "armeabi-v7a" + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + ndkBuild { + path '../../external/SDL2/Android.mk' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + compile 'com.android.support:appcompat-v7:28.0.0' + testCompile 'junit:junit:4.12' +} diff --git a/build-android/android/SDL2/proguard-rules.pro b/build-android/android/SDL2/proguard-rules.pro new file mode 100644 index 0000000..c662747 --- /dev/null +++ b/build-android/android/SDL2/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /home/luap/Logiciels/android-sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# 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 diff --git a/build-android/android/app/.gitignore b/build-android/android/app/.gitignore new file mode 100644 index 0000000..8375cf7 --- /dev/null +++ b/build-android/android/app/.gitignore @@ -0,0 +1 @@ +.externalNativeBuild/ diff --git a/build-android/android/app/build.gradle b/build-android/android/app/build.gradle new file mode 100644 index 0000000..217465d --- /dev/null +++ b/build-android/android/app/build.gradle @@ -0,0 +1,45 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + buildToolsVersion "28.0.3" + defaultConfig { + applicationId "pvallet.com.github.hello_sdl2" + minSdkVersion 21 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + externalNativeBuild { + cmake { + cppFlags "-std=c++11 -frtti -fexceptions" + } + } + ndk { + abiFilters "armeabi-v7a" + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + cmake { + path "../../CMakeLists.txt" + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + compile 'com.android.support:appcompat-v7:28.0.0' + // Uncomment this line to build SDL2 + // Uncomment a line in ../gradle.settings as well + compile project(':SDL2') + testCompile 'junit:junit:4.12' +} diff --git a/build-android/android/app/proguard-rules.pro b/build-android/android/app/proguard-rules.pro new file mode 100644 index 0000000..c662747 --- /dev/null +++ b/build-android/android/app/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /home/luap/Logiciels/android-sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# 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 diff --git a/build-android/android/build.gradle b/build-android/android/build.gradle new file mode 100644 index 0000000..b9278c2 --- /dev/null +++ b/build-android/android/build.gradle @@ -0,0 +1,31 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +// Instead of having a build dir inside android app dir use the same project top level build dir to reduce clutter +buildDir = file("distribution/${rootProject.name}/${project.name}") +subprojects { + buildDir = file("../distribution/${rootProject.name}/${project.name}") +} + +allprojects { + repositories { + google() + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/build-android/android/gradle.properties b/build-android/android/gradle.properties new file mode 100644 index 0000000..aac7c9b --- /dev/null +++ b/build-android/android/gradle.properties @@ -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=-Xmx1536m + +# 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 diff --git a/build-android/android/gradle/wrapper/gradle-wrapper.jar b/build-android/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..13372aef5e24af05341d49695ee84e5f9b594659 GIT binary patch literal 53636 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ⅅ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 H-+%o-(+5aQ literal 0 HcmV?d00001 diff --git a/build-android/android/gradle/wrapper/gradle-wrapper.properties b/build-android/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..caeb3ee --- /dev/null +++ b/build-android/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Jul 05 15:54:11 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip diff --git a/build-android/android/gradlew b/build-android/android/gradlew new file mode 100755 index 0000000..9d82f78 --- /dev/null +++ b/build-android/android/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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 +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# 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 + +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" ] ; 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 + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/build-android/android/gradlew.bat b/build-android/android/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/build-android/android/gradlew.bat @@ -0,0 +1,90 @@ +@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 + +@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= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@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 Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_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=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +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 diff --git a/build-android/android/settings.gradle b/build-android/android/settings.gradle new file mode 100644 index 0000000..031d202 --- /dev/null +++ b/build-android/android/settings.gradle @@ -0,0 +1,4 @@ +include ':app' +// Uncomment this line to build SDL2 +// Uncomment the dependency line in app/build.gradle as well +include ':SDL2' diff --git a/build-android/get_dependencies.sh b/build-android/get_dependencies.sh new file mode 100755 index 0000000..5b73224 --- /dev/null +++ b/build-android/get_dependencies.sh @@ -0,0 +1,17 @@ +#!/bin/bash -x + +pushd external/SDL2 + +wget https://www.libsdl.org/release/SDL2-2.0.8.zip +unzip -q SDL2-2.0.8.zip +mv SDL2-2.0.8 SDL2 +rm SDL2-2.0.8.zip + +wget https://www.libsdl.org/projects/SDL_image/release/SDL2_image-2.0.3.zip +unzip -q SDL2_image-2.0.3.zip +mv SDL2_image-2.0.3 SDL2_image +rm SDL2_image-2.0.3.zip + +popd +# No need to copy SDLActivity.java et al, repo contains those from 2.0.8 +# cp external/SDL2/SDL2/android-project/app/src/main/java/org/libsdl/app/*.java android/app/src/main/java/org/libsdl/app/ diff --git a/build-android/res/TerminusTTF.ttf b/build-android/res/TerminusTTF.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d302264a1cd10ccd6d96d410add3ac93f03af651 GIT binary patch literal 453248 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%0AmI52CVM(Fznv#olx<&&6fWb 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 GVgCmP7!C&j literal 0 HcmV?d00001 diff --git a/build-android/src/audio.c b/build-android/src/audio.c new file mode 100644 index 0000000..32949b3 --- /dev/null +++ b/build-android/src/audio.c @@ -0,0 +1,226 @@ +#include "audio.h" +#include +#include +#include + +void generate_audio(void); +struct game_sounds game_sounds; + +Uint8 big_hum(unsigned int t); +Uint8 func0(unsigned int t); + +int mute = false; + + void fill_audio(void *func, Uint8 *stream, int len) { + Uint8 (*wavegen)(unsigned int) = func; + + unsigned int t = 1; + for(int i = 0; i < len; i++,t++) { + stream[i] = wavegen(t); + } +} + +void callbackfn3(void *unused, Uint8 *stream, int len) { + static int t = 1; + for(int i = 0; i < len; i++,t++) { + stream[i] = big_hum(t); + stream[i] = 0; + } +} + +void start_audio(void) { + + //audio_format.callback = callbackfn3; + /* just for playing background music + if (SDL_OpenAudio(&audio_format, NULL)) { + fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError()); + exit(1); + } + */ + //SDL_PauseAudio(0); + if (Mix_OpenAudio(G_AUDIO_SFREQ,AUDIO_U8,1,G_AUDIO_BSIZE) < 0) { + fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError()); + exit(1); + } + + if (Mix_AllocateChannels(20) < 0) { + fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError()); + } + + generate_audio(); + +} + +void +play_game_sound(Mix_Chunk *chunk, int len, int channel) +{ + if (mute || Mix_Playing(channel)) { + return; + } + + if(Mix_PlayChannelTimed(channel, chunk, -1, len) < 0) + fprintf(stderr, "Unable to play audio: %s\n", SDL_GetError()); + //Mix_FadeInChannelTimed(channel, chunk, -1, 1, len); +} + +Uint8 +pitchshift_hit(unsigned int t, void *arg) +{ + double *hitt = arg; + double hit = *hitt; + hit = 6 - hit; + int lower = 10 + 2 * (hit > 6 ? 6 : hit); + int higher = 1 + (hit > 6 ? 6 : hit); +// printf("mag %f higher %d lower %d\n", hit, higher, lower); + + const double R=8000; // sample rate (samples per second) + const double C=125; // frequency of middle-C (hertz) + const double F=R/256; // bytebeat frequency of 1*t due to 8-bit truncation (hertz) + const double V=127; // a volume constant + double volume = (((t / 3000) > 2) ? 2 : t / 3000); + + return ((sin(t*2*M_PI/R*(125 + hit))+1)*10 + (sin(t*2*M_PI/R*(250 + hit))+1)*(50 + hit) + (sin(t*2*M_PI/R*(900 + hit))+1)*100 + (sin(t*2*M_PI/R*(150+ hit))+1)*5) ; + + return ((t < 3000) * ((t % higher) * (3000 - t % 3000) * 0.00351)); + //+ ((t < 3500) * ((3500 - t % 3500)) * 0.0002 * (t % lower)); +} + +void +synthplay_now(Uint8 (*bbgen)(unsigned int t, void *), void *arg, int len) +{ + static Mix_Chunk *new_chunk = NULL; + + if (Mix_Playing(1)) { + return; + } + + if (new_chunk) + Mix_FreeChunk(new_chunk); + + int generate_len = len * (512); + + Uint8 raw_data[generate_len]; + + unsigned int t = 1; + for(int i = 0; i < generate_len; i++,t++) { + raw_data[i] = bbgen(t, arg); + } + + new_chunk = Mix_QuickLoad_RAW(raw_data, generate_len); + Mix_PlayChannelTimed(1, new_chunk, -1, len); +} + + + +void callbackfn2(void *unused, Uint8 *stream, int len) { + static int t = 1; + int i; + int yeet = rand(); + for(i=0; i < len; i++,t++) { + stream[i] = func0(t); + } +} + + +void callbackfn(void *unused, Uint8 *stream, int len) +{ + static int t = 0; + int i; + int yeet = rand(); + for( i=0; i < len; i++,t++) { + /* + stream[i] = (t*5&(t>>7))|(t*3&(t>>8)); + */ + //stream[i] = (( t >> 10 ) & 42) * t; + stream[i] = (t & yeet) % 73; + } +} + +/* Waveform Generators */ + +Uint8 big_hum(unsigned int t) { + return ( (sin(t*2*3.14159265/8000*200)+1) * 20) + ( (sin(t*2*3.14159265/8000*110)+1) * 20) + ( (sin(t*2*3.14159265/8000*230)+1) * 20) + ( (sin((t % 80)*2*3.14159265/8000 * 6)+1) * 12); +} + +unsigned int sine_wave_sound(unsigned int t) { + const double R=8000; // sample rate (samples per second) + const double C=261.625565; // frequency of middle-C (hertz) + const double F=R/256; // bytebeat frequency of 1*t due to 8-bit truncation (hertz) + const double V=127; // a volume constant + return (sin(t*2*M_PI/R*C)+1)*V; +} + +Uint8 winch_sound(unsigned t) { + return (((t % 80) | (t % 36) | (6 * (t % 6))) + 20) * 1.6; + return ((t % 80) | (t % 36) | (6 * (t % 6))); +} + +Uint8 beat_2(unsigned t) { + return ((((t % 250) / 2)) | (t % 129)) % 110; +} + +Uint8 func0(unsigned t) { + int note = 18; + if (!(t % 1240)) { + note = (1 + rand()) % 18 + 1; + } + + fflush(stdout); + return (t % note); +} + +Uint8 func1(unsigned int t) { + int note = 10; + if (!(t % 8000)) { + note = (1 + rand()) % 10 + 5; + } + return ((t & 110) % 73 | (t % 1711)); +} + +Uint8 collision_test(unsigned int t) { + // try not to loop + return ((t < 3000) * ((t % 5) * (3000 - t % 3000) * 0.00351)) + + ((t < 3500) * ((3500 - t % 3500)) * 0.0002 * (t % 14)); + return ((t < 3000) * ((t % 5) * (3000 - t % 3000) * 0.00351)) + + ((4000 - t % 4000) * 0.0003 * (t % 128)) + + ((t < 3500) * ((3500 - t % 3500)) * 0.0002 * (t % 64)); // has a big low sound + return (t % 4) * (4000 - t % 4000) * 0.01; +} + + +Uint8 boop_noise(unsigned int t) { + return ((t >> 10) & 42) * t; +} + +Uint8 rope_attach(unsigned int t) { + return (((2000 - (t % 2000)))/40 * (t % 20) | (t % 44) + 20) * 1.6; +} + +void generate_audio(void) { + const int sfx_len_ms = 1000; + unsigned int sfx_len = (G_AUDIO_SFREQ/1000) * G_AUDIO_BSIZE * sfx_len_ms; + sfx_len = G_AUDIO_BSIZE; + + Uint8 *raw_data = malloc(sfx_len); + fill_audio(winch_sound, raw_data, sfx_len); + game_sounds.rope_pull = Mix_QuickLoad_RAW(raw_data, sfx_len); + Mix_Volume(BC_ROPE_PULL, MIX_MAX_VOLUME * 0.3); + + Uint8 *raw_data2 = malloc(sfx_len); + fill_audio(collision_test, raw_data2, sfx_len); + game_sounds.collision = Mix_QuickLoad_RAW(raw_data2, sfx_len); + Mix_Volume(BC_COLLISION, MIX_MAX_VOLUME * 1.2); + + Uint8 *raw_data3 = malloc(sfx_len); + fill_audio(big_hum, raw_data3, sfx_len); + game_sounds.background = Mix_QuickLoad_RAW(raw_data3, sfx_len); + Mix_Volume(BC_MUSIC, MIX_MAX_VOLUME * 0.1); + + Uint8 *raw_data4 = malloc(sfx_len); + fill_audio(rope_attach, raw_data4, sfx_len); + game_sounds.rope_attach = Mix_QuickLoad_RAW(raw_data4, sfx_len); + Mix_Volume(BC_ROPE_ATTACH, MIX_MAX_VOLUME * 0.1); + +} + + diff --git a/build-android/src/audio.h b/build-android/src/audio.h new file mode 100644 index 0000000..72d96cf --- /dev/null +++ b/build-android/src/audio.h @@ -0,0 +1,37 @@ + +#ifndef AUDIO_H +#define AUDIO_H + +#include +#include "types.h" +#include +#include + +#define G_AUDIO_SFREQ 8000 +#define G_AUDIO_BSIZE 512 + +struct game_sounds { + unsigned int t; + Mix_Chunk *collision; + Mix_Chunk *rope_attach; + Mix_Chunk *rope_pull; + Mix_Chunk *background; + /* Looping samples (need backbuffer to fill while playing ) */ + Mix_Chunk *menu; +}; + +extern struct game_sounds game_sounds; + +void play_game_sound(Mix_Chunk *chunk, int len, int channel); +void start_audio(void); + +Uint8 pitchshift_hit(unsigned int t, void *arg); +void synthplay_now(Uint8 (*bbgen)(unsigned int t, void *), void *arg, int len); + +enum audio_channel_names { + BC_COLLISION = 1, + BC_ROPE_PULL = 2, + BC_ROPE_ATTACH = 3, + BC_MUSIC = 4, +}; +#endif diff --git a/build-android/src/basic-lib.c b/build-android/src/basic-lib.c new file mode 100644 index 0000000..e46df59 --- /dev/null +++ b/build-android/src/basic-lib.c @@ -0,0 +1,97 @@ +#include +#include +#include + +// #define LOGGING 1 +// #define log_var(string, content) if (LOGGING) {time_t current_time; time(¤t_time); printf("%s",asctime(localtime(¤t_time))); printf(string, content);} +// #define log_str(string) if (LOGGING) {time_t current_time; time(¤t_time); puts(asctime(localtime(¤t_time))); puts(string);} + +void enumerate_string(char test_array[]) { + char *ptr; + ptr = &(test_array[0]); + for (int i=0; i<5; i++) { + printf("%c", *(ptr + i)); + } +} + +int copy_str(char * out_string, char * string) { + /* A drop in replacement for strcpy() from */ + int len = 0; + while (*(string + len) != '\0') { + *(out_string + len) = *(string + len); + len++; + } + *(out_string + len) = '\0'; + return (1); +} + +int get_str_len(char * string) { + int len = 0; + while (*(string + len) != '\0') { + len++; + } + return(len); +} + +void replace_char(char * array, char * charac) { + int length; + length = sizeof(*array)/sizeof(char); + char combined[length]; + + int i = 0; + int counter = 0; + + while (*(array + i) != '\0') { + if (*(array + i) != *charac) { + *(array + counter) = *(array + i); + counter++; + } + i++; + } + *(array + counter + 1) = '\0'; +} + +void join_strings(char * return_val, char * str_1, char * str_2) { + + int len1 = get_str_len(str_1); + int len2 = get_str_len(str_2); + + int length = len1 + len2; + char combined[length+1]; + int i = 0; + int j = 0; + + while (*(str_1 + i) != '\0') { + combined[i] = *(str_1 + i); + i++; + } + + while (*(str_2 + j) != '\0') { + combined[len1+j] = *(str_2 + j); + j++; + } + copy_str(return_val, combined); +} + +void get_time(void) { + char time_str[100]; + time_t current_time; + time(¤t_time); + copy_str(time_str, asctime(localtime(¤t_time))); + + strcat(time_str, "hello world"); + //replace_char(&(time_str[0]), "\n"); + printf("%s", time_str); + + char string[20]; + copy_str(string, "NONEWLINEhello"); + + + replace_char(&(time_str[0]), "\n"); + char newstr[120]; + join_strings(&(newstr[0]), &(time_str[0]), &(string[0])); + replace_char(&(newstr[0]), "\n"); + + printf("%s", newstr); +} + diff --git a/build-android/src/colours.c b/build-android/src/colours.c new file mode 100644 index 0000000..9a88792 --- /dev/null +++ b/build-android/src/colours.c @@ -0,0 +1,238 @@ +#include "colours.h" + +struct colour get_random_color(unsigned int seed) { + srand(seed); + int red = rand() % 255; + int blue = rand() % 255; + int green = rand() % 255; + + struct colour col; + col.r = red; + col.g = green; + col.b = blue; + return col; +} + +double m_min(double arr[], int len) { + double largest = arr[0]; + for (int i = 0; i < len; i ++) { + if (arr[i] < largest) { + largest = arr[i]; + } + } + return largest; +} + +double m_max(double arr[], int len) { + double largest = arr[0]; + for (int i = 0; i < len; i ++) { + if (arr[i] > largest) { + largest = arr[i]; + } + } + return largest; +} + +bool m_equal(double a, double b) { + return (a - b) * (a - b) < 0.00001; +} + +// https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB +// h = [0,360], s = [0,1], v = [0,1] +// if s == 0, then h = -1 (undefined) +struct colour get_hs_l_v(struct colour c, enum colour_space sp) { + struct colour ret; + memset(&ret, 0, sizeof(struct colour)); + double r = (double)c.r / 255; + double g = (double)c.g / 255; + double b = (double)c.b / 255; + + double arr[] = {r, g, b}; + double max = m_max(arr, 3); + double min = m_min(arr, 3); + + ret.v = max; + + double chroma = max - min; + + double lum = (max + min) / 2; + ret.l = lum; + + if (m_equal(chroma, 0)) { + ret.h = 0; + } else if (m_equal(ret.v,r)) { + ret.h = (g - b) / chroma; + } else if (m_equal(max,g)) { + ret.h = 2 + (b - r) / chroma; + } else if (m_equal(max,b)) { + ret.h = 4 + (r - g) / chroma; + } else { + printf("NOPE BAD ERROR\n"); + } + + ret.h *= 60; + ret.h = fmod(ret.h, 360); + if (ret.h < 0) { + ret.h += 360; + } + + if (sp == CS_HSV) { + if (m_equal(max,0)) { + ret.s = 0; + } else { + ret.s = chroma / max; + } + } else { + double arr[] = {ret.l, 1 - ret.l}; + if (m_equal(0, lum) || m_equal(1, lum)) { + ret.s = 0; + } else { + ret.s = (ret.v - ret.l) / m_min(arr, 2); + } + } + ret.sp = CS_HSL; + return ret; +} + +struct colour get_hsl(struct colour c) { + return get_hs_l_v(c, CS_HSL); +} + +struct colour get_hsv(struct colour c) { + return get_hs_l_v(c, CS_HSV); +} + +double magic_hsv_function(int n, struct colour c) { + double res = 0; + double k = fmod(n + (c.h / 60), 6); + double arr[] = {k, 4 - k, 1}; + double k_b = m_min(arr, 3); + double k_c = 0 > k_b ? 0 : k_b; + res = c.v - c.v * c.s * k_c; + return res; +} + +struct colour get_rgb_from_hsv(struct colour c) { + + double r = magic_hsv_function(5, c); + double g = magic_hsv_function(3, c); + double b = magic_hsv_function(1, c); + + struct colour res; + res.r = round(r * 255); + res.g = round(g * 255); + res.b = round(b * 255); + return res; + +} + +double magic_hsl_function(int n, struct colour c) { + double arr[] = {c.l, 1-c.l}; + double a = c.s * ( c.l < (1-c.l) ? c.l : 1 - c.l ); + double k = fmod(n + c.h / 30, 12); + + double b[] = {k - 3, 9 -k, 1}; + double d[] = {-1, m_min(b, 3)}; + return c.l - a * (d[0] > d[1] ? d[0] : d[1]); +} + +struct colour get_rgb_from_hsl(struct colour c) { + double r = magic_hsl_function(0, c); + double g = magic_hsl_function(8, c); + double b = magic_hsl_function(4, c); + + struct colour res; + res.r = round(r * 255); + res.g = round(g * 255); + res.b = round(b * 255); + return res; +} + +struct colour get_rgb(struct colour c) { + if (c.sp == CS_HSL) { + return get_rgb_from_hsl(c); + } + if (c.sp == CS_HSV) { + return get_rgb_from_hsv(c); + } + + struct colour d; + memset(&d, 0, sizeof(struct colour)); + return d; +} + +int compare_perceived_lum(const void* c1, const void* c2) { + struct colour rgb_c1 = *(struct colour*)c1; + struct colour rgb_c2 = *(struct colour*)c2; + + double lum1 = 0.299*rgb_c1.r + 0.587*rgb_c1.g + 0.114*rgb_c1.b; + double lum2 = 0.299*rgb_c2.r + 0.587*rgb_c2.g + 0.114*rgb_c2.b; + return lum1 - lum2; +} + +struct colour *get_adjacent(struct colour base, int deg, int num) { + struct colour col = get_hsl(base); + num += 1; + + struct colour* colours = calloc(sizeof(struct colour), num); + + for (int i = 0; i < num; i++) { + int m = (i % 2 == 0) ? -i : i; + + colours[i] = col; + colours[i].h += m * deg; + colours[i].h += fmod(colours[i].h, 360); + } + + struct colour *ret_colours = calloc(num, sizeof(struct colour) * num); + + for (int i = 0; i < num; i++) { + ret_colours[i] = get_rgb(colours[i]); + } + + // sort from dark to bright + qsort(ret_colours, num, sizeof(struct colour), compare_perceived_lum); + + free(colours); + return ret_colours; +} + +void print_colour(struct colour c) { + char *colour = calloc(20, sizeof(char)); + sprintf(colour, "#%02x%02x%02x", c.r, c.g, c.b); + + printf("\x1b[38;2;%d;%d;%dm%s\x1b[0m\n", c.r, c.g, c.b, colour); + free(colour); +} + +void test(int seed) { + struct colour c = get_random_color(seed); + //print_colour(c); + struct colour hsl = get_hsl(c); + struct colour hsv = get_hsv(c); + /*printf("RGB: %d %d %d\n",c.r,c.g,c.b);*/ + /*printf("HSL: %f %f %f\n",hsl.h, hsl.s, hsl.l);*/ + /*printf("HSV: %f %f %f\n",hsv.h, hsv.s, hsv.v);*/ + + struct colour *adj = get_adjacent(c, 5, 4); + for (int i = 0; i < 5; i++) { + print_colour(adj[i]); + } + + free(adj); + printf("\n"); +} + +void test_print_wheel() { + struct colour c; + c.s = 0.999; + c.v = 0.99; + c.l = 0.5; + c.sp = CS_HSV; + for(int i = 0; i < 360; i+=5) { + c.h = (double)i; + struct colour rgb = get_rgb(c); + print_colour(rgb); + + } +} diff --git a/build-android/src/colours.h b/build-android/src/colours.h new file mode 100644 index 0000000..849cca2 --- /dev/null +++ b/build-android/src/colours.h @@ -0,0 +1,43 @@ + +#ifndef COSMOPOLITAN +#include +#include +#include +#include +#include +#endif + +#ifndef H_COLOURS +#define H_COLOURS + +enum colour_space { + CS_INVALID = 0, + CS_RGB = 1, // default to RGB + CS_HSV = 2, + CS_HSL = 3, +}; + +struct colour { + double h; + double s; + double v; + double l; + int r; + int g; + int b; + enum colour_space sp; +}; + +struct colour get_random_color(unsigned int seed); + +// doesn't support hsl-hsv or converse, conversion +struct colour get_hsl(struct colour c); +struct colour get_hsv(struct colour c); + +struct colour get_rgb(struct colour c); + +struct colour *get_adjacent(struct colour base, int deg, int num); + +void print_colour(struct colour c); + +#endif diff --git a/build-android/src/controlscheme.c b/build-android/src/controlscheme.c new file mode 100644 index 0000000..a24365d --- /dev/null +++ b/build-android/src/controlscheme.c @@ -0,0 +1,106 @@ + +#include "controlscheme.h" +#include + +#ifdef LOAD_INPUTS +#include +#endif + +struct InputMap input_map; + +#define INPUT_MAP_ID 83921864 +#define INPUT_MAP_VERSION 0 +#define CONTROLS_FNAME "controls.bin" + +struct InputMap_Ser { + unsigned int id; + unsigned int ver; + struct InputMap input_map; +}; + +int load_controls(void); + +void get_input_map(void) { + +#ifdef LOAD_INPUTS + if (load_controls()) { + return; + } +#endif + + input_map.player_down = SDL_SCANCODE_S; + input_map.player_up = SDL_SCANCODE_W; + input_map.player_right = SDL_SCANCODE_D; + input_map.player_left = SDL_SCANCODE_A; + input_map.player_rope = SDL_SCANCODE_E; + input_map.player_pull_rope = SDL_SCANCODE_Q; + input_map.mute = SDL_SCANCODE_M; + input_map.pause = SDL_SCANCODE_ESCAPE; + input_map.quit = SDL_SCANCODE_Q; + input_map.goto_level = SDL_SCANCODE_G; + input_map.mouse_attach_rope_pull = SDL_BUTTON_LEFT; + input_map.mouse_attach_rope = SDL_BUTTON_RIGHT; +} + +#ifdef LOAD_INPUTS +struct Buffer { + size_t len; + char *data; +}; + +struct Buffer read_whole_file(char const *file_name) { + struct stat sb; + struct Buffer b = {}; + + if (stat (file_name, & sb) != 0) { + return b; + } + + char *buffer = malloc(sb.st_size + 1); + + FILE *f = fopen(file_name, "r"); + size_t bytes_read = fread(buffer, sizeof (unsigned char), sb.st_size, f); + if (bytes_read != sb.st_size) { + fclose(f); + free(buffer); + return b; + } + + fclose(f); + + b.data = buffer; + b.len = bytes_read; + return b; +} + +int load_controls(void) { + struct Buffer b = read_whole_file(CONTROLS_FNAME); + + if (b.len != sizeof (struct InputMap_Ser)) { + return 0; + } + + struct InputMap_Ser *im = (struct InputMap_Ser *)b.data; + + if (im->id != INPUT_MAP_ID) + return 0; + + if (im->ver != INPUT_MAP_VERSION) + return 0; + + memcpy(&input_map, &im->input_map, sizeof(struct InputMap)); + return 1; +} + +void save_controls(void) { + FILE *f = fopen(CONTROLS_FNAME, "w"); + + struct InputMap_Ser im; + im.id = INPUT_MAP_ID; + im.ver = INPUT_MAP_VERSION; + im.input_map = input_map; + + fwrite(&im, sizeof(unsigned char), sizeof(struct InputMap_Ser), f); + fclose(f); +} +#endif diff --git a/build-android/src/controlscheme.h b/build-android/src/controlscheme.h new file mode 100644 index 0000000..78da83c --- /dev/null +++ b/build-android/src/controlscheme.h @@ -0,0 +1,35 @@ +#include + +#ifndef CRTL_H +#define CRTL_H + +enum Game_Interaction { + PLAYER_UP, + PLAYER_DOWN, + PLAYER_RIGHT, + PLAYER_LEFT +}; + + +struct InputMap { + SDL_Scancode player_up; + SDL_Scancode player_down; + SDL_Scancode player_left; + SDL_Scancode player_right; + SDL_Scancode player_rope; + SDL_Scancode player_pull_rope; + SDL_Scancode mute; + SDL_Scancode quit; + SDL_Scancode pause; + SDL_Scancode goto_level; + Uint8 mouse_attach_rope_pull; + Uint8 mouse_attach_rope; +}; + +extern struct InputMap input_map; + +void get_input_map(void); +void save_controls(void); + +#endif + diff --git a/build-android/src/datatypes.c b/build-android/src/datatypes.c new file mode 100644 index 0000000..534ea3a --- /dev/null +++ b/build-android/src/datatypes.c @@ -0,0 +1,159 @@ +#include "datatypes.h" +#include +#include + +ArrayList new_arlst(int num_elems) { + ArrayList new_list; + new_list.free = NULL; + new_list.size = 0; + new_list.capacity = num_elems; + new_list.items = calloc(num_elems, sizeof(void *)); + + return new_list; +} + +ArrayList new_arlst_wfree(int num_elems, void (*nfree)(void *ptr)) { + ArrayList new_list = new_arlst(num_elems); + new_list.free = nfree; + return new_list; +} + +ArrayList new_arlst_using_array(void **array, int length) { + ArrayList l = new_arlst(length); + free(l.items); + l.items = array; + return l; +} + +ArrayList new_arlst_using_array_wfree(void **array, int length, + void (*f)(void *)) { + ArrayList l = new_arlst_using_array(array, length); + l.free = f; + return l; +} + +ArrayList new_arlst_from_array(void *array, size_t elem_size, int length) { + ArrayList l = new_arlst(length); + l.free = free; + + for (int i = 0; i < length; i++) { + void *item = calloc(1, elem_size); + void *start = array + i * elem_size; + memmove(item, start, elem_size); + arlst_add(&l, item); + } + return l; +} + +void *arlst_get(ArrayList *l, int id) { + // out of bounds + if (id < 0 || id >= l->size) { + return 0; + } + + return l->items[id]; +} + +void * arlst_del(ArrayList *l, int id) { + void *item = arlst_get(l, id); + + if (!item) { + return NULL; + } + + l->size--; + // move elements backward + for (int i = id; i < l->size; i++) { + l->items[i] = l->items[i+1]; + } + + + // shrink capacity if neccessary + if (D_SHRINK_THRESHOLD != 0 + || l->size < (100 / D_SHRINK_THRESHOLD) * l->capacity) { + + size_t new_size = l-> size + (l->capacity * (100 / D_SHRINK_THRESHOLD) * + 100 / D_GROW_AMOUNT); + +// l->items = reallocarray(l->items, new_size, sizeof(void *)); + l->items = realloc(l->items, new_size * sizeof(void *)); + } + + return item; +} + +int arlst_add(ArrayList *l, void *item) { + int item_index = l->size; + + // grow if neccessary + if (l->size == l->capacity) { + size_t new_size = l->capacity + (100 / D_GROW_AMOUNT) * l->capacity; + l->capacity = new_size; + //l->items = (void **)reallocarray(l->items, new_size, sizeof(void *)); + l->items = (void **)realloc(l->items, new_size * sizeof(void *)); + } + + l->items[item_index] = item; + l->size++; + + return 0; +} + +void * arlst_pop(ArrayList *l) { + l->size--; + return l->items[l->size]; +} + +int arlst_push(ArrayList *l, void *item) { + return arlst_add(l, item); +} + +int arlst_destroy(ArrayList *l) { + if (l->free != NULL) { + for (int i = 0; i < l->size; i++) { + l->free(l->items[i]); + } + } + free(l->items); + return 0; +} + + +ListFiFo new_fifo(int start_size) { + ListFiFo l; + memset(&l, 0, sizeof(ListFiFo)); + l.inner = new_arlst(start_size); + l.size = &l.inner.size; + return l; +} + +void *fifo_get(ListFiFo *l) { + if (l->fifo_head >= l->inner.size) { + return NULL; + } + + void *value = arlst_get(&l->inner, l->fifo_head); + + if (value != NULL) { + l->fifo_head++; + } + + if (l->fifo_head == l->inner.capacity + || (l->fifo_head == l->inner.size + && l->inner.size >= D_MAX_QUEUE_LEN)) { + // start rewriting from the beginning + l->inner.size = 0; + l->fifo_head = 0; + // memset(l->inner.items, 0, sizeof(void *) * l->inner.capacity); + } + return value; +} + +/* It is up to the caller to free any data stored */ +int fifo_add(ListFiFo *l, void *i) { + if (l->inner.size >= D_MAX_QUEUE_LEN) { + return -1; + } + return arlst_add(&l->inner, i); +} + diff --git a/build-android/src/datatypes.h b/build-android/src/datatypes.h new file mode 100644 index 0000000..efb5ec2 --- /dev/null +++ b/build-android/src/datatypes.h @@ -0,0 +1,61 @@ +#ifndef DTYPE_H +#define DTYPE_H + +#include + +enum { + D_SHRINK_THRESHOLD = 50, // percentage of capacity + // 0 = never shrink + // 100 = always shrink + D_GROW_AMOUNT = 50, // percentage of size + // 0 = 1 = grow by 1 on every add + // 100 = double size whenever space runs out + D_MAX_QUEUE_LEN = 400 // maximum num of items allowed in a fifo +}; + +struct array_list { + size_t size; + size_t capacity; + + void **items; + void (*free)(void *ptr); // free function +}; + +struct list_fifo { + struct array_list inner; + size_t fifo_head; + size_t *size; +}; + +typedef struct array_list ArrayList; +typedef struct list_fifo ListFiFo; + +/* FIFOs */ + +ListFiFo new_fifo(int start_size); +int fifo_add(ListFiFo *l, void *i); +void *fifo_get(ListFiFo *l); + +/* Stacks */ + +int arlst_push(ArrayList *l, void *item); +void * arlst_pop(ArrayList *l); + +/* Array Lists */ + +// constructors +ArrayList new_arlst_from_array(void *array, size_t elem_size, int length); +ArrayList new_arlst_using_array_wfree(void **, int, void (*)(void *)); +ArrayList new_arlst_using_array(void **array, int length); +ArrayList new_arlst_wfree(int num_elems, void (*nfree)(void *ptr)); +ArrayList new_arlst(int num_elems); + +// destructors +int arlst_destroy(ArrayList *l); +void * arlst_del(ArrayList *l, int id); + +// functions +int arlst_add(ArrayList *l, void *item); +void *arlst_get(ArrayList *l, int id); + +#endif diff --git a/build-android/src/debuginfo.h b/build-android/src/debuginfo.h new file mode 100644 index 0000000..5870433 --- /dev/null +++ b/build-android/src/debuginfo.h @@ -0,0 +1,2 @@ + +#define SHOWCOLLISIONBOX 1 diff --git a/build-android/src/draw.c b/build-android/src/draw.c new file mode 100644 index 0000000..64cbdde --- /dev/null +++ b/build-android/src/draw.c @@ -0,0 +1,881 @@ +#include "draw.h" +#include "game.h" +#include "types.h" +#include +#include +#include + +Vect viewport_pos; +int width, height; + +bool high_contrast_mode = 0; + +/* generic rendering functions */ +int MAX_ONSCREEN_OBJECTS = 99; + +int num_onscreen = 0; + +double time_delta = 0; + +void render_texture_at(struct SDL_Renderer * ren, struct SDL_Texture * texture,int x, int y) { + /* draw a texture at x.y */ + + SDL_Rect destination; + destination.x = x; + destination.y = y; + + SDL_QueryTexture(texture, NULL, NULL, &destination.w, &destination.h); + + SDL_RenderCopy(ren, texture, NULL, &destination); +} + +/*SDL_Texture * load_image(SDL_Renderer * ren, char fname[]) {*/ + /*[> Load an image into a texture <]*/ + + /*SDL_Surface * surface = IMG_Load(fname) ;*/ + /*if(!surface) {*/ + /*printf("IMG_Load: %s\n", IMG_GetError());*/ + /*// handle error*/ + /*}*/ + + /*SDL_Texture * png_image = SDL_CreateTextureFromSurface(ren, surface);*/ + /*cleanup(surface, SURFACE);*/ + /*return (png_image);*/ +/*}*/ + + +double sigmoid(double x) { + return exp(x) / (exp(x) + 1); +} + +struct timespec get_now_d() { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + if (now.tv_nsec >= 1000000000) { + now.tv_nsec -= 1000000000; + } + return now; +} + +Vect in_view(Vect V) { + Vect ret; + ret.x = V.x -viewport_pos.x; + ret.y = V.y -viewport_pos.y; + return ret; +} + +/* Game Specific rendering functions */ +void draw_player(SDL_Renderer * ren, int x, int y, bool red) { + // translate to viewport + + Vect V; + V.x = x; V.y = y; + V = in_view(V); + + /* draw the player as a coloured rect */ + SDL_Rect player_rect; + + player_rect.x = V.x -10; + player_rect.y = V.y -10; + + player_rect.w = 20; + player_rect.h = 20; + + struct colour c = get_scene_watch()->colours.bg; + + SDL_SetRenderDrawColor(ren, c.r, c.g, c.b, 255); + + SDL_RenderDrawRect(ren, &player_rect); + SDL_RenderFillRect(ren, &player_rect); + // draw strings + for (int i = 0; i < player.physics->num_strings; i++) { + if (!player.physics->strings[i].attached) { + continue; + } + Vect B = {x, y}; + B = in_view(B); + Vect E; + E = in_view(player.physics->strings[i].end_point); + SDL_RenderDrawLine(ren, B.x, B.y, E.x, E.y); + } + + + SDL_SetRenderDrawColor(ren, 0,0,0, 255); + +} + +void draw_floor(SDL_Renderer *ren, FloorPoly *poly, bool down) { + SDL_Point left; + SDL_Point right; + left.x = poly->left.x - viewport_pos.x; + + if (left.x > width) { + return; + } + + right.x = poly->right.x - viewport_pos.x; + if (right.x < 0) { + return; + } + + left.y = poly->left.y - viewport_pos.y; + right.y = poly->right.y - viewport_pos.y; + + if (left.y <= 0 && right.y <= 0) { + return; + } + if (left.y >= height && right.y >= height) { + return; + } + + SDL_SetRenderDrawColor(ren, 100,100,100, 255); + + int end = down ? height : 0; + + double m = (double)(right.y - left.y) / (double)(right.x - left.x); + double c = (double)left.y - ((double)left.x * m); + + for (int i = left.x; i <= right.x; i++) { + int y = (int)(m * i + c); + if (y > 0) { + SDL_RenderDrawLine(ren, i, y, i, end); + } + } + + SDL_SetRenderDrawColor(ren, 0,0,0, 255); +} + +// draw collision poly +void draw_collision_poly(SDL_Renderer* ren, Body *body) { + if (!SHOWCOLLISION) { + return; + } + + if (body->colliding) { + SDL_SetRenderDrawColor(ren, 255, 0, 0, 255); + } else { + SDL_SetRenderDrawColor(ren, 0, 255, 0, 255); + } + + // draw strings + for (int i = 0; i < body->num_strings; i++) { + if (!body->strings[i].attached) { + continue; + } + Vect B; + B = in_view(body->position); + Vect E; + E = in_view(body->strings[i].end_point); + SDL_RenderDrawLine(ren, B.x, B.y, E.x, E.y); + } + + if (body->collision_poly_size == 1) { + SDL_Rect dot; + Vect V; + V.x = body->collision_poly[0].x; + V.y = body->collision_poly[0].y; + Vect T = in_view(V); + dot.x = T.x; + dot.y = T.y; + dot.w = 4; + dot.h = 4; + SDL_RenderDrawRect(ren, &dot); + SDL_RenderFillRect(ren, &dot); + } else if (body->collision_poly_size == 2) { + int x_st = body->collision_poly[0].x -viewport_pos.x; + int y_st = body->collision_poly[0].y -viewport_pos.y; + int x_en = body->collision_poly[1].x -viewport_pos.x; + int y_en = body->collision_poly[1].y -viewport_pos.y; + SDL_RenderDrawLine(ren, x_st, y_st, x_en, y_en); + } else if (body->collision_poly_size > 2) { + int x_st = body->collision_poly[0].x -viewport_pos.x; + int y_st = body->collision_poly[0].y -viewport_pos.y; + int x_en, y_en; + for (int i = 1; i < body->collision_poly_size; i++) { + x_en = body->collision_poly[i].x -viewport_pos.x; + y_en = body->collision_poly[i].y -viewport_pos.y; + SDL_RenderDrawLine(ren, x_st, y_st, x_en, y_en); + x_st = x_en; + y_st = y_en; + } + x_en = body->collision_poly[0].x-viewport_pos.x; + y_en = body->collision_poly[0].y-viewport_pos.y; + SDL_RenderDrawLine(ren, x_st, y_st, x_en, y_en); + } + SDL_SetRenderDrawColor(ren, 0,0,0, 255); +} + +// draw indicator of forces acting on an object +void draw_forces(SDL_Renderer *ren, Body *body) { + if (!SHOWFORCES) { + return; + } + Vect start; + start.x = (int)body->position.x -viewport_pos.x; + start.y = (int)body->position.y -viewport_pos.y; + + Vect F; + for (int i = 0; i < body->num_motors; i++) { + if (!get_motor_active(body, i)) { + continue; + } + F.x += body->motors[i].x; + F.y += body->motors[i].y; + Vect end = vect_add(start, F); + SDL_SetRenderDrawColor(ren, 255,0,0, 255); + SDL_RenderDrawLine(ren, start.x, start.y, start.x, end.y); + SDL_SetRenderDrawColor(ren, 0,0,255, 255); + SDL_RenderDrawLine(ren, start.x, start.y, end.x, start.y); + } + + SDL_SetRenderDrawColor(ren, 0,0,0, 255); +} + +void draw_wall(SDL_Renderer *ren, Wall *wall) { + SDL_SetRenderDrawColor(ren, 120, 85, 188, 255); + int x_st, x_en, y_st, y_en; + x_st = wall->nodes[0].x + wall->physics->position.x -viewport_pos.x; + y_st = wall->nodes[0].y + wall->physics->position.y -viewport_pos.y; + for (int i = 1; i < wall->numNodes; i++) { + x_en = wall->nodes[i].x + wall->physics->position.x -viewport_pos.x; + y_en = wall->nodes[i].y + wall->physics->position.y -viewport_pos.y; + SDL_RenderDrawLine(ren, x_st, y_st, x_en, y_en); + x_st = x_en; + y_st = y_en; + } + x_en = wall->nodes[0].x + wall->physics->position.x -viewport_pos.x; + y_en = wall->nodes[0].y + wall->physics->position.y -viewport_pos.y; + SDL_RenderDrawLine(ren, x_st, y_st, x_en, y_en); + + SDL_SetRenderDrawColor(ren, 0,0,0, 255); +} + +int distance_colour(int x, int y, int x2, int y2, int blackpoint) { + + int dist = (int) sqrt((x2 - x)*(x2 - x) + (y2 - y)*(y2 - y) ); + + if (dist == 0) { + return 255; + } + if (dist > blackpoint) { + return 0; + } + + int frac = blackpoint / dist; + +// int frac = 255 * (int) sin((double)dist / blackpoint); + if (frac > 255) { + return 255; + } + + return frac; +} + +void new_update_viewport(Body const * const pl) { + int const xmargin = 100; // pixels + int const ymargin = 100; // pixels + int const xmovmagin = width / 4; + int const ymovmagin = height / 3; + double const xrange = width - (2 * xmargin); + double const yrange = height - (2 * ymargin); + double const x_max = 2; + double const y_max = 3; + static Vect target = {}; + static double dirchange = 0; + static Vect last_plpos = {}; + if (last_plpos.x == 0 && last_plpos.y == 0) + last_plpos = pl->position; + + static Vect lmove = {}; + + Vect iv = in_view(pl->position); + + Vect left_pos = {0, 2 * height / 4}; + left_pos = vect_add(pl->position, vect_scalar(left_pos, -1)); + + Vect right_pos = {7 * (width / 8), 2 * height / 4}; + right_pos = vect_add(pl->position, vect_scalar(right_pos, -1)); + + bool recover = false; + + Vect screen_dist = vect_add(in_view(pl->position), vect_scalar(lmove, -1)); + + static double swap = 0; + if (iv.x > width + || (iv.x < 0 + || iv.y > height + || iv.y < 0)) + recover = true; + + Vect delta = vect_add(vect_scalar(pl->position, -1), last_plpos); + double dist_change = vect_mag(delta); + if ((recover) //|| vect_mag((Vect){pl->vel.x, 0}) > 0.4 + || (dist_change > width / 100)) { + if (iv.x < width / 6) { + target = right_pos; + } else { + target = left_pos; + } + lmove = target; + + last_plpos = pl->position; + } + + if (recover) + viewport_pos = target; + // emergency + + /*if (!(iv.x > width / 3 && iv.x < (2 * (width / 3)))) {*/ + /*if (!(iv.y > height / 3 && iv.y < (2 * (height / 3)))) {*/ + + /*if ( true ||*/ + /*( in_view(pl->position).x > xmargin + xrange */ + /*|| in_view(pl->position).x < xmargin)*/ + /*) */ + + /*{*/ + + + /*double m = (width / 2) / (2.0 * x_max);*/ + /*double x = m * v;*/ + /*target.x = pl->position.x - x;*/ + + + /*}*/ + + //target.y = pl->position.y - height/2; // + y; + + /*}*/ + /*}*/ + + double v = pl->vel.x; + if (v > x_max) + v = x_max; + if (v < -x_max) + v = -x_max; + if (v < 0) + v = -v; + + // move towards target a set proportion + Vect urgency = vect_add(target, vect_scalar(pl->position, -1)); + Vect p = vect_add(target, vect_scalar(viewport_pos, -1)); + + double proportion = (sigmoid(time_delta) / 100); + proportion = 2 * (pl->vel.x * pl->vel.x / (width)); + proportion = proportion < 0 ? -proportion : proportion; + + proportion > 0.4 ? proportion = 0.4 : 0; + proportion < 0 ? proportion = 0.000001 : 0; + + p = vect_scalar(p, proportion); + viewport_pos = vect_add(viewport_pos, p); + + +} + +void update_viewport(Body *pl) { + float xmargin = 0.5; + float ymargin = 0.2; + + if (pl->position.x - viewport_pos.x > (1-xmargin) * width) { + viewport_pos.x = - ((1-xmargin) * width - pl->position.x); + } + + if (pl->position.x - viewport_pos.x < xmargin * width) { + viewport_pos.x = -(xmargin * width - pl->position.x); + } + + if (pl->position.y - viewport_pos.y > (1-ymargin) * height) { + viewport_pos.y = -((1-ymargin) * height - pl->position.y); + } + + if (pl->position.y - viewport_pos.y < ymargin * height) { + viewport_pos.y = -(ymargin * height - pl->position.y); + } +} + +void accel_update_viewport(Body *pl) { + const double accel = 0.0001; + const float xmargin = 0.5; + const float ymargin = 0.2; + + static Vect target_pos = (Vect) {.x=0, .y=0}; + static Vect vel = (Vect) {.x=0.0, .y=0.0}; + + + if (pl->position.x - viewport_pos.x > (1-xmargin) * width) { + target_pos.x = - ((1-xmargin) * width - pl->position.x); + } + + if (pl->position.x - target_pos.x < xmargin * width) { + target_pos.x = -(xmargin * width - pl->position.x); + } + + if (pl->position.y - target_pos.y > (1-ymargin) * height) { + target_pos.y = -((1-ymargin) * height - pl->position.y); + } + + if (pl->position.y - target_pos.y < ymargin * height) { + target_pos.y = -(ymargin * height - pl->position.y); + } + + viewport_pos = target_pos; + return; + + Vect delta = vect_add(target_pos, vect_scalar(viewport_pos, -1)); + printf("d %f %f\n", delta.x, delta.y); + printf("d %f\n", vect_mag(delta)); + + if ((vect_mag(delta) < 0.001 )) { + return; + } + + Vect dir = vect_scalar(delta, 1/vect_mag(delta)); + Vect vel_delta = vect_scalar(dir, accel * time_delta); + vel = vect_add(vel, vel_delta); + + Vect pos_delta = vect_scalar(vel, time_delta/2); + + viewport_pos = vect_add(viewport_pos, pos_delta); +} + +double interpolate_h(Vect left, Vect right, double x) { + static Vect v; + + double m = (double)(right.y - left.y) / (double)(right.x - left.x); + double c = (double)left.y - ((double)left.x * m); + + return x * m + c; +} + +void draw_environment(SDL_Renderer * ren, const struct environment *scene) { + struct colour c1 = scene->colours.bg; + struct colour c2 = scene->colours.fg1; + struct colour c3 = scene->colours.fg2; + struct colour c4 = scene->colours.fg3; + + /*c1->r = 255;*/ + /*c2->g = 255;*/ + /*c3->b = 255;*/ + /*c4->g = 255;*/ + /*c4->b = 255;*/ + + // background colour + SDL_SetRenderDrawColor(ren, c1.r, c1.g, c1.b, 255); + /*SDL_SetRenderDrawColor(ren, 255, 0, 0, 255);*/ + + SDL_Rect rect; + rect.x = 0; + rect.y = 0; + rect.w = width; + rect.h = height; + SDL_RenderFillRect(ren, &rect); + +/* + * double m = (double)(right.y - left.y) / (double)(right.x - left.x); + * double c = (double)left.y - ((double)left.x * m); + * + * for (int i = left.x; i <= right.x; i++) { + * int y = (int)(m * i + c); + * if (y > 0) { + * SDL_RenderDrawLine(ren, i, y, i, end); + * } + * } + */ + + int k = 0; +// printf("from 0 to (room tiles) %d - 1, res %d width %d\n", E_ROOM_TILES, E_ROOM_RES, E_ROOM_WIDTH); + for (int i = 0; i < E_ROOM_TILES - 1; i++) { + for (int j = 0; j < E_ROOM_RES; j++) { + k++; + Vect *left; + Vect *right; + + int x = (i * E_ROOM_RES) + j; + if (x - viewport_pos.x > width) { + break; + } + if (x - viewport_pos.x < 0) { + continue; + } + + left = arlst_get(&scene->ceil, i); + right = arlst_get(&scene->ceil, i+1); + + int y0 = interpolate_h(*left, *right, x) - viewport_pos.y; + + left = arlst_get(&scene->bg1, i); + right = arlst_get(&scene->bg1, i+1); + + int y1 = interpolate_h(*left, *right, x) - viewport_pos.y; + + left = arlst_get(&scene->bg2, i); + right = arlst_get(&scene->bg2, i+1); + + int y2 = interpolate_h(*left, *right, x) - viewport_pos.y; + + left = arlst_get(&scene->floor, i); + right = arlst_get(&scene->floor, i+1); + + int y3 = interpolate_h(*left, *right, x) - viewport_pos.y; + + x = x - viewport_pos.x; + /*printf("x: %d, ", x);*/ + /*printf("y: %d %d %d %d\n", y0, y1, y2, y3);*/ + + // 3. bg2 to floor + SDL_SetRenderDrawColor(ren, c4.r, c4.g, c4.b, 255); + SDL_RenderDrawLine(ren, x, y2, x, y3); + + // 1. ceil to bg1 + SDL_SetRenderDrawColor(ren, c2.r, c2.g, c2.b, 255); + SDL_RenderDrawLine(ren, x, y0, x, y1); + + // 2. bg1 to bg2 + SDL_SetRenderDrawColor(ren, c3.r, c3.g, c3.b, 255); + SDL_RenderDrawLine(ren, x, y1, x, y2); + + + + } + } +} + + +void draw_text(SDL_Renderer *rend, Vect pos, struct colour colour, char *text) { + static SDL_Renderer *ren = NULL; + static TTF_Font* font = NULL; + if (!font) { + font = TTF_OpenFont("res/TerminusTTF.ttf", 44); + const char *err = SDL_GetError(); + if (err) { + printf("%s\n", err); + } + } + if (!ren) { + ren = rend; + } + + SDL_Colour sdl_col = {colour.r, colour.g, colour.b, 255}; + SDL_Rect position = {.x = pos.x, .y = pos.y}; + + SDL_SetRenderDrawColor(ren, colour.r, colour.g, colour.b, 255); + SDL_Surface *surf = TTF_RenderText_Solid(font, text, sdl_col); + + SDL_Texture *texture = SDL_CreateTextureFromSurface(ren, surf); + SDL_QueryTexture(texture, NULL, NULL, &position.w, &position.h); + SDL_RenderCopy(ren, texture, NULL, &position); + + SDL_DestroyTexture(texture); + SDL_FreeSurface(surf); + +} + + +void draw_level_chooser_tbox(SDL_Renderer *ren) { + + char string[250]; + snprintf(string, 250,"GOTO LEVEL: %s", gameui.currently_bound_textbox->text_input); + + const struct environment *scene = get_scene_watch(); + + Vect pos = {.x= width - (width / 4), .y = height - (height / 10)}; + draw_text(ren, pos, scene->colours.fg1, string); +} + + +int draw_end_screen(SDL_Renderer *ren) { + const struct environment *scene = get_scene_watch(); + + struct colour bg = scene->colours.bg; + struct colour colour = scene->colours.fg1; + struct colour_pallete pal = scene->colours; + + SDL_SetRenderDrawColor(ren, bg.r, bg.g, bg.b,255); + + SDL_RenderClear(ren); + + + SDL_SetRenderDrawColor(ren, pal.fg2.r, pal.fg2.g, pal.fg2.b,255); + for (int i = width - 300; i < width; i++) { + SDL_RenderDrawLine(ren, i, height, width, height - i); + if (!(i % 240)) { + SDL_SetRenderDrawColor(ren, pal.fg3.r, pal.fg3.g, pal.fg3.b,255); + } + if (!(i % 350)) { + SDL_SetRenderDrawColor(ren, pal.fg1.r, pal.fg1.g, pal.fg1.b,255); + } + } + + + int margin = height / 10; + // Vect position = {.x = width - width / 10, .y = height - margin}; + + long fastest = draw_watch.best_time; + + int minutes = level_time / 60000; + float seconds = level_time * 0.001; + char time_string[250]; + + + char * end_str = "level over."; + + Vect position = {.x = width/4, .y = height / 4}; + draw_text(ren, position, colour, end_str); + position.y += 60; + + char level_string[250]; + snprintf(level_string, 250,"course: %d", level); + draw_text(ren, position, colour, level_string); + position.y += 40; + + char fastest_time[250]; + minutes = fastest / 60000; + seconds = fastest * 0.001; + if (fastest > 0) { + snprintf(fastest_time, 250, "old best time: %d:%.4f", minutes, seconds); + draw_text(ren, position, colour, fastest_time); + position.y += 40; + } + + /* + char qual_str[250]; + snprintf(qual_str, 250, "physics quality: %d", quality); + draw_text(ren, position, colour, qual_str); + position.y += 40; + */ + + + minutes = level_time / 60000; + seconds = level_time * 0.001; + if (level_time > fastest) { + // base time not set yet. + snprintf(time_string, 250, "TIME: %d:%.4f", minutes, seconds); + } else { + snprintf(time_string, 250, "NEW BEST TIME: %d:%.4f", minutes, seconds); + } + + Vect position2 = {.x = position.x + width/3, .y = 60 + height / 4}; + + if (fastest > 0) { + //position.y += 40; + long diff = level_time - fastest; + char diff_str[250]; + minutes = diff / 60000; + seconds = diff * 0.001; + if (minutes > 0) { + snprintf(diff_str, 250, "DIFF: %d:%.4f", minutes, fabs(seconds)); + } else { + snprintf(diff_str, 250, "DIFF: %.4f s", seconds); + } + + draw_text(ren, position2, colour, diff_str); + position2.y += 40; + } + + draw_text(ren, position2, colour, time_string); + + position.y += 200; + char * ct_str = "press any key to continue"; + draw_text(ren, position, colour, ct_str); + + SDL_RenderPresent(ren); + return 0; +} + + +void draw_level_time(SDL_Renderer *ren, const struct environment * e) { + + int margin = height / 10; + + struct colour colour = e->colours.fg1; + + int minutes = level_time / 60000; + float seconds = level_time * 0.001; + char time_string[250]; + snprintf(time_string, 250, "time: %d:%.4f", minutes, seconds); + + char level_string[250]; + snprintf(level_string, 250,"course: %d", level); + + Vect position = {.x = margin, .y = height - margin}; + draw_text(ren, position, colour, level_string); + position.y += 40; + draw_text(ren, position, colour, time_string); + +} + +void draw_pause_screen(SDL_Renderer *ren, struct environment *e) { + + char *te[] = { "GAME PAUSED.", + "controls", + "________", + "pause/unpause: esc", + "grapple: right click", + "grapple and pull: left click", + "go to level: g", + "mute/unmute: m", + "quit: q" + }; + + struct colour textc = e->colours.fg1; + struct colour bg = e->colours.bg; + + SDL_SetRenderDrawColor(ren, bg.r, bg.g, bg.b, 255); + int boxwidth = 360; + int boxheight = 348; + int box_x = (2 * width / 3) - (boxwidth / 2); + int box_y = (height - boxheight) / 2; + SDL_Rect r = {.w = boxwidth, .h = boxheight, .x = box_x, .y = box_y}; + SDL_RenderFillRect(ren, &r); + + Vect p = {.x = box_x + 10, .y = box_y + 10}; + draw_text(ren, p, textc, te[0]); + p.y += 60; + + draw_text(ren, p, textc, te[1]); + p.y += 5; + draw_text(ren, p, textc, te[2]); + p.y += 35; + + draw_text(ren, p, textc, te[3]); + p.y += 40; + + draw_text(ren, p, textc, te[4]); + p.y += 40; + + draw_text(ren, p, textc, te[5]); + p.y += 40; + + draw_text(ren, p, textc, te[6]); + p.y += 40; + + draw_text(ren, p, textc, te[7]); + p.y += 40; + + draw_text(ren, p, textc, te[8]); + p.y += 40; + +} + +void redraw_buffer(SDL_Renderer * ren) { + static int mousex = 0; + static int mousey = 0; + static int newmousex = 0; + static int newmousey = 0; + static SDL_Point *bgPixels[256]; + static int numpixels[256]; + + /*if (!width && !height) {*/ + /*for (int i = 0; i < 256; i++) {*/ + /*bgPixels[i] = malloc(sizeof(SDL_Point) * width * height);*/ + /*memset(bgPixels[i], 0, sizeof(SDL_Point) * width * height);*/ + /*memset(numpixels, 0, (sizeof(int) * 256));*/ + /*}*/ + /*}*/ + + int col = 0; + Body lplayer; + + if (SDL_LockMutex(player.physics->lock) == 0){ + lplayer = *player.physics; + update_viewport(&lplayer); + } else { + return; + } + + static struct timespec last = {}; + static struct timespec now; + now = get_now_d(); + time_delta = now.tv_nsec - last.tv_nsec; + time_delta *= 0.000001; // convert to ms from ns + + last = now; + + //SDL_GetMouseState(&newmousex, &newmousey); + + const struct environment* scene = get_scene_watch(); + draw_environment(ren, scene); + draw_level_time(ren, scene); + SDL_UnlockMutex(player.physics->lock); + + for (int i=0; i < world.items.size; i++) { + world_thing thing; + thing = world.get(i); + + switch (thing.kind) { + case STATIC_WALL_W: + draw_wall(ren, thing.wall); + draw_collision_poly(ren, thing.wall->physics); + continue; + case FLOOR: + for (int i = 0; i < thing.floor->numPolys; i++) { + draw_floor(ren, &thing.floor->polys[i], true); + draw_collision_poly(ren, thing.floor->polys[i].physics); + } + continue; + case CEILING: + for (int i = 0; i < thing.floor->numPolys; i++) { + draw_floor(ren, &thing.floor->polys[i], false); + draw_collision_poly(ren, thing.floor->polys[i].physics); + } + continue; + case ROOM_W: + for (int i = 0; i < thing.room->ceil.numItems; i++) { + draw_collision_poly(ren, thing.room->ceil.items[i]); + } + for (int i = 0; i < thing.room->floor.numItems; i++) { + draw_collision_poly(ren, thing.room->floor.items[i]); + } + default: + continue; + } + } + + draw_player(ren, lplayer.position.x, lplayer.position.y, + lplayer.colliding); + draw_collision_poly(ren, &lplayer); + draw_forces(ren, &lplayer); + + if (gameui.currently_bound_textbox) { + draw_level_chooser_tbox(ren); + } + + if (game_paused) { + SDL_Rect r = {.x = 0, .y = 0, .w = width, .h = height}; + SDL_SetRenderDrawColor(ren, 0, 0, 0, 90); + SDL_RenderFillRect(ren, &r); + if (in_game) + draw_pause_screen(ren, scene); + } + + /*if (newmousex != mousex || newmousey != mousey) {*/ /*mousey = newmousey;*/ + /*mousex = newmousex;*/ + /*for (int i = 0; i < 256; i++) {*/ + /*memset(bgPixels[i], 0, sizeof(SDL_Point) * width * height);*/ + /*memset(numpixels, 0, (sizeof(int) * 256));*/ + /*}*/ + /*for (int i=0; i < width; i++) {*/ + /*for (int j = 0; j < height; j++) {*/ + /*col = distance_colour(i, j, mousex, mousey, 10000);*/ + + /*SDL_Point *row = bgPixels[col];*/ + /*SDL_Point point;*/ + /*point.x = i;*/ + /*point.y = j;*/ + /*row[numpixels[col]] = point;*/ + /*numpixels[col] += 1;*/ + + /*}*/ + /*}*/ + /*}*/ + + /*for (int i = 0; i < 255; i++) {*/ + /*SDL_Point *row = bgPixels[i];*/ + /*col = i;*/ + /*SDL_SetRenderDrawColor(ren, col, col, col, 255);*/ + /*SDL_RenderDrawPoints(ren, row, numpixels[i]);*/ + /*}*/ + +} + diff --git a/build-android/src/draw.h b/build-android/src/draw.h new file mode 100644 index 0000000..e98d4c7 --- /dev/null +++ b/build-android/src/draw.h @@ -0,0 +1,42 @@ +#ifndef _DEFDRAW +#define _DEFDRAW + +#include +#include + +#include "colours.h" +#include "vect.h" +#include "environment.h" +#include "game.h" + +#define SHOWCOLLISION 0 +#define SHOWFORCES 0 + +typedef enum { + PLAYER +} draw_type; + +void render_texture_at(struct SDL_Renderer * ren, struct SDL_Texture * texture,int x, int y) ; +/* draw a texture at x.y */ + +SDL_Texture * load_image(SDL_Renderer * ren, char fname[]); +/* Load an image into a texture */ + +void draw_player(SDL_Renderer * ren, int x, int y, bool red); +/* draw the player as a coloured rect */ + +//void queue_draw_item(void * object, draw_type kind); +void add_to_view(draw_type kind, void * object); + +void redraw_buffer(SDL_Renderer * ren); + +void draw_text(SDL_Renderer *ren, Vect pos, struct colour colour, char *text); + +int draw_end_screen(SDL_Renderer *ren); + +extern Vect viewport_pos; +extern int width, height; +extern bool high_contrast_mode; + + +#endif diff --git a/build-android/src/environment.c b/build-android/src/environment.c new file mode 100644 index 0000000..c496f56 --- /dev/null +++ b/build-android/src/environment.c @@ -0,0 +1,244 @@ +#include "environment.h" +#include "game.h" +#include "types.h" + +struct environment * environment_watch = NULL; + +const struct environment* get_scene_watch(void) { + return environment_watch; +} + +double linear_interpolate(double a0, double a1, double w) { + return (1.0f - w) * a0 + w * a1; +} + +int destroy_environment(struct environment *e) { + /*Vect position;*/ + /*ArrayList ceil; // doubles*/ + /*ArrayList floor; // doubles*/ + /*ArrayList bg1; // doubles*/ + /*ArrayList bg2; // doubles*/ + /*struct colour_pallete colours;*/ + /*struct physics_collection physics;*/ + + arlst_destroy(&e->ceil); + arlst_destroy(&e->floor); + arlst_destroy(&e->bg1); + arlst_destroy(&e->bg2); + + return 0; +} + +double perlin(Vect coordinate) { + Vect a = coordinate; + Vect b = coordinate; + a.x = (int)a.x; + a.y = (int)a.y; + b.x = a.x + 1; + b.y = a.y + 1; + + double sx = coordinate.x - a.x; + double sy = coordinate.y - a.y; + + Vect d = b; + d.y = a.y; + + double n0 = vect_dot(a, coordinate); + double n1 = vect_dot(d, coordinate); + + double ix0 = linear_interpolate(n0, n1, sx); + + Vect c; + c.x = a.x; + c.y = b.y; + + n0 = vect_dot(c, coordinate); + n1 = vect_dot(b, coordinate); + double ix1 = linear_interpolate(n0, n1, sy); + + return linear_interpolate(ix0, ix1, sy); +} + +struct colour_pallete get_pallete_high_contrast(void) { + struct colour white = {.r = 255, .g=255,.b=255, .sp=CS_RGB}; + struct colour black = {.r = 0, .g=0,.b=0, .sp=CS_RGB}; + struct colour_pallete p = {.bg = black, .fg1 = white, .fg2 = white, .fg3 = white}; + return p; +} + + +struct colour_pallete get_pallete (int seed) { + struct colour base; + + if (high_contrast_mode) + return get_pallete_high_contrast(); + + int n = seed; + int red = get_rand(&n) % 255; + int blue = get_rand(&n) % 255; + int green = get_rand(&n) % 255; + + base.r = red; + base.g = green; + base.b = blue; + + struct colour c1 = get_hsv(base); + c1 = get_hsv(base); + struct colour_pallete cols; + + c1.l = 0.1; + cols.bg = get_rgb(c1); + c1.l += 0.3; + + c1.h += 30; + cols.fg1 = get_rgb(c1); + + c1.h += 30; + cols.fg2 = get_rgb(c1); + + c1.h += 30; + cols.fg3 = get_rgb(c1); + + return cols; + + struct colour *adj = get_adjacent(base, 20, 4); + ArrayList l = new_arlst_wfree(4, free); + + for (int i = 0; i < 4; i++) { + arlst_add(&l, adj + i); + } + +} + +int comp(const void *one, const void *two) { + return *(int*)one >= *(int*)two; +} + +struct environment get_scene_at(Vect coordinate, int seed) { + // TODO: Environment needs to be shared between threads + // - this implementation duplicates it: each thread that calls + // get_scene_at gets manages its state in its own static vairables in + // this function but share pointers to the malloced environment object- + // it is thoroughly broken. + // - Fix by having a heap allocated environment object that the draw thread + // (T0) can watch and the physics thread (T3) can modify + // - the entry point can either be through a function returning + // a pointer or just a global const pointer + // + // basic cache for the last room generated + static bool init; + static Vect last_room; + static struct environment e; + static int oldseed; + Vect room_coord; + room_coord.x = (int)coordinate.x - (int)coordinate.x % E_ROOM_WIDTH; + room_coord.y = 0; + + if (init && room_coord.x == last_room.x && oldseed == seed) { + return e; + } else if (init) { + destroy_environment(&e); + } + last_room = room_coord; + oldseed = seed; + + e.floor = new_arlst_wfree(E_ROOM_TILES, free); + e.ceil = new_arlst_wfree(E_ROOM_TILES, free); + e.bg1 = new_arlst_wfree(E_ROOM_TILES, free); + e.bg2 = new_arlst_wfree(E_ROOM_TILES, free); + + e.colours = get_pallete(seed); + + Vect bit; + bit.y = 100 * seed; + Vect pos; + + int n = seed; + get_rand(&n); + + Vect node; + node.y = 0; + + for (int i = 0; i < E_ROOM_WIDTH; i += E_ROOM_RES) { + bit.x = room_coord.x + i; + bit.y = seed; + node.x = bit.x; + + int r1 = 1.5*(get_rand(&n) % 500) - (get_rand(&n) % 100); + int r2 = 1.5*(get_rand(&n) % 200) - (get_rand(&n) % 200); + int r3 = (get_rand(&n) % 100) - (get_rand(&n) % 500); + int r4 = 50; + + int r[4] = {r1, r2, r3, r4}; + qsort(r, 4, sizeof(int), comp); + // r[0] == ceiling + // r[3] == floor + + //node.y += r[0]; + int h[4] = {node.y,node.y,node.y,node.y}; + h[0] += r[0]; + h[1] = h[0] + r[1]; + h[2] += h[0] + r[2]; + h[3] += h[0] + r[3]; + qsort(h, 4, sizeof(int), comp); + +// node.y = fmod(perlin(bit), 3000); + Vect *z = malloc(sizeof(Vect)); + *z = node; + z->y = h[0]; + + arlst_add(&e.floor, z); + + z = malloc(sizeof(Vect)); + *z = node; +// z->y += r[1]; + z->y = h[1]; + arlst_add(&e.bg1, z); + + z = malloc(sizeof(Vect)); + *z = node; + //z->y += r[2]; + z->y = h[2]; + arlst_add(&e.bg2, z); + + + z = malloc(sizeof(Vect)); + *z = node; +// z->y += r[3]; + z->y = h[3]; + arlst_add(&e.ceil, z); + + } + + Vect *v = arlst_get(&e.floor, 0); + Vect v2 = *(Vect *)arlst_get(&e.ceil, 0); + + Vect v3 = vect_add(v2, vect_scalar(vect_add(*v, vect_scalar(v2, -1)) , 0.5)); + + int d[] = {0, E_ROOM_TILES - 1}; + int a; + for (int i = 0; i <= 1; i++) { + a = d[i]; + v = arlst_get(&e.floor, a); + v->y = v3.x; + v = arlst_get(&e.ceil, a); + v->y = v3.x; + v = arlst_get(&e.bg1, a); + v->y = v3.x; + v = arlst_get(&e.bg2, a); + v->y = v3.x; + } + + // join the end and start together + + if (!init) { + struct environment *ee = malloc(sizeof(struct environment)); + environment_watch = ee; + } + + *environment_watch = e; + + init = true; + + return e; +} diff --git a/build-android/src/environment.h b/build-android/src/environment.h new file mode 100644 index 0000000..aefe92b --- /dev/null +++ b/build-android/src/environment.h @@ -0,0 +1,12 @@ +#ifndef ENVIRO_H +#define ENVIRO_H + +#include "vect.h" +#include "types.h" + +struct environment get_scene_at(Vect coordinate, int seed); +int destroy_environment(struct environment *e); + +const struct environment *get_scene_watch(void); + +#endif diff --git a/build-android/src/game.c b/build-android/src/game.c new file mode 100644 index 0000000..95df00b --- /dev/null +++ b/build-android/src/game.c @@ -0,0 +1,1869 @@ +#include "game.h" +#include "draw.h" +#include "audio.h" +#include "types.h" +#include +#include +#include +#include +#include + +#define FLOOR_THICKNESS 200 +#define MAX_ROPE_GRAB_LEN 80000 +#define MIN_PHYSICS_STEP 6.0 + +#define TIMESTEP_LENGTH 3.0 + +#define BREAKPOINT *(int *)0 = 1; + +GlobWorld world; + +player_st *glob_player; +SDL_Renderer *debug_ren; + + +bool in_game; +bool game_paused = false; +extern bool mute; +int level; +int quality = 100; +long level_time; + +int get_floor_ceiling(); + +#ifdef SCORE_SYSTEM +struct sg_times_list save_times_lst; +#endif + +/* object that draw.c watches */ +struct draw_watcher draw_watch = {}; +/* object that handles ui interactions in game.c and which draw watches */ +struct textbox_info textboxes[1]; + + +struct ui_state gameui = {}; + +struct timespec last_tick; +double time_remaining = 0; +struct timespec get_now(); + +/* array of all the things in the world and their kinds */ +//world_thing *world; + +void startgame(SDL_Renderer * ren) ; + +void process_keydown(SDL_Keysym key); +void process_keyup(SDL_Keysym key); + +void add_to_world(world_thing thing); +void set_motor_timeout(Body *thing, int motorID, uint32_t timeout); +void set_motor_status(Body *thing, int motorID, bool run); +void stop_pull_rope(void); + +player_st player; + +// local +void set_motor_newtons(Body *thing, int motorID, double x, double y); +void set_motor_max_velocity(Body *thing, int motorID, double max); +void get_new_physics(Body **phys); +void ratcheted_winch_motor_update(Motor* motor); +void winch_motor_update (struct motorstruct *motor); +void load_level(); + +void reset_textbox(struct textbox_info *tb) { + tb->text_input_bufpos = 0; + memset(tb->text_input, 0, sizeof(tb->text_input)); +} + +void write_to_textbox(struct textbox_info *tb, char c) { + if (tb->text_input_bufpos >= sizeof(tb->text_input)) { + return; + } + + tb->text_input[tb->text_input_bufpos++] = c; +} + +void delete_from_textbox(struct textbox_info *tb) { + if (tb->text_input_bufpos > 0) + tb->text_input[--tb->text_input_bufpos] = 0; +} + +// move the collision poly to the position of the player +void default_update_collision_poly(Body *body) { + for (int i=0; i < body->collision_poly_size; i++) { + + double x = body->collision_shape[i].x + body->position.x; + double y = body->collision_shape[i].y + body->position.y; + + body->collision_poly[i].x = x; + body->collision_poly[i].y = y; + } +} + +void new_level_tb_close_callback(struct textbox_info*textbox, void*callback) { + for (int i = 0; i < textbox->text_input_bufpos; i++) { + if (textbox->text_input[i] < '0' || textbox->text_input[i] > '9') + return; + } + + int lid = atoi(textbox->text_input); + + gameui.goto_new_level = lid; + +} + +void level_timer_update(bool reset) { + static int start_point = 0; + long now = SDL_GetTicks(); + if (reset) { + level_time = 0; + start_point = now; + } else { + level_time = now - start_point; + } +} + + +int long_comparator(const void *a, const void *b, void *non) { + long A = *(long *)a; + long B = *(long *)b; + + return A - B; +} + +int win_long_comparator(void *context_unused, const void *a, const void *b) { + long A = *(long *)a; + long B = *(long *)b; + + return A - B; +} + +#ifdef SCORE_SYSTEM +void sort_times(void) { +#ifdef __linux__ + qsort_r(save_times_lst.times, save_times_lst.size, sizeof(long), long_comparator, NULL); +#elif WIN32 + qsort_s(save_times_lst.times, save_times_lst.size, sizeof(long), win_long_comparator, NULL); +#endif +} + +long get_best_time(void) { + save_times_lst.sort(); + if (save_times_lst.size > 0) + return save_times_lst.times[0]; + return -1; +} + + +int load_score_times(char *filename) { + + char *fn; + char lvl_str[250]; + snprintf(lvl_str, 250, "%d", level); + int fnlen = strlen(filename) + strlen(lvl_str) + 3; + fn = malloc(fnlen); + snprintf(fn, fnlen, "%s-%s", filename, lvl_str); + + //printf("%d, lvl_str %s fn %s\n", level, lvl_str, fn); + + FILE *f = fopen(fn, "r"); + // unload existing score times + if (save_times_lst.times) { + write_times(); + free(save_times_lst.times); + free(save_times_lst.filename); + } + + if (f) { + // get file size + fseek(f, 0L, SEEK_END); + long sz = ftell(f); + fseek(f, 0L, SEEK_SET); + char *text = calloc((sz + 1), (sizeof(char))); + + // read file + fread(text,sizeof(char),sz, f); + fclose(f); + + int count = 1; + for (int i = 0; i < strlen(text); i++) { + if (text[i] == '\n') { + count++; + } + } + + long *times = malloc(count * sizeof(long)); + char *saveptr; + + // extract times + char * token = strtok_r(text, "\n", &saveptr); + int i = 0; + while (token != NULL) { + if (strlen(token) > 2) { + times[i] = atol(token); + i++; + } + token = strtok_r(NULL, "\n", &saveptr); + } + /* here somweher? */ + + struct sg_times_list stl = {.size=i, .capacity = count, .times=times, .sort=sort_times}; + save_times_lst = stl; + sort_times(); + free(text); + } else { + perror("loading times"); + save_times_lst = (struct sg_times_list){.size=0, .capacity = 5, .times=calloc(5, sizeof(long)), .sort=sort_times}; + } + + save_times_lst.filename = fn; + return 0; +} + +int add_time(long time) { + if (save_times_lst.capacity == save_times_lst.size + 5) { + long newcap = save_times_lst.capacity * 2; + save_times_lst.times = realloc(save_times_lst.times, newcap); + save_times_lst.capacity = newcap; + } + save_times_lst.times[save_times_lst.size] = time; + save_times_lst.size++; + + return 0; +} + +int write_times(void) { + FILE *f = fopen(save_times_lst.filename, "w"); + if (!f) { + perror("Saving game"); + return 1; + } + for (int i = 0; i < save_times_lst.size; i++) { + fprintf(f, "%ld\n", save_times_lst.times[i]); + } + fclose(f); + return 0; +} + +#endif // SCORE_SYSTEM + + +// collision poly size must be 2x shape_size + 1 +void cast_update_collision_poly(Body *body) { + for (int i=0; i < body->collision_shape_size; i++) { + double x = body->collision_shape[i].x + body->position.x; + double y = body->collision_shape[i].y + body->position.y; + + body->collision_poly[i].x = x; + body->collision_poly[i].y = y; + } + for (int i=body->collision_shape_size - 1; i >= 0; i--) { + double x = body->collision_shape[i].x + body->next_position.x; + double y = body->collision_shape[i].y + body->next_position.y; + int k = body->collision_shape_size + i; + + body->collision_poly[k].x = x; + body->collision_poly[k].y = y; + } + + /*int i=body->collision_shape_size - 1;*/ + /*double x = body->collision_shape[i].x + body->next_position.x;*/ + /*double y = body->collision_shape[i].y + body->next_position.y;*/ + + /*body->collision_poly[body->collision_shape_size - 1 + i].x = x; */ + /*body->collision_poly[body->collision_shape_size - 1 + i].y = y; */ + + + /*body->collision_poly[(body->collision_poly_size - 1) * 2 + 1] = body->collision_poly[(body->collision_poly_size - 1) * 2 + 1];*/ + /*body->collision_poly[(body->collision_poly_size - 1) * 2 + 2] = body->collision_poly[(body->collision_poly_size - 1)];*/ +} + +void default_motor_curve(Motor *motor) { + // constant + return; +} + +world_thing world_getter(int i) { + world_thing *item = arlst_get(&world.items, i); + return *item; +} + + +FloorPoly* generate_floor_simple(int num_polys, bool extend_down, int st_height) { + FloorPoly *floor = calloc(num_polys, sizeof(FloorPoly)); + Vect last, next; + last.x = 10; + last.y = st_height; + + int n = 1; + for (int i = 0; i < num_polys; i++) { + double run = (get_rand(&n) % 900); + double rise = (get_rand(&n) % 100) - (get_rand(&n) % 100); + next.x = last.x + run; + next.y = last.y + rise; + + FloorPoly poly; + poly.left = last; + poly.right = next; + get_new_physics(&poly.physics); + + poly.physics->position = last; + + int offset = extend_down ? FLOOR_THICKNESS : -FLOOR_THICKNESS; + + poly.physics->collision_poly_size = 4; + poly.physics->collision_poly = calloc(4, sizeof(Vect)); + poly.physics->collision_shape = calloc(4, sizeof(Vect)); + + poly.physics->collision_shape[0].x = 0; + poly.physics->collision_shape[0].y = 0; + + poly.physics->collision_shape[1].x = run; + poly.physics->collision_shape[1].y = rise; + + poly.physics->collision_shape[2].x = run; + poly.physics->collision_shape[2].y = rise+offset; + + poly.physics->collision_shape[3].x = 0; + poly.physics->collision_shape[3].y = rise+offset; + + default_update_collision_poly(poly.physics); + + last = next; + floor[i] = poly; + } + + return floor; +} + +// @param uninitialised Body pointer +// @result: malloc and configure a physics thing pointer +void get_new_physics(Body **phys) { + static int uid = 0; + + /* physics */ + Body * physics = malloc(sizeof(Body)); + memset(physics, 0, sizeof(Body)); + + // give it the next uid + physics->uid = uid++; + + physics->dynamics = false; + physics->glob_gravity = false; + physics->glob_friction = 0.0000; + + physics->obj_mass = 100; + //kgs + + physics->motors = malloc(sizeof(Motor) * 100); + memset(physics->motors, 0, sizeof(Motor) * 100); + physics->max_motors = 100; + physics->num_motors = 0; + + // gravity + add_motor(physics, 0.0, 0.0); + // friction + add_motor(physics, 0.0, 0.0); + + clock_gettime(CLOCK_MONOTONIC, &physics->last_advance_time); + + physics->updateCollisionPoly = default_update_collision_poly; + *phys = physics; + return; +} + +/*void updatePlayerCollision(Body *physics) {*/ + /*physics->collision_poly[0].x = physics->x_pos;*/ + /*physics->collision_poly[0].y = physics->y_pos;*/ +/*}*/ + + +int string_update_fixed(String *string) { + return 0; +} + +int string_set_end(String *string, Vect *setting) { + string->end_point = *setting; + return 0; +} + +String *get_fixed_strings(int number) { + String *strings = calloc(number, sizeof(String)); + for (int i = 0; i < number; i++) { + + strings[i].attached = false; + + // takes self (String) and does nothing. + strings[i].update_end_point = string_update_fixed; + + // takes self and vect to set string end point + strings[i].set_end_point = string_set_end; + } + return strings; +} + +player_st get_player(int x, int y) { + /* creates player at given postion and zeroes physics */ + + // player + player_st player; + memset(&player, 0, sizeof(player)); + player.max_walking_speed = 100; + + // physics settings + get_new_physics(&player.physics); + player.physics->dynamics = true; + player.physics->lock = SDL_CreateMutex(); + + player.physics->position.x = x; + player.physics->position.y = y; + player.physics->next_position = player.physics->position; + player.physics->obj_elasticity = 0.5; + player.physics->obj_friction = 0.2; + + // friction (not in use) + player.physics->glob_friction = 40; // drag coef * area + + player.physics->collision_poly_size = 4 + 4; + player.physics->collision_poly = calloc(player.physics->collision_poly_size, sizeof(Vect)); + + player.physics->collision_shape_size = 4; + player.physics->collision_shape = calloc(4, sizeof(Vect)); + + player.physics->updateCollisionPoly = cast_update_collision_poly; + + // swing rope + player.physics->num_strings = 1; + player.physics->max_strings = 1; + player.physics->strings = get_fixed_strings(1); + player.physics->strings->max_length = 210; + + Vect *rect = player.physics->collision_shape; + rect[0].x = -10; rect[0].y = -10; + rect[1].x = 10; rect[1].y = -10; + rect[2].x = 10; rect[2].y = 10; + rect[3].x = -10; rect[3].y = 10; + + // gravity + set_motor_newtons(player.physics, M_GRAVITY, 0.0, + player.physics->obj_mass * 5); + set_motor_status(player.physics, M_GRAVITY, true); + set_motor_timeout(player.physics, M_GRAVITY, 99999999); + + // walking motor + add_motor(player.physics, 0.0, player.physics->obj_mass * 9.81); + set_motor_max_velocity(player.physics, M_PLAYER_WALK, 5); // has to be > grav + set_motor_max_velocity(player.physics, M_GRAVITY, 5); + + // winch motor for string + add_motor(player.physics, 0.0, 0); + player.physics->motors[M_WINCH].update_motor = ratcheted_winch_motor_update; + + return (player); +} + +bool point_point_colcheck(Vect one, Vect two) { + if (one.x == two.x && one.y == two.y) { + return true; + } + return false; +} + +typedef struct { + Vect one; + Vect two; +} Collision; + +bool point_line_colcheck(Vect line[2], Vect point) { + // point is outside the rectangle made by the line + if ((point.x > line[0].x && point.x > line[1].x) + || (point.x < line[0].x && point.x < line[0].x) + || (point.y > line[0].y && point.y > line[0].y) + || (point.y < line[0].y && point.y < line[0].y) + ){ + return false; + } + + double m = (double)(line[1].y - line[0].y) / (double)(line[1].x - line[0].x); + double c = (double)(line[0].y - (m * line[0].x)); + + double y = point.x * m + c; + + // point is in the line +- 1 + if ((int)y == point.y || ((int)y < point.y && (int)y + 1 > point.y)) { + return true; + } + + return false; +} + +double *project_col_poly(Body *shape, Vect V) { + double *proj = calloc(shape->collision_poly_size, sizeof(double)); + for (int i = 0; i < shape->collision_poly_size; i++) { + Vect point; + point.x = shape->collision_poly[i].x; + point.y = shape->collision_poly[i].y; + //double mag = vect_mag(point); +// printf("point: %f %f mag: %f\n", point.x, point.y, vect_mag(point)); +// printf("Vectp: %f %f mag: %f\n", V.x, V.y, vect_mag(V)); + proj[i] = vect_scalar_projection(point, V); + } + double min, max; + max = -99999999; + min = 99999999; + for (int i = 0; i < shape->collision_poly_size; i++) { + if (proj[i] > max) { + max = proj[i]; + } + if (proj[i] < min) { + min = proj[i]; + } + } + double *res = calloc(2, sizeof(double)); + res[0] = min; + res[1] = max; + + free(proj); + return res; +} + +Vect get_normal(Vect start, Vect end) { + Vect norm; + double x = (end.x - start.x); + double y = (end.y - start.y); + norm.x = -y; + norm.y = x; + double len = vect_mag(norm); + norm.x /= len; + norm.y /= len; + + return norm; +} + +// reference: +// http://www.dyn4j.org/2010/01/sat/#sat-inter +// +// TODO: Maybe avoid calculating the centre of the boxes; have body->position +// be the centre and translate the collision box to be around that. It does +// make placing a little more complex but no bother. +bool sat_collision_check(Body *one, Body *two, Vect *translation) { + int num_axes = one->collision_poly_size + two->collision_poly_size; + Vect *axes = calloc(num_axes, sizeof(Vect)); + + Vect end; + Vect start = one->collision_poly[0]; + Vect one_position; + Vect two_position; + + two_position = two->collision_poly[0]; + one_position = one->collision_poly[0]; + + for (int i = 1; i < one->collision_poly_size; i++) { + one_position = vect_add(one_position, one->collision_poly[i]); + end = one->collision_poly[i]; + axes[i-1] = get_normal(start, end); + start = end; + } + + end = one->collision_poly[0]; + axes[one->collision_poly_size - 1] = get_normal(start, end); + + start = two->collision_poly[0]; + for (int i = 1; i < two->collision_poly_size; i++) { + two_position = vect_add(two_position, two->collision_poly[i]); + end = two->collision_poly[i]; + axes[i - 1 + one->collision_poly_size] = get_normal(start, end); + start = end; + } + + end = two->collision_poly[0]; + axes[two->collision_poly_size + one->collision_poly_size - 1] = get_normal(start, end); + + double *proj_one, *proj_two; + proj_one = proj_two = 0; + + Vect min_axis; + double min_overlap = 99999999; + for (int i = 0; i < num_axes; i++) { + // project + if (proj_one) { + free(proj_one); + } + if (proj_two) { + free(proj_two); + } + proj_one = project_col_poly(one, axes[i]); + proj_two = project_col_poly(two, axes[i]); + + if ((proj_one[0] >= proj_two[1]) + || (proj_two[0] >= proj_one[1])) { + free(axes); + free(proj_one); + free(proj_two); + return false; + } else { + double overlap; + double left = proj_one[1] < proj_two[1] ? proj_one[1] : proj_two[1]; + double right = proj_one[0] > proj_two[0] ? proj_one[0] : proj_two[0]; + overlap = left - right; + + // one of the shapes is contained + if ((overlap > (proj_one[1] - proj_one[0]) + || (overlap > (proj_two[1] - proj_two[0])))) { + + double min = proj_one[0] - proj_two[0]; + double max = proj_one[1] - proj_two[1]; + + min = min < 0 ? -min : min; + max = max < 0 ? -max : max; + + overlap += min < max ? min : max; + + } + + if (overlap < min_overlap && overlap > 0) { + min_overlap = overlap; + min_axis = axes[i]; + } + } + } + + // flip the MTV if it is pointing INTO the object + Vect trans; + trans.x = min_overlap * min_axis.x; + trans.y = min_overlap * min_axis.y; + + // https://gamedev.stackexchange.com/questions/27596/implementing-separating-axis-theorem-sat-and-minimum-translation-vector-mtv/27629#27629 + Vect direction; + one_position.x /= one->collision_poly_size; + one_position.y /= one->collision_poly_size; + two_position.x /= two->collision_poly_size; + two_position.y /= two->collision_poly_size; + + /*printf("ONE POS: %f %f", one_position.x, one_position.y);*/ + /*printf("TWO POS: %f %f", two_position.x, one_position.y);*/ + + direction.x = one_position.x - two_position.x; + direction.y = one_position.y - two_position.y; + + double dot = vect_scalar_projection(trans, direction); + + /*printf("DIRECTION: %f %f\n\n", direction.x, direction.y);*/ + /*printf("DOT: %f\n\n", dot);*/ + + if (dot > 0) { + trans = vect_scalar(trans, -1); + } + + // return the MTV + *translation = trans; + + + free(axes); + free(proj_one); + free(proj_two); + return true; +} + +bool check_collision(Body *one, Body *two, Vect *translation) { + int onesize = one->collision_poly_size; + int twosize = two->collision_poly_size; + + // point-point + if (one->collision_poly_size == 1 && two->collision_poly_size == 1) { + if (point_point_colcheck(one->collision_poly[0], two->collision_poly[0])) { + return true; + } + } + // point-line + if ((onesize == 1 || twosize == 1) && (onesize == 2 || twosize == 2)) { + Vect line[2]; + Vect point; + if (onesize > twosize) { + line[0] = one->collision_poly[0]; + line[1] = one->collision_poly[1]; + point = two->collision_poly[0]; + } else { + line[0] = two->collision_poly[0]; + line[1] = two->collision_poly[1]; + point = one->collision_poly[0]; + } + + return point_line_colcheck(line, point); + } + + // line-line + if ((onesize == 2 && twosize == 2)) { + return false; + } + + // point-poly + if ((onesize == 1 || twosize == 1) && (onesize > 2 || twosize > 2)) { + return false; + } + + // line-poly + if ((onesize == 2 || twosize == 2) && (onesize > 2 || twosize > 2)) { + return false; + } + + // poly-poly + if ((onesize > 2 && twosize > 2)) { + return sat_collision_check(one, two, translation); + + } +} + +void destroy_physics_body(Body *b) { + // collisions + Vect *collision_poly; + Vect *collision_shape; + int collision_poly_size; + int collision_shape_size; + void (*updateCollisionPoly)(struct BodyStruct *); + // applying forces + int num_strings; + int max_strings; + String *strings; + +} + +void destroy_physics_collection(struct physics_collection *s) { + for (int i = 0; i < s->numItems; i++) { + if (s->items[i]) { + //free(s->items[i]); + } + } + + free(s->items); +} + + + +void next_level(int lvl) { + level = lvl; + load_level(); +} + +void load_level() { + SDL_LockMutex(player.physics->lock); + Vect v; + v.x = 0; + v.y = 0; + player.physics->vel = v; + player.physics->acc = v; + stop_pull_rope(); + player.physics->strings[0].attached = false; + + int d = -1; + for (int i = 0; i < world.items.size; i++) { + if (world.get(i).kind == ROOM_W) { + d = i; + break; + } + } + + if (d != -1) { + world_thing *w = arlst_del(&world.items, d); + } + + destroy_physics_collection(&world.uniques_index[ROOM_W]->room->ceil); + destroy_physics_collection(&world.uniques_index[ROOM_W]->room->floor); + //destroy_environment(&world.uniques_index[ROOM_W]->room->env); + + get_floor_ceiling(); +#ifdef SCORE_SYSTEM + draw_watch.best_time = get_best_time(); + load_score_times("saves/times"); +#endif + + v = world.uniques_index[ROOM_W]->room->ceil.items[2]->collision_poly[0]; + v.y -= 15; + + player.physics->position = v; + player.physics->next_position = v; + + quality = 100; + level_timer_update(true); + draw_watch.finish_level = false; + + viewport_pos.x = player.physics->position.x - width / 2; + viewport_pos.y = player.physics->position.y - height / 2; + + SDL_UnlockMutex(player.physics->lock); + + Mix_HaltChannel(-1); + + // reset physics + + struct timespec now = get_now(); + double time_delta = now.tv_nsec - last_tick.tv_nsec; + if (now.tv_nsec < last_tick.tv_nsec) { + time_delta = now.tv_nsec - (last_tick.tv_nsec - 1000000000); + } + last_tick = now; + + + +} + +int get_floor_ceiling() { + struct environment e = get_scene_at(viewport_pos, level); + struct physics_collection floor; + struct physics_collection ceil; + + floor.items = calloc(e.floor.size - 1, sizeof(Body*)); + ceil.items = calloc(e.ceil.size - 1, sizeof(Body*)); + + floor.numItems = e.floor.size - 1; + ceil.numItems = e.ceil.size - 1; + + for (int i = 0; i < e.floor.size-1; i++) { + get_new_physics(&floor.items[i]); + get_new_physics(&ceil.items[i]); + Body *fseg = floor.items[i]; + Body *cseg = ceil.items[i]; + + fseg->position = *(Vect *)arlst_get(&e.floor, i); + fseg->collision_poly_size = 4; + fseg->collision_poly = calloc(4, sizeof(Vect)); + fseg->collision_shape = calloc(4, sizeof(Vect)); + + cseg->position = *(Vect *)arlst_get(&e.ceil, i); + cseg->collision_poly_size = 4; + cseg->collision_poly = calloc(4, sizeof(Vect)); + cseg->collision_shape = calloc(4, sizeof(Vect)); + + Vect fthis = fseg->position; + Vect cthis = cseg->position; + + Vect fnext = *(Vect *)arlst_get(&e.floor, i + 1); + Vect cnext= *(Vect *)arlst_get(&e.ceil, i + 1); + + double frise = fnext.y - fthis.y; + double frun = fnext.x - fthis.x; + + double crise = cnext.y - cthis.y; + double crun = cnext.x - cthis.x; + + double fdepth = -(fabs(frise) * 2 + 100); + double cdepth = fabs(crise) * 2 + 100; + + fseg->collision_shape[0].x = 0; + fseg->collision_shape[0].y = 0; + + fseg->collision_shape[1].x = frun; + fseg->collision_shape[1].y = frise; + + fseg->collision_shape[2].x = frun; + fseg->collision_shape[2].y = frise + fdepth; + + fseg->collision_shape[3].x = 0; + fseg->collision_shape[3].y = frise + fdepth; + + cseg->collision_shape[0].x = 0; + cseg->collision_shape[0].y = 0; + + cseg->collision_shape[1].x = crun; + cseg->collision_shape[1].y = crise; + + cseg->collision_shape[2].x = crun; + cseg->collision_shape[2].y = crise + cdepth; + + cseg->collision_shape[3].x = 0; + cseg->collision_shape[3].y = crise + cdepth; + + default_update_collision_poly(cseg); + default_update_collision_poly(fseg); + } + + world_thing room_world; + room_world.kind = ROOM_W; + + struct room* room = malloc(sizeof(struct room)); + room->ceil = ceil; + room->floor = floor; + room_world.room = room; + + add_to_world(room_world); + world.uniques_index[ROOM_W]->room->env = e; + return 0; +} + +Wall *get_long_wall(int numNodes, int *nodes) { + Wall wall; + memset(&wall, 0, sizeof(wall)); + + wall.numNodes = numNodes; + wall.nodes = calloc(numNodes, sizeof(SDL_Point)); + get_new_physics(&wall.physics); + + wall.physics->collision_poly = calloc(numNodes, sizeof(Vect)); + wall.physics->collision_shape = calloc(numNodes, sizeof(Vect)); + wall.physics->collision_poly_size = numNodes; + + // collisions + //SDL_Point *collision_poly; + for (int i = 0; i < numNodes; i++) { + wall.nodes[i].x = nodes[2*i]; + wall.nodes[i].y = nodes[2*i+1]; + + wall.physics->collision_shape[i].x = nodes[2*i]; + wall.physics->collision_shape[i].y = nodes[2*i+1]; + } + + Wall *wallwall = malloc(sizeof(wall)); + *wallwall = wall; + return wallwall; +} + +void accel_thing(Body * thing, double x, double y) { + /* takes acceleration in m/s2 and converts to m/ms adding + * it to physics_thing + */ + + // convert to m / millisecond + double x_adj = x / 1000.0; + double y_adj = y / 1000.0; + + thing->acc.y += y_adj; + thing->acc.x += x_adj; + +} + +void set_motor_max_velocity(Body *thing, int motorID, double max) { + thing->motors[motorID].max_velocity = max; +} + +void set_motor_timeout(Body *thing, int motorID, uint32_t timeout) { + // this don't work yo + clock_gettime(CLOCK_MONOTONIC, &thing->motors[motorID].timeout); + thing->motors[motorID].timeout.tv_nsec += 1000000 * timeout; + thing->motors[motorID].timeout.tv_sec + = thing->motors[motorID].timeout.tv_nsec * 0.000000001; +} + +void set_motor_status(Body *thing, int motorID, bool run) { + if (motorID == M_GRAVITY && run == false) { + *(int *)0 = 1; + } + thing->motors[motorID].stop = !run; + +} + +void set_motor_newtons(Body *thing, int motorID, double x, double y) { + thing->motors[motorID].x =x; + thing->motors[motorID].y =y; +} + +void add_motor_newtons(Body *thing, int motorID, double x, double y) { + thing->motors[motorID].x +=x; + thing->motors[motorID].y +=y; +} + +// @param thing: the body to apply the motor to +// @param x, y: The initial motor force vector. +void add_motor(Body *thing, double x, double y) { + Motor motor; + memset(&motor, 0, sizeof(Motor)); + motor.x = x; + motor.y = y; + motor.stop = true; + + motor.timeout.tv_sec = 0; + motor.timeout.tv_nsec = 0; + + motor.max_velocity = 999899; + + motor.update_motor = default_motor_curve; + + motor.end_object = thing; + + if (thing->num_motors == thing->max_motors) { + thing->motors = realloc(thing->motors, sizeof(Motor) * (thing->max_motors *=2)); + } + + thing->motors[thing->num_motors] = motor; + thing->num_motors += 1; +} + +void ratcheted_winch_motor_update(Motor* motor) { + + Body *body = motor->end_object; + + if (body->strings[0].max_length < 0.1 + || !body->strings[0].attached || motor->stop) { + motor->stop = true; + return; + } + + Vect st; + st.x = body->position.x - body->strings[0].end_point.x; + st.y = body->position.y - body->strings[0].end_point.y; + + if (body->strings[0].max_length > vect_mag(st)) + body->strings[0].max_length = vect_mag(st); + winch_motor_update(motor); + + play_game_sound(game_sounds.rope_pull, 300, BC_ROPE_PULL); + +} + +void winch_motor_update (Motor* motor) { + Vect v; + v.x = motor->x; + v.y = motor->y; + double mod = vect_mag(v); + + Body *body = motor->end_object; + + if (body->strings[0].max_length < 0.1 + || !body->strings[0].attached) { + motor->stop = true; + return; + } + + // set the motor direction to the string direction + Vect end_point = body->strings[0].end_point; + Vect start_point = body->position; + Vect dir = vect_add(end_point, vect_scalar(start_point, -1)); + + double arg = vect_arg(dir); + Vect force = vect_modarg(mod, arg); + + motor->x = force.x; + motor->y = force.y; +} + +void pull_rope(double newtons) { + if (!player.physics->strings[0].attached) { + return; + } + if (!player.physics->motors[M_WINCH].stop) { + return; + } + + // set force + set_motor_newtons(player.physics, M_WINCH, 0, newtons); + set_motor_status(player.physics, M_WINCH, true); + // point it in the right direction + winch_motor_update(player.physics->motors + M_WINCH); + player.physics->motors[M_WINCH].stop = false; +} + +void stop_pull_rope(void) { + Mix_HaltChannel(BC_ROPE_PULL); + player.physics->motors[M_WINCH].stop = true; +} + + +void add_rope(int mouse_x, int mouse_y) { + if (player.physics->strings[0].attached) { + return; + } + if (game_paused || !in_game) { + return; + } + + Vect mouse; + mouse.x = mouse_x; + mouse.y = mouse_y; + mouse = vect_add(mouse, viewport_pos); + + Vect start = player.physics->position; + + Vect end; + end.x = 0; + end.y = 0; + + struct room *room = world.uniques_index[ROOM_W]->room; + struct physics_collection floor = room->floor; + struct physics_collection ceil = room->ceil; + + Vect ray_e = player.physics->position; + Vect ray_s = mouse; + + double x1 = ray_s.x; + double y1 = ray_s.y; + + double x2 = ray_e.x; + double y2 = ray_e.y; + + int i; + struct physics_collection s; + bool found = false; + double len = MAX_ROPE_GRAB_LEN; + double t, u; + for (int j = 0; j < (floor.numItems + ceil.numItems - 2); j++) { + if (j >= floor.numItems - 1) { + s = ceil; + i = j - floor.numItems + 1; + } else { + s = floor; + i = j; + } + + Vect wall_s = s.items[i]->position; + Vect wall_e = s.items[i+1]->position; + + Vect intersect; + + double x3 = wall_s.x; + double y3 = wall_s.y; + + double x4 = wall_e.x; + double y4 = wall_e.y; + + t = ((x1 - x3)*(y3 - y4) - (y1 - y3)*(x3-x4)) + / ((x1-x2)*(y3-y4) - (y1 - y2)*(x3-x4)); + + u = ((x1 - x2)*(y1 - y3) - (y1 - y2)*(x1 - x3)) + / ((x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4)); + + if (t < 0 || u > 0 || t > 1 || u < -1) { + continue; + } else { + intersect.x = ((x1*y2 - y1*x2)*(x3 - x4) -(x1 - x2)*(x3*y4 - y3*x4)) + / ((x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4)); + + intersect.y = ((x1*y2 - y1*x2)*(y3 - y4) - (y1 - y2) * (x3*y4 - y3 * x4)) + / ((x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4)); + + Vect rope = vect_add(intersect, vect_scalar(start, -1)); + double mag = vect_mag(rope); + if (mag < len) { + len = mag; + found = true; + end = intersect; + } + } + } + + if (found) { + + SDL_SetRenderDrawColor(debug_ren, 200, 255, 30, 255); + SDL_RenderDrawLine(debug_ren, start.x - viewport_pos.x,start.y-viewport_pos.y, + end.x - viewport_pos.x, end.y -viewport_pos.y); + + Vect rop = vect_add(end, vect_scalar(start, -1)); + + player.physics->strings[0].max_length = vect_mag(rop); + + player.physics->strings[0].set_end_point(player.physics->strings, &end); + player.physics->strings[0].attached = true; + play_game_sound(game_sounds.rope_attach, 100, BC_ROPE_ATTACH); + + } +} + +void delete_rope(void) { + player.physics->strings[0].attached = false; +} + +// basic collision handler for testing +// +int process_collisions(Body *thing, Vect *trans) { + Vect translation; + translation.x = translation.y = 0; + Vect temptrans; + temptrans.x = temptrans.y = 0; + int num_cols = 0; + int num = 0; + + thing->was_colliding += thing->colliding > 0 ? 1 : -1; + if (thing->was_colliding < -1000) { + thing->was_colliding = -1000; + } + if (thing->was_colliding > 1000) { + thing->was_colliding = 1000; + } + + for (int k = 0; k < world.items.size; k++) { + if (world.get(k).kind == STATIC_WALL_W + && world.get(k).wall->physics->uid != thing->uid) { + if ((world.get(k).wall->physics->position.x - viewport_pos.x) > (width * 2) + || world.get(k).wall->physics->position.x - viewport_pos.x < 0) { + continue; + } else { + num++; + } + if (check_collision(world.get(k).wall->physics, thing, &temptrans)) { + num++; + thing->colliding = true; + translation = vect_add(translation, temptrans); + } + } else if (world.get(k).kind == FLOOR || world.get(k).kind == CEILING) { + for (int i = 0; i < world.get(k).floor->numPolys; i++) { + if ((world.get(k).floor->polys[i].physics->position.x - viewport_pos.x) > (2 *width) + || (world.get(k).floor->polys[i].physics->position.x - viewport_pos.x) < -width) { + continue; + } else { + num++; + } + if (check_collision(world.get(k).floor->polys[i].physics, thing, &temptrans)) { + num_cols++; + thing->colliding = true; + translation = vect_add(translation, temptrans); + } + } + } else if (world.get(k).kind == ROOM_W) { + for (int i = 0; i < world.get(k).room->floor.numItems; i++) { + Body *s = world.get(k).room->floor.items[i]; + if (s->position.x + E_ROOM_RES < viewport_pos.x) { + continue; + } else if (s->position.x > viewport_pos.x + width) { + continue; + } else { + num++; + } + + if (check_collision(s, thing, &temptrans)) { + num_cols++; + thing->colliding = true; + translation = vect_add(translation, temptrans); + } + } + + for (int i = 0; i < world.get(k).room->ceil.numItems; i++) { + Body *s = world.get(k).room->ceil.items[i]; + if (s->position.x + E_ROOM_RES < viewport_pos.x) { + continue; + } else if (s->position.x > viewport_pos.x + width) { + continue; + } else { + num++; + } + + if (check_collision(s, thing, &temptrans)) { + num_cols++; + thing->colliding = true; + translation = vect_add(translation, temptrans); + } + } + } + } + + + if (!num_cols) { + thing->colliding = false; + return false; + } else { + *trans = translation; + return true; + } +} + +struct timespec get_now() { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + if (now.tv_nsec >= 1000000000) { + now.tv_nsec -= 1000000000; + } + return now; +} + +bool get_motor_active(Body *thing, int i) { + if (thing->motors[i].stop) { + return false; + } + + struct timespec now = get_now(); + if (thing->motors[i].timeout.tv_nsec + || thing->motors[i].timeout.tv_sec) { + // 0 is a sentinel for infinity + if (thing->motors[i].timeout.tv_sec > now.tv_sec + && thing->motors[i].timeout.tv_nsec > now.tv_nsec) { + return false; + } + } + + return true; +} + +/* Basic physics works by adding up the acceleratino caused by all the forces on + * the object, converting it to velocity, then doing collision detection and + * applying various reactive forces (friction etc) and finally adjusting the + * object's position. + */ +void advance_thing(Body * thing) { + // TODO: fix ordering of collision detection + physics sim so that collisions + // are less bad. + + thing->acc.x = 0; + thing->acc.y = 0; + + if (!thing->dynamics) { + return; + } + + double time_delta = TIMESTEP_LENGTH; + + Vect translation; + Vect friction; + translation.x = translation.y = 0; + int numcols = 0; + + // collisions + if ((numcols = process_collisions(thing, &translation))) { + /*double check = vect_scalar_projection(translation, thing->vel);*/ + /*if (check >= 0) {*/ + /*translation.x *= -1;*/ + /*translation.y *= -1;*/ + /*}*/ + + // correct position using translation vector. + thing->next_position.x += translation.x; + thing->next_position.y += translation.y; + + // make unit vector + double mag = vect_mag(translation); + translation.x = translation.x / mag; + translation.y = translation.y / mag; + + if (mag > 0.10) { + Mix_Volume(BC_COLLISION, MIX_MAX_VOLUME * mag); + play_game_sound(game_sounds.collision, 230, BC_COLLISION); + } + + // get velocity in direction of collision + mag = vect_scalar_projection(thing->vel, translation); + + Vect revert_vel; + + revert_vel.x = cos(vect_dir(translation)) * mag; + revert_vel.y = sin(vect_dir(translation)) * mag; + + thing->vel.x -= revert_vel.x; + thing->vel.y -= revert_vel.y; + + // add elasticity + thing->vel.x -= revert_vel.x * sqrt(thing->obj_elasticity); + thing->vel.y -= revert_vel.y * sqrt(thing->obj_elasticity); + + // add friction + + if (vect_mag(translation) < 0.1) { + Vect friction = vect_rotate(revert_vel, M_PI / 2); + double dir = vect_scalar_projection(thing->vel, friction); + if (dir > 0) { + friction = vect_rotate(friction, M_PI); + } + + if (fabs(thing->vel.x) <= fabs(friction.x)) { + thing->vel.x = 0; + } else { + thing->vel.x += thing->obj_friction * friction.x; + } + if (fabs(thing->vel.y) <= fabs(friction.y)) { + thing->vel.y = 0; + } else { + thing->vel.y += thing->obj_friction * friction.y; + } + + } + + /*double norm = 9.81 * thing->obj_mass;*/ + /*Vect x;*/ + /*x.y = 0; x.x = 1;*/ + /*if (vect_scalar_projection(thing->vel, translation) > 0) {*/ + /*friction.x = translation.y;*/ + /*friction.y = -translation.x;*/ + /*} else {*/ + /*friction.x = -translation.y;*/ + /*friction.y = translation.x;*/ + /*}*/ + + // force + /*friction.x = friction.x * norm * fric_const;*/ + /*friction.y = friction.y * norm * fric_const;*/ + // printf("friction accel: %e %e\n", friction.x, friction.y); + // printf("velocity: %e %e\n", thing->vel.x, thing->vel.y); + + /*set_motor_newtons(thing, M_FRICTION, friction.x, friction.y);*/ + /*set_motor_max_velocity(thing, M_FRICTION, vect_mag(project_vect(thing->vel, friction)));*/ + + // restitution force + /*rest.x = 0;*/ + /*rest.y = 0;*/ + /*double impulse = thing->obj_mass * vect_mag(thing->vel) * thing->obj_elasticity;*/ + /*rest.x = cos(vect_dir(translation)) * impulse;*/ + /*rest.y = sin(vect_dir(translation)) * impulse;*/ + /*accel_thing(thing, rest.x, rest.y);*/ + } + + // pendulums + for (int i = 0; i < thing->num_strings; i++) { + if (!thing->strings[i].attached) { + continue; + } + + double st_len = vect_distance(thing->next_position, thing->strings[i].end_point); + double max_len = thing->strings[i].max_length; + if (st_len > max_len) { + Vect string_end = thing->strings[i].end_point; + Vect thing_position = thing->next_position; + + Vect string; + string.x = string_end.x - thing_position.x; + string.y = string_end.y - thing_position.y; + double mag = vect_mag(string); + string.x /= mag; + string.y /= mag; + double angle = atan2(string.y, string.x); + double disp = mag - max_len; + + thing->next_position.x += cos(angle) * disp; + thing->next_position.y += sin(angle) * disp; + + // set velocity to 0 in direction of string + double corr_mag = vect_scalar_projection(thing->vel, string); + + thing->vel.x -= corr_mag * cos(angle); + thing->vel.y -= corr_mag * sin(angle); + } + } + + // motors + for (int i = 0; i < thing->num_motors; i++) { + + if (!get_motor_active(thing, i)) { + continue; + } + + // dynamic motor curve + if (thing->motors[i].update_motor) { + thing->motors[i].update_motor(thing->motors + i); + } + Vect F; + Vect V; + F.x = thing->motors[i].x; + F.y = thing->motors[i].y; + V.x = thing->vel.x; + V.y = thing->vel.y; + + Vect vel_in_dir = project_vect(V, F); + double dirF = atan2(F.y, F.x); + double dirV = atan2(V.y, V.x); + + double diff = dirV > dirF ? dirV - dirF: dirF - dirV; + + if (thing->motors[i].max_velocity > vect_mag(vel_in_dir) + || diff >= M_PI) { + double acc_x = thing->motors[i].x / thing->obj_mass; + double acc_y = thing->motors[i].y / thing->obj_mass; + accel_thing(thing, acc_x, acc_y); + } + } + + // accelerate based on accel + thing->vel.x += thing->acc.x * (double)time_delta; + thing->vel.y += thing->acc.y * (double)time_delta; + + double velocity = sqrt((double)(thing->vel.x * thing->vel.x + thing->vel.y * thing->vel.y)); + + // simple air drag + + /*if (velocity > 0.000000000000000) {*/ + /*double dir = atan2((double)thing->y_vel, (double)thing->x_vel) + M_PI;*/ + + /*double absolute_force = 5 * thing->obj_mass * (velocity / time_delta); // 2 * 0.1231 * 5;*/ + /*printf("dir %e %e\n\n", dir, dir - M_PI);*/ + /*printf("force %e\n\n", absolute_force);*/ + + /*double f_x = (cos(dir)) * absolute_force;*/ + /*double f_y = (sin(dir)) * absolute_force;*/ + + /*accel_thing(thing, (float)f_x, (float)f_y);*/ + + /*}*/ + + if (fabsl((*thing).vel.x) < 0.001) { + (*thing).vel.x = 0; + } + if (fabsl((*thing).vel.y) < 0.001) { + (*thing).vel.y = 0; + } + + if (thing->lock) { + SDL_LockMutex(thing->lock); + } + thing->position = thing->next_position; + + double oldx = thing->position.x; + double oldy = thing->position.y; + thing->next_position.x += (thing->vel.x * 1/2 * (double)time_delta); + thing->next_position.y += (thing->vel.y * 1/2 * (double)time_delta); + + + if (!thing->collision_poly[0].y) { + thing->updateCollisionPoly(thing); + } + + thing->updateCollisionPoly(thing); + if (thing->lock) { + SDL_UnlockMutex(thing->lock); + } + +} + +/*void destroy_projectile(Projectile** proj) {*/ + /*for (int i = 0; i < things_in_world; i ++) {*/ + /*if (world[i].projectile == *proj) {*/ + + /*}*/ + /*}*/ +/*}*/ + +void *default_projectile_step(struct Projectile *self) { + advance_thing(self->physics); + return NULL; +} + +void *default_projectile_on_collision(struct Projectile *self) { + self->physics->dynamics = false; + return NULL; +} + +void get_projectile(Projectile** proj) { + *proj = calloc(1, sizeof(Projectile)); + get_new_physics(&(*proj)->physics); + (*proj)->physics->dynamics = true; + (*proj)->on_step = default_projectile_step; + (*proj)->on_collision = default_projectile_on_collision; +} + +void advance_things(void) { + int numcols; + Vect translation; + + /* update the timer */ + struct timespec now = get_now(); + double time_delta = now.tv_nsec - last_tick.tv_nsec; + + if (now.tv_nsec < last_tick.tv_nsec) { + time_delta = now.tv_nsec - (last_tick.tv_nsec - 1000000000); + } + time_delta *= 0.000001; // convert to ms from ns + time_remaining += time_delta; + last_tick = now; + + while (time_remaining > TIMESTEP_LENGTH) { + time_remaining -= TIMESTEP_LENGTH; + for (int i = 0; i < world.items.size; i++) { + switch (world.get(i).kind) { + case PLAYER_W : + advance_thing((world.get(i).player->physics)); + continue; + case STATIC_WALL_W: + advance_thing(world.get(i).wall->physics); + continue; + case FLOOR: + continue; + /*for (int k = 0; k < world.get(i).floor->numPolys; k++) {*/ + /*advance_thing(world.get(i).floor->polys[k].physics);*/ + /*}*/ + /*break;*/ + case CEILING: + continue; + /*for (int k = 0; k < world.get(i).floor->numPolys; k++) {*/ + /*advance_thing(world.get(i).floor->polys[k].physics);*/ + /*}*/ + /*break;*/ + case PROJECTILE: + if ((numcols = process_collisions(world.get(i).projectile->physics, + &translation))) { + world.get(i).projectile->on_collision(world.get(i).projectile); + } + world.get(i).projectile->on_step(world.get(i).projectile); + continue; + default: + continue; + } + } + } +} + +bool unique_world_kind(enum world_thing_kind k) { + switch (k) { + case PLAYER_W: + case FLOOR: + case CEILING: + case ROOM_W: + return true; + default: + return false; + } +} + +// grow array of world things if needed +void add_to_world(world_thing thing) { + thing.nid = world.items.size; + world_thing *t = calloc(1, sizeof(world_thing)); + memcpy(t, &thing, sizeof(world_thing)); + + arlst_add(&world.items, t); + + if (unique_world_kind(thing.kind)) { + void *ref = arlst_get(&world.items, world.items.size - 1); + world.uniques_index[thing.kind] = ref; + } + +} + +/* Send a projectile from body in direction dir (in degrees) with the force of + * sten newtons */ +void fire_projectile(Body *from, int stren, int dir) { + Projectile *proj; + get_projectile(&proj); + + double radians = (double)dir * (M_PI / 180); + set_motor_newtons(proj->physics, 0, stren * cos(radians), + stren * sin(radians)); + set_motor_timeout(proj->physics, 0, 2); + + world_thing proj_world; + proj_world.projectile = proj; + proj_world.kind = PROJECTILE; + add_to_world(proj_world); +} + +void get_room(void) { + int floorsize = 100; + FloorPoly *polys = generate_floor_simple(floorsize, true, 1300); + Floor *floor = calloc(1, sizeof(Floor)); + floor->polys = polys; + floor->numPolys = floorsize; + + FloorPoly *ceil = generate_floor_simple(floorsize, false, 1000); + +// printf("floor: %f %f\n", polys[2].left.x, polys[2].left.y); +// printf("ceil: %f %f\n", ceil[2].left.x, ceil[2].left.y); + + Floor* ceiling = calloc(1, sizeof(Floor)); + ceiling->polys = ceil; + ceiling->numPolys = floorsize; + + world_thing ceilingthing; + ceilingthing.kind = CEILING; + ceilingthing.floor = ceiling; + add_to_world(ceilingthing); + + world_thing floorthing; + floorthing.kind = FLOOR; + floorthing.floor = floor; + add_to_world(floorthing); +} + +GlobWorld create_world() { + GlobWorld c; + c.items = new_arlst(100); + c.uniques_index = calloc(10, sizeof(world_thing*)); + c.get = world_getter; // gross + return c; +} + +void startgame(SDL_Renderer * ren) { + get_input_map(); + + textboxes[TB_LEVEL_CHOOSER].id = TB_LEVEL_CHOOSER; + textboxes[TB_LEVEL_CHOOSER].close_callback = new_level_tb_close_callback; + + debug_ren = ren; + + level = 1; + +#ifdef SCORE_SYSTEM + save_times_lst = (struct sg_times_list){}; + load_score_times("saves/times"); +#endif + + world = create_world(); + + SDL_GetRendererOutputSize(ren, &width, &height); + +// player = get_player(200,1300); + + get_floor_ceiling(); + Vect stpos = *world.uniques_index[ROOM_W]->room->ceil.items[2]->collision_poly; + player = get_player(stpos.x, stpos.y - 15); + world_thing player_world; + + player_world.kind = PLAYER_W; + player_world.player = malloc(sizeof(player)); + *player_world.player = player; + glob_player = player_world.player; + + add_to_world(player_world); + + viewport_pos.x = 700; + viewport_pos.y = 0; + + level_timer_update(true); + game_paused = 0; + viewport_pos.x = player.physics->position.x - width / 2; + viewport_pos.y = player.physics->position.y - height / 2; + + //get_room(); +} + +double get_abs(double x,double y) { + return (sqrt(x*x + y*y)); +} + +void walk_player(int x, int y) { + + set_motor_status(player.physics, M_PLAYER_WALK, true); + set_motor_newtons(player.physics, M_PLAYER_WALK, 0, 0); + // turned off special case for going down for TESTing + if (false && y == -1) { + add_motor_newtons(glob_player->physics, M_PLAYER_WALK, 0, 100 * 10 * y); + return; + } + add_motor_newtons(glob_player->physics, M_PLAYER_WALK, 100 *5* x , 100 * 5 * y); +} + +void handle_input_event(SDL_Event event) { + SDL_Scancode sc = event.key.keysym.scancode; + static bool mouse_down = false; + switch (event.type) { + case SDL_KEYDOWN: + + if (gameui.currently_bound_textbox) { + if (event.key.keysym.scancode == SDL_SCANCODE_BACKSPACE + || event.key.keysym.scancode == SDL_SCANCODE_DELETE) { + delete_from_textbox(gameui.currently_bound_textbox); + } else if (event.key.keysym.scancode == SDL_SCANCODE_RETURN + || event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) { + if (gameui.currently_bound_textbox->close_callback) { + gameui.currently_bound_textbox->close_callback( + gameui.currently_bound_textbox, NULL); + } + gameui.currently_bound_textbox = NULL; + } + else if (event.key.keysym.sym >= 32 + && event.key.keysym.sym <= 126) { + write_to_textbox(gameui.currently_bound_textbox, + (char)event.key.keysym.sym); + } + // dont do anything else when the textbox is bound; + break; + } + + +#ifdef DEBUGMODE + if (sc == input_map.player_up) { + walk_player(0, -1); + } if ( sc == input_map.player_left) { + walk_player(-1, 0); + } if (sc == input_map.player_down) { + walk_player(0, 1); + } if (sc == input_map.player_right) { + walk_player(1, 0); + } +#endif + if (sc == input_map.player_pull_rope) { + pull_rope(900); + } if (sc == input_map.mute) { + mute = !mute; + } if (sc == input_map.pause) { + if (in_game) + game_paused = !game_paused; + } + + if (sc == input_map.goto_level) { + gameui.currently_bound_textbox = textboxes + TB_LEVEL_CHOOSER; + }; + + + break; + case SDL_KEYUP: + if (event.key.keysym.scancode == input_map.player_rope) { + delete_rope(); + } +#ifdef DEBUGMODE + if (sc == input_map.player_up || sc == input_map.player_down + || sc == input_map.player_left + || sc == input_map.player_right) { + set_motor_newtons(player.physics, M_PLAYER_WALK, 0, 0); + set_motor_status(player.physics, M_PLAYER_WALK, false); + } + if (event.key.keysym.scancode == SDL_SCANCODE_F10) { + next_level(); + } +#endif + if (sc == input_map.player_pull_rope) { + stop_pull_rope(); + } + + break; + case SDL_MOUSEBUTTONDOWN: + add_rope(event.button.x, event.button.y); + if (event.button.button == input_map.mouse_attach_rope) + mouse_down = true; + if (event.button.button == input_map.mouse_attach_rope_pull) + pull_rope(900); + break; + case SDL_MOUSEBUTTONUP: + if (event.button.button == input_map.mouse_attach_rope) { + mouse_down = false; + delete_rope(); + } + if (event.button.button == input_map.mouse_attach_rope_pull) { + stop_pull_rope(); + if (!mouse_down) { + delete_rope(); + } + } + } +} + + +/* temporary storage variable *n should be initialised to 1 */ +int get_rand(int *n) { + const unsigned c = 11; + const unsigned m = (1 << 31) - 1; + const unsigned initial_n = 1; + const unsigned a = 48271; + + *n = (a * *n + c) % m; + return *n; +} + + +int step(void) { + static int first_run = 0; + if (!first_run) { + start_audio(); +// play_game_sound(game_sounds.background, -1, BC_MUSIC); + first_run = 1; + game_paused = 0; + } + + if (gameui.goto_new_level) { + int nevl = gameui.goto_new_level; + gameui.goto_new_level = 0; + return nevl; + } + + if (player.physics->position.x > world.uniques_index[ROOM_W]->room + ->floor.items[world.uniques_index[ROOM_W]->room->floor.numItems - 1]->position.x) { + draw_watch.finish_level = true; + return level + 1; + } + + if (!game_paused) { + if (player.physics->position.x > world.uniques_index[ROOM_W]->room + ->floor.items[3]->position.x) { + level_timer_update(false); + } + + if (in_game) { + advance_things(); + } + } else { + last_tick = get_now(); + } + + return 0; +} + diff --git a/build-android/src/game.h b/build-android/src/game.h new file mode 100644 index 0000000..405d935 --- /dev/null +++ b/build-android/src/game.h @@ -0,0 +1,82 @@ +#ifndef _DEFGAME +#define _DEFGAME + +#include "environment.h" +#include "datatypes.h" +#include "vect.h" +#include "controlscheme.h" +#include "colours.h" +#include "types.h" +#include "draw.h" + +extern GlobWorld world; +extern int level; +extern bool in_game; +extern int quality; + +struct draw_watcher { + long best_time; + bool finish_level; +}; + + +struct ui_state { + struct textbox_info *currently_bound_textbox; + int goto_new_level; +}; + + +enum TextBoxId { + TB_LEVEL_CHOOSER = 0 +}; + +struct textbox_info { + enum TextBoxId id; + bool capture_input_text_field; + char text_input[32]; + int text_input_bufpos; + void (*close_callback)(struct textbox_info *textbox, void*callback); +}; + +extern struct draw_watcher draw_watch; +extern struct textbox_info* texboxes; +extern struct ui_state gameui; + + +void handle_input_event(SDL_Event event); + +// add a motor to the world +void add_motor(Body *thing, double x, double y); + +bool get_motor_active(Body *thing, int i); + +/* temporary storage variable *n should be initialised to the seed value */ +int get_rand(int *n); + +void next_level(int lvl); + +int step(void); + +struct sg_times_list { + long *times; + int size; + int capacity; + void (*sort)(void); + char *filename; +}; + +int load_score_times(char *filename); + + +/* array of all the things in the world and their kinds */ + +extern struct sg_times_list save_times_lst; +int add_time(long time); +int write_times(void); +extern void startgame(SDL_Renderer * ren) ; +extern void process_keydown(SDL_Keysym key); +extern void process_keyup(SDL_Keysym key); +extern player_st player; +extern long level_time; +extern bool game_paused; +#endif diff --git a/build-android/src/main.c b/build-android/src/main.c new file mode 100644 index 0000000..f56c5fc --- /dev/null +++ b/build-android/src/main.c @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audio.h" // only for mkdirat lol +#include +#include + +#include "game.h" +#include "draw.h" +#include "types.h" + +const int screen_width = 800; +const int screen_height = 600; + + +SDL_sem *resume; + + +struct SDL_Window* make_window(void) { + if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { + printf("error initializing SDL: %s\n", SDL_GetError()); + } + + + return SDL_CreateWindow("space_game", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + 0, 0, + SDL_WINDOW_FULLSCREEN_DESKTOP); +} + + +void redraw(struct SDL_Renderer * ren) { + SDL_RenderClear(ren); + redraw_buffer(ren); + SDL_RenderPresent(ren); +} + + +void godophysics(void) { + int lvl; + if ((lvl = step())) { + // display end level screen + in_game = false; + game_paused = true; + SDL_Delay(300); + SDL_SemWait(resume); +#ifdef SCORE_SYSTEM + add_time(level_time); +#endif + + SDL_LockMutex(player.physics->lock); + next_level(lvl); + in_game = true; + SDL_UnlockMutex(player.physics->lock); + game_paused = false; + } +} + +int physics_loop(void *ptr) { + game_paused = 1; + while (1) { + godophysics(); + } +} + + + +int game(void) { + //SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "2" ); + + SDL_Window * win = make_window(); + SDL_Renderer * ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED); +// | SDL_RENDERER_PRESENTVSYNC); + SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND); + + + in_game = true; + resume = SDL_CreateSemaphore(0); + + // IMG_Init(IMG_INIT_PNG | IMG_INIT_JPG); + + if (ren == NULL) { + SDL_DestroyWindow(win); + SDL_Quit(); + } + + TTF_Init(); + + int close = 0; + + //draw_pictures(ren); + + SDL_Thread *physics_thread; + int ignore; + + startgame(ren); + + physics_thread = SDL_CreateThread(physics_loop, "physics", (void *)ren); + + int count = 0; + + while (!close) { + SDL_Event event; + while(SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + goto endfunc; + case SDL_KEYDOWN: + if (event.key.keysym.scancode == SDL_SCANCODE_Q) { + goto endfunc; + } + if (!in_game && !SDL_SemValue(resume)) { + SDL_SemPost(resume); + } + case SDL_KEYUP: + case SDL_MOUSEBUTTONDOWN: + if (!in_game && !SDL_SemValue(resume)) { + SDL_SemPost(resume); + break; + } + case SDL_MOUSEBUTTONUP: + handle_input_event (event); + } + } + + if (!in_game && draw_watch.finish_level) { + draw_end_screen(ren); + } else { + redraw(ren); + } + } + +endfunc: + + SDL_DestroyRenderer(ren); + SDL_DestroyWindow(win); + return 0; +} + + +int main (int argc, char** argv) { + +#ifdef __linux__ + mkdirat(AT_FDCWD, "saves", 0777); +#elif WIN32 + if (access("saves", 0) == 0) { + struct stat status; + stat("saves", &status ); + if ((status.st_mode & S_IFDIR) == 0) { + _mkdir("saves"); + } + } +#endif + + game(); + + SDL_Quit(); + return 0; +} + + + +/* + * TODO: + * - Allow setting and saving/loading differnet control schemes + * - Allow starting a specific level + * - Allow adjusging level lengths? + * - Ensure next_level doesn't leak memory + * - fix that weird jitter + * - make the end of level look sane + * - restart level + * - make sure goto level doesn't log an end-0f-level time + */ + diff --git a/build-android/src/main.cpp b/build-android/src/main.cpp new file mode 100644 index 0000000..c6fca0c --- /dev/null +++ b/build-android/src/main.cpp @@ -0,0 +1,61 @@ +#include +#include + +int main(int /*argc*/, char* /*argv*/[]) { + + SDL_Window *window; // Declare a pointer + + SDL_Init(SDL_INIT_VIDEO); // Initialize SDL2 + + // Create an application window with the following settings: + window = SDL_CreateWindow( + "An SDL2 window", // window title + SDL_WINDOWPOS_UNDEFINED, // initial x position + SDL_WINDOWPOS_UNDEFINED, // initial y position + 640, // width, in pixels + 480, // height, in pixels + SDL_WINDOW_OPENGL // flags - see below + ); + + // Check that the window was successfully created + if (window == NULL) { + // In the case that the window could not be made... + printf("Could not create window: %s\n", SDL_GetError()); + return 1; + } + + // The window is open: could enter program loop here (see SDL_PollEvent()) + // Setup renderer + SDL_Renderer* renderer = NULL; + renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED); + + // Set render color to red ( background will be rendered in this color ) + SDL_SetRenderDrawColor( renderer, 255, 0, 0, 255 ); + + // Clear winow + SDL_RenderClear( renderer ); + + // bouyatest + + // Creat a rect at pos ( 50, 50 ) that's 50 pixels wide and 50 pixels high. + SDL_Rect r; + r.x = 50; + r.y = 50; + r.w = 500; + r.h = 500; + + // Set render color to blue ( rect will be rendered in this color ) + SDL_SetRenderDrawColor( renderer, 0, 200, 255, 255 ); + + // Render the rect to the screen + SDL_RenderPresent(renderer); + + SDL_Delay(1000); // Pause execution for 3000 milliseconds, for example + + // Close and destroy the window + SDL_DestroyWindow(window); + + // Clean up + SDL_Quit(); + return 0; +} diff --git a/build-android/src/physics.c b/build-android/src/physics.c new file mode 100644 index 0000000..1eb9348 --- /dev/null +++ b/build-android/src/physics.c @@ -0,0 +1,2 @@ +#include "physics.h" +#include "types.h" diff --git a/build-android/src/physics.h b/build-android/src/physics.h new file mode 100644 index 0000000..e69de29 diff --git a/build-android/src/types.h b/build-android/src/types.h new file mode 100644 index 0000000..8f641d3 --- /dev/null +++ b/build-android/src/types.h @@ -0,0 +1,236 @@ + +#ifndef PTYPES_H +#define PTYPES_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "colours.h" +#include "datatypes.h" +#include "vect.h" + +enum motors { + M_GRAVITY = 0, + M_FRICTION = 1, + M_PLAYER_WALK = 2, + M_WINCH = 3 +}; + +enum world_thing_kind { + PLAYER_W = 0, + FLOOR = 1, + CEILING = 2, + ROOM_W, + STATIC_WALL_W, + PROJECTILE +}; + + +// used to exert a force on an object +typedef struct motorstruct { + double x; // positive is right + double y; // positive is down + double torque; // positive is anticlockwise + double max_velocity; // max motor output velocity + // does not apply force if the velocity in the + // direction of the motor vector is greater than + // max_velocity + + struct timespec timeout;// absolute time (in ns, from when the program starts) + // for how long the motor runs before stopping + // automatically + // set to -1 for infinity + bool stop; // turn the motor off or on + void (*update_motor)(struct motorstruct *motor); // function pointer for generating + // the motor's output curve + struct BodyStruct *end_object; +} Motor; + +typedef struct { + int x; + int y; +} Point; + +/* String structure, used as a sub-element of a Body, compressible, but not + * stretchable. */ +struct String { + bool attached; + double max_length; + Vect end_point; + int (*update_end_point)(struct String*); // method to update the end pt + // say if string is attached + // to another object + int (*set_end_point)(struct String*, Vect *); // manually set the end point of + // string +}; + +typedef struct String String; + +typedef struct BodyStruct{ + // turn on dynamic physics + // For moving objects. + // eg. on for payer, off for walls + bool dynamics; + + // unique identifier + int uid; + + bool colliding; + int was_colliding; + + // position in viewport (pixels) + SDL_Point screen_pos; + SDL_mutex * lock; + + // SI Unit kinematics + /*------------------*/ + Vect position; + Vect next_position; // used for casting collision + Vect vel; + Vect acc; + + // properties + double obj_mass; // kgs + double obj_elasticity; // rho + double obj_friction; // between 0 and 1 (fraction of lateral velocity + // that is removed) + + //float x_vel; + //float y_vel; + + //float x_acc; + //float y_acc; + /*------------------*/ + + // collisions + Vect *collision_poly; + Vect *collision_shape; + int collision_poly_size; + int collision_shape_size; + void (*updateCollisionPoly)(struct BodyStruct *); + + // fields + double glob_friction; + bool glob_gravity; // t/f + +// uint32_t last_advance_time; + + struct timespec last_advance_time; + + // applying forces + int num_motors; + int max_motors; + Motor *motors; + + int num_strings; + int max_strings; + String *strings; + +} Body; + +typedef struct { + bool has_physics; + Body *physics; + int max_walking_speed; + int colliding; +} player_st; + +typedef struct { + int numNodes; + SDL_Point *nodes; + Body *physics; +} Wall; + +typedef struct { + Vect left; + Vect right; + Body *physics; +} FloorPoly; + +typedef struct { + FloorPoly *polys; + int numPolys; +} Floor; + +struct physics_collection { + int numItems; + Body **items; +}; + +typedef struct Projectile { + Body *physics; + void*(*on_collision)(struct Projectile*); + void*(*on_step)(struct Projectile*); +} Projectile; + +struct colour_pallete { + struct colour bg; + struct colour fg1; + struct colour fg2; + struct colour fg3; +}; + +struct environment { + int level; + Vect position; + ArrayList ceil; // doubles + ArrayList floor; // doubles + ArrayList bg1; // doubles + ArrayList bg2; // doubles + struct colour_pallete colours; + struct physics_collection physics; +}; + +struct room { + struct environment env; + struct physics_collection ceil; + struct physics_collection floor; +}; + +struct world_thing { + enum world_thing_kind kind; + int nid; + bool collisions; + bool physics; + // free function for the below type + void (*free)(void *); + union { + player_st *player; + Wall *wall; + Floor *floor; + Projectile *projectile; + struct room *room; + }; +}; + +typedef struct world_thing world_thing; + +struct world { + world_thing (*get)(int i); + ArrayList items; + int modified; + world_thing** uniques_index; +}; + +typedef struct world GlobWorld; + +// environment + +enum { +// E_ROOM_WIDTH = 100000, + E_ROOM_WIDTH = 10000, + E_ROOM_RES = 500, + E_ROOM_TILES = E_ROOM_WIDTH / E_ROOM_RES, +}; + +#endif diff --git a/build-android/src/vect.c b/build-android/src/vect.c new file mode 100644 index 0000000..ff123bf --- /dev/null +++ b/build-android/src/vect.c @@ -0,0 +1,81 @@ +#include "vect.h" + +double vect_dot(Vect V, Vect B) { + return V.x * B.x + V.y * B.y; +} + +double vect_mag(Vect v) { + double mag = sqrt((v.x * v.x) + (v.y * v.y)); + return mag; +} + +Vect vect_scalar(Vect V, double s) { + Vect ret; + ret.x = V.x * s; + ret.y = V.y * s; + return ret; +} + +double vect_arg(Vect v) { + return atan2(v.y, v.x); +} + +Vect vect_modarg(double mod, double arg) { + Vect ret; + ret.x = mod * cos(arg); + ret.y = mod * sin(arg); + return ret; +} + +Vect vect_rotate(Vect v, double radians) { + Vect res; + + res.x = v.x * cos(radians) - v.y * sin(radians); + res.y = v.x * sin(radians) + v.y * cos(radians); + return res; +} + +Vect vect_add(Vect v1, Vect v2) { + Vect res; + res.x = v1.x + v2.x; + res.y = v1.y + v2.y; + return res; +} + +// project one onto two +Vect project_vect(Vect one, Vect two) { + // $$ a \cdot \frac {|b|} {b} $$ + Vect unittwo; + unittwo.x = two.x / vect_mag(two); + unittwo.y = two.y / vect_mag(two); + + Vect proj; + proj.x = unittwo.x * one.x; + proj.y = unittwo.y * one.y; + + return proj; +} + +// project V onto P +double old_vect_scalar_projection(Vect V, Vect P) { + double angle = vect_dir(V) - vect_dir(P); + return cos(angle) * vect_mag(V); +} + +double vect_scalar_projection(Vect v, Vect p) { + double num = vect_dot(v, p); + return num / vect_mag(p); +} + +double vect_dir(Vect V) { + return atan2(V.y, V.x); +} + +// distance between two points +double vect_distance(Vect A, Vect B) { + double y = (B.y - A.y); + double x = (B.x - A.x); + y = y * y; + x = x * x; + return sqrt(x + y); +} diff --git a/build-android/src/vect.h b/build-android/src/vect.h new file mode 100644 index 0000000..c711a8d --- /dev/null +++ b/build-android/src/vect.h @@ -0,0 +1,45 @@ + +#ifndef VECT_H +#define VECT_H +#include + +// origin-centred vector +typedef struct { + double x; + double y; +} Vect; + +/* Return the magnitude of two vectors */ +double vect_mag(Vect v); + +/* Return the angle of a vector in radians (using atan2) */ +double vect_dir(Vect V); + +/* Return the dot product of two vectors */ +double vect_dot(Vect V, Vect B); + +/* return a vector multiplied by a scalar */ +Vect vect_scalar(Vect V, double s); + +/* Return the sum of two vectors */ +Vect vect_add(Vect v1, Vect v2); + +/* Return the projection of vector one onto vector two */ +Vect project_vect(Vect one, Vect two); + +/* Return the scalar projection of V onto P */ +double vect_scalar_projection(Vect V, Vect P); + +/* Return the vector v rotated by radians radians. */ +Vect vect_rotate(Vect v, double radians); + +/* Return the argument of v in radians */ +double vect_arg(Vect v); + +/* create a vector using modarg form */ +Vect vect_modarg(double mod, double arg); + +/* Return the distance between two point vectors A and B */ +double vect_distance(Vect A, Vect B); + +#endif