• 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!

Fixed [4.1] Double leveling on hired captured Captains

Tingyun

Corsair
Storm Modder
In a test game boarded an enemy ship, captain surrendered and joined me.

He was level 14 originally when I could view his stats. This is in line with expectations based on his ship class.

However, as soon as I hired him, he was suddenly level 26.

Note my test character is high level. I don't know whether that did it, or they are simply leveling twice somehow as if hired in a tavern again, but some kind of bug is triggering a round of leveling after hire as an officer.
 
In a test game boarded an enemy ship, captain surrendered and joined me.

He was level 14 originally when I could view his stats. This is in line with expectations based on his ship class.

However, as soon as I hired him, he was suddenly level 26.

Note my test character is high level. I don't know whether that did it, or they are simply leveling twice somehow as if hired in a tavern again, but some kind of bug is triggering a round of leveling after hire as an officer.
Could be there is something in the hire function or the dialog file to generate the officer again?
 
Yup found the problem.
It's in these lines:

Code:
TIH_OfficerHiredProcess(NPChar, false, false, true, true, !UsableOfficer(NPChar));// bLowSalary, bAutoAssign, bPurgeCrud, bSetType, bCreateOfficer

Because the UsabelOfficer is false it will set that last value to true which will make it regenerate the officer.
But I believe this whole code has become obsolete, but I need to look into it a little bit more.

At least it's a confirmed bug and will be changed.
 
@Tingyun you might be able to fix this. Check the officer dialog files and check for the place where they are hired. Copy this code and replace the part of the captain dialog with this part of the officer dialog and let me know if it works. You might need to add something to remove the ship from the captain.

Don't worry about it if you don't have time.
 
@Levis

Happy to help you in any way of course, as you work to make everything wonderful. :)

I found the code, but I'm unclear as to what part to replace with what. It seems like there is one general hire officer function, and a special one for prisoners. I think captains hired directly run through the general one. All are from dialog_func.c

I'm stepping out the door for the afternoon, but if you can direct me specifically what to try (the functions rather confuse me), I can do whatever edits and tests you like tomorrow morning. :) Good luck with your exam!


Here is the part you mentioned before from cabinfight:

Code:
TIH_OfficerHiredProcess(NPChar, false, false, true, true, !UsableOfficer(NPChar));// bLowSalary, bAutoAssign, bPurgeCrud, bSetType, bCreateOfficer
                int tmpIdx = GetCharacterIndex(NPChar.id);

Here is the entire part of the dialogue function for hiring normal officers:

Code:
void TIH_OfficerHiredProcess(ref RefChar, bool bLowSalary, bool bAutoAssign, bool bPurgeCrud, bool bSetType, bool bCreateOfficer)
{
    ref PChar = GetMainCharacter();
    ref NewOfficer = &NullCharacter();
    int NewOfficerIdx;
    string NewOfficerID;

    PChar.quest.HiringOfficerIDX = GetCharacterIndex(RefChar.id);

    int freeidx = FindFreeRandomOfficer();// get an open officer slot to use (50 maximum)
    if(freeidx != -1)
    {
        //trace("-----------------------------------------------------------------");
        //trace("dialog_func.c:");

        // cleaner method
        NewOfficerIdx     = sti(Characters[freeidx].index);
        NewOfficer         = GetCharacter(NewOfficerIdx);
        NewOfficerID     = NewOfficer.id;// store this because we have to put it back after the copyattr

        PChar.quest.NewOfficerIdx = NewOfficerIdx;// store for second function

        //    trace("Hiring Officer IDX is " + PChar.quest.HiringOfficerIDX + " and he has id  " + Characters[sti(PChar.quest.HiringOfficerIDX)].id);
        //    trace("Free Random Officer IDX is " + NewOfficerIdx + " and he has id  " + NewOfficerID);

//MAXIMUS: hired captain will stay in location with player -->
//        ChangeCharacterAddressGroup(&RefChar, "None", "", "");// get rid of the reference character
        if(RefChar.sex == "woman") RefChar.greeting = "Gr_Officer_f common";
        else RefChar.greeting = "Gr_Officer_m common";
        RefChar.Dialog.Filename = "Enc_Officer_dialog.c";
        RefChar.Dialog.CurrentNode = "Hired";
        CopyAttributes(&NewOfficer, &RefChar);// copy all attr from the reference character (the hiring officer) to the new officer char
        if(bSetType) { LAi_SetOfficerType(RefChar); }
        LAi_group_MoveCharacter(RefChar, LAI_GROUP_PLAYER);
        RefChar.location = "none";
//MAXIMUS: hired captain will stay in location with player <--

        NewOfficer.index = NewOfficerIdx;// reset these after the copyattr
        NewOfficer.id    = NewOfficerID; // reset these after the copyattr
        NewOfficer.friend = true;
        NewOfficer.ship.type = "Not Used";
        NewOfficer.ship.idx = 1000;
        NewOfficer.nodisarm = 1;
        NewOfficer.location = "none";


        //trace("New Officer IDX is " + NewOfficer.index + " and he has id  " + NewOfficer.id);
        //trace("dialog_func.c - > officer dump before cleaning:"); DumpAttributes( Characters[sti(PChar.quest.NewOfficerIdx)] );
/*
        // these do fixing
        if(NewOfficer.sex == "woman")     NewOfficer.greeting = "Gr_Officer_f common";
        else                             NewOfficer.greeting = "Gr_Officer_m common";
        NewOfficer.Dialog.Filename = "Enc_Officer_dialog.c";
        NewOfficer.Dialog.CurrentNode = "Hired";
        NewOfficer.AbordageMode = 1;// boal 05.09.03 officer set to go to abordage
*/
        // TIH --> suggested additions from maximus, adjusted for this code Aug29'06
// TIH --> no, see if this attr doesnt exist for NEW officer, its because REF officer didn't have it in the first place, so trying to assign it the null field again is a bit pointless and dangerous - Oct30'06
//        if (!CheckAttribute(NewOfficer,"location.locator") && CheckAttribute(RefChar, "location.locator")) NewOfficer.location.locator = RefChar.location.locator;// ? was a bug with not existent location.locator // KK now that should be OK
//        if (!CheckAttribute(NewOfficer,"location.locator"))    NewOfficer.location.locator = "";// TIH - actually set it to nothing for fresh reset - Oct30'06
// TIH <--
        if (!CheckAttribute(NewOfficer,"model.entity"))     NewOfficer.model.entity = "NPCharacter";// ? sometimes was an error - "can't create class"
        if (!CheckAttribute(NewOfficer,"noFollowType"))     NewOfficer.noFollowType = "nofollow";// ? new identifier for template LAi_follow.c
        if (!CheckAttribute(NewOfficer,"money"))             NewOfficer.money = 1+rand(100);// ? was a bug with not existent money
        // TIH <--

        if (bPurgeCrud) {// purge a whole bunch of broken crud that causes bugs for this new officer (captured captains only)
            if ( CheckAttribute(NewOfficer,"ship") )             DeleteAttribute(NewOfficer,"ship");
            if ( CheckAttribute(NewOfficer,"shiptype") )         DeleteAttribute(NewOfficer,"shiptype");
            if ( CheckAttribute(NewOfficer,"shipmoney") )         DeleteAttribute(NewOfficer,"shipmoney");
            if ( CheckAttribute(NewOfficer,"curshipnum") )         DeleteAttribute(NewOfficer,"curshipnum");
            if ( CheckAttribute(NewOfficer,"seaai") )             DeleteAttribute(NewOfficer,"seaai");
            if ( CheckAttribute(NewOfficer,"features") )         DeleteAttribute(NewOfficer,"features");
            if ( CheckAttribute(NewOfficer,"seatime") )         DeleteAttribute(NewOfficer,"seatime");
            if ( CheckAttribute(NewOfficer,"relation") )         DeleteAttribute(NewOfficer,"relation");
            if ( CheckAttribute(NewOfficer,"fantomtype") )         DeleteAttribute(NewOfficer,"fantomtype");
            if ( CheckAttribute(NewOfficer,"canfiretime") )     DeleteAttribute(NewOfficer,"canfiretime");
            if ( CheckAttribute(NewOfficer,"rankscale") )         DeleteAttribute(NewOfficer,"rankscale");
            if ( CheckAttribute(NewOfficer,"tmpperks") )         DeleteAttribute(NewOfficer,"tmpperks");
            if ( CheckAttribute(NewOfficer,"tmpskill") )         DeleteAttribute(NewOfficer,"tmpskill");
            if ( CheckAttribute(NewOfficer,"points") )             DeleteAttribute(NewOfficer,"points");
            if ( CheckAttribute(NewOfficer,"randofficers") )     DeleteAttribute(NewOfficer,"randofficers");
            if ( CheckAttribute(NewOfficer,"skillnatmult") )     DeleteAttribute(NewOfficer,"skillnatmult");
            if ( CheckAttribute(NewOfficer,"nodisarm") )         DeleteAttribute(NewOfficer,"nodisarm");
            if ( CheckAttribute(NewOfficer,"points") )             DeleteAttribute(NewOfficer,"points");
            if ( CheckAttribute(NewOfficer,"isFantom") )         DeleteAttribute(NewOfficer,"isFantom");
            if ( CheckAttribute(NewOfficer,"status") )             DeleteAttribute(RefChar,"status");
            if ( CheckAttribute(NewOfficer,"position") )         DeleteAttribute(RefChar,"position");
            if ( CheckAttribute(NewOfficer,"fight") )             DeleteAttribute(RefChar,"fight");
            if ( CheckAttribute(NewOfficer,"cabinfight") )         DeleteAttribute(RefChar,"cabinfight");
        }
        LAi_SetOfficerType(NewOfficer);
        if(bCreateOfficer)
        {
            /*switch(Rand(7))
            {
                case 0: NewOfficer.quest.officertype = OFFIC_TYPE_BOATSWAIN; break;
                case 1: NewOfficer.quest.officertype = OFFIC_TYPE_CANNONEER; break;
                case 2: NewOfficer.quest.officertype = OFFIC_TYPE_QMASTER; break;
                case 3: NewOfficer.quest.officertype = OFFIC_TYPE_NAVIGATOR; break;
                case 4: NewOfficer.quest.officertype = OFFIC_TYPE_FIRSTMATE; break;
                case 5: NewOfficer.quest.officertype = OFFIC_TYPE_CARPENTER; break;
                case 6: NewOfficer.quest.officertype = OFFIC_TYPE_DOCTOR; break;
                case 7: NewOfficer.quest.officertype = OFFIC_TYPE_ABORDAGE; break;
            }*/
            if(DEBUG_EXPERIENCE>0) TraceAndLog("TIH_OfficerHiredProcess: Set officer type for " + GetMySimpleName(NewOfficer));
            NewOfficer.quest.officertype = GetRandomOfficerType(); //Levis let's use a global function so we can easily add types later.
            LAi_Create_Officer(rand(8), &NewOfficer);/*
            NewOfficer.offgen = true;
            NewOfficer.officer = true;
            LAi_Create_Officer(rand(1),NewOfficer);*/
        }
        else
        {
//            if (!CheckAttribute(PChar, "IsOnDeck") && !bAbordageStarted) NewOfficer.location = "None"; // KK
            //NewOfficer.location.group = "officers";// not needed
            NewOfficer.chr_ai.type = "officer";
            NewOfficer.offgen = true;
            NewOfficer.officer = true;
            NewOfficer.friend = true;
            NewOfficer.land = "0";
            if(CheckAttribute(NewOfficer,"wealth"))              NewOfficer.wealth = makeint(NewOfficer.wealth);// clean up the ungainly float!
            if(CheckAttribute(NewOfficer,"quest.officertype"))    NewOfficer.current = NewOfficer.quest.officertype;
        }

        if (bAutoAssign)
        {
            // added by MAXIMUS [for placing new officer in empty slot (default for some officers)] -->
            int ofNum = OFFICER_MAX-1 ;// default force into last slot
            if ( CheckAttribute(NewOfficer,"quest.officertype") ) {
                switch( NewOfficer.quest.officertype )
                {
                    case OFFIC_TYPE_FIRSTMATE: ofNum = 1; break;
                    case OFFIC_TYPE_BOATSWAIN: ofNum = 2; break;
                    case OFFIC_TYPE_NAVIGATOR: ofNum = 3; break;
                    //case OFFIC_TYPE_QMASTER:   ofNum = 1; break;
                    //case OFFIC_TYPE_CANNONEER: ofNum = 2; break;
                }
            }
            SetOfficersIndex(PChar, ofNum, NewOfficerIdx);
            // added by MAXIMUS [for placing new officer in empty slot (default for some officers)] <--
        } else {
            //SetOfficersIndex(PChar, -1, NewOfficerIdx);// put them into an available slot if one exists (do not force it)
            AddPassenger(PChar,NewOfficer,false);// just add them as a passenger for now
//            AssignCaptiveAsCompanion(RefChar, NewOfficer);//MAXIMUS: not needed

            if(CheckAttribute(NewOfficer, "quest.officertype") && NewOfficer.quest.officertype == OFFIC_TYPE_ABORDAGE)
            {
               DeleteAttribute(NewOfficer, "quest.officertype");
            }
        }

        // if they joined freely (no cost), they have a low salary ?
        if(!CheckAttribute(NewOfficer,"quest.officerprice"))
        {
            NewOfficer.quest.officerprice = makeint((sti(PChar.rank) * 2 + 10)*100) - 330 + rand(40)*15 + rand(10);
        }
        if(bLowSalary) NewOfficer.quest.officerprice = makeint( sti(NewOfficer.quest.officerprice) / 2 );

        if (!CheckCharacterEquipByGroup(NewOfficer, BLADE_ITEM_TYPE)) // KK
            LAi_NPC_Equip(NewOfficer, sti(PChar.rank), true, 0);// give the officer some weapons and equip them (NO GUN)

        // move any money they may have into their wealth, so MC can't steal their money
        if (CheckAttribute(NewOfficer,"money") && sti(NewOfficer.money) > 0)
        {
            if (!CheckAttribute(NewOfficer,"wealth")) { NewOfficer.wealth = 0; } // set if not set
            NewOfficer.wealth = makeint(sti(NewOfficer.wealth) + sti(NewOfficer.money)); // merge together into wealth
            NewOfficer.money = 0; // clear out their money now that its moved
        }

        // keep the NewOfficerIdx attribute, as its needed in the second function

        //trace("dialog_func.c -> officer dump at end:"); DumpAttributes( Characters[sti(PChar.quest.NewOfficerIdx)] );
        //trace("-----------------------------------------------------------------");
    }
    else
    {
        Log_SetStringToLog(QUEST_MESSAGE12);
    }
    DeleteAttribute(Pchar, "quest.HiringOfficerIDX");// deleted here, because we certainly no longer need it now
//    if (!CheckAttribute(PChar, "IsOnDeck") && bAbordageStarted) refEnCharacter = NewOfficer;
}

Here is the Pirsoner hired as officer part:

Code:
void TIH_PrisonerHiredAsOfficerProcess(ref RefChar, bool bLowSalary, bool bSetType, bool bCreateOfficer)
{
    if(FindFreeRandomOfficer()==-1) { RefChar.Dialog.CurrentNode = "officer"; DialogMain(RefChar); return; }
    ref PChar = GetMainCharacter();
    int freeidx = FindFreeRandomOfficer();

    RemovePassenger(PChar, RefChar);

    if (CheckAttribute(RefChar, "prisoned")) DeleteAttribute(RefChar, "prisoned");
    if (CheckAttribute(RefChar, "status")) DeleteAttribute(RefChar, "status");
    if (CheckAttribute(RefChar, "ransom")) DeleteAttribute(RefChar, "ransom");
    if (CheckAttribute(RefChar, "released")) DeleteAttribute(RefChar, "released");
    if (CheckAttribute(RefChar, "position")) DeleteAttribute(RefChar, "position");
    if (CheckAttribute(RefChar, "fight")) DeleteAttribute(RefChar, "fight");
    if (CheckAttribute(RefChar, "cabinfight")) DeleteAttribute(RefChar, "cabinfight");

    // these do fixing
    if(RefChar.sex=="woman") RefChar.greeting = "Gr_Officer_f common";
    else RefChar.greeting = "Gr_Officer_m common";
    RefChar.Dialog.Filename = "Enc_Officer_dialog.c";
    RefChar.Dialog.CurrentNode = "Hired";

    Pchar.quest.HiringOfficerIDX = RefChar.index;
    Pchar.quest.FreeRandomOfficerIdx = Characters[freeidx].index;
    Pchar.quest.FreeRandomOfficerID = Characters[makeint(Pchar.quest.FreeRandomOfficerIdx)].id;

    trace("Free Random Officer IDX is " + Pchar.quest.FreeRandomOfficerIdx + " and he has id  " + Pchar.quest.FreeRandomOfficerID);

    CopyAttributes(&Characters[makeint(Pchar.quest.FreeRandomOfficerIdx)], &Characters[makeint(Pchar.quest.HiringOfficerIDX)]);

    Characters[makeint(Pchar.quest.FreeRandomOfficerIdx)].index = Pchar.quest.FreeRandomOfficerIdx;
    Characters[makeint(Pchar.quest.FreeRandomOfficerIdx)].id = Pchar.quest.FreeRandomOfficerID;
    Characters[makeint(Pchar.quest.FreeRandomOfficerIdx)].location = "None";
    Characters[makeint(Pchar.quest.FreeRandomOfficerIdx)].Dialog.Filename = "Enc_Officer_dialog.c";
    Characters[makeint(Pchar.quest.FreeRandomOfficerIdx)].Dialog.CurrentNode = "hired";
    Characters[makeint(Pchar.quest.FreeRandomOfficerIdx)].AbordageMode = makeint(1);
    ref NewOfficer = &Characters[makeint(Pchar.quest.FreeRandomOfficerIdx)];
    DeleteAttribute(Pchar, "quest.FreeRandomOfficerIdx");
    DeleteAttribute(Pchar, "quest.FreeRandomOfficerID");
    DeleteAttribute(Pchar, "quest.HiringOfficerIDX");

//    RefChar.AbordageMode = 1;// boal 05.09.03 officer set to go to abordage

//    if (!CheckAttribute(RefChar, "model.entity"))     RefChar.model.entity = "NPCharacter";// ? sometimes was an error - "can't create class"
//    if (!CheckAttribute(RefChar, "noFollowType"))     RefChar.noFollowType = "nofollow";// ? new identifier for template LAi_follow.c
    if (!CheckAttribute(NewOfficer, "money")) NewOfficer.money = makeint(0);// ? was a bug with not existent money

    NewOfficer.chr_ai.type = "officer";
    if(bSetType) LAi_SetOfficerType(NewOfficer);
    NewOfficer.offgen = true;
    NewOfficer.officer = true;
    NewOfficer.friend = true;
    NewOfficer.land = "0";
    NewOfficer.nation = GetServedNation();
    NewOfficer.nation.name = RefChar.nation;
    NewOfficer.location = "none";
    if ( CheckAttribute(NewOfficer,"wealth") )              NewOfficer.wealth = makeint(NewOfficer.wealth);// clean up the ungainly float!
    if ( CheckAttribute(NewOfficer,"quest.officertype") )    NewOfficer.current = NewOfficer.quest.officertype;

    // if they joined freely (no cost), they have a low salary ?
    if ( CheckAttribute(NewOfficer,"quest.officerprice") ) {
        if (bLowSalary) NewOfficer.quest.officerprice = makeint( sti(NewOfficer.quest.officerprice) / 2 );
    }

    if (!CheckCharacterEquipByGroup(NewOfficer, BLADE_ITEM_TYPE)) // KK
        LAi_NPC_Equip(NewOfficer, sti(NewOfficer.rank), true, 0);// give the officer some weapons and equip them (NO GUN)

    // move any money they may have into their wealth, so MC can't steal their money
    if (CheckAttribute(NewOfficer,"money") && sti(NewOfficer.money) > 0)
    {
        if (!CheckAttribute(NewOfficer,"wealth")) { NewOfficer.wealth = 0; } // set if not set
        NewOfficer.wealth = makeint(sti(NewOfficer.wealth) + sti(NewOfficer.money)); // merge together into wealth
        NewOfficer.money = 0; // clear out their money now that its moved
    }
    NewOfficer.old.chr_ai.group = "player";
    NewOfficer.chr_ai.group = "player";

    LAi_group_MoveCharacter(NewOfficer, LAI_GROUP_PLAYER);
    LAi_group_MoveCharacter(RefChar, LAI_GROUP_PLAYER);
    RefChar.location = "none";
    RefChar.friend = true;
    if(bSetType) LAi_SetOfficerType(RefChar);

    AddPassenger(PChar,NewOfficer,false);// just add them as a passenger for now
}
 
Yes, but the TIH function is the one causing the problem. and I think we don't need it anymore.
Code:
case "exit_hire":
            // TODO: Aconcagua: here is still some bug, had
            // an officer being equiped with a gun he could not handle...

            if (ENABLE_AMMOMOD) {    // LDH change
                //JRH fix oct 06: rewritten "checkcharacteritem" wasn't enough any longer -->
                if(CheckAttribute(NPChar,"equip."+GUN_ITEM_TYPE))
                {
                    if(CheckAttribute(weapon, "secondary") && weapon.secondary == "true")
                    {
                        //do nothing
                    }else{
                        TakenItems(NPChar, "gunpowder", -100);TakenItems(NPChar, "pistolbullets", -100);
                        TakenItems(NPChar, "pistolgrapes", -100);TakenItems(NPChar, "musketbullets", -100);
                        TakenItems(NPChar, "gunpowder", 6);
                    }
                }

                if(CheckAttribute(weapon, "shottype") && weapon.shottype == "pb") TakenItems(NPChar, "pistolbullets", MAX_SHOTS);
                if(CheckAttribute(weapon, "shottype") && weapon.shottype == "pb2") TakenItems(NPChar, "pistolbullets", MAX_SHOTS);
                if(CheckAttribute(weapon, "shottype") && weapon.shottype == "pg") TakenItems(NPChar, "pistolgrapes", MAX_SHOTS);
                if(CheckAttribute(weapon, "shottype") && weapon.shottype == "pg2") TakenItems(NPChar, "pistolgrapes", MAX_SHOTS);
                if(CheckAttribute(weapon, "shottype") && weapon.shottype == "mb") TakenItems(NPChar, "musketbullets", (MAX_SHOTS / 2));
                //<-- JRH
            }
            Pchar.quest.HiringOfficerIDX = GetCharacterIndex(NPChar.id);
            AddDialogExitQuest("LandEnc_OfficerHired");
            Diag.CurrentNode = Diag.TempNode;
            NPChar.quest.meeting = NPC_Meeting;
            DialogExit();
        break;

this is from the officer code.
What I would like you to test is to take these lines:
Code:
Pchar.quest.HiringOfficerIDX = GetCharacterIndex(NPChar.id);
            AddDialogExitQuest("LandEnc_OfficerHired");

And replace the TIH function with this in the cabin fight dialog and test it.
 
@Levis You mean change

TIH_OfficerHiredProcess(NPChar, false, false, true, true, !UsableOfficer(NPChar));// bLowSalary, bAutoAssign, bPurgeCrud, bSetType, bCreateOfficer
int tmpIdx = GetCharacterIndex(NPChar.id);
to

Pchar.quest.HiringOfficerIDX = GetCharacterIndex(NPChar.id);
AddDialogExitQuest("LandEnc_OfficerHired");

Is that right?

Sorry if I'm not understanding correctly, still pretty ignorant on these things.

Will be back in the morning! :)
 
keep the tmpIdx line aswell. for now I don't know what it does
 
@ANSEL

Could you install the attached file into your PROGRAM/DIALOGS folder, overwriting the current one, and then test?

Make certain to make a BACKUP first, you may need to restore the original.

What you need to do is capture a captain who surrenders, then follow the dialogue options to ask for his money, then for him to be your officer.

Ask to see his skills before hiring, and write down what level he is.

Then hire him, and check to make sure his level stays the same. Also, make sure he doesn't have a ship or any weird stats.

Then report the results?

It looks like I won't be able to test things in-game myself today or tomorrow, though I'll still try to help out with the files in the meantime. So stopped in one last time to get the testing file ready :)
 

Attachments

  • Cabinfight_dialog.c
    43.9 KB · Views: 214
@Tingyun any results on this?
I can't test it my self anymore (out of time). if you could test it and let me know if it works I can include it tomorrow
 
@Levis Sadly it doesn't work: The captains still regenerate (my test character is level 80 something, and even though the two captains I hired were originally level 10 or so, they were all above level 60 after hiring) They seemed to automatically take on new roles though, as normal officers rather than continuing their old captain role as before, otherwise didn't notice a change.

I might have messed something up in how I coded it, but the file I posted above doesn't work.
 
okay, will have a look at this later.
 
@Levis Not sure if this bug is related to the unable to hire surrendered captain bug or not, but wanted to bring it back to attention, in case it can also be fixed, since at least then players would have something useful to do with surrendered captains.
 
@Levis In test reported in other thread, I noticed something--if a captain is captured and then hired from the prisoner hold, he does retain his proper level.

So maybe all that needs to be done to fix this bug on double leveling of surrendered captains is probably copying over the code that gets used when a prisoner is hired from the ship's hold, and use it to replace the code for hiring a captain directly after surrender.

Sorry I haven't had more time to help lately, thanks again, see you next week or so.
 
Back
Top