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

WIP Improve the Crewmembers on Shore Mod

The TAVERNBRAWL_FROMDIALOG toggle is meant to prevent such tavern brawls altogether. Just for people who are annoyed by it.

I'll include it for the soldier raids too then.

Code:
void Random_Brawl()
{
   ref PChar = GetMainCharacter();
   if (TAVERNBRAWL_FISTSONLY)   PChar.TAVERNBRAWL = true; //MT: makes spawned enemies fight with their fists
   LAi_LocationFightDisable(LoadedLocation, false);
   Random_Raid("smugglers", 5, PIRATE,"enemy","friend",LanguageConvertString(tmpLangFileID,"TAVERNBRAWL") + "!!!!");
   DeleteAttribute(PChar,"TAVERNBRAWL"); //MT: makes spawned enemies fight with normal weapons again
}
Then you can call it with:
Code:
if (TAVERNBRAWL_FROMDIALOG) Random_Brawl();

So the first 'if' has no {} because it's on the same line, and second 'if' got removed because deleting something that wasn't there in the first place doesn't cause errors?
 
So the first 'if' has no {} because it's on the same line
Almost correct. But this should have worked too:
Code:
void Random_Brawl()
{
ref PChar = GetMainCharacter();
if (TAVERNBRAWL_FISTSONLY)
            PChar.TAVERNBRAWL = true; //MT: makes spawned enemies fight with their fists
LAi_LocationFightDisable(LoadedLocation, false);
Random_Raid("smugglers", 5, PIRATE,"enemy","friend",LanguageConvertString(tmpLangFileID,"TAVERNBRAWL") + "!!!!");
DeleteAttribute(PChar,"TAVERNBRAWL"); //MT: makes spawned enemies fight with normal weapons again
}
If you leave the brackets for an if away, it'll assume you mean brackets around the first line that follows.
Sometimes that can make for cleaner code, but you should be careful with it. If you're not sure, you can't go wrong with adding the brackets anyway.

and second 'if' got removed because deleting something that wasn't there in the first place doesn't cause errors?
Correct. :yes
 
Okay, here's another thing. Damn this convoluted code. At the moment, the code still works with the system where having a LoM equals serving a nation, while if i understand correctly, serving a nation in the future is actually going to be defined as a navy career, right? A LoM should count as something seperate in those circumstances. I'm wondering if that should affect this code:

Code:
        if (HaveLetterOfMarque(sti(Npchar.nation))) //MT: You have a LoM with a nation, which counts as serving
         {
           if(frnd()<makefloat(GetRank(pchar, sti(NPChar.nation))))/12.0) //MT: The higher your rank with the nation you serve, the easier you are to recognise.
           {

I'm thinking it would become something like this:

Code:
        if (HaveLetterOfMarque(sti(Npchar.nation)) || condition where it check if you SERVE a nation, which is distinct from having a LoM) 
         {
           if(frnd()<makefloat(GetRank(pchar, sti(NPChar.nation))))/12.0) //MT: The higher your rank with the nation you serve, the easier you are to recognise.
           {

Sorry for all the questions, especially now you're so busy. This one's a lot more ccomplicated than the guards and pirates, and i haven't even started on the patrols yet.
 
Use 'IsInServiceOf' instead of 'HaveLetterOfMarque'. That covers both privateers AND navy characters.
I don't intend to make any further changes to this background coding; it currently already works for both types of "serving a nation".

Mostly those two functions are equivalent; the only current exception is Hornblower during the story part of his storyline.
There he IS in the navy, but does NOT have a LoM. You don't notice that in the game, but it does prevent 'automatic' promotions during the game.
 
Another question: does GetFlagRMRelation(sti(Npchar.nation)) include the player's personal flag, or does this only work with flags that are not your personal flag? The way the dialog was originally written suggests the latter.
 
GetFlagRMRelation simply returns the relation between a nation and your current flag:
Code:
int GetFlagRMRelation(int iNation)
{
   return GetNationRelation(GetCurrentFlag(), iNation);
}
So this returns "friendly" when checking it for France while flying a French flag, because those are the same.
And if England and France are neutral/allied, it will return "friendly" too.

If you are flying your personal flag, the return value will depend on your REAL nation relations.
 
GetFlagRMRelation simply returns the relation between a nation and your current flag:
Code:
int GetFlagRMRelation(int iNation)
{
   return GetNationRelation(GetCurrentFlag(), iNation);
}
So this returns "friendly" when checking it for France while flying a French flag, because those are the same.
And if England and France are neutral/allied, it will return "friendly" too.

If you are flying your personal flag, the return value will depend on your REAL nation relations.
GetFlagRMRelation simply returns the relation between a nation and your current flag:
Code:
int GetFlagRMRelation(int iNation)
{
   return GetNationRelation(GetCurrentFlag(), iNation);
}
So this returns "friendly" when checking it for France while flying a French flag, because those are the same.
And if England and France are neutral/allied, it will return "friendly" too.

If you are flying your personal flag, the return value will depend on your REAL nation relations.

Right, okay. I'm fairly sure that whoever wrote this code was unaware that you can't detect a false flag if you're not at war first. That said, some situations that were considered are quite interesting: I've just found one where you can be attacked if you're not at war with the nation a sailor belongs to, but are flying a flag that's hostile to him. It's really quite fancy sometimes.
 
I'm fairly sure that whoever wrote this code was unaware that you can't detect a false flag if you're not at war first.
You COULD fly a false HOSTILE flag, I suppose. Imagine France and England being at war and you being friendly with England and hostile with France.
Now fly a French flag near English ships. They won't appreciate. ;)

But there is no "false flag detection" involved there. For all they know, you're hostile and that is how they'll treat you.
So that makes sense with that sailor dialog you mention.
 
I've done some testing in-game to see which sailors spawned where, and it seems that in the portugese La Grenade tavern, only portugese sailors spawned. If that means that their nationality is dependant on the town, that would really simpify things for me. It might not though. It could be that their nationality is based on the nation the island belongs to, not the town. This is an important difference, since those two nationalities don't always match.

I've also discovered that Diag.TempNode = "exit"; has the effect that you can't initiate dialogue with the sailor anymore after finishing the dialogue the first time. This is good.
 
I've done some testing in-game to see which sailors spawned where, and it seems that in the portugese La Grenade tavern, only portugese sailors spawned. If that means that their nationality is dependant on the town, that would really simpify things for me. It might not though. It could be that their nationality is based on the nation the island belongs to, not the town. This is an important difference, since those two nationalities don't always match.
It uses:
Code:
GetLocationNation(location)
So it takes into account the nation of the town you're in. :yes
 
It uses:
Code:
GetLocationNation(location)
So it takes into account the nation of the town you're in. :yes

That's confirmed then. :) More rewriting it is. I'll just upload the files i've finished now, since that would still give you some time to fish out the most obvious flaws. The tavern sailors' code is not going to be finished any time soon at this rate.
 

Attachments

  • PROGRAM.zip
    9.4 KB · Views: 177
I'm in the process of rewriting now, and i've managed to reduce the amount of code that was originally used by quite a bit, i'm pleased to say. All sections of code where you could hire crew have been reduced to a single one, without decreasing the situations to which it applied. There's one last potion of code that i don't understand though, and i suspect it's important. I keep seeing things like this above the 'if' conditions:

Code:
int MyNation = PERSONAL_NATION;
int MyFlag = GetCurrentFlag();
HoistFlag(MyNation);
if(GetRMRelation(PChar, sti(Npchar.nation)) <= REL_WAR)

Mynation = GetServedNation();
HoistFlag(MyNation);
if(GetFlagRMRelation(sti(Npchar.nation)) <= REL_WAR && makeint(NPchar.nation)!=PIRATE)

MyFlag = GetCurrentFlag();
HoistFlag(MyFlag);
if(GetFlagRMRelation(sti(Npchar.nation)) <= REL_WAR && frnd()>0.8)

I know what the 'if' conditions mean, and i suspect the code above them involves calling up their required data, but the 2nd and 3rd example in particular don't seem very consistent. I'm also seeing a lot of 'if' statements where nothing gets called up at all, like if (IsInServiceOf(sti(Npchar.nation))). It's confusing.
 
GetCurrentFlag() returns the nation whose flag you are currently flying
HoistFlag CHANGES the flag you are currently flying, so I'd definitely recommend getting rid of all those function calls!
GetServedNation() returns the nation where the game figures your loyalties lie (marked in YELLOW in the Questbook Interface)

I think ideally there should be no need for initializing any variables prior to the if-checking.
There are plenty of functions that will do what is needed in one go.
This is a good example:
Code:
if (IsInServiceOf(sti(Npchar.nation)))
That checks if you're in the service of the nation the character you are talking to belongs to.
 
m also seeing a lot of 'if' statements where nothing gets called up at all, like if (IsInServiceOf(sti(Npchar.nation))). It's confusing.
Something does get called, it's just the code is formatted in a confusing manner. For all the whitespace between them, that
Code:
if(GetRMRelation(PChar, sti(Npchar.nation)) <= REL_WAR)
still affects execution of that
Code:
Mynation = GetServedNation();

The normal way to write such conditions would be either
Code:
if (condition) action;
or
Code:
if (condition)
   action;
but extra blank lines do not affect execution, they only confuse a human reader.
 
GetCurrentFlag() returns the nation whose flag you are currently flying
HoistFlag CHANGES the flag you are currently flying, so I'd definitely recommend getting rid of all those function calls!
GetServedNation() returns the nation where the game figures your loyalties lie (marked in YELLOW in the Questbook Interface)

So the code was constantly changing your flag to your personal flag, and then checked to see if the flag belonged to a nation the sailor was hostile with? That makes no sense. o_O What's this one for though?

Code:
int MyNation = PERSONAL_NATION;

I'd think your personal nation would always be the same, so i'm not sure why it needs to be initialised, but i wouldn't know.

I'm also seeing the code sometimes stick 'int' in front of the variable, while sometimes it doesn't.
 
Something does get called, it's just the code is formatted in a confusing manner. For all the whitespace between them, that
Code:
if(GetRMRelation(PChar, sti(Npchar.nation)) <= REL_WAR)
still affects execution of that
Code:
Mynation = GetServedNation();

The normal way to write such conditions would be either
Code:
if (condition) action;
or
Code:
if (condition)
   action;
but extra blank lines do not affect execution, they only confuse a human reader.

White space? You do realise i just selected a trio of fragments, right? That's not how the code actually looks in the file.
 
So the code was constantly changing your flag to your personal flag, and then checked to see if the flag belonged to a nation the sailor was hostile with? That makes no sense. o_O
Indeed it doesn't.

I'd think your personal nation would always be the same, so i'm not sure why it needs to be initialised, but i wouldn't know.
It doesn't need to be initialized, so also quite pointless code. :yes

I'm also seeing the code sometimes stick 'int' in front of the variable, while sometimes it doesn't.
The first time you define a variable, you have to add the 'type'. After that, you can reuse it without doing that again.
In fact, redefining it will cause an error.
 
Indeed it doesn't.


It doesn't need to be initialized, so also quite pointless code. :yes


The first time you define a variable, you have to add the 'type'. After that, you can reuse it without doing that again.
In fact, redefining it will cause an error.

The only reason for automatically hoisting the personal flag that i can think of would be to prevent repeated incidents of detection. As in, your character got detected, realised the mistake, and hoisted their own flag to prevent it from happening again.

Oh well. More code to remove. I think it does more now than it originally did, with only half the code. Not bad.
 
The only reason for automatically hoisting the personal flag that i can think of would be to prevent repeated incidents of detection. As in, your character got detected, realised the mistake, and hoisted their own flag to prevent it from happening again.
We had discussions before on doing that in 3D Sailing Mode, but decided against it.
If you DO want to do that, I'd recommend HoistFlag( GetServedNation() ) instead of HoistFlag(PERSONAL_NATION) .
This to accommodate players who don't want to play as "personal".

Oh well. More code to remove. I think it does more now than it originally did, with only half the code. Not bad.
Good job! :woot
 
Back
Top