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

Included in Build False Flags: Modify Memory Functionality

Simply removing the setting of "PlayerNation" would disable fort memory entirely, wouldn't it?
Yes, it would.

But if the bit from the third line down were changed to this:
Code:
if (ship_range < visibility_range)
{
if (GetNationRelation(iNation, nNation) == RELATION_ENEMY)
{
chr.PlayerNation = iNation;
Trace("FLAGS: The " + GetMyShipNameShow(chr) + " has spotted us at " + ship_range + " and will remember us as " + GetNationDescByType(iNation) + " with visibility=" + visibility_range);
}
else Trace("FLAGS: The " + GetMyShipNameShow(chr) + " has spotted us at " + ship_range + "but is not interested as we are not hostile");
}
Would that not prevent it from remembering you if you're friendly, while still allowing it to remember you if you're hostile?
I think so, yes. :onya

I wonder if perhaps an additional attribute could be set:
Code:
chr.PlayerShip = GetCharacterShipID(GetMainCharacter());
Then have something like this to check whether you still have that ship:
Code:
bool HasSameShip = false;
ref comp;
int n;
for (n = 0; n<4; n++)
{
comp = GetCharacter(GetCompanionIndex(GetMainCharacter(), n));
if (GetCharacterShipID(comp) == chr.PlayerShip) HasSameShip = true;
}
If I read things like 'IsCompanion' in "CharacterUtilite.c" correctly, that ought to cycle through the player and companion ships, and set 'HasSameShip' to true if any of them is the one that 'chr' remembered. If it's false then the memory can be wiped because you no longer have the ship which the fort recognises, so you're safe until it recognises you again.
Sounds possible to me.
You may want to add a check for if GetCompanionIndex returns a value smaller than 0; that happens whenever you have an empty companion slot.

How do you propose to deal with a quick free repaint to reset the fort memory?
I forgot to mention it earlier, but that would be another potential exploit.
 
You may want to add a check for if GetCompanionIndex returns a value smaller than 0; that happens whenever you have an empty companion slot.
In that case, 'GetCharacter' returns &NullCharacter. What does 'CheckAttribute(char, "Ship.type")' do if 'char' is &NullCharacter? Crash, or return false?

How would you recommend that this code be incorporated into "Screwface_functions.c"?

How do you propose to deal with a quick free repaint to reset the fort memory?
I forgot to mention it earlier, but that would be another potential exploit.
I'd allow it. It's more or less the same as a crook who steals a car and repaints it, so the police are looking for a red car of that type while he's driving around in a blue one. It's not visibly the same ship that the fort remembers and they can't be suspicious of every ship looking remotely like the one they want. Don't forget that the fort still has the chance to get you anyway by recognising your false flag.

Anyway, I've no idea how to check for every possible variant of the ship, if variants even exist. At least it means if you're sailing around in something unique like the Black Pearl, they won't forget you in a hurry!

And it's precisely the same ruse that the French try when they want to sneak their warship into Sao Jorge during "Assassin". But Elting passes his false flag check. :D
 
In that case, 'GetCharacter' returns &NullCharacter. What does 'CheckAttribute(char, "Ship.type")' do if 'char' is &NullCharacter? Crash, or return false?
I have no clue what would happen. My recommendation is to avoid it.
Console.c contains an example for "if companion index < 0, then skip"; you should be able to reuse that.

How would you recommend that this code be incorporated into "Screwface_functions.c"?
For starters, I think you can make a separate function out of your 'HasSameShip' check.

And then just before the code that you already found, something like:
Code:
if(!HasSameShip(chr)) ResetMemory(chr);

I'd allow it. It's more or less the same as a crook who steals a car and repaints it, so the police are looking for a red car of that type while he's driving around in a blue one.
My main concern is that repainting has no cost and is immediate, so that makes it a potentially excessively easy exploit.

Anyway, I've no idea how to check for every possible variant of the ship, if variants even exist. At least it means if you're sailing around in something unique like the Black Pearl, they won't forget you in a hurry!
IDEA! Instead of storing the "ship ID", store the "ship MODEL"!
That is the variable that defines what ship you can repaint into another.
When you do that, then repainting doesn't work to reset the fort memory anymore. :cheeky
 
Neither does capturing another ship of similar type. This may be a bit of a problem in "Napoleonic" if the ship you're trying to lose is a Fast Merchantman since there are a lot of them about. I seem to recall you looking for merchants and finding nothing but Fast Merchantmen on one occasion. ;)

Anyway, ship ID has one really big advantage. I've found the function for it. ;)
 
Neither does capturing another ship of similar type.
True. You'd need to be sighted with a different ship for the memory to be reset.
Buy a Tartane, berth your Fast Merchantman, wave a friendly flag in front of the fort with your tartane, then get your Fast Merchantmen back. :rofl

I seem to recall you looking for merchants and finding nothing but Fast Merchantmen on one occasion. ;)
Indeed, that did happen one time. Can't remember if we ever did anything about that. Maybe not.... :wp

Anyway, ship ID has one really big advantage. I've found the function for it. ;)
If you want to use "model", a similar function would be easily arranged. :cheeky
 
True. You'd need to be sighted with a different ship for the memory to be reset.
Buy a Tartane, berth your Fast Merchantman, wave a friendly flag in front of the fort with your tartane, then get your Fast Merchantmen back. :rofl
Short of making it so restrictive as to be useless, there's always going to be some way round the system for a pirate who knows the system in detail. :wp Besides, that's not quite the full story.

Go somewhere else, buy a tartane, berth your fast merchantman, sail back to the place you want to forget you, hope nothing meets you on the way, wave a friendly flag at the fort and hope the flag is not recognised, then sail back to wherever you parked the fast merchantman and get it back. (I don't think you can berth a ship at a shipyard unless the ship is in port, not moored at a beach round the corner, so you can't do the ship-switching in the enemy port.)

Indeed, that did happen one time. Can't remember if we ever did anything about that. Maybe not.... :wp
It would probably involve inventing some new medium and large merchant ships for "Napoleonic". So, almost certainly not. ;)

Have a look at these and see if there's any reason why they won't work before I put them to the test... ("nations.c" needed to be amended because if the system is going to remember your ship as 'chr.PlayerShip', then 'ResetCharacterMemory' which is defined in "nations.c" needs to wipe it.)
 

Attachments

  • nations.c
    60.8 KB · Views: 217
  • Screwface_functions.c
    24 KB · Views: 201
Anyway, ship ID has one really big advantage. I've found the function for it. ;)
GetCharacterShipModel already exists and works similarly to the ID one.
Except it would disallow repainting as an exploit.
If you care to make use of it. :doff

Have a look at these and see if there's any reason why they won't work before I put them to the test... ("nations.c" needed to be amended because if the system is going to remember your ship as 'chr.PlayerShip', then 'ResetCharacterMemory' which is defined in "nations.c" needs to wipe it.)
I think this:
Code:
      if (CheckAttribute(chr, "PlayerShip"))
         if (!HasThisShip(chr.PlayerShip))
         {
           Trace("FLAGS: The " + GetMyShipNameShow(chr) + " remembered us as having " + chr.PlayerShip + ", which we no longer have.");
           ResetCharacterMemory(chr);
         }
       }
       else
       {
         iNation = sti(chr.PlayerNation);
         Trace("FLAGS: The " + GetMyShipNameShow(chr) + " remembers us as " + GetNationDescByType(iNation) );
       }
Should probably be this:
Code:
      if (CheckAttribute(chr, "PlayerShip") && !HasThisShip(chr.PlayerShip))
       {
         Trace("FLAGS: The " + GetMyShipNameShow(chr) + " remembered us as having " + chr.PlayerShip + ", which we no longer have.");
         ResetCharacterMemory(chr);
       }
       else
       {
         iNation = sti(chr.PlayerNation);
         Trace("FLAGS: The " + GetMyShipNameShow(chr) + " remembers us as " + GetNationDescByType(iNation) );
       }
Otherwise, the memory is skipped as soon as the fort commander* remembers you, which would probably throw things out of whack....

* = Or other ship! This function applies equally to ships and forts.

And this:
Code:
bool HasThisShip(string Ship)
{
   int n, compidx;
   bool hasship = false;
   ref comp;
   for (n = 0; n<4; n++)
   {
     compidx = GetCompanionIndex(GetMainCharacter(), n);
     if (compidx > 0)
     {
       comp = GetCharacter(compidx);
       if (GetCharacterShipID(comp) == chr.PlayerShip) hasship = true;
     }
   }
   return hasship;
}
Should be this:
Code:
bool HasThisShip(string Ship)
{
   int n, compidx;
   bool hasship = false;
   ref comp;
   for (n = 0; n<4; n++)
   {
     compidx = GetCompanionIndex(GetMainCharacter(), n);
     if (compidx > 0)
     {
       comp = GetCharacter(compidx);
       if (GetCharacterShipID(comp) == Ship) hasship = true; // <----------- change on this line ------------------
     }
   }
   return hasship;
}
Otherwise you use a variable that doesn't exist within that function.

Good to see you found the "index smaller than 0" check. Should be much safer. :woot

All in all, with the changes I propose here, this may just work! :onya
 
Last edited:
GetCharacterShipModel already exists and works similarly to the ID one.
Except it would disallow repainting as an exploit.
If you care to make use of it. :doff
Apart from rather liking the idea of repainting a ship to make it harder for the authorities to detect, 'GetCharacterShipModel' contains this:
Code:
if (!CheckShipAttribute(arship, rship, "Model")) { trace("GetCharacterShipModel: missing Model attrib"); return ""; }
So if the ship has no "model" attribute, that's going to generate a log message and then return a blank. So you get caught with an "EdinburghTrader", go away, complete the "Strange Things" quest, return in the Mefisto, and the fort recognises you because that's the same "model".:facepalm (That's assuming it doesn't just crash out or do something else unpleasant.)

I think this:
Code:
      if (CheckAttribute(chr, "PlayerShip"))
         if (!HasThisShip(chr.PlayerShip))
         {
           Trace("FLAGS: The " + GetMyShipNameShow(chr) + " remembered us as having " + chr.PlayerShip + ", which we no longer have.");
           ResetCharacterMemory(chr);
         }
       }
       else
       {
         iNation = sti(chr.PlayerNation);
         Trace("FLAGS: The " + GetMyShipNameShow(chr) + " remembers us as " + GetNationDescByType(iNation) );
       }
Should probably be this:
Code:
      if (CheckAttribute(chr, "PlayerShip") && !HasThisShip(chr.PlayerShip))
       {
         Trace("FLAGS: The " + GetMyShipNameShow(chr) + " remembered us as having " + chr.PlayerShip + ", which we no longer have.");
         ResetCharacterMemory(chr);
       }
       else
       {
         iNation = sti(chr.PlayerNation);
         Trace("FLAGS: The " + GetMyShipNameShow(chr) + " remembers us as " + GetNationDescByType(iNation) );
       }
Otherwise, the memory is skipped as soon as the fort commander* remembers you, which would probably throw things out of whack....
Indeed that won't work the way I'd hoped. So I may as well lose the 'CheckAttribute(chr, "PlayerShip")' check entirely. It was supposed to protect the '!HasThisShip(chr.PlayerShip)' from being called when 'chr.PlayerShip' doesn't exist, but that shouldn't be a problem because the existence of 'chr.PlayerNation' has already been checked, and 'chr.PlayerShip' is set at the same time as 'chr.PlayerNation' - if one exists then the other must exist.

And this:
Code:
       if (GetCharacterShipID(comp) == chr.PlayerShip) hasship = true;
Should be this:
Code:
       if (GetCharacterShipID(comp) == Ship) hasship = true; // <----------- change on this line ------------------
Oops! And that's why I put it up for proof-reading before actually trying it. (I'd copied and pasted from the earlier proposal, changed some variables, and forgot to change that one. :facepalm )
 
Apart from rather liking the idea of repainting a ship to make it harder for the authorities to detect, 'GetCharacterShipModel' contains this:
Code:
if (!CheckShipAttribute(arship, rship, "Model")) { trace("GetCharacterShipModel: missing Model attrib"); return ""; }
So if the ship has no "model" attribute, that's going to generate a log message and then return a blank. So you get caught with an "EdinburghTrader", go away, complete the "Strange Things" quest, return in the Mefisto, and the fort recognises you because that's the same "model".:facepalm (That's assuming it doesn't just crash out or do something else unpleasant.)
True in theory. Except that in ships_init.c, I added a line that adds a "model" attribute to EVERY ship in the game.
If it isn't actually set before, then it is just set to the ship ID instead just so it has something.
That line is marked "PB: Set a model for ALL ships to prevent potential CTDs", so it seems important.

So that Trace line should never actually get triggered. If it does, then something is REALLY wrong.

Indeed that won't work the way I'd hoped. So I may as well lose the 'CheckAttribute(chr, "PlayerShip")' check entirely. It was supposed to protect the '!HasThisShip(chr.PlayerShip)' from being called when 'chr.PlayerShip' doesn't exist, but that shouldn't be a problem because the existence of 'chr.PlayerNation' has already been checked, and 'chr.PlayerShip' is set at the same time as 'chr.PlayerNation' - if one exists then the other must exist.
That might indeed be OK.
The way I have it in the code I posted does maintain that check, but has it moved so that it should work as you intended.
If the CheckAttribute fails, then the second part of that if-statement is never executed, so it does function like you wanted it to then. :yes

Oops! And that's why I put it up for proof-reading before actually trying it. (I'd copied and pasted from the earlier proposal, changed some variables, and forgot to change that one. :facepalm )
Always happy to help. That is why I also appreciate if other people look at my code.
Wouldn't be the first time a stupid mistake slips through. :ninja
 
It works - sort of. It's probably fine for general game-play but not for my story.

The memory code is only called at the start of battle. It seems to be called again if you change to hostile but is not called again if you change ship. So I sail from Playa de Sierra Maestra under a false Spanish flag and the payroll ship ignores me because I'm friendly. Santiago and Havana forts will probably do so if I start a whole new game rather than load a savegame from Playa de Sierra Maestra - what actually happened is that, as both forts remember me due to calls on 'CheckInitialFlagRelations' before it was modified (it remembered you even if you were friendly), it spawned error messages because "PlayerNation" had been set but "PlayerShip" didn't exist then and hadn't been set. Then I hoisted pirate flag to start the attack, both the ship and Santiago fort promptly logged me as a Pirate in a Shnyava2 (Snow Brig) - and the fort never updated it when I took the SP_CastelF. So, having sailed away, I returned to Santiago, which remembered me in a Shnyava2 which I no longer had, and forgot about me. :facepalm

Is there a place where I can refresh the fort's memory, either when you go to worldmap or when you swap ship? Otherwise I'll need to do something specific within my storyline. (I've an idea what I could try. Quest 1 triggers on "mapenter", sets or resets quest 2, changes "PlayerShip" to your current ship, and sets false flag recognition to normal; and quest 2 triggers on entry to Cuba waters, if you still have that ship then reset quest 1 and set false flag recognition to always pass. If you arrive at Cuba without the marked ship then quest 2 does nothing, quest 1 isn't reset and the cycle ends.)
 
The memory code is only called at the start of battle. It seems to be called again if you change to hostile but is not called again if you change ship. So I sail from Playa de Sierra Maestra under a false Spanish flag and the payroll ship ignores me because I'm friendly. Santiago and Havana forts will probably do so if I start a whole new game rather than load a savegame from Playa de Sierra Maestra
New game shouldn't be required. There is a ResetAllForts() function in nations.c (check spelling) that should make every fort in the game forget you "as if it were a new game".
You can run that through console.

Is there a place where I can refresh the fort's memory, either when you go to worldmap or when you swap ship?
The section of code you changed only gets executed when it should be needed to do so.
This is for performance reasons so that the game isn't continuously checking what it should do with relations between ships 9and forts) when they don't change anyway.

However, "when needed" was manually determined by me based on situations where I figured relations between ships may change, such as hoisting another flag.
Your ship type was not a factor and therefore the necessary update does not trigger when your ship type changes.

If I recall correctly, the relevant function is RefreshBattleInterface, which can be called as RefreshBattleInterface(true) and RefreshBattleInterface(false) .
The difference between those two is that "true" does trigger a relation update and "false" doesn't.
Probably you can search the PROGRAM folder for all calls to that function, then see if there are any that used to be "false" but should be "true" now.
Most likely files that need changing are the ransack/transfer interface ones.

This is assuming that the way your ship type changes is because you captured it.


Because I am writing this from memory, I may be missing some relevant points.
Can you please upload the following files?
PROGRAM\Screwface_Functions.c
PROGRAM\BATTLE_INTERFACE\BattleInterface.c
PROGRAM\BATTLE_INTERFACE\Log[*something*].c (sorry, can't remember its name - but it has "log" in it)
 
Here are "Screwface_functions.c" as modified above, including the corrections; and the other two as of the 2nd April installation. The 17th April update does not appear to contain newer versions.
 

Attachments

  • Screwface_functions.c
    23.9 KB · Views: 210
  • BattleInterface.c
    105.9 KB · Views: 239
  • LogInterface.c
    31.4 KB · Views: 221
Here are "Screwface_functions.c" as modified above, including the corrections; and the other two as of the 2nd April installation.
Thanks! The relevant section from BattleInterface.c is:
Code:
void RefreshBattleInterface(bool CheckRelations)
{
// KK -->
[...]
if (CheckRelations)
{
CheckAllShips("forts", true); // PB: Set initial relations for ships
CheckAllShips("ships", true); // PB: Set initial relations for forts
UpdateRelations();
}
[...]
}
The 'true' in the CheckAllShips call makes it trigger the CheckInitialFlagRelations that you have modified.
LogInterface.c also contains a call to CheckAllShips which is repeated every ingame minute, but with 'false' instead.
Every minute the "false flag detection" is checked, but NOT the "initial flag relations" because doing that so often would be a bit overkill.

The current state is correct based on the assumption that only certain events do affect those initial relations.
You have now added more variables, which means that the limited amount of updates is no longer sufficient.

So indeed I remembered correctly and you need to call RefreshBattleInterface(true) after any event that can now affect those "initial flag relations" as well.
Probably the transfer/ransack interface files already contain a call to that function and you should only need to change the 'false' to 'true'.
 
"LAi_boarding.c" contains a call to "RefreshBattleInterface(true)", but it's commented out.

"transfer_main.c" contains a call to "RefreshBattleInterface(true)" as well. So does "ransack_main.c".

Looking specifically for "RefreshBattleInterface(false)", "BattleInterface.c" has a couple in "BI_DeleteShip" and "BI_DeadShip". And "sea.c" has one a long way down "in "SeaLogin".
 
"transfer_main.c" contains a call to "RefreshBattleInterface(true)" as well. So does "ransack_main.c".
Where exactly do they have those lines? Ideally, they should be done when you Exit those interfaces.

You can also add some Trace lines at the top of the various relevant functions, then capture a ship and close the game straight after.
Then the last thing showing in compile.log should tell you how far it actually got.

(Of course if you can use Windowed mode, then you don't need to close the game and can use Alt+Tab instead; but if I recall, you can't do that for some reason.... :( )
 
"transfer_main.c" has it somewhere near the end of a function "ProcessCancelExit":
Code:
   btmp2 = CheckAttribute(pc,"shiptransferinterface.calledfrominterface"); // NK 05-04-14 add checkattr
   if(btmp2) btmp2 = sti(pc.shiptransferinterface.calledfrominterface);
   if (btmp2 == 0)
   {
   // <-- added by KAM after build 11

     RefreshBattleInterface(true);
     if( CheckAttribute(&PeopleOnShip,"IsOnDeck") && sti(PeopleOnShip.IsOnDeck) )
     {
       PeopleOnShip.IsOnDeck = true;
     }

   // added by KAM after build 11 -->
   }
"ransack_main.c" has it somewhere near the end of a function "ProcessCancelExit":
Code:
  DeleteAttribute(xi_refMainChar, "CapturedShipData"); // KK

   interfaceResultCommand = RC_INTERFACE_RANSACK_MAIN_EXIT;
   Exit();

   RefreshBattleInterface(true);
   if( CheckAttribute(&PeopleOnShip,"IsOnDeck") && sti(PeopleOnShip.IsOnDeck) )
   {
     PeopleOnShip.IsOnDeck = true;
   }
Yes, the same name function in both. o_O
 
"transfer_main.c" has it somewhere near the end of a function "ProcessCancelExit":
[...]
"ransack_main.c" has it somewhere near the end of a function "ProcessCancelExit":
That should actually be OK then, I think. I recommend putting those Trace lines in there so you can double-check what does and doesn't happen.

Yes, the same name function in both. o_O
Interface are loaded only at the time when they are needed; that is why they can reuse the same function names.
Dialogs do that too.
 
@Grey Roger: I just did a quick test and it seems the necessary functions already are executed after capturing a ship.
This is what happened in my test:
Code:
CheckAllShips: forts, initialize = 1
CheckInitialFlagRelations: Greenford Commander
FLAGS: The 'Bridgetown Fort' remembers us as Dutch
FLAGS: The 'Bridgetown Fort' believes our current flag
CheckInitialFlagRelations: Oxbay Commander
FLAGS: The 'Speightstown Fort' is out of range and is not checking our flag
FLAGS: The 'Speightstown Fort' turned hostile as they believe us to be Dutch

Bridgetown here is English, Speightstown is French. Player is Dutch. Holland is friendly to England and hostile to France.

What exactly is the problem you're having? So far, it seems to behave itself.

One thing worth mentioning: Looking at it now, maybe CheckInitialFlagRelations does not get called often enough.
The range-dependent section in there does not get triggered if you approach another ship/fort.

It is checked once; basically when you enter 3D Sailing Mode or when something (EXCLUDING distance!) changes and never again afterwards.
This means that, for example, a fort should not remember you AT ALL after your first approach.
It only would after leaving (because that is entering 3D Sailing Mode again) or if you hoist another flag.
All "turning hostile" effects afterwards are handled through the "False Flag Detection" instead,
but that stores only a "recognized" attribute and not the nation/ship type.


Now I have two code suggestions, both in Screwface_Functions.c .
Replace this:
Code:
       if (initialize)
       {
         CheckInitialFlagRelations(chr, visibility_range, ship_range);
       }
       else
       {
         Recognized = CheckForMainCharacterfalseflag(chr, visibility_range, ship_range);
       }
With this:
Code:
       if (initialize)
       {
         CheckInitialFlagRelations(chr, visibility_range, ship_range);
       }
       else
       {
         if (!CheckAttribute(chr, "PlayerShip") && ship_range < visibility_range) CheckInitialFlagRelations(chr, visibility_range, ship_range);
         Recognized = CheckForMainCharacterfalseflag(chr, visibility_range, ship_range);
       }
Then if a ship/fort has no memory of you, they WILL store a memory if you get within their visible range (I think.... not tested).

Replace this:
Code:
if (!CheckAttribute(chr, "PlayerNation") || GetNationRelation(iNation, nNation) == RELATION_ENEMY)
With this:
Code:
if (!CheckAttribute(chr, "PlayerShip") || GetNationRelation(iNation, nNation) == RELATION_ENEMY)
That way it should "update" its memory at some point to include the player ship, if it already had a memory without ship.
This is mainly for "backwards compatibility" on Beta 4.0 saves.


I'm also not yet sure what to think of this section:
Code:
         if (GetNationRelation(iNation, nNation) == RELATION_ENEMY)
         {
           chr.PlayerNation = iNation;
           chr.PlayerShip = GetCharacterShipID(GetMainCharacter());
           Trace("FLAGS: The " + GetMyShipNameShow(chr) + " has spotted us at " + ship_range + " and will remember us as " + GetNationDescByType(iNation) + " in " + chr.PlayerShip + " with visibility=" + visibility_range);
         }
         else Trace("FLAGS: The " + GetMyShipNameShow(chr) + " has spotted us at " + ship_range + ", but is not interested as we are not hostile");
I quite like them always storing a memory.

If friendly forts/ships remember you, that doesn't matter because they're not hostile anyway.
The ONLY "weirdness" that arises is what Hylie originally reported, which is quite an exceptional circumstance.
But assuming that they do have amazing memory, that IS exactly what they should have done. So I don't see that as being particularly wrong.

Now that changing ship types (or even ship SKINS*!) resets the memory, the fact that they remember becomes almost a non-issue.
This creates quite an easy workaround so the chance that you actually get into trouble because of the "memory feature" is quite unlikely.

To me that is almost a shame, because it means the whole "hostile towns are dangerous" effect that the memory introduced is once again gone.
If a town is hostile and its fort fires on you, that may be annoying, but it isn't wrong.
And I never did get a clear answer on my question: "Why do you want to go into that hostile town in the first place"?

* = I'm severely tempted to use GetCharacterShipModel instead of GetCharacterShipID so that repainting doesn't work.
Seems a bit too easy to me.
 
Last edited:
What exactly is the problem you're having? So far, it seems to behave itself.
Attached is quite a long "compile.log". The game started with me at Playa de Sierra Maestra about to put to sea to attack the payroll ship, SP_CastelF Siroco. My ship at this time was Shnyava2 Tonina. There are some error messages to do with me having got a locator for where I thought I could place 'Siroco' by standing on the beach with visible locators - wrong, that shows locators associated with the shore, not with the island. The ship always shows up anyway so I never noticed until now, but a bit of messing with TOOL got me a genuine island locator in about the right area. Anyway.

Next come some "FLAGS" messages as both Havana and Santiago forts try to remember me but don't have "PlayerShip" because they got their "PlayerNation" memories before the changes. No harm done. Siroco correctly reports that I'm not hostile and doesn't care.

Up goes the pirate flag. Both Siroco and Santiago fort note me as pirate in a shnyava2. Boarding happens - lots of messages to do with that. You can tell when the boarding is over because "Quest name piratebook" happens, shortly followed by "Quest name payroll_ship_taken". I sail away, now in the Siroco. Shnyava2 Tonina is now at the bottom of the sea. Off to Isla Mona for quest stuff, then to Santo Domingo, whose fort reports not being interested because I'd put the Spanish flag back up before arriving there. After doing stuff in Santo Domingo I'm back at sea, and the fort again reports not interested - I'm completely unfamous so it has almost no chance of recognising a false flag.

And then I return to Santiago, whose fort reports remembering me in a Shnyava2 which I no longer have.

One thing worth mentioning: Looking at it now, maybe CheckInitialFlagRelations does not get called often enough.
The range-dependent section in there does not get triggered if you approach another ship/fort.
Or if you change ship while still in sight of the fort.

Now I have two code suggestions, both in Screwface_Functions.c .
Replace this:
In effect, adding:
Code:
 if (!CheckAttribute(chr, "PlayerShip") && ship_range < visibility_range) CheckInitialFlagRelations(chr, visibility_range, ship_range);
to "CheckAllShips".

Then if a ship/fort has no memory of you, they WILL store a memory if you get within their visible range (I think.... not tested).

Replace this:
Code:
if (!CheckAttribute(chr, "PlayerNation") || GetNationRelation(iNation, nNation) == RELATION_ENEMY)
With this:
Code:
if (!CheckAttribute(chr, "PlayerShip") || GetNationRelation(iNation, nNation) == RELATION_ENEMY)
That way it should "update" its memory at some point to include the player ship, if it already had a memory without ship.
This is mainly for "backwards compatibility" on Beta 4.0 saves.[/quote]
Take no chances, check both:
Code:
if (!CheckAttribute(chr, "PlayerNation") || !CheckAttribute(chr, "PlayerShip") || GetNationRelation(iNation, nNation) == RELATION_ENEMY)

I'm also not yet sure what to think of this section:
Code:
         if (GetNationRelation(iNation, nNation) == RELATION_ENEMY)
         {
           chr.PlayerNation = iNation;
           chr.PlayerShip = GetCharacterShipID(GetMainCharacter());
           Trace("FLAGS: The " + GetMyShipNameShow(chr) + " has spotted us at " + ship_range + " and will remember us as " + GetNationDescByType(iNation) + " in " + chr.PlayerShip + " with visibility=" + visibility_range);
         }
         else Trace("FLAGS: The " + GetMyShipNameShow(chr) + " has spotted us at " + ship_range + ", but is not interested as we are not hostile");
I quite like them always storing a memory.

If friendly forts/ships remember you, that doesn't matter because they're not hostile anyway.
True if relations are static. Not true if they're not. This is where @Hylie Pistof ran into trouble - he'd entered a French port under a Portuguese flag while France and Portugal were at peace, then got into trouble because the fort remembered him as Portuguese after France and Portugal had gone to war. If you're in the habit of using any friendly false flag to get into port then you're in trouble if the one you used last time is now hostile even if the fort didn't recognise your false flag so has no idea who you are, and didn't recognise your false flag this time either. If the fort doesn't remember you unless it knows you're hostile then this problem should not arise. Anyway, why should the fort remember you if it has no reason to believe you're hostile?

Now that changing ship types (or even ship SKINS*!) resets the memory, the fact that they remember becomes almost a non-issue.
This creates quite an easy workaround so the chance that you actually get into trouble because of the "memory feature" is quite unlikely.
And yet we have already had one instance of it causing trouble.

To me that is almost a shame, because it means the whole "hostile towns are dangerous" effect that the memory introduced is once again gone.
If a town is hostile and its fort fires on you, that may be annoying, but it isn't wrong.
It is if the fort has never recognised you and you've never been there under a hostile flag. And don't forget that the town is still dangerous, especially later in the game, because the fort could recognise your false flag - and then it has good reason to remember you.

And I never did get a clear answer on my question: "Why do you want to go into that hostile town in the first place"?
Quests. In my case, Santiago has significant plot purposes, but I have very definite ideas about when the player is going to be encouraged to make peace with Spain. In "Tales of a Sea Hawk", because you have to go to San Juan repeatedly, including one occasion where you have to go to the port and may not know that the quest will probably work if you enter town from the jungle and then walk into port. In "Bartolomeu", where one of your earliest missions takes you to Marigot, but France is hostile because you're Portuguese. Or in "Early Explorers" when you're playing anyone other than Spanish or Portuguese, and almost all towns are hostile.

* = I'm severely tempted to use GetCharacterShipModel instead of GetCharacterShipID so that repainting doesn't work.
Seems a bit too easy to me.
Whereas I'm still remembering your earlier offer:
Maybe @Grey Roger feels like diving into the code to remove all "memory" code altogether though.
Later you even inadvertently suggested the code to do exactly that. ;)

More seriously: the more the game tightens up and becomes annoying or inconvenient for players, the more players are likely to either find and stick with an earlier, less annoying version, or give it up entirely.

I wonder how hard it would be to have some of this based on difficulty setting? "Landlubber", no memory at all; "Mariner", remember ship type; "Swashbuckler", remember ship model; "Sea Dog", I've still to decide. :D
 

Attachments

  • compile.log
    58 KB · Views: 209
Attached is quite a long "compile.log". The game started with me at Playa de Sierra Maestra about to put to sea to attack the payroll ship, SP_CastelF Siroco. My ship at this time was Shnyava2 Tonina. There are some error messages to do with me having got a locator for where I thought I could place 'Siroco' by standing on the beach with visible locators - wrong, that shows locators associated with the shore, not with the island. The ship always shows up anyway so I never noticed until now, but a bit of messing with TOOL got me a genuine island locator in about the right area. Anyway.

Next come some "FLAGS" messages as both Havana and Santiago forts try to remember me but don't have "PlayerShip" because they got their "PlayerNation" memories before the changes. No harm done. Siroco correctly reports that I'm not hostile and doesn't care.

Up goes the pirate flag. Both Siroco and Santiago fort note me as pirate in a shnyava2. Boarding happens - lots of messages to do with that. You can tell when the boarding is over because "Quest name piratebook" happens, shortly followed by "Quest name payroll_ship_taken". I sail away, now in the Siroco. Shnyava2 Tonina is now at the bottom of the sea. Off to Isla Mona for quest stuff, then to Santo Domingo, whose fort reports not being interested because I'd put the Spanish flag back up before arriving there. After doing stuff in Santo Domingo I'm back at sea, and the fort again reports not interested - I'm completely unfamous so it has almost no chance of recognising a false flag.

And then I return to Santiago, whose fort reports remembering me in a Shnyava2 which I no longer have.
But what is the part that seems wrong to you? I read through that 1.5 times and I must be missing something.
Code:
FLAGS: The 'Santiago Fort' has spotted us at 696.52 and will remember us as Pirate in Shnyava2 with visibility=867.5
[...]
FLAGS: The 'Santiago Fort' remembered us as having Shnyava2, which we no longer have.
^ There it also seems like it is doing exactly what you meant it to.

I do wonder if this section needs to be reordered a bit:
Code:
    // Remember player's previous flag
     if (!CheckAttribute(chr, "PlayerShip") || GetNationRelation(iNation, nNation) == RELATION_ENEMY)
     {
       if (ship_range < visibility_range)
       {
         if (GetNationRelation(iNation, nNation) == RELATION_ENEMY)
         {
           chr.PlayerNation = iNation;
           chr.PlayerShip = GetCharacterShipModel(GetMainCharacter());
           Trace("FLAGS: The " + GetMyShipNameShow(chr) + " has spotted us at " + ship_range + " and will remember us as " + GetNationDescByType(iNation) + " in " + chr.PlayerShip + " with visibility=" + visibility_range);
         }
         else Trace("FLAGS: The " + GetMyShipNameShow(chr) + " has spotted us at " + ship_range + ", but is not interested as we are not hostile");
       }
       else
       {
       //   Trace("FLAGS: The " + GetMyShipNameShow(chr) + " is out of range and is not checking our flag");
       }
     }
     else
     {
       if (CheckAttribute(chr, "PlayerShip") && !HasThisShip(chr.PlayerShip))
       {
         Trace("FLAGS: The " + GetMyShipNameShow(chr) + " remembered us as having " + chr.PlayerShip + ", which we no longer have");
         ResetCharacterMemory(chr);
       }
       else
       {
         iNation = sti(chr.PlayerNation);
         Trace("FLAGS: The " + GetMyShipNameShow(chr) + " remembers us as " + GetNationDescByType(iNation) );
       }
     }
Should the ResetCharacterMemory not be triggered only within visible range? Otherwise they'd forget even if they don't even know you're there.
And when they do forget, shouldn't they immediately remember your new ship and flag instead?

In effect, adding:
Code:
if (!CheckAttribute(chr, "PlayerShip") && ship_range < visibility_range) CheckInitialFlagRelations(chr, visibility_range, ship_range);
to "CheckAllShips".
If intialize = true, then it does CheckInitialFlagRelations for ALL ships and forts.
If it is false, it does it once for any ship that comes within range. So it isn't entirely the same in both cases.

Take no chances, check both:
Code:
if (!CheckAttribute(chr, "PlayerNation") || !CheckAttribute(chr, "PlayerShip") || GetNationRelation(iNation, nNation) == RELATION_ENEMY)
Why not? Can't hurt. :onya

True if relations are static. Not true if they're not. This is where @Hylie Pistof ran into trouble - he'd entered a French port under a Portuguese flag while France and Portugal were at peace, then got into trouble because the fort remembered him as Portuguese after France and Portugal had gone to war. If you're in the habit of using any friendly false flag to get into port then you're in trouble if the one you used last time is now hostile even if the fort didn't recognise your false flag so has no idea who you are, and didn't recognise your false flag this time either. If the fort doesn't remember you unless it knows you're hostile then this problem should not arise. Anyway, why should the fort remember you if it has no reason to believe you're hostile?
While Hylie's example was definitely surprising, as I said earlier, I don't see it as being actually wrong.
For all that fort knows, the flag you flew last time was actually true. Which means that now you're an enemy because war has been declared.
They don't know it was a false flag. There was no need to use that flag, so that makes it a player mistake, not an issue with the system.
A mistake that can be corrected within the game; just costs some money.

Reading Hornblower, those crews can recognize specific ships (with names!) when they appear at the horizon, quite literally "from the cut of their jibs", e.g. from their rigging.
So if ship crews can do that, it isn't such a stretch that a fort could maintain some sort of log of all ships that it sees.

Probably this wouldn't be 100% watertight as ship identification systems and record keeping at the time must have been less good than they are now.
But still, I imagine they would have given it their best shot.

So why shouldn't they remember and act based on that memory?
As far as I'm concerned, that fort in Hylie's example did do exactly what it was meant to do.

And yet we have already had one instance of it causing trouble.
"Trouble" or "misunderstanding/confusion"?

Anyway, I was referring there to the situation WITH your "changing ship type resets memory" feature included.
What happened before was unlikely and with your change becomes even MORE unlikely, with extra (FREE!) ways around it.

Quests. In my case, Santiago has significant plot purposes, but I have very definite ideas about when the player is going to be encouraged to make peace with Spain. In "Tales of a Sea Hawk", because you have to go to San Juan repeatedly, including one occasion where you have to go to the port and may not know that the quest will probably work if you enter town from the jungle and then walk into port. In "Bartolomeu", where one of your earliest missions takes you to Marigot, but France is hostile because you're Portuguese. Or in "Early Explorers" when you're playing anyone other than Spanish or Portuguese, and almost all towns are hostile.
If the main quest requires you to go to towns, nation relations and such should all make sense and not make it very difficult.
Unless it is meant to be difficult in the context of the story (e.g. an infiltration mission).

If you cannot do certain side quests, because they involve towns that are hard to get into, that is part of the game.
Ideally, the side quests shouldn't be a "collect them all" situation, but should actually fit in with the "story of the character you're playing".
If your character is hostile to France, it doesn't make so much sense to then do all sorts of quests that involve going into French towns.

Having towns hostile to you is part of the challenge. How you deal with that is part of the game experience. It isn't meant to be all too easy.
If in Early Explorers, you want to do quests in the hostile towns, maybe you first need to make some allies somehow.
Or, if that isn't an option, prey on your enemies until you're strong enough to FORCE towns to become your allies.
For example.

Whereas I'm still remembering your earlier offer:
Later you even inadvertently suggested the code to do exactly that. ;)
Big question here is what you would want to accomplish: A "stripped down" version of my original functionality for "personal use"?
Or something that goes into the main modpack?

For the first case, everyone is welcome to make whatever changes they want and I'll happily point in the direction to make those changes.
For the second case, more thought is required as it should ideally be something everyone is happy with.

As long as it is me making the modpack updates, I do have ultimate control over what goes in and how.
Normally I try to make everybody happy with the decisions made, but of course I myself do also need to agree that it is the best choice.

On this particular subject, I am not yet sold. Your "changing ship type resets memory" feature does sound sensible to me and I'm quite happy to add that.
But for all the other things that have been mentioned, I am decidedly undecided.

Of course it is rather dubious how much longer I will remain "in charge", because I don't particularly want to be.
Not by myself anyway. See: Discussion - Releasing Future Modpack Versions | PiratesAhoy!

More seriously: the more the game tightens up and becomes annoying or inconvenient for players, the more players are likely to either find and stick with an earlier, less annoying version, or give it up entirely.
Features added to the game can often go two ways: They can either add to the gameplay, or be annoying.
The difference can depend hugely on the point of view of the player.

Some people may consider it annoying that forts and other ships maintain a memory of the last time they saw you.
Other people might consider dealing with those complexities to be part of the fun.
This is especially true for the various "realism modes/toggles" that we have.
Neither opinion is objectively "right" or "wrong". It is just a matter of personal preference.

Which brings up the fact that I asked SO MANY TIMES now if some sort of "Arcade mode" is desired for some of the more complicated features I have introduced this past year.
This mainly for the stuff dealing with "false flag detection" and also the "acts of piracy".
I personally like the concept of how I wrote it now, but I don't necessarily want to force my preferences onto everyone.
That is why I asked that question. So many times.

But since I never did get an answer, I no longer care at all and I just focus on "doing it my way".
Not because I consider "my way to be the right way", but because I cannot try to second-guess the wishes of people who don't tell me their wishes.
And also because I DO believe there is value to my ideas and trying them is the only way to get feedback.

I wonder how hard it would be to have some of this based on difficulty setting? "Landlubber", no memory at all; "Mariner", remember ship type; "Swashbuckler", remember ship model; "Sea Dog", I've still to decide. :D
Should not be hard at all. Though I'd suggest considering it a "Realism Setting" rather than a "Difficulty Level".
Maybe with an extra independent toggle at the top of PROGRAM\InternalSettings.h as well.
Normally I don't like having more toggles than necessary, but this seems like a potentially important one to have.
 
Back
Top