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

Unconfirmed Bug Some officer creation functions use enemy table

Tingyun

Corsair
Storm Modder
@Pieter Boelen We've got another problem with the tavern dialogue officers, though this one should prove uncontroversial, as it is clearly an error.

Tavern dialogue hires, and officers created in certain quests (for example, the officer created when you take sides in the fight outside Nevis pirate settlement), are using the formula for enemy encounters, rather than the "friendly" hired officer formula.

So in CreateOfficer we have the code I am posting below, and these guys use the enemy encounter formula. This creates two problems:

1) This is going to create the wrong relationship with difficulty level (ie, lower difficulties will get lower ranked officers, higher difficulties will get higher ranked, the opposite of intended effect.

2) In the current version, it will create too generous a scaling with player level (.9 as intended for enemies, instead of the .75 as intended for friendly officers), in my version, it will put them into the deleveled formula intended for bandits.

Aside from the difficulties being reversed in effect, this isn't too much of a problem for the created officers, as they are rare enough to be on their own special formula.

But for the tavern dialogue officers, it is a problem, as we are talking about a significant source of officers leveling on the incorrect table.

And also explains why I found the officers overpowered on swashbuckler--it makes them higher level the higher the player difficulty level!

Any way to tell these guys to use the if(isfriend) table?

Code:
if(isfriend)
   {
     //friendly encounter
     pRank = makeint(sti(PChar.rank)*0.75 + rand(sti(PChar.rank)*(1.1-(GetDifficulty()*0.2))) + offset);
     //pRank = makeint(pRank * (1+((pLuck*2)+(pLead*3))/200)); //Take in account leadership and luck
     pRank = makeint(pRank * (1+(pLead*5)/200)); //TY Let's remove some magical influence of luck
     pRank = pRank + rand(bonus); //Take in account different officertypes;
     if (pRank < 5) pRank = 2 + rand(5);
   }
   else
   {
     //enemy encounter
     //pRank = makeint(sti(PChar.rank)*0.90 + rand(sti(PChar.rank)*(0.2+(GetDifficulty()*0.2))) + offset);
     pRank = makeint(15*0.90 + rand(15*(0.2+(GetDifficulty()*0.2))) + offset); // TY let's try leveling the world as if the player were permanently level 15 (increased to compensate for now weaker randchar template), for random bandits and such
     pRank = pRank + rand(bonus); //Take in account different officertypes;
     if (pRank < 3) pRank = 1 + rand(4);
   }
 
In addition to another mention in that CreateOfficer file, the other instances of "isfriend" that show up are in LA_utils

I searched them out, couldn't really understand what any of them are doing. This one will probably take someone better than me to fix. :)
 
I don't know. Here is everything I find searching the program folder. I don't understand where it is being determined unfortunately.

Createofficer file uses:

Npchar.rank = GetRandomRank(CharacterIsFriend(Npchar), GetAttribute(Npchar,"quest.officertype"), rankch);

and later

int GetRandomRank(bool isfriend, string officertype, int offset)


and in la_uitls:

Code:
//Создать фантомного персонажа
ref LAi_CreateFantomCharacter(bool isfriend, int offset, bool genrank, bool hasblade, float hasgun, string model, string group, string locator) // NK
{
   return LAi_CreateFantomCharacterEx(isfriend, offset, genrank, hasblade, hasgun, model, group, locator); // NK
}

//Levis:
ref LAi_CreateFantomCharacterEx(bool isfriend, int offset, bool genrank, bool hasblade, float hasgun, string model, string group, string locator)
{
   string officertype = OFFIC_TYPE_RANDCHAR;
   if(isfriend)   officertype = FindRandomModelOfficerType(model);
   else       officertype = GetRandomEnemyType();
   return LAi_CreateFantomCharacterRk(isfriend, offset, genrank, officertype, hasblade, hasgun, model, group, locator);
}

// PB: Split this further for group use
ref LAi_CreateFantomCharacterRk(bool isfriend, int offset, bool genrank, string officertype, bool hasblade, float hasgun, string model, string group, string locator)
{
   int rank = offset;
   if (genrank) rank = GetRandomRank(isfriend, officertype, offset);
   return LAi_CreateFantomCharacterExOt(isfriend, officertype, rank, hasblade, hasgun, model, group, locator);
}

//Levis split this up to assign even more stuff to a fantom if we like
ref LAi_CreateFantomCharacterExOt(bool isfriend, string officertype, int rank, bool hasblade, float hasgun, string model, string group, string locator)
{
   return LAi_CreateFantomCharacterExOtAt(isfriend, officertype, "", "", "", rank, hasblade, hasgun, model, group, locator);
}

//Создать фантомного персонажа
ref LAi_CreateFantomCharacterExOtAt(bool isfriend, string officertype, string attr1, string attr2, string attr3, int rank  , bool hasblade,
                  float hasgun , string model, string group  , string locator)

And later

chr.friend = isfriend;
 
Looks like you found the same thing that I did (see above). :cheeky

In CreateOfficer_Cheat, it is probably quite safe to just add 'ch.friend = true;' prior to the 'LAi_Create_Officer' call.
That would take take of those tavern-owner dialog guys.

For other types of officers though, it is probably less simple.
 
@Pieter Boelen No luck, I added it like the last two lines below, but no change:

Code:
ref CreateOfficer_Cheat(string otype, string model, int offset, int nation, bool blank)
{
    ref pchar = GetMainCharacter();
    ref ch;
    ch = GetCharacter(FindFreeRandomOfficer());
    ch.dialog.filename = "enc_officer_dialog.c";
    ch.offgen = true;
    ch.officer = true;
    ch.Dialog.CurrentNode = "hired";
    ch.Dialog.TempNode = "hired";
    ch.quest.meeting = "1";
    ch.quest = "True";
    ch.reputation = 65;
    ch.nation = nation;
    ch.skill.Leadership = "1";
    ch.skill.Fencing = "1";
    ch.skill.Sailing = "1";
    ch.skill.Accuracy = "1";
    ch.skill.Cannons = "1";
    ch.skill.Grappling = "1";
    ch.skill.Repair = "1";
    ch.skill.Defence = "1";
    ch.skill.Commerce = "1";
    ch.skill.Sneak = "1";
    ch.money = 1;
    ch.AbordageMode = 0;
    LAi_SetLoginTime(ch, 0.0, 24.0); // KK
    LAi_group_MoveCharacter(ch, LAI_GROUP_PLAYER); // KK
    ch.quest.officertype = otype
    ch.friend = true
    LAi_Create_Officer(offset, &ch);
 
I can see no reason why that shouldn't work. The attribute gets set BEFORE it is checked.
Can't think of anything else right now.
 
This is how the function looks now in the current version. I believe this is working as intended right?
Code:
int GetRandomRank(bool isfriend, string officertype, int offset)
{
    ref pchar = GetMainCharacter();
    int pLuck = CalcCharacterSkill(PChar,SKILL_SNEAK);
    int pLead = CalcCharacterSkill(PChar,SKILL_LEADERSHIP);
    int bonus = GetOfficTypeRankBonus(officertype);
    int pRank = 1;
    //Let's get a baserank first
    if(isfriend)
    {
        //friendly encounter
        pRank = makeint(sti(PChar.rank)*0.75 + rand(sti(PChar.rank)*(1.1-(GetDifficulty()*0.2))) + offset);
        pRank = makeint(pRank * (1+((pLuck*2)+(pLead*3))/200)); //Take in account leadership and luck
        pRank = pRank + rand(bonus); //Take in account different officertypes;
        if (pRank < 5) pRank = 2 + rand(5);
    }
    else
    {
        //enemy encounter
        pRank = makeint(sti(PChar.rank)*0.90 + rand(sti(PChar.rank)*(0.2+(GetDifficulty()*0.2))) + offset);
        pRank = pRank + rand(bonus); //Take in account different officertypes;
        if (pRank < 3) pRank = 1 + rand(4);
    }
    return makeint(pRank);
}
If your difficulty is higher you will encounter higher enemies but lower officers.
Luck and leadership determine if a friendly encounter is a higher level, while for enemies the skills don't matter.
 
Back
Top