aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGene Carlson <kvngncrlsn@gmail.com>2023-09-18 21:20:25 +0900
committerWilly Sudiarto Raharjo <willysr@slackbuilds.org>2023-09-23 08:31:14 +0700
commit5953128575bad1c6aee541924e5081ce9e0898f0 (patch)
tree2fdbfa6e5e05e21d95e92374e4ed14e4f6606b92
parentda54a60c5a13c1deb3d621247b7ef14d7c87cb78 (diff)
games/pioneer: Updated for version 20230203; new maintainer.
Signed-off-by: Willy Sudiarto Raharjo <willysr@slackbuilds.org>
-rw-r--r--games/pioneer/README10
-rw-r--r--games/pioneer/alignment.patch22
-rw-r--r--games/pioneer/fix-build.patch41
-rw-r--r--games/pioneer/pioneer.SlackBuild17
-rw-r--r--games/pioneer/pioneer.info10
-rw-r--r--games/pioneer/ship-ai.patch447
6 files changed, 508 insertions, 39 deletions
diff --git a/games/pioneer/README b/games/pioneer/README
index 389b6706afd2..68ed72ee23ad 100644
--- a/games/pioneer/README
+++ b/games/pioneer/README
@@ -11,13 +11,17 @@ self-determination.
WARNING: If pioneer is already installed, uninstall it before compiling
or important game data may be missing and pioneer will fail to start.
-OpenGL core profile version 3.1 or newer is a runtime dependency, to
-determine what version of OpenGL is installed use:
+OpenGL core profile version 3.1 or newer is a runtime dependency. To
+determine what version of OpenGL is installed, use:
glxinfo | grep "core profile version"
lua52 is an optional dependency.
-To build with debugging support use:
+To build with debugging support, use:
DEBUG="yes" ./pioneer.SlackBuild
+
+If building in a graphical session, a popup window about a failure to
+write to a log file will likely appear and stop the build. This is
+harmless, and the build proceeds normally after clicking "OK."
diff --git a/games/pioneer/alignment.patch b/games/pioneer/alignment.patch
deleted file mode 100644
index 10ad31ed08c5..000000000000
--- a/games/pioneer/alignment.patch
+++ /dev/null
@@ -1,22 +0,0 @@
---- a/src/scenegraph/Serializer.orig.h 2019-10-09 13:49:04.000000000 +0200
-+++ b/src/scenegraph/Serializer.h 2019-10-09 18:47:05.238671730 +0200
-@@ -23,14 +23,14 @@
- // where possible, prefer serializing state information via JSON instead.
- namespace Serializer {
- static_assert((sizeof(Uint32) == 4 && alignof(Uint32) == 4), "Int32 is sized differently on this platform and will not serialize properly.");
-- static_assert((sizeof(Uint64) == 8 && alignof(Uint64) == 8), "Int64 is sized differently on this platform and will not serialize properly.");
-+ static_assert((sizeof(Uint64) == 8 && alignof(Uint64) <= 8), "Int64 is sized differently on this platform and will not serialize properly.");
- static_assert((sizeof(Color) == 4 && alignof(Color) == 1), "Color is padded differently on this platform and will not serialize properly.");
- static_assert((sizeof(vector2f) == 8 && alignof(vector2f) == 4), "Vector2f is padded differently on this platform and will not serialize properly.");
-- static_assert((sizeof(vector2d) == 16 && alignof(vector2d) == 8), "Vector2d is padded differently on this platform and will not serialize properly.");
-+ static_assert((sizeof(vector2d) == 16 && alignof(vector2d) <= 8), "Vector2d is padded differently on this platform and will not serialize properly.");
- static_assert((sizeof(vector3f) == 12 && alignof(vector3f) == 4), "Vector3f is padded differently on this platform and will not serialize properly.");
-- static_assert((sizeof(vector3d) == 24 && alignof(vector3d) == 8), "Vector3d is padded differently on this platform and will not serialize properly.");
-+ static_assert((sizeof(vector3d) == 24 && alignof(vector3d) <= 8), "Vector3d is padded differently on this platform and will not serialize properly.");
- static_assert((sizeof(Quaternionf) == 16 && alignof(Quaternionf) == 4), "Quaternionf is padded differently on this platform and will not serialize properly.");
-- static_assert((sizeof(Aabb) == 56 && alignof(Aabb) == 8), "Aabb is padded differently on this platform and will not serialize properly.");
-+ static_assert((sizeof(Aabb) == 56 && alignof(Aabb) <= 8), "Aabb is padded differently on this platform and will not serialize properly.");
-
- class Writer {
- public:
-
diff --git a/games/pioneer/fix-build.patch b/games/pioneer/fix-build.patch
new file mode 100644
index 000000000000..35afdec062b8
--- /dev/null
+++ b/games/pioneer/fix-build.patch
@@ -0,0 +1,41 @@
+--- src/lua/LuaPushPull.h.orig 2023-09-18 14:23:51.229000000 +0900
++++ src/lua/LuaPushPull.h 2023-09-18 14:32:10.777000000 +0900
+@@ -12,10 +12,10 @@
+ #include <tuple>
+
+ inline void pi_lua_generic_push(lua_State *l, bool value) { lua_pushboolean(l, value); }
+-inline void pi_lua_generic_push(lua_State *l, int value) { lua_pushinteger(l, value); }
++inline void pi_lua_generic_push(lua_State *l, int32_t value) { lua_pushinteger(l, value); }
+ inline void pi_lua_generic_push(lua_State *l, int64_t value) { lua_pushinteger(l, value); }
+-inline void pi_lua_generic_push(lua_State *l, unsigned int value) { lua_pushinteger(l, value); }
+-inline void pi_lua_generic_push(lua_State *l, size_t value) { lua_pushinteger(l, value); }
++inline void pi_lua_generic_push(lua_State *l, uint32_t value) { lua_pushinteger(l, value); }
++inline void pi_lua_generic_push(lua_State *l, uint64_t value) { lua_pushinteger(l, value); }
+ inline void pi_lua_generic_push(lua_State *l, double value) { lua_pushnumber(l, value); }
+ inline void pi_lua_generic_push(lua_State *l, const char *value) { lua_pushstring(l, value); }
+ inline void pi_lua_generic_push(lua_State *l, const std::string &value)
+@@ -29,10 +29,10 @@
+ inline void pi_lua_generic_push(lua_State *l, const std::nullptr_t &value) { lua_pushnil(l); }
+
+ inline void pi_lua_generic_pull(lua_State *l, int index, bool &out) { out = lua_toboolean(l, index); }
+-inline void pi_lua_generic_pull(lua_State *l, int index, int &out) { out = luaL_checkinteger(l, index); }
++inline void pi_lua_generic_pull(lua_State *l, int index, int32_t &out) { out = luaL_checkinteger(l, index); }
+ inline void pi_lua_generic_pull(lua_State *l, int index, int64_t &out) { out = luaL_checkinteger(l, index); }
+-inline void pi_lua_generic_pull(lua_State *l, int index, unsigned int &out) { out = luaL_checkunsigned(l, index); }
+-inline void pi_lua_generic_pull(lua_State *l, int index, size_t &out) { out = luaL_checkunsigned(l, index); }
++inline void pi_lua_generic_pull(lua_State *l, int index, uint32_t &out) { out = luaL_checkinteger(l, index); }
++inline void pi_lua_generic_pull(lua_State *l, int index, uint64_t &out) { out = luaL_checkinteger(l, index); }
+ inline void pi_lua_generic_pull(lua_State *l, int index, float &out) { out = luaL_checknumber(l, index); }
+ inline void pi_lua_generic_pull(lua_State *l, int index, double &out) { out = luaL_checknumber(l, index); }
+ inline void pi_lua_generic_pull(lua_State *l, int index, const char *&out) { out = luaL_checkstring(l, index); }
+--- src/lua/LuaShip.cpp.orig 2023-09-18 14:42:08.048000000 +0900
++++ src/lua/LuaShip.cpp 2023-09-18 14:42:20.781000000 +0900
+@@ -19,7 +19,7 @@
+ #include "SpaceStation.h"
+ #include "ship/PlayerShipController.h"
+ #include "ship/PrecalcPath.h"
+-#include "src/lua.h"
++#include "lua.h"
+
+ /*
+ * Class: Ship
diff --git a/games/pioneer/pioneer.SlackBuild b/games/pioneer/pioneer.SlackBuild
index 83daea8051e8..1e08159da2fc 100644
--- a/games/pioneer/pioneer.SlackBuild
+++ b/games/pioneer/pioneer.SlackBuild
@@ -3,6 +3,7 @@
# Slackware build script for pioneer
# Copyright 2015-2019 Hunter Sezen California, USA
+# Copyright 2023 K. Eugene Carlson Tsukuba, JP
# All rights reserved.
#
# Redistribution and use of this script, with or without modification, is
@@ -25,8 +26,7 @@
cd $(dirname $0) ; CWD=$(pwd)
PRGNAM=pioneer
-SRCNAM=$(printf %s $PRGNAM | tr [a-z] [A-Z])
-VERSION=${VERSION:-20191117}
+VERSION=${VERSION:-20230203}
BUILD=${BUILD:-1}
TAG=${TAG:-_SBo}
PKGTYPE=${PKGTYPE:-tgz}
@@ -53,16 +53,12 @@ OUTPUT=${OUTPUT:-/tmp}
if [ "$ARCH" = "i586" ]; then
SLKCFLAGS="-O2 -march=i586 -mtune=i686"
- LIBDIRSUFFIX=""
elif [ "$ARCH" = "i686" ]; then
SLKCFLAGS="-O2 -march=i686 -mtune=i686"
- LIBDIRSUFFIX=""
elif [ "$ARCH" = "x86_64" ]; then
SLKCFLAGS="-O2 -fPIC"
- LIBDIRSUFFIX="64"
else
SLKCFLAGS="-O2"
- LIBDIRSUFFIX=""
fi
set -eu
@@ -98,9 +94,10 @@ pkg-config --exists glew && GLEW=ON
# as cmake seems to search for them in order.
pkg-config --exists lua5.2 && ! pkg-config --exists lua && LUA=ON
-# Workaround 32-bit build failure
-# https://github.com/pioneerspacesim/pioneer/issues/4691
-patch -p1 < $CWD/alignment.patch
+# Fix 32-bit build (PR 5538) and system lua build (PR 5526).
+patch -p0 < $CWD/fix-build.patch
+# Post-release autopilot fixes (PR 5481, 5551).
+patch -p1 < $CWD/ship-ai.patch
# 20210214 bkw: prevent the build from writing to /root/, without
# breaking ccache if it's in use.
@@ -121,6 +118,7 @@ cd build
-DCMAKE_BUILD_TYPE=$RELEASE ..
make
make install DESTDIR=$PKG
+ make -C . build-data install DESTDIR=$PKG
cd ..
if [ "$DEBUG" = 0 ]; then
@@ -130,6 +128,7 @@ fi
mkdir -p $PKG/usr/doc/$PRGNAM-$VERSION
cp -a licenses *.txt README.md $PKG/usr/doc/$PRGNAM-$VERSION
+rm -f $PKG/usr/doc/$PRGNAM-$VERSION/{CMakeLists,SAVEBUMP}.txt
cat $CWD/$PRGNAM.SlackBuild > $PKG/usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild
mkdir -p $PKG/install
diff --git a/games/pioneer/pioneer.info b/games/pioneer/pioneer.info
index 81cb877e8d33..dc36db10b9a3 100644
--- a/games/pioneer/pioneer.info
+++ b/games/pioneer/pioneer.info
@@ -1,10 +1,10 @@
PRGNAM="pioneer"
-VERSION="20191117"
+VERSION="20230203"
HOMEPAGE="https://pioneerspacesim.net/"
-DOWNLOAD="https://github.com/pioneerspacesim/pioneer/archive/20191117/pioneer-20191117.tar.gz"
-MD5SUM="4b7b7e5119b371b65cd61f45c6747dad"
+DOWNLOAD="https://github.com/pioneerspacesim/pioneer/archive/20230203/pioneer-20230203.tar.gz"
+MD5SUM="15f3e74303a098bb198f15c8674c43d4"
DOWNLOAD_x86_64=""
MD5SUM_x86_64=""
REQUIRES="assimp"
-MAINTAINER="Hunter Sezen"
-EMAIL="orbea@riseup.net"
+MAINTAINER="K. Eugene Carlson"
+EMAIL="kvngncrlsn@gmail.com"
diff --git a/games/pioneer/ship-ai.patch b/games/pioneer/ship-ai.patch
new file mode 100644
index 000000000000..834788155461
--- /dev/null
+++ b/games/pioneer/ship-ai.patch
@@ -0,0 +1,447 @@
+--- pioneer-20230203.orig/src/ShipAICmd.cpp 2023-07-19 23:54:00.245824921 +0900
++++ pioneer-20230203/src/ShipAICmd.cpp 2023-07-19 23:54:15.061778840 +0900
+@@ -731,7 +731,7 @@
+ //2 - unsafe escape from effect radius
+ //3 - unsafe entry to effect radius
+ //4 - probable path intercept
+-int CheckCollision(DynamicBody *dBody, const vector3d &pathdir, double pathdist, const vector3d &tpos, double endvel, double r)
++int CheckCollision(DynamicBody *dBody, const vector3d &pathdir, double pathdist, double tlen, double endvel, double r)
+ {
+ Propulsion *prop = dBody->GetComponent<Propulsion>();
+ if (!prop) // This body doesn't have any propulsion to avoid collision
+@@ -742,24 +742,35 @@
+ Body *body = Frame::GetFrame(dBody->GetFrame())->GetBody();
+ if (!body) return 0;
+ vector3d spos = dBody->GetPosition();
+- double tlen = tpos.Length(), slen = spos.Length();
++ double slen = spos.Length();
+ double fr = MaxFeatureRad(body);
+
+- // if target inside, check if direct entry is safe (30 degree)
++ // find closest point to obstructor
++ double distToTangent = -spos.Dot(pathdir);
++
++ // if target inside, check if direct entry is safe
++ // no 30 deg aproach anymore as after FlyAround this couses overshoot
+ if (tlen < r) {
+- double af = (tlen > fr) ? 0.5 * (1 - (tlen - fr) / (r - fr)) : 0.5;
+- if (pathdir.Dot(tpos) > -af * tlen)
+- if (slen < fr)
++
++ vector3d tangent = spos + distToTangent * pathdir;
++
++ //The target is obscured
++ if(distToTangent < pathdist && tangent.LengthSqr() < fr * fr) {
++ if (slen < fr )
+ return 1;
+- else
+- return 3;
+- else
+- return 0;
++
++ return 3;
++ }
++
++ //The speed checks are now done in the CheckSuicide function
++ return 0;
+ }
+
+- // if ship inside, check for max feature height and direct escape (30 degree)
++ // if ship inside, target outside, check for max feature height and direct escape (30 degree)
+ if (slen < r) {
+- if (slen < fr) return 1;
++ if (slen < fr)
++ return 1;
++
+ double af = (slen > fr) ? 0.5 * (1 - (slen - fr) / (r - fr)) : 0.5;
+ if (pathdir.Dot(spos) < af * slen)
+ return 2;
+@@ -768,25 +779,15 @@
+ }
+
+ // now for the intercept calc
+- // find closest point to obstructor
+- double tanlen = -spos.Dot(pathdir);
+- if (tanlen < 0 || tanlen > pathdist) return 0; // closest point outside path
++ if (distToTangent < 0 || distToTangent > pathdist) return 0; // closest point to obstructor outside path
++
++
++ vector3d sidePos = spos - pathdir * spos.Dot(pathdir);
++
++ //Check if the path goes through the obstructor effective radious
++ if(sidePos.LengthSqr() < r * r) return 4;
+
+- vector3d perpdir = (tanlen * pathdir + spos).Normalized();
+- double perpspeed = dBody->GetVelocity().Dot(perpdir);
+- double parspeed = dBody->GetVelocity().Dot(pathdir);
+- if (parspeed < 0) parspeed = 0; // shouldn't break any important case
+- if (perpspeed > 0) perpspeed = 0; // prevent attempts to speculatively fly through planets
+-
+- // find time that dBody will pass through that point
+- // get velocity as if accelerating from start or end, pick smallest
+- double ivelsqr = endvel * endvel + 2 * prop->GetAccelFwd() * (pathdist - tanlen); // could put endvel in here
+- double fvelsqr = parspeed * parspeed + 2 * prop->GetAccelFwd() * tanlen;
+- double tanspeed = sqrt(ivelsqr < fvelsqr ? ivelsqr : fvelsqr);
+- double time = tanlen / (0.5 * (parspeed + tanspeed)); // actually correct?
+
+- double dist = spos.Dot(perpdir) + perpspeed * time; // spos.perpdir should be positive
+- if (dist < r) return 4;
+ return 0;
+ }
+
+@@ -828,20 +829,57 @@
+ return true;
+ }
+
++
+ // check for collision course with frame body
+-// tandir is normal vector from planet to target pos or dir
+-static bool CheckSuicide(DynamicBody *dBody, const vector3d &tandir)
++//#define DEBUG_CHECK_SUICIDE
++static bool CheckSuicide(DynamicBody *dBody, const vector3d &obspos, double obsMass, double safeAlt, double targetAlt, bool recovering)
+ {
+- Body *body = Frame::GetFrame(dBody->GetFrame())->GetBody();
+ if (!dBody->HasComponent<Propulsion>()) return false;
+ Propulsion *prop = dBody->GetComponent<Propulsion>();
+ assert(prop != nullptr);
+- if (!body || !body->IsType(ObjectType::TERRAINBODY)) return false;
+
+- double vel = dBody->GetVelocity().Dot(tandir); // vel towards is negative
+- double dist = dBody->GetPosition().Length() - MaxFeatureRad(body);
+- if (vel < -1.0 && vel * vel > 2.0 * prop->GetAccelMin() * dist)
++ double obsDist = obspos.Length();
++
++ //sanity check
++ if(obsDist > 100 * safeAlt)
++ return false;
++
++ vector3d velDir = dBody->GetVelocity().NormalizedSafe();
++ double tangDist = obspos.Dot(velDir);
++
++ //ship passed the planet
++ if(tangDist < 0) return false;
++
++ double tangLenSqr = (obspos - velDir * tangDist).LengthSqr();
++
++ //or pitched speed vector above the safty horizon
++ if(tangLenSqr > safeAlt * safeAlt) return false;
++
++ //Ignore speed check -> continue the recovery until speed vector is over horizon
++ if(recovering) return true;
++ //below are more strict speed conditions to enter the recovery
++
++ //for final apreach the targetAlt must be used for safe speed check
++ double zeroSpeedAlt = std::min(safeAlt, targetAlt);
++
++ if(zeroSpeedAlt*zeroSpeedAlt < tangLenSqr) return false;
++
++ //distance to point of pircing of planet surface or sefe alt sphere by speed vector
++ double breakingDist = tangDist - sqrt(zeroSpeedAlt*zeroSpeedAlt - tangLenSqr);
++
++#ifdef DEBUG_CHECK_SUICIDE
++ if (dBody->IsType(ObjectType::PLAYER)) {
++ std::cout << "CheckSuicide breakingDist=" << breakingDist << std::endl;
++ std::cout << "Speed Check v^2 vs maxV^2: " << dBody->GetVelocity().LengthSqr() << "\t"
++ << 2*(prop->GetAccelFwd()*breakingDist - G*obsMass*(obsDist-zeroSpeedAlt)/(obsDist*zeroSpeedAlt)) << std::endl;
++ }
++#endif
++
++ //Energy equation with planet gravity taken into account
++ if (breakingDist > 100
++ && dBody->GetVelocity().LengthSqr() > 2*(prop->GetAccelFwd()*breakingDist - G*obsMass*(obsDist-zeroSpeedAlt)/(obsDist*zeroSpeedAlt)))
+ return true;
++
+ return false;
+ }
+
+@@ -869,7 +907,6 @@
+ {
+ AICommand::PostLoadFixup(space);
+ m_target = space->GetBodyByIndex(m_targetIndex);
+- m_lockhead = true;
+ m_frameId = m_target ? m_target->GetFrame() : FrameId();
+ // Ensure needed sub-system:
+ m_prop = m_dBody->GetComponent<Propulsion>();
+@@ -884,10 +921,11 @@
+ assert(m_prop != nullptr);
+ m_frameId = FrameId::Invalid;
+ m_state = -6;
+- m_lockhead = true;
+ m_endvel = 0;
+ m_tangent = false;
+ m_is_flyto = true;
++ m_suicideRecovery = false;
++
+ if (!target->IsType(ObjectType::TERRAINBODY))
+ m_dist = VICINITY_MIN;
+ else
+@@ -915,8 +953,8 @@
+ m_endvel(endvel),
+ m_tangent(tangent),
+ m_state(-6),
+- m_lockhead(true),
+- m_frameId(FrameId::Invalid)
++ m_frameId(FrameId::Invalid),
++ m_suicideRecovery(false)
+ {
+ m_prop = dBody->GetComponent<Propulsion>();
+ assert(m_prop != nullptr);
+@@ -933,6 +971,10 @@
+ m_endvel = jsonObj["end_vel"];
+ m_tangent = jsonObj["tangent"];
+ m_state = jsonObj["state"];
++ if(jsonObj.find("suicide_recovery") != jsonObj.end())
++ m_suicideRecovery = jsonObj["suicide_recovery"];
++ else
++ m_suicideRecovery = false;
+ } catch (Json::type_error &) {
+ throw SavedGameCorruptException();
+ }
+@@ -952,6 +994,7 @@
+ aiCommandObj["end_vel"] = m_endvel;
+ aiCommandObj["tangent"] = m_tangent;
+ aiCommandObj["state"] = m_state;
++ aiCommandObj["suicide_recovery"] = m_suicideRecovery;
+ jsonObj["ai_command"] = aiCommandObj; // Add ai command object to supplied object.
+ }
+
+@@ -992,9 +1035,51 @@
+ FrameId targframeId = m_target ? m_target->GetFrame() : m_targframeId;
+ ParentSafetyAdjust(m_dBody, targframeId, targpos, targvel);
+ vector3d relpos = targpos - m_dBody->GetPosition();
++ double targdist = relpos.Length();
++
++ Body* planetNear = Frame::GetFrame(m_dBody->GetFrame())->GetBody();
++ double targetAlt = targpos.Length();
++
++ if(planetNear) {
++ double M = planetNear->IsType(ObjectType::TERRAINBODY) ? planetNear->GetMass() : 0;
++ double safeAlt = MaxEffectRad(planetNear, m_prop);
++ vector3d obspos = -m_dBody->GetPosition();
++
++
++ if ((m_suicideRecovery = CheckSuicide(m_dBody, obspos, M, safeAlt, targetAlt, m_suicideRecovery))) {
++
++ //find best orientationg to get to horizon
++ vector3d sidedir = obspos.Cross(m_dBody->GetVelocity()).NormalizedSafe();
++ vector3d updir = sidedir.Cross(m_dBody->GetVelocity()).NormalizedSafe();
++
++ //clamped tangent of Yaw mismatch to target - for driving side trust
++ constexpr double cSideDriveRange = 0.02;
++ double targetSideTan = Clamp(targdist > 1 ? relpos.Dot(sidedir)/targdist : 0, -cSideDriveRange, cSideDriveRange);
++
++ //Bellow safe alt (gravity too big for thrusters) breaking will kill the ship eventually
++ //so in this case ship accelerates along speed vector otherwise it is safe to break
++ float sign = G*M/obspos.LengthSqr() > 0.9 * m_prop->GetAccelUp() ? 1.0 : -1.0;
++
++ double ang = m_prop->AIFaceDirection(m_dBody->GetVelocity() * sign);
++ m_prop->AIFaceUpdir(updir);
++#ifdef DEBUG_CHECK_SUICIDE
++ if (m_dBody->IsType(ObjectType::PLAYER)) {
++ std::cout << "SUICIDE recovery! ang=" << ang << " targetSideTan=" << targetSideTan << std::endl;
++ std::cout << "safeAlt=" << safeAlt << " obsdist=" << obspos.Length() << " targetpos.Length()=" << targpos.Length() << std::endl;
++ }
++#endif
++
++ //Full Up and Forward thruster.
++ //Side thrust depends on relative pos of the target - not relevant for recovery but it is nice
++ //to be aligned with the target after surviving.
++ m_prop->SetLinThrusterState(ang < 0.05 ? vector3d(sign * targetSideTan * (1/cSideDriveRange), 1, -1) : vector3d(0.0));
++
++ return false;
++ }
++ }
++
+ vector3d reldir = relpos.NormalizedSafe();
+ vector3d relvel = targvel - m_dBody->GetVelocity();
+- double targdist = relpos.Length();
+
+ #ifdef DEBUG_AUTOPILOT
+ if (m_ship->IsType(ObjectType::PLAYER))
+@@ -1018,7 +1103,7 @@
+ double erad = MaxEffectRad(body, m_prop);
+ Frame *targframe = Frame::GetFrame(targframeId);
+ if ((m_target && body != m_target) || (targframe && (!m_tangent || body != targframe->GetBody()))) {
+- int coll = CheckCollision(m_dBody, reldir, targdist, targpos, m_endvel, erad);
++ int coll = CheckCollision(m_dBody, reldir, targdist, targetAlt, m_endvel, erad);
+ if (coll == 0) { // no collision
+ if (m_child) {
+ m_child.reset();
+@@ -1027,7 +1112,7 @@
+ double ang = m_prop->AIFaceDirection(m_dBody->GetPosition());
+ m_prop->AIMatchVel(ang < 0.05 ? 1000.0 * m_dBody->GetPosition().Normalized() : vector3d(0.0));
+ } else { // same thing for 2/3/4
+- if (!m_child) m_child.reset(new AICmdFlyAround(m_dBody, Frame::GetFrame(m_frameId)->GetBody(), erad * 1.05, 0.0));
++ if (!m_child) m_child.reset(new AICmdFlyAround(m_dBody, body, erad * 1.05, 0.0));
+ static_cast<AICmdFlyAround *>(m_child.get())->SetTargPos(targpos);
+ ProcessChild();
+ }
+@@ -1068,7 +1153,7 @@
+ const vector3d perpdir = (perpspeed > 1e-30) ? perpvel / perpspeed : vector3d(0, 0, 1);
+
+ double sidefactor = perpspeed / (tt * 0.5);
+- if (curspeed > (tt + timestep) * maxdecel || maxdecel < sidefactor) {
++ if (curspeed - m_endvel > (tt + timestep) * maxdecel || maxdecel < sidefactor) {
+ m_prop->AIFaceDirection(relvel);
+ m_prop->AIMatchVel(targvel);
+ m_state = -5;
+@@ -1118,7 +1203,11 @@
+ // then flip the ship so we can use our main thrusters to decelerate
+ if (m_state && !is_zero_exact(sdiff) && sdiff < maxdecel * timestep * 60) head = -head;
+ if (!m_state && decel) sidefactor = -sidefactor;
+- head = head * maxdecel + perpdir * sidefactor;
++
++ // check that head does not become zero length
++ if (maxdecel > 0.001 || abs(sidefactor) > 0.001) {
++ head = head * maxdecel + perpdir * sidefactor;
++ }
+
+ // face appropriate direction
+ if (m_state >= 3) {
+@@ -1410,7 +1499,6 @@
+ assert(!std::isnan(alt));
+ assert(!std::isnan(vel));
+ m_obstructor = obstructor;
+- m_alt = alt;
+ m_vel = vel;
+ m_targmode = mode;
+
+@@ -1423,6 +1511,8 @@
+ Frame *nonRot = Frame::GetFrame(obsFrame->GetNonRotFrame());
+ alt = std::min(alt, 0.95 * nonRot->GetRadius());
+
++ m_alt = alt;
++
+ // generate suitable velocity if none provided
+ double minacc = (mode == 2) ? 0 : m_prop->GetAccelMin();
+ double mass = obstructor->IsType(ObjectType::TERRAINBODY) ? obstructor->GetMass() : 0;
+@@ -1484,8 +1574,8 @@
+ assert(prop != 0);
+
+ if (targalt > m_alt) return m_vel;
+- double t = sqrt(2.0 * targdist / prop->GetAccelFwd());
+- double vmaxprox = prop->GetAccelMin() * t; // limit by target proximity
++ //either use reverse accel or implement dir flipping in FlyAround
++ double vmaxprox = sqrt(2 * prop->GetAccelRev() * targdist);
+ double vmaxstep = std::max(m_alt * 0.05, m_alt - targalt);
+ vmaxstep /= Pi::game->GetTimeStep(); // limit by distance covered per timestep
+ return std::min(m_vel, std::min(vmaxprox, vmaxstep));
+@@ -1514,22 +1604,16 @@
+ }
+
+ double timestep = Pi::game->GetTimeStep();
+- vector3d targpos = (!m_targmode) ? m_targpos :
+- m_dBody->GetVelocity().NormalizedSafe() * m_dBody->GetPosition().LengthSqr();
++ vector3d targpos = (!m_targmode) ? m_targpos : m_dBody->GetVelocity().NormalizedSafe() * m_dBody->GetPosition().LengthSqr();
+ vector3d obspos = m_obstructor->GetPositionRelTo(m_dBody);
+ double obsdist = obspos.Length();
+ vector3d obsdir = obspos / obsdist;
+ vector3d relpos = targpos - m_dBody->GetPosition();
++ double targetDist = relpos.Length();
++ vector3d shipToTargDir = relpos / targetDist;
+
+- // frame body suicide check, response
+- if (CheckSuicide(m_dBody, -obsdir)) {
+- m_prop->AIFaceDirection(m_dBody->GetPosition()); // face away from planet
+- m_prop->AIMatchVel(vector3d(0.0));
+- return false;
+- }
+-
+- // if too far away, fly to tangent
+- if (obsdist > 1.1 * m_alt) {
++ // if too far away or overshoot -> fly to tangent
++ if (obsdist > 1.1 * m_alt || m_dBody->GetVelocity().Dot(shipToTargDir) < 0) {
+ double v;
+ FrameId obsframeId = Frame::GetFrame(m_obstructor->GetFrame())->GetNonRotFrame();
+ vector3d tangent = GenerateTangent(m_dBody, obsframeId, targpos, m_alt);
+@@ -1546,7 +1630,7 @@
+ }
+
+ // limit m_vel by target proximity & distance covered per frame
+- double vel = (m_targmode) ? m_vel : MaxVel(relpos.Length(), targpos.Length());
++ double vel = (m_targmode) ? m_vel : MaxVel(targetDist, targpos.Length());
+
+ // all calculations in ship's frame
+ vector3d fwddir = (obsdir.Cross(relpos).Cross(obsdir)).NormalizedSafe();
+--- pioneer-20230203.orig/src/ShipAICmd.h 2023-07-19 23:54:00.245824921 +0900
++++ pioneer-20230203/src/ShipAICmd.h 2023-07-20 00:09:55.718866370 +0900
+@@ -137,10 +137,10 @@
+ bool m_tangent; // true if path is to a tangent of the target frame's body
+ int m_state;
+
+- bool m_lockhead;
+ int m_targetIndex; // used during deserialisation
+ vector3d m_reldir; // target direction relative to ship at last frame change
+ FrameId m_frameId; // last frame of ship
++ bool m_suicideRecovery;
+ };
+
+ class AICmdFlyAround : public AICommand {
+--- pioneer-20230203.orig/src/lua/LuaSpace.cpp 2023-07-19 23:54:00.250824905 +0900
++++ pioneer-20230203/src/lua/LuaSpace.cpp 2023-07-20 00:12:51.625324432 +0900
+@@ -187,7 +187,7 @@
+ }
+
+ // functions from ShipAiCmd.cpp
+-extern int CheckCollision(DynamicBody *dBody, const vector3d &pathdir, double pathdist, const vector3d &tpos, double endvel, double r);
++extern int CheckCollision(DynamicBody *dBody, const vector3d &pathdir, double pathdist, double targAlt, double endvel, double r);
+ extern double MaxEffectRad(const Body *body, Propulsion *prop);
+
+ /*
+@@ -256,19 +256,20 @@
+ // check for collision at spawn position
+ const vector3d shippos = ship->GetPosition();
+ const vector3d targpos = targetbody->GetPositionRelTo(ship->GetFrame());
++ double targAlt = targpos.Length();
+ const vector3d relpos = targpos - shippos;
+ const vector3d reldir = relpos.NormalizedSafe();
+ const double targdist = relpos.Length();
+ Body *body = Frame::GetFrame(ship->GetFrame())->GetBody();
+ const double erad = MaxEffectRad(body, ship->GetPropulsion());
+- const int coll = CheckCollision(ship, reldir, targdist, targpos, 0, erad);
++ const int coll = CheckCollision(ship, reldir, targdist, targAlt, 0, erad);
+ if (coll) {
+ // need to correct positon, to avoid collision
+- if (targpos.Length() > erad) {
++ if (targAlt > erad) {
+ // target is above the effective radius of obstructor - rotate the ship's position
+ // around the target position, so that the obstructor's "effective radius" does not cross the path
+ // direction obstructor -> target
+- const vector3d z = targpos.Normalized();
++ const vector3d z = targpos/targAlt;
+ // the axis around which the position of the ship will rotate
+ const vector3d y = z.Cross(shippos).NormalizedSafe();
+ // just the third axis of this basis
+@@ -276,7 +277,7 @@
+
+ // this is the basis in which the position of the ship will rotate
+ const matrix3x3d corrCS = matrix3x3d::FromVectors(x, y, z).Transpose();
+- const double len = targpos.Length();
++ const double len = targAlt;
+ // two possible positions of the ship, when flying around the obstructor to the right or left
+ // rotate (in the given basis) the direction from the target to the obstructor, so that it passes tangentially to the obstructor
+ const vector3d safe1 = corrCS.Transpose() * (matrix3x3d::RotateY(+asin(erad / len)) * corrCS * -targpos).Normalized() * targdist;
+@@ -288,7 +289,7 @@
+ ship->SetPosition(safe2 + targpos);
+ } else {
+ // target below the effective radius of obstructor. Position the ship direct above the target
+- ship->SetPosition(targpos + targpos.Normalized() * targdist);
++ ship->SetPosition(targpos + targpos/targAlt * targdist);
+ }
+ // update velocity direction
+ ship->SetVelocity((targpos - ship->GetPosition()).Normalized() * pp.getVel() + targetbody->GetVelocityRelTo(ship->GetFrame()));
+--- pioneer-20230203.orig/src/ship/Propulsion.cpp 2023-07-19 23:54:00.252824899 +0900
++++ pioneer-20230203/src/ship/Propulsion.cpp 2023-07-20 00:14:14.857068275 +0900
+@@ -185,9 +185,9 @@
+ double Propulsion::GetThrustMin() const
+ {
+ // These are the weakest thrusters in a ship
+- double val = static_cast<double>(m_linThrust[THRUSTER_UP]);
+- val = std::min(val, static_cast<double>(m_linThrust[THRUSTER_RIGHT]));
+- val = std::min(val, static_cast<double>(m_linThrust[THRUSTER_LEFT]));
++ double val = GetThrust(THRUSTER_UP);
++ val = std::min(val, GetThrust(THRUSTER_RIGHT));
++ val = std::min(val, GetThrust(THRUSTER_LEFT));
+ return val;
+ }
+