• New Horizons on Maelstrom
    Maelstrom New Horizons


    Visit our website www.piratehorizons.com to quickly find download links for the newest versions of our New Horizons mods Beyond New Horizons and Maelstrom New Horizons!

Fix in Progress Captive captains are just clones

It's nice to see this being worked on as on the few occasions when I managed to capture Silehard he always jumped ship.
 
That doesn't make much sense to me. Avoiding them to be hired makes sense, I suppose. But why shouldn't you be able to ransom them? Especially when one method does work, but the other doesn't?
Maybe change it to allow ransoming and see if anything breaks...
Also: Does 'ClearCharacter' ever get called on prisoners that jumped ship?
As far as I can tell from a Windows search, it's called:
  • In "Dialog_func.c", function 'FindFreeCabinCaptain()', which I believe is called when you're about to get a new officer or prisoner;
  • In "MAXIMUS_Functions.c", function 'GenerateTreasureQuest()', when the rival pirates are generated;
  • In "characters_login.c", function 'LogoffCharactersFromLocation', when you leave a location and any "Enc_Officer" characters that aren't still allies or enemy boarding captains are wiped;
  • In "AIFantom.c", function 'Fantom_AddFantomCharacter', which I believe sets up captains of ships in random encounters at sea;
  • In "Sea.c", function 'SetCoastTraffic', which presumably does the same for coastal ships.
So it might be called on clone-Silehard after he's jumped ship if one of those situations requires a slot to be re-used. Or it might not, depending on whether it's his slot that happens to be re-used.
 
It's nice to see this being worked on as on the few occasions when I managed to capture Silehard he always jumped ship.
He still might do that. The intent here is to arrange that, if he hasn't jumped overboard by the time you get to Port Royale, you can collect the reward for bringing him in alive. It's up to you to get to Port Royale as soon as possible to reduce his chances of escaping... (Which, incidentally, the storyline does not encourage you to do. One of these days I'll get round to rewriting the ending so that it behaves sensibly if you go to Port Royale first. At present it wants you to go all the way to Barbados to release Clement Aurentius, then go to Port Royale. That might have made sense with an earlier map layout, but doesn't make sense now that Jamaica is much closer to Cozumel while Barbados is pretty much the opposite end of the map.)
 
As far as I can tell from a Windows search, it's called:
I'm mainly concerned about regular non-Silehard captains.
If I recall, there are conditions to be met before a character is "cleared".
I wonder if those conditions apply when a character escapes, instead of "dies".
 
Presumably they're cleared by 'FindFreeCabinCaptain()', at the very least. That searches through all "Enc_CabinCaptain_" characters - of which the prisoner will certainly be one, as the clone is generated to be one of them. The first one 'FindFreeCabinCaptain()' finds which isn't an ally or the captain of a ship which you're currently boarding, it calls 'ClearCharacter' and returns that character's index as the one to be used for the new character. A character who has jumped ship is removed from your passengers, so he isn't an ally any more, so he's fair game for 'FindFreeCabinCaptain()'.
 
A character who has jumped ship is removed from your passengers, so he isn't an ally any more, so he's fair game for 'FindFreeCabinCaptain()'.
Are we sure that indeed works? If so, then there's no worries there. :cheeky
 
Given the rate at which I go through enemy ships in any serious game (a full play through a storyline, as opposed to a quick FreePlay to test something specific), if characters weren't being cleared to make way for new ones, I'd probably have noticed serious problems by now. :aar
 
You let lots your captives escape then?
Well, that'd be proof enough! :cheers
 
They don't tend to ask permission before jumping overboard. ;)

But I do know that 'FindFreeCabinCaptain' wipes any character it thinks isn't an ally because when I was developing "Ardent", an officer whom I'd detached (because he was supposed to be taking your ship back to Santiago during the "Convoy Strike" section) got wiped, which meant he didn't have the ship any more. When I ran into the same problem later, you suggested 'LAi_StoreFantom' - that sets an attribute which 'bAllies' recognises, so 'FindFreeCabinCaptain' doesn't clear the character. Without that, if a character has been removed as a passenger, he's no longer an ally. And the code to make a prisoner escape includes a 'RemovePassenger' command.
 
Thanks for confirming! Sounds like that's one less thing to worry about. :cheers
 
Having got to the end of "Tales of a Sea Hawk", I finally got to test this properly. The check on character "Robert Christopher Silehard" having the "prisoned" attribute works, the dialog proceeds to the bit about you getting the reward, and then Silehard remains your prisoner. This is because the dialog calls 'RemovePassenger(Pchar,CharacterFromID("Robert Christopher Silehard"));' - which isn't going to work because character "Robert Christopher Silehard" is not one of your passengers.

'RemovePassenger(ref _refCharacter,ref _refPassenger)" is defined in "CharacterUtilite.c" and starts by trying to find "_refPassenger":
Code:
   RemoveOfficersIndex(_refCharacter,sti(_refPassenger.index));
   int PsgQuantity = GetPassengersQuantity(_refCharacter);
   int psgNum = GetPassengerNumber(_refCharacter,sti(_refPassenger.index));
   if(psgNum==-1) return PsgQuantity;
I've tried modifying it so that, if the first search leaves "psgNum" at -1, it then searches for clones:
Code:
   if(psgNum==-1)   // GR: if actual _refPassenger not found, see if a prisoner clone with the same name exists
   {
       int cn;
       ref cr;
       for(i=0; i<PsgQuantity; i++)
       {
           cn = GetPassenger(_refCharacter,i);
           if(cn==-1) break;
           cr = GetCharacter(cn);
           if(HasSubStr(GetAttribute(cr,"id"), "Enc_CabinCaptain") && GetMySimpleName(cr) == GetMySimpleName(_refPassenger) && GetAttribute(cr,"model") == GetAttribute(_refPassenger,"model"))
           {
               psgNum = i;
               if (CheckAttribute(cr, "passenger")) DeleteAttribute(cr, "passenger");
               break;
           }
       }
   }

   if(psgNum==-1) return PsgQuantity;
It scans through all your passengers, looking for one who has the same name and model type as "_refPassenger" and who is an "Enc_CabinCaptain". If it finds one, it assumes that's the clone of "_refPassenger", sets "psgNum" to point to him so that the rest of the function can do its work, and then deletes him as a passenger because the function normally only deletes "_refPassenger".

It certainly allows Silehard's clone to be removed and allows for other clones to be removed if necessary. Further testing will hopefully show if there are any weird side-effects, or preferably show that there are none...

There is one potential loophole. If you rename another of your passengers to "Robert Christopher Silehard" and give him the same model, the code might pick him instead. Probably not, because you can only change outfit on your officers and they'll have the "Enc_Officer" prefix instead of "Enc_CabinCaptain". But as far as I'm concerned, if someone goes to all that trouble to stitch up one of his other passengers in order to save the Silehard clone, fine - that's who will be removed by the governor's troops. :D
 
Last edited:
There is one potential loophole. If you rename another of your passengers to "Robert Christopher Silehard" and give him the same model, the code might pick him instead. Probably not, because you can only change outfit on your officers and they'll have the "Enc_Officer" prefix instead of "Enc_CabinCaptain". But as far as I'm concerned, if someone goes to all that trouble to stitch up one of his other passengers in order to save the Silehard clone, fine - that's who will be removed by the governor's troops. :D
What I was wondering... Should perhaps "questch" characters get their character names and outfits locked?
Otherwise you could rename Silehard and keep him even afterwards.
 
Next time I play, I'll need to check if you can change the name or outfit of a passenger who is not an officer. If so, I'll need to move that clone-seeking code into 'GetPassenger(ref _refCharacter,int idx)', which returns the character index number of passenger number idx in _refCharacter's list of passengers. Then the dialog can use it to check if you have Silehard, or a clone. If you've renamed Silehard then you won't claim to have him and won't get the big bounty.

I'm fairly certain you can't change outfit for a non-officer passenger, but I'm less sure about changing names. If you can change name but not outfit then this will work. You might be able to rename some random prisoner to be Robert Christopher Silehard but that won't fool the system if the prisoner looks like a random captain. And changing one of your officers won't work if their ID starts with "Enc_Officer" and the system is looking for "Enc_CabinCaptain".
 
Next time I play, I'll need to check if you can change the name or outfit of a passenger who is not an officer.
As far as I know, you can cycle through ALL passengers, companions and prisoners in the F2>Character menu.
That is also the interface that allows you to change anyone's name by clicking on it.

My thought would be to find the interface code that allows renaming and add a check on the "questch" attribute to disable that functionality.
Same with the "Change Outfit" button.
 
Where would I find that code? Blocking quest characters from being renamed or having outfits changed might not be a good idea; I know at least one player likes giving his officers new outfits even if they're quest characters. Blocking non-officer passengers from being renamed or having outfits changed might be better.

But Silehard is safe. You can't change the name or outfit of a prisoner. I tried. Clicking on Silehard's name does nothing, and the "Change Outfit" button is greyed out. The same goes for a couple of non-quest prisoners.

Sabine Matton and Marc Blacque are another matter - they can both be renamed and have new outfits even while they're still passengers because you haven't delivered them to their respective destinations. So it seems that passengers who are not prisoners are treated the same as officers - you can even assign them roles from the "Passengers" interface.
 
Where would I find that code?
The file is PROGRAM\INTERFACE\character.c .
This is probably relevant:
Code:
    SetSelectable("RENAMEBOX",!LAi_IsBoardingProcess() && !LAi_group_IsActivePlayerAlarm());
    SetNodeUsing("B_CHARNAME",!LAi_IsBoardingProcess() && !LAi_group_IsActivePlayerAlarm());
    SetSelectable("B_CHARNAME",!LAi_IsBoardingProcess() && !LAi_group_IsActivePlayerAlarm());
And also this:
Code:
SetNodeUsing("B_CHARNAME",!CheckAttribute(xi_refCharacter,"prisoned") && !IsTrader(xi_refCharacter));

And for the character model:
Code:
SetSelectable("CHANGE_OUTFIT", !LAi_IsBoardingProcess() && !LAi_group_IsActivePlayerAlarm() && !CheckAttribute(MainChar, "DisableModelSelect") && !CheckAttribute(xi_refCharacter,"prisoned") && !IsTrader(xi_refCharacter) && !CheckAttribute(xi_refCharacter, "model.old")); // grey out button
Looks like @LarryHookins (LDH) was once involved in modifying this too.

Blocking quest characters from being renamed or having outfits changed might not be a good idea; I know at least one player likes giving his officers new outfits even if they're quest characters.
Fair enough; scratch that idea then. :doff

But Silehard is safe. You can't change the name or outfit of a prisoner. I tried. Clicking on Silehard's name does nothing, and the "Change Outfit" button is greyed out. The same goes for a couple of non-quest prisoners.
Good to hear!
Indeed I now see some "prisoned" attributes used in relevant code here and there, so that's probably what makes the difference.

Sabine Matton and Marc Blacque are another matter - they can both be renamed and have new outfits even while they're still passengers because you haven't delivered them to their respective destinations. So it seems that passengers who are not prisoners are treated the same as officers - you can even assign them roles from the "Passengers" interface.
Sometimes characters are set to "follow" the player around, without being added to the "Passengers" list.
But any character that is on that list (excluding prisoners) is automatically handled as an "Officer" (whether that makes sense in the story or not...).
 
Oh well, worth a try, and I learned something in the process...

Trying to limit changing outfit to only your officers and companions proved too limiting. "IsOfficer" only shows true for the officers in your shore party, so doing that means you can't change the outfit of any of your officers unless you first assign them to your party. (I should have known this, as in the past I've used "IsOfficer" to check for a character being in your party! :facepalm)

Preventing quest characters from being renamed also prevented me from renaming myself! A 'DumpAttributes(PChar)' showed why. My character has the "questchar" attribute. This is important to note in case anyone wants to do anything specific to quest characters - it will be done to the player character as well.

The important thing is that prisoners can't be renamed or have their outfits changed, which is already the case, so I think I'll just leave things the way they are. Anyone who really wants to prevent non-player quest characters from being renamed or having new outfits is welcome to do the work themselves, and they'd better test it thoroughly!
 
Back
Top