smcameron / space-nerds-in-space

Multi-player spaceship bridge simulator game. Captain your starship through adventures with your friends. See https://smcameron.github.io/space-nerds-in-space
GNU General Public License v2.0
733 stars 75 forks source link

Make missions scripts compatible with several ships #246

Open MCMic opened 4 years ago

MCMic commented 4 years ago

If playing with several ships, only one of them will be taken into account by mission scripts as they use player_ids[1].

I look into the source of spacepox mission, and there are a lot of calls to functions which take the player id as parameter and the script passes player_ids[1], if they could be adapted to support receiving an array, we could pass the whole player_ids array and avoid having loops everywhere, is that possible?

Only thing that looked difficult was when forcing zarkon attack, not sure how to chose who they attack, ideally they should attack the closer player ship.

MCMic commented 4 years ago

@smcameron Something like this? multiplayer-spacepox.patch.txt

smcameron commented 4 years ago

Thanks. Seems reasonable. I think you might have left a debug print in there at the end, "No zarkon attack this time".

How much testing have you done with this?

I guess only one player ship will get the vaccine, and then get attacked by the zarkons, and the others will just tag along, which I suppose is fine.

smcameron commented 4 years ago

if they could be adapted to support receiving an array, we could pass the whole player_ids array and avoid having loops everywhere, is that possible?

player_ids[] is a global, so you don't even need to pass it to access it. You can pass an array (actually a "table" in lua parlance) as a parameter if you want to though. You can see I do it in share/snis/luascripts/MISSIONS/lib/interactive_fiction.lua in the intfic.format_table() function. Lua doesn't have much in the way of type checking parameters though, but you can dynamically check the type of function parameters and do different things depending on what type they are. I am no Lua expert though.

MCMic commented 4 years ago

The debug prints were already there, I only moved them along. I did not test it.

Only problem left is zarkons will attack player 1 always, I think we should store the id of the player who gets the vaccine.

As having ships attacking the player(s) until death seems a pretty common thing in missions, maybe we should have a lua function that does that, instead of having to use a callback each time to tell them to keep pressing?

smcameron commented 4 years ago

The debug prints were already there, I only moved them along. I did not test it.

Ah, you're right about that.

Only problem left is zarkons will attack player 1 always, I think we should store the id of the player who gets the vaccine.

? Looks to me like it does attack the player with the vaccine, or am I missing something?

-- check to see if the space vaccine has been acquired and if so send in the zarkons
function check_for_zarkon_attack()
        print("checking for zarkon attack");
        for i, player_id in ipairs(player_ids) do
                text_to_speech(player_id, "Incoming transmission from Icarus station");
                if player_has_vaccine(player_id) then
                        -- player has vaccine, send in the zarkons
                        send_in_the_zarkons(player_id);
                        -- do not register the callback to check for zarkon attack again
                        return;
                end
        end

        print("No zarkon attack this time");
        -- player doesn't have vaccine, check for zarkon attack again in a little while
        register_timer_callback("check_for_zarkon_attack", zarkon_check_interval, 0);
end

It checks which player (if any) has the vaccine, then sends the zarkons to attack the one with the vaccine, right?

As having ships attacking the player(s) until death seems a pretty common thing in missions, maybe we should have a lua function that does that, instead of having to use a callback each time to tell them to keep pressing?

That is pretty awkward. I'll try to think of a way to make that easier. Maybe AI_MODE_FIGHT_TO_DEATH or something.

smcameron commented 4 years ago

One problem I see:

-- find the player and move him where we want him to start.
player_ids = get_player_ship_ids();
if (player_ids[1]) then
        for i, player_id in ipairs(player_ids) do
                move_object(player_id, startx, starty, startz);
                reset_player_ship(player_id);
                set_faction(player_id, 0); -- set player faction to neutral so only zarkons fight
        end
end

Probably shouldn't move all the players to be exactly on top of each other at the beginning of the game.

MCMic commented 4 years ago

Yes they first attack the one with the vaccine but then the periodic callback to keep pressing is on the first one. A fight to death mode would fix that.

Nice catch about moving them to the same point, I need to fix that.

smcameron commented 4 years ago

Hmm, trying it with 3 ships just now, it seems to get stuck saying "incoming transmission from icarus station" repeatedly. Well, I was running on a single machine, so all three bridges use the same TTS server, so all their messages get queued up, but even so, I would expect it to repeat the message 3 times, not... many many more than 3 times, so something's broken. I get the first comms message, but not subsequent ones, and the interval between the "incoming transmission from Icarus station" messages seems shorter than the 2 minutes that it ought to be.

MCMic commented 4 years ago

Tried with 2 ships, same thing. I get 2 TTS messages, then a brief pause, then the 2 same TTS messages again. Investigating.

smcameron commented 4 years ago

Ah, it's because your patch adds this:

 -- check to see if the space vaccine has been acquired and if so send in the zarkons
 function check_for_zarkon_attack()
        print("checking for zarkon attack");
-       if not player_has_vaccine(player_ids[1]) then
-               print("No zarkon attack this time");
-               -- player doesn't have vaccine, check for zarkon attack again in a little while
-               register_timer_callback("check_for_zarkon_attack", zarkon_check_interval, 0);
-               return;
+       for i, player_id in ipairs(player_ids) do
+               text_to_speech(player_id, "Incoming transmission from Icarus station");
+               if player_has_vaccine(player_id) then
+                       -- player has vaccine, send in the zarkons
+                       send_in_the_zarkons(player_id);
+                       -- do not register the callback to check for zarkon attack again
+                       return;
+               end
        end

That text_to_speech in check_for_zarkon_attack() should not be there.

MCMic commented 4 years ago

Thanks. Seems like a failed copy/paste. It also shows that this check may be done too often :-P

smcameron commented 4 years ago

It's every 15 seconds. So when you get the vaccine, the zarkons will attack, on average, 7.5 seconds later (maybe sooner, but no later than 15 seconds after). From an efficiency standpoint, every 15 seconds is no big deal. From a gameplay standpoint, maybe 7.5 seconds is too quick.

MCMic commented 4 years ago

Fixed version of the patch. Removed the TTS in the check, and made sure all ships are not at the same point.

multiplayer-spacepox.patch.txt

MCMic commented 4 years ago

Maybe it would make sense to add a delay once the check is done, to avoid immediate attack, so instead of calling send_in_the_zarkons, calling it with register_timer_callback to wait like 20 more seconds at least.

smcameron commented 4 years ago

Yeah, maybe. But I don't want to give the players enough time to warp anywhere before disabling the warp drive.

smcameron commented 4 years ago

Alright, I have a patch to make the zarkons attack the correct player. If I may add your Signed-off-by, then I'll go ahead and commit it.

Author: Stephen M. Cameron <stephenmcameron@gmail.com>
Date:   Thu Feb 20 09:47:46 2020 -0500

    spacepox: Make sure the zarkons attack the correct player ship

    Signed-off-by: Stephen M. Cameron <stephenmcameron@gmail.com>

diff --git a/share/snis/luascripts/MISSIONS/SPACEPOX.LUA b/share/snis/luascripts/MISSIONS/SPACEPOX.LUA
index 66e7941..cd7f9ee 100644
--- a/share/snis/luascripts/MISSIONS/SPACEPOX.LUA
+++ b/share/snis/luascripts/MISSIONS/SPACEPOX.LUA
@@ -20,6 +20,7 @@ clear_all();
 startx = 101000;
 starty = 6000;
 startz = 0;
+player_with_vaccine = nil

 -- how many survivors on the station
 survivors = 8;
@@ -335,8 +336,10 @@ function distress_call8()
 end

 function maintain_zarkon_focus()
-       for i = 1, number_of_zarkons do
-               ai_push_attack(zarkon_ship[i], player_ids[1]);
+       if (player_with_vaccine ~= nil) then
+               for i = 1, number_of_zarkons do
+                       ai_push_attack(zarkon_ship[i], player_with_vaccine);
+               end
        end
        register_timer_callback("maintain_zarkon_focus", 20, 0);
 end
@@ -367,6 +370,7 @@ function check_for_zarkon_attack()
                if player_has_vaccine(player_id) then
                        -- player has vaccine, send in the zarkons
                        send_in_the_zarkons(player_id);
+                       player_with_vaccine = player_id;
                        -- do not register the callback to check for zarkon attack again
                        return;
                end
smcameron commented 4 years ago

To be clear, I am asking you for permission to add your signed-off-by to your patch, or you can send me another patch with your signed-off-by already in it.

-- steve

MCMic commented 4 years ago

Sure, you have my permission.

smcameron commented 4 years ago

Thanks!

MCMic commented 4 years ago

@smcameron If a player ship is destroyed, does it respawn with the same id?

My initial thought was that if a player ship is destroyed the other ones are allowed to complete the mission and consider it half a success, but I did not thought of the fact that destroyed ships will actualy respawn.

Well, maybe we can just leave the players decide if they want to continue or restart the mission in these cases, not sure it’s useful for the game to do something specific in this case.

smcameron commented 4 years ago
smcameron commented 4 years ago

If a player ship is destroyed, does it respawn with the same id?

Heh. Actually, player ships cannot be killed, they are just "mostly dead". So with multi-ship games, I expect that if one player ship witnesses the "death" of another player ship, what they will see is an explosion around the player ship, and then the player ship just sitting there, and then when it "re-spawns" they will see the player ship disappear as it is moved to it's new location. It might even zip off at ridiculous speed towards its new location due to client side interpolation of position, I'm not sure.

Player ships are only "killed" (removed as entities within the server) when all players disconnect from a bridge.

All this behavior could be considered a bug. I haven't thought too hard about how to fix this because multi-bridge sessions are vanishingly rare in my experience so far, but it seems likely that is no longer true if you're going to the trouble of making missions accomodate multiple bridges.

One easy (but klunky) way to "fix" this problem would be to temporarily move the "dead" player ship to an obscure corner of the solar system, out of sight of any other player ships for the duration of their "death".

smcameron commented 4 years ago

Oh, and to answer your question, because player ships don't really "die", they retain the same ID throughout the experience. Bridges will get new IDs if all players on a bridge disconnect and reconnect whether by quitting, or by going through warp gates.

smcameron commented 4 years ago

Or at least this is how I remember it works. It has been a long time since I looked at this part of the code, so I might have some details wrong, but there is definitely some strange behavior around player ship death. For example, I am pretty sure you can still be hit by enemy fire while you are "dead", and it might be the case that your crew might asphyxiate after you have already been killed.

MCMic commented 4 years ago

multi-bridge sessions are vanishingly rare in my experience so far, but it seems likely that is no longer true if you're going to the trouble of making missions accomodate multiple bridges.

Heh, nothing sure yet, I will keep you updated :smiley:

Indeed it would be better if player ship explosion left a derelict and cargo boxes. But it won’t be that big of a problem for first sessions, most likely we’ll reset everything after player death.

smcameron commented 4 years ago

I wrote:

with multi-ship games, I expect that if one player ship witnesses the "death" of another player ship, what they will see is an explosion around the player ship, and then the player ship just sitting there, and then when it "re-spawns" they will see the player ship disappear as it is moved to it's new location. It might even zip off at ridiculous speed towards its new location due to client side interpolation of position, I'm not sure.

I just tested this. On the client that witnesses the death of another player ship, the other player ship does disappear during "death", and o->alive on the client goes to zero for the "dead" ship for the duration of the "death", and goes to 1 when it is respawned. The only anomaly I saw was that it might be that the respawned ship appeared at the location of its death for a split second at the instant it is respawned before being moved to its new location. Apparently I already fixed most of the described behavior or it wasn't ever as broken as I imagined. Serves me right for trying to go by memory for something I wrote so long ago.