Page 2 of 3

Re: (WIP) Ship Repurchase

Posted: Wed Jul 18, 2018 12:54 am
by phkb
gsagostinho wrote:
how likely is it for this scenario to happen?
Well, it depends on the situations you find yourself in. If you're always fighting small numbers of ships, the chances are probably small. But if your being hounded by a dozen, and two or three of them all fire missiles at you at once, it could actually happen regularly.

As for the fix, my analysis so far indicates the solution might have to be in the core code. In the routine that deals energy damage (ShipEntity.m, line 13109ff) it's checking for a ship status of "STATUS_DEAD" to determine if damage should be delivered to the ship. If it's dead, then it exits at that point. I think this routine should also check for STATUS_ESCAPE_SEQUENCE (possibly for the player only), and if so, exit the routine. I'm doing some tests on this logic at the moment and will let you know what I find out.

[Edit] OK, the code change needs to be in PlayerEntity.m, in the "takeEnergyDamage" routine (line 6154ff). Adding this line, just after the check for [self status] == STATUS_DEAD, seems to do the trick:

Code: Select all

if ([self status] == STATUS_ESCAPE_SEQUENCE) return;
I'll submit a pull request shortly for comment.

Re: (WIP) Ship Repurchase

Posted: Wed Jul 18, 2018 12:36 pm
by another_commander
It's a trivial change, but it's effect on game mechanics is less trivial. From my point of view, it would make sense that if an escape pod has just launched and, at that precise moment, the ship explodes, then the pod gets hit by the shockwave and has a good chance of being destroyed.

On the other hand, it also makes sense to make the escape pod a bit more "guaranteed" as means of escape. People already rarely (if ever) use it because loading the last save may be just as good a solution. Imagine how much use it will get if it can't even do the job it's advertised for at all times. So yes, from this point of view, safeguarding it in situations like phkb described might actually be more desired.

Let's see what others think and we can decide on the fate of the pull request based on feedback here.

Re: (WIP) Ship Repurchase

Posted: Wed Jul 18, 2018 1:00 pm
by Disembodied
Realism would lean towards having the Escape Pod sometimes fail, or be destroyed. But it's impossible to tell the difference, from the player's point of view, between a "realistic" failure and a bug.

If it's felt desirable, an option - in the event of a probably "realistically" fatal Escape Pod ejection - might be to script in a mission screen which tells the player that their Escape Pod was badly damaged in the explosion, and to wind the game clock on by a significantly larger margin.

Re: (WIP) Ship Repurchase

Posted: Wed Jul 18, 2018 1:14 pm
by gsagostinho
another_commander wrote: Wed Jul 18, 2018 12:36 pm
People already rarely (if ever) use it because loading the last save may be just as good a solution. Imagine how much use it will get if it can't even do the job it's advertised for at all times.
I think this is a very good point.

Re: (WIP) Ship Repurchase

Posted: Wed Jul 18, 2018 1:20 pm
by Cody
another_commander wrote: Wed Jul 18, 2018 12:36 pm
... it would make sense that if an escape pod has just launched and, at that precise moment, the ship explodes, then the pod gets hit by the shockwave and has a good chance of being destroyed.
This!

Re: (WIP) Ship Repurchase

Posted: Wed Jul 18, 2018 2:11 pm
by montana05
Cody wrote: Wed Jul 18, 2018 1:20 pm
another_commander wrote: Wed Jul 18, 2018 12:36 pm
... it would make sense that if an escape pod has just launched and, at that precise moment, the ship explodes, then the pod gets hit by the shockwave and has a good chance of being destroyed.
This!
Totally agreed to that, a ship explosion does produce a shockwave, shrapnel's and burning fuel so not every lifepod will make it. Based on my personal created explosions actually quite a few got caught up in the explosion.

Re: (WIP) Ship Repurchase

Posted: Wed Jul 18, 2018 2:25 pm
by phkb
Part of the issue comes down to how I'm attempting to make the OXP work. I am trying to do an escape pod sequence as the very last thing, when the "shipTakingDamage" event is triggered, and the amount of energy damage being passed to the ship is greater than the current energy reserves - essentially, the ship is about the die in the next frame or so. It's only in this scenario that the issue really appears.
Disembodied wrote:
If it's felt desirable, an option - in the event of a probably "realistically" fatal Escape Pod ejection - might be to script in a mission screen which tells the player that their Escape Pod was badly damaged in the explosion, and to wind the game clock on by a significantly larger margin.
The difficulty here is that, by the time the "failure" of the escape pod is apparent, the "Game Over" screen is being displayed ... which is probably too late to tell the player about it!

Coding a potentially "random" failure, and making it believable to the player, would be simpler if I could rely on the abandonShip function working 100% of the time for the player. As it stands, the randomness of the failure has no explanation, other than, "Well, I guess it didn't work that time".
another_commander wrote:
it's effect on game mechanics is less trivial
Well, I don't think the impact on core game mechanics would be particularly large. At the moment, in the core game, the only way to launch the escape pod is manually. You have to do this with some energy left in your energy banks, otherwise you'd already be dead. Because of this, the scenario where multiple energy damage events arriving at the same instant while transitioning to the escape pod is extremely remote. You would have to double-press the escape key with almost no energy left, just as two damage events (say two missiles striking) arrive for the player ship. Possible, but highly unlikely. I'd go so far as to say, I think by now someone would have reported an issue where they launched the escape pod in normal space and got the Game Over screen instead.

I can certainly understand the desire for realism. Having escape pods destroyed by shockwaves makes some sense, but on the other hand, surely escape pod manufacturers would have taken that fact into consideration when designing their pods, knowing that their first taste of space would likely be right beside the exploding hulk of a large ship. Throwing some hand-wavium into the mix, you could say escape pods are launched inside a short-term energy field, designed to protect them from any immediate danger outside the ship, but this shield can only operate for a second or two due to the limited energy reserves of the pod, which is why escape pods are so fragile afterwards.

Re: (WIP) Ship Repurchase

Posted: Wed Jul 18, 2018 4:18 pm
by Disembodied
Stuff realism. There's enough realism in reality, without it elbowing its way into games!
phkb wrote:
As it stands, the randomness of the failure has no explanation, other than, "Well, I guess it didn't work that time".
This is problematic, I think, because something which works most times but doesn't work sometimes, with no cause visible to the user, is annoying. The player is less likely to think "I guess it didn't work that time (for valid in-game reasons)", and is more likely to think "I guess it didn't work that time (because the game/OXP is buggy)".

Maybe we need to take a more radical approach to the whole Escape Pod idea. Rather than relying on the player - or an automatic process - to eject the pod in time, it could be an automatic one-off "Get Out Of Death Free" card. If you have an Escape Pod, and your ship is blown up, you don't die - you just spin off in your pod.

This, of course, would have a much larger effect on the game, and might need further refinements: e.g. regular payments to keep the Escape Pod active, and a chance - depending on how dangerous a system might be, and/or the player's current legal status - of having to pay a ransom (or a fine) to your "rescuers" before being allowed to continue.

Re: (WIP) Ship Repurchase

Posted: Fri Jul 20, 2018 10:21 am
by another_commander
I'm all up for realism but after some consideration on the responses so far, I tend to think that game behavior being interpreted as a possible bug should never be allowed to happen and might have to be the primary factor for a decision to change.

Maybe we can merge phkb's PR and then see if we could make the default behavior OXPable somehow.

Re: (WIP) Ship Repurchase

Posted: Sat Jul 21, 2018 2:48 pm
by Redspear
Disembodied wrote:
Stuff realism. There's enough realism in reality, without it elbowing its way into games!
To the point where it is not counterintuitive I'd generally agree.

Speaking of 'realism', the player's pod is already the only one in the game that's effectively cloaked (never scooped or shot).

If we're worrying about probabilities then rather than have the pod blow up very rarely with no in game explamation, what about being scooped very rarely with an in-game explanation, "Your escape pod was scooped by pirates/police/rescuers and you have lost 50%/25%/0% of your credits in bribes/fines/daring escape/gratitude. Finally, and much later than hoped, you gain access to your replacement ship".

Re: (WIP) Ship Repurchase

Posted: Wed Jul 01, 2020 4:09 pm
by Milo
Ship Repurchase ejected me when I was hit by a missile (a TCAT Kamikaze Orbiter) that did damage (after shields) greater than my remaining ship energy, however I still had full IronHide armour (which would have restored ship energy). So Ship Repurchase is conflicting with the intended function of IronHide (ship shouldn't be lost before IronHide is gone).

My initial theory was that Ship Repurchase was being called first, before IronHide had a chance to refund the ship's energy. However, I see IronHide's script listed in worldScripts before Ship Repurchase, which appears to match the calling order of the event handlers. So that theory was wrong.

Instead, what appears to be happening is that IronHide is called first and restores the energy, but Ship Repurchase still receives the full "amount", and incorrectly expects that the amount will be subtracted from the ship's energy after the call. In actuality, the amount value is deducted from the ship's energy by the game before the shipTakingDamage event handlers are called [see e.g., (void) takeEnergyDamage at lines 13119 and 13260 in ShipEntity.m, and equivalent in other cases where shipTakingDamage is called (energy damage, collisions, heat, etc.)].

Instead of checking whether (player.ship.energy - amount) <= 0, Ship Repurchase should simply check if (player.ship.energy <= 0).

Note that this would still leave any similar OXP that restores ship energy in shipTakingDamage in trouble if Ship Repurchase is earlier in the worldScripts list, and I don't know what determines the order of scripts in the worldScripts list. I tried moving OXZ files from ManagedAddOns to AddOns and the order did not change. I also tried renaming OXZ files or changing the identifier in the manifest inside the OXZ, but still no change in the order.

For compatibility with other OXPs that restore energy in shipTakingDamage and whose position in the worldScripts list is after Ship Repurchase, Ship Repurchase could move its code from shipTakingDamage to a shipDied event handler. shipDied is called just before the ship actually gets destroyed, and knowing the damage amount isn't actually necessary for what Ship Repurchase does. See lines 9020-9022 and 9006 of ShipEntity.m.

For example, change lines 156-161 of the Ship Repurchase script to this:

Code: Select all

this.shipDied = function (whom, type) {
    if (this._simulator) return;
    // are we just about to die?
    // award the escape pod and eject
    log(this.name, "ship destroyed by damage type '" + type + "' : current ship energy " + (player.ship ? player.ship.energy : "no player ship!"));
    if (player.ship && player.ship.energy <= 0) {
If it moves to a shipDied event, which should be infrequent(!), the _debug condition can be removed from both log statements (I removed one from the snippet above, and would also remove it from line 279).

Not directly related, but the "monkey patch" approach used in Ship Repurchase itself w.r.t ShipConfiguration (copy SC's event handler to a different function, delete SC's event handler, call the copied function from Ship Repurchase's event handler so SC runs after) could be used by other OXPs w.r.t. ShipPurchase, but if multiple OXPs tried to use that approach they would need to implement extra coordination between all of them to avoid multiple OXPs calling ShipRepurchase's event. I think that's too complicated to be a practical solution.

We should find a better way to resolve the Ship Repurchase vs. Ship Configuration sequencing issue (it's the shipLaunchedFromStation event in that case). Maybe Ship Repurchase can use 'shipWillLaunchFromStation' instead?

Re: (WIP) Ship Repurchase

Posted: Wed Jul 01, 2020 10:24 pm
by phkb
The trouble is, in all my testing, the shipDied event is too late. I tried the code you suggested, launched from the station with an escape pod, and then deliberately ran into the nav buoy. Game over. All the debug events show up in the log, but nothing happened. The abandonShip call fails to eject the player to safety.

As for a better solution, I suppose I could add a new event to the core, something like shipHasTakenDamage, which would be called after all the shipTakingDamage events have finished (at around line 13422 of ShipEntity.m), but before the shipDied event, but it feels kind of like I'm adding a solution for an extreme edge case.

Another solution might be to switch to using a framecallback, although that might be slightly unreliable, for the situation where the ship goes below zero energy and dies before the framecallback fires. I'd have to test this one out pretty thoroughly.

Another possible solution might be to scan all worldscripts for a shipTakingDamage function, and monkey patch the lot of them! Okay, that's probably not a great solution.

For the time being, I'll add the suggested tweak into the routine. If you think of another solution please let me know.

Re: (WIP) Ship Repurchase

Posted: Wed Jul 01, 2020 10:26 pm
by Milo
My post above addresses technical issues. Here are some ideas for improving this OXP from a gameplay perspective.

If the cost of ship replacement significantly exceeds profits from a play session, there is a strong incentive to reload the saved game. As discussed up-thread, ironman is a special case. I'm not an ironman player, and I would like to have stronger incentives to use the features of this OXP...

A reason to prefer not to reload is loss of progress (reputation, for example, or missions completed, or waypoints and derelicts left in the system that you've marked to haul in, which would all disappear upon reloading), but the cost of such lost progress is weighed against the cost of ship replacement. Worse, when you choose replacement instead of reloading, you often have mission failures, because the transition also adjusts the clock several days forward and your current missions expire, so the cost of ship replacement includes additional side-effect penalties.

What can be done to preserve progress, provide other incentives, and reduce the cost factor of choosing to "replace" instead of "reload"?

To preserve progress ... As-is, I question the purpose of the escape pod plus, as it has no function other than reducing the repurchase cost. It is a fixed cost investment with long-term benefit, but if you suffer an accident soon after buying it then the sunk cost weighs in favor of reloading. It can't carry your cargo (it isn't big enough), but it could include a pair of special police beacons (one left near the ship, as a waypoint, and one staying with the escape pod). These "beacons" would give a chance for some of your cargo to be recovered by police and returned to you (in the station storage facility, with another of your OXPs!), if your "accident" happened in-system and not interstellar, and would reduce the time taken to find and rescue you.

Following that last idea, the escape pod plus could reduce the amount of clock timeskip, ideally to just a few hours, because the police can rescue you faster if they have a beacon to follow... So you might have the prospect of actually going out again to where your ship met its end and having another go at the problem (if it is still there)... or perhaps recover some of your cargo (if it was thrown free of your ship and not scooped up already). It could even include a feature to (attempt to) instruct the ship's computer to eject all cargo when the escape pod plus is launched, for which having a waypoint beacon (in the form of a scoopable machinery cargo-pod) would be helpful for a player wanting to go back and retrieve it.

To reduce the cost factor ... actually, I think the cost of the repurchase itself is fine when you have active insurance. But when you don't have active insurance, the cost of replacement is too high. So always having active insurance needs to be affordable and easy. I therefore suggest increasing the amount of time each renewal lasts; 30 days can go by very quickly when jumping between systems and especially when buying equipment. Making renewals last longer would both increase convenience and reduce the cost over time (if you keep the cost the same).

If your insurance lapses while you are in flight, it should cover you until you next dock (e.g., add && this._doRepurchase === false to line 334 in the script, so it won't check for lapsed insurance before presenting you with repurchase options).

It would be more realistic if the renewal cost went up if you have a history of recent mishaps, not the repurchase price (see e.g., line 569 in the script). However, this cost increase is an incentive to reload instead of repurchasing, so I suggest a bit of hand-waving there and ignore history of mishaps when calculating premium or repurchase costs.

It leaves the question of how the insurance company can make a profit on its clients, and perhaps the answer is that the company runs its repurchase insurance business at a loss but turns a profit in other ways... for example, the insurance company knows who are its least accident-prone clients, and could sell such statistics to anyone seeking an extremely reliable courier for highly sensitive or valuable parcels or similar situations where the goods or information being transported would be unreasonably costly to lose or replace. In effect, they would be selling another kind of insurance: pay them for data leading to a higher probability of a successful outcome. It's also likely that the insurance company has a supply chain that can obtain ships and equipment for significantly below market rate, so they have to pay a lot less to replace a ship for you than what you paid for the original ship and equipment as an independent...

To provide other incentives ... re-consider the repurchase options themselves. Unless someone lacks the credits to pay, why would they choose anything other than option 1 (replace with identical ship and equipment)? If they lack the credits to pay, they will presumably reload (unless ironman). So options 2 and 3 (cheaper, free) are for ironman players only?

One idea would be to tie-in with Bank of the Black Monks and give an option for the player to take on debt to offset the cost of option 1. This would probably be a feature aimed mostly at ironman players, but could find application for non-ironman players if combined with ideas below.

Another idea is to change how options 1 and 2 work:

"Realistically," getting a fully-equipped replacement should take a lot of extra time, because someone (who isn't you) has to acquire a new base model of your ship, fly it around to acquire all the equipment you had (including some perhaps not-quite-legal additions?) at presumably significantly below-market prices, get it resprayed to match your original ship, and then bring it to you wherever you happen to be at that time (or leave it at some pre-arranged delivery location).

So instead of "give me an identical replacement right away" the full replacement option 1 would become "order an identical replacement for eventual delivery" and you would still need to choose another ship (a "loaner" ship, like some car dealerships offer when you leave your car for service...) to use until your replacement ship is ready. But flying a "loaner" ship that lacks equipment while waiting for your replacement wouldn't be fun, so you get a choice of an iron-ass combat ship or a well-equipped bulk cargo hauler, for example, or even more specialized options... giving an opportunity for players to "test-drive" interesting ships without the commitment of a full purchase. A reason not to reload!

But, you have to trade in the loaner once your replacement is ready. Perhaps put a restriction on how far in LY you are allowed to travel in the "loaner" away from the "delivery location" system, rather than a time limit. If you leave the "permitted distance zone" with the "loaner" then the insurance company will send "recovery" specialists after you ... to force you to eject so they can take the "loaner" ship back, and then send you [involuntarily] via passenger service to where your replacement ship is, with a large clock timeskip. In addition to (or instead of) sending recovery specialists, the insurance company could arrange for GalCop stations to arrest you the next time you dock with the "loaner" in any system outside the approved zone, with the same result.

This "loaner" concept assumes the use of ship storage helper (which Ship Repurchase already uses) and some consideration for Hyperspace Hangar (to ensure that loaner status doesn't get lost if they are put in storage).

Some thought needs to go into what happens if your loaner ship also suffers an unfortunate demise before the replacement is ready. In this case, the replacement is still being prepared for you, so your options would be other replacement "loaner" ships. The insurance company might decide to give you a "protection" detail (some escort ships) to decrease the odds of further mishaps, for example. The idea here is not to penalize the player in ways that would encourage them to reload, but instead to create more interesting game-play dynamics if they decide to continue.

Some thought also needs to go into the treatment of fugitive players. I imagine the insurance company wouldn't want to deal with them, but you may have other ideas on this...

Earlier I mentioned "options 1 and 2" and the idea is that there could be a variation of option 2 where you are given a base model of your original ship and a mission to go to a specific designated system main station (of sufficient tech level to cover your highest TL equipment) not too far away, and upon docking there, you would be given the replacement equipment "for free" (note that you wouldn't be given credits to purchase directly, as the assumption is that the insurance company has access to much better prices than you do). This would be a compromise option, letting you avoid the "loaner" trade-in scenario but having to go somewhere else to finish the replacement. This option could be less expensive than option 1, giving the player a chance to weigh two "positive" options with different pros and cons (the base model would come with some standard equipment [injectors are essential], so the player doesn't feel compelled to make too many duplicate equipment purchases).

You could also offer GalcopBB missions along these lines to sufficiently high-reputation/high-score players:

(1) take a "base" model replacement ship to a certain system to have it equipped and re-sprayed for a random insurance client and then deliver it to another system

(2) "recover" a loaner ship whose pilot has refused to return it and is avoiding GalCop stations, with a bonus if you also recover the rogue pilot's escape pod

For the latter mission type, rather than a tie-in with Towbar OXP, I'd suggest something along the lines of attaching a recovery beacon to the loaner ship after it becomes derelict (which would be either by broadcast comms intimidating the pilot to eject or convincing them more forcefully) and then standing guard until a "towbar-"equipped tug shows up. (You'd have to be careful with these recovery missions. Destroying a loaner ship you were supposed to recover would fail the contract and you'd have to pay a hefty penalty [maybe taken up-front as a deposit when you accept the mission]. High risk, but high reward...)

Re: (WIP) Ship Repurchase

Posted: Wed Jul 01, 2020 10:33 pm
by phkb
That's a lot to chew through! Thanks for putting so much thought into this. After I digest it all I'll get a new release out.

Re: (WIP) Ship Repurchase

Posted: Wed Jul 01, 2020 10:44 pm
by Milo
phkb wrote: Wed Jul 01, 2020 10:24 pm
The trouble is, in all my testing, the shipDied event is too late. I tried the code you suggested, launched from the station with an escape pod, and then deliberately ran into the nav buoy. Game over. All the debug events show up in the log, but nothing happened. The abandonShip call fails to eject the player to safety.
The log shows:
19:03:03.813 [ShipEntity.abandonShip.notPossible] -[ShipEntity abandonShip]: Ship <PlayerEntity 0x3cc2b20>{"Cobra Mark IV" position: (13431.1, -82547.2, 356805) scanClass: CLASS_PLAYER status: STATUS_DEAD} cannot be abandoned at this time.
Here's why shipTakingDamage works and shipDied doesn't:

First, when shipTakingDamage is called, the status is not STATUS_DEAD, so abandonShip works. When shipDied is called, the status is STATUS_DEAD, so abandonShip doesn't work (because launchEscapeCapsule from PlayerEntity.m returns nil).

Second, launchEscapeCapsule in PlayerEntity.m sets energy = 25 (see line 6562). So calling abandonShip, which calls launchEscapeCapsule, prevents the ship from being destroyed (instead, the shipEnergyIsLow handler is called). PlayerEntity.m's version of launchEscapeCapsule also sets the status to STATUS_ESCAPE_SEQUENCE, which disables collisions (line 2441 of PlayerEntity.m), disables taking energy damage (line 6222 of PlayerEntity.m) and disables heat damage (line 2872 of PlayerEntity.m). So it would be difficult to kill yourself at that point before the escape sequence finishes and docks you.

Third, becomeExplosion or becomeLargeExplosion (which happens after the shipDied event) sets STATUS_DEAD, which interrupts STATUS_ESCAPE_SEQUENCE.

Fourth, getDestroyedBy in PlayerEntity.m calls playGameOver (the sound effects).

Fifth, getDestroyedBy in PlayerEntity.m calls showGameOver. (The fourth and fifth are cosmetic; STATUS_DEAD is what tells PlayerEntityControls.m to watch for spacebar down and trigger a restart, although after 30 seconds it happens automatically [the actual restart occurs in PlayerEntity.m's performDeadUpdates method].)

Sixth, as a consequence of the player ship avoiding destruction when using shipTakingDamage, a removeAllEquipment call at line 6916 in getDestroyedBy is avoided, so post-docking, player.ship.equipment remains intact. This is an idiosyncracy of the core game too (without Ship Repurchase) -- if you buy an escape pod and hit ESC-ESC to eject (whether or not your ship is destroyed just after), when you arrive at the dock you will have your fully equipped ship (although if it had equipment damage, that will be retained), other than the escape pod equipment itself (which is removed when you eject). Cargo does get removed, though (line 6560).

That removeAllEquipment poses a problem for your script, which relies on player.ship.equipment being the full list (unless it was interstellar).

Considering how escape pods are handled currently by the game, I think it's valid to allow OXPs to call abandonShip in the shipDied handler to have an escape pod launch happen "just in time" and from that point onwards basically follow the same logic as before, i.e., don't actually destroy the ship, and don't remove the equipment, so it will be restored (minus cargo) when you arrive at the station. If the behavior of escape pods in general is changed to be more realistic (not giving you all the equipment if your ship actually gets vaporized after you launch in the pod), that change then could be extended to the shipDied variation, and you would need to change your script to use the interstellar solution for the general case.

Here are some changes (in diff form) that allow the shipDied method to call abandonShip and disable the problematic calls noted above:

Code: Select all

diff --git a/src/Core/Entities/PlayerEntity.m b/src/Core/Entities/PlayerEntity.m
index f6727c44..7eda0a50 100644
--- a/src/Core/Entities/PlayerEntity.m
+++ b/src/Core/Entities/PlayerEntity.m
@@ -6890,22 +6890,40 @@ NSComparisonResult marketSorterByMassUnit(id a, id b, void *market);
 	
 	// Let scripts know the player died.
 	[self noteKilledBy:whom damageType:type]; // called before exploding, consistant with npc ships.
-	
-	[self becomeLargeExplosion:4.0]; // also sets STATUS_DEAD
-	[self moveForward:100.0];
-	
-	flightSpeed = 160.0f;
-	velocity = kZeroVector;
-	flightRoll = 0.0;
-	flightPitch = 0.0;
-	flightYaw = 0.0;
-	[[UNIVERSE messageGUI] clear];		// No messages for the dead.
+
+	if ([roleSet hasRole:@"abandoning_ship"])
+	{
+		[self abandonShip]; // if OXPs call abandonShip from shipDied worldscript event handlers, we queue the action by adding a role, wait for them all to finish, then call it once here
+	}
+	else // only explode and suppress messages if we didn't escape in time; becomeLargeExplosion would override the STATUS_ESCAPE_SEQUENCE set in launchEscapeCapsule (called by abandonShip)
+	{
+		[self becomeLargeExplosion:4.0]; // also sets STATUS_DEAD
+		[self moveForward:100.0];
+
+		flightSpeed = 160.0f;
+		velocity = kZeroVector;
+		flightRoll = 0.0;
+		flightPitch = 0.0;
+		flightYaw = 0.0;
+
+		[[UNIVERSE messageGUI] clear];		// No messages for the dead.
+	}
+
 	[self suppressTargetLost];			// No target lost messages when dead.
-	[self playGameOver];
-	[UNIVERSE setBlockJSPlayerShipProps:YES];	// Treat JS player as stale entity.
-	[self removeAllEquipment];			// No scooping / equipment damage when dead.
+
+	if (![roleSet hasRole:@"abandoning_ship"]) // only play game over sounds and remove equipment if we failed to launch an escape pod in time; setBlockJSPlayerShipProps:YES is redundant (launchEscapeCapsule sets it too)
+	{
+		[self playGameOver];
+		[UNIVERSE setBlockJSPlayerShipProps:YES];	// Treat JS player as stale entity.
+		[self removeAllEquipment];			// No scooping / equipment damage when dead.
+	}
+
 	[self loseTargetStatus];
-	[self showGameOver];
+
+	if (![roleSet hasRole:@"abandoning_ship"]) // only call showGameOver if the game actually is over
+	{
+		[self showGameOver];
+	}
 }
 
 
diff --git a/src/Core/Entities/ShipEntity.m b/src/Core/Entities/ShipEntity.m
index 5ba69113..2f589486 100644
--- a/src/Core/Entities/ShipEntity.m
+++ b/src/Core/Entities/ShipEntity.m
@@ -9019,6 +9019,7 @@ NSComparisonResult ComparePlanetsBySurfaceDistance(id i1, id i2, void* context)
 {
 	[self noteKilledBy:whom damageType:type];
 	[self abortDocking];
+	if ([roleSet hasRole:@"abandoning_ship"]) [self abandonShip]; // if OXPs call abandonShip from shipDied event handlers, wait for them all to finish, then call it again once here
 	[self becomeExplosion];
 }
 
@@ -13349,6 +13350,11 @@ Vector positionOffsetForShipInRotationToAlignment(ShipEntity* ship, Quaternion q
 		}
 
 	}
+	else if ([self status] == STATUS_DEAD)
+	{
+		// probably an OXP calling abandonShip from a shipDied event handler - add a role to signal getDestroyedBy method to call abandonShip again after all shipDied event handlers are finished
+		[self addRole:@"abandoning_ship"];
+	}
 	else
 	{
 		// this shouldn't happen any more!
The method I picked (using addRole and hasRole to signal that abandonShip needs to be called later) might not be the best, but it avoided needing to add new properties and there is some precedent with addRole and hasRole for other things (e.g., asteroids and boulders).

I also noticed (as a purely aesthetic issue) that there is something not right with how the escape pod ejection sequence looks visually when Ship Repurchase initiates the abandonShip call from shipDied (after ship energy hits zero) in a collision specifically. Instead of launching backwards and up and viewing your ship, the ship disappears and the camera continues forwards. Hitting esc-esc with or without Ship Repurchase gives the usual launch up and backwards and shows the ship flying away from you, and if Ship Repurchase auto-ejects due to energy damage, it also shows the launch up and backwards. I'm not sure why it doesn't happen for scrape/collision damage. I haven't tried heat damage either.

And, unfortunately, the "time until rescue" seems to be hard-coded (line 6470 in PlayerEntity.m), and clock.addSeconds doesn't accept negative values. I'd like to change that line to read the values from somewhere that an OXP could override.