• 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 WorldMap Related Error.log Entry

Using this code instead may help and give us more information too if it happens:
I can reproduce that thing quite reliably. I'm attaching the log and I'll keep my saves in case we'll need them to get more info.
 

Attachments

  • compile.log.zip
    40.1 KB · Views: 134
From the file:
Code:
ERROR in wdmEvent_EncounterCreate:  does not have 'name' attribute. Please post your compile.log file at piratesahoy.net!
That line SHOULD have an island name in it, but clearly it doesn't. Which completely explains why this would cause error.log entries.
After all, 'no island' also doesn't have a name attribute

Some relevant parts of code:

From worldmap_globals.c:
Code:
#define WDM_NONE_ISLAND     "open_sea"
[...]
string wdmCurrentIsland = WDM_NONE_ISLAND;

From quests.c:
Code:
void QuestToSeaLogin_Launch()
{
   if (LAi_boarding_process) return;
   if (CheckAttribute(GetMainCharacter(), "IsOnDeck") == true && sti(characters[GetMainCharacterIndex()].IsOnDeck) == true) return;
[...]
     if (wdmCurrentIsland == "") wdmCurrentIsland = WDM_NONE_ISLAND;

And here ALL apparent uses of 'wdmCurrentIsland':
upload_2015-12-16_22-57-26.png


A simple fix could be:
Code:
  if(wdmCurrentIsland !=WDM_NONE_ISLAND && wdmCurrentIsland !="") // PB: Prevent error.log entries
   {
     wdmLoginToSea.island = worldMap.islands.(wdmCurrentIsland).name;
     float ix = MakeFloat(worldMap.islands.(wdmCurrentIsland).position.rx);
     float iz = MakeFloat(worldMap.islands.(wdmCurrentIsland).position.rz);
     worldMap.playerShipDispX = (playerShipX - ix);
     worldMap.playerShipDispZ = (playerShipZ - iz);
   }else{
     worldMap.playerShipDispX = 0.0;
     worldMap.playerShipDispZ = 0.0;
   }

But I don't understand though WHY that function gets called with "" as island name in the first place.
Where does that get caused???

I think, @Grey Roger was commenting on the log posted by @nonusnomeni in #49. There are some WorldMap dumps in that one.
Ah, yes; of course! I completely missed that was the file he was referring to.
That's what happens when I REALLY don't have time to pay proper attention. Sorry about that. :facepalm

I can reproduce that thing quite reliably.
Now that we know this much, the .log entries won't be likely to tell us anything new. But maybe you can.
Under what circumstances can you reliably reproduce that? Maybe that could tell us how and when that variable can get set to "".
 
Under what circumstances can you reliably reproduce that?
That was an Ironman game, I was directsailing from Martinique in general direction of Cuba. Just about when I was expecting to make landfall at Puerto Rico, I got chasing by pirates, which can be relevant as Directsail gets aborted in battle. At this point I had to stop playing, so I've made a save with the pirates still on my tail.

Now, if I load that save in Realistic mode and go to the worldmap, the first thing I get is that error message. If I continue to move, I get more errors and there is that stuttering @Hylie Pistof describes. Not exactly game freezing in my case, but still very noticeable.

Maybe that could tell us how and when that variable can get set to "".
The biggest problem with me figuring that out is that I have not a slightest idea of how event processing works in PotC. I'd appreciate a brief overview of how its different parts interact or pointers to the relevant code. Or both. The most relevant part right now being "where GetEventData gets the data from?" :)

From what I've seen yesterday, the most suspicious lines are those "wdmCurrentIsland = GetEventData();". The corresponding event generation code seems to boil down to something like
Code:
if(GenerateMapEncounter(WDM_ETYPE_FOLLOW, wdmGetCurrentIsland(), &i1, &i2) == false) return;
If the arguments to that call are what GetEventData() later sees, then I do see a problem with this, as wdmGetCurrentIsland() is defined as
Code:
string wdmGetCurrentIsland()
{
    if(wdmCurrentIsland != WDM_NONE_ISLAND)
    {
        return wdmCurrentIsland;
    }
    return "";
}
 
Last edited:
That IS a good find!

There are some relatively clear examples of events in NK.c . Search for Kraken or Steam.
@Levis may be able to explain it a bit too. Unfortunately I still won't have time for a while.
 
Okay a quick Explanation:

There are two Kind of Event calls:

Postevent(string eventname, int delay, string arguments, [arguments])
Event(string eventname, string arguments, [arguments])´

And Postevent will be "scheduled" and will run paralell to the other functions.
And Event will be done before the rest of the code continues, so ist similar as an function call.

In an Event function you will often see the GetEventData() function. This function picks up the arguments which are given.
In the string arguments you declare which arguments are passed to this Event.
You use the following letters:

l = int
s = string
i = aref
f = float

Those are the ones I know from the top of my head at least.
So say you want to pass a character index and experience and skillname you do it like this:
Code:
int idx = 0;
int exp = 100;
string skillname = "cannons";
Postevent("myevent",1000,"lls",idx,exp,skillname);

You Need a place where the Event is declared. This can be done with an Event_handler which you will see an example of in the top part of leveling.c.
You can also use the function(I believe) SetEventHandler(string eventname, string functionname, bool something)
I dont know what the something is, I haven't found out what is different. Maybe it has something to do with if it is allowed to run paralell or not.
If you use the SetEventHandler you can also use the DelEventHandler(string eventname, string functionname) to remove it again.

Say we set an Event handler for the function void fubar() and the Event Name is myevent then the fubar function would look like this:

Code:
void fubar()
{
int idx = GetEventData();
int exp = GetEventData();
string skillname = GetEventData();
SomeFunctionToUseIt(idx, exp, skillname);
}

Now after 1 second (1000 ms) after the post Event is called this function will be executed and it will get the data which was send with the post Event.

I hope this is clear enough?
 
I've been having a further quick look into the code related to this and it is confusing.

Look at this, for example:
Code:
string wdmGetCurrentIsland()
{
   if(wdmCurrentIsland != WDM_NONE_ISLAND)
   {
     return wdmCurrentIsland;
   }
   return "";
}
And:
Code:
void wdmShipEncounter(float dltTime, float playerShipX, float playerShipZ, float playerShipAY)
{
   int numShips = wdmGetNumberShipEncounters();
   if(numShips < 5)
   {
     rand(1001);
     //Вероятности появления
     wdmTimeOfLastMerchant = wdmTimeOfLastMerchant + dltTime*WDM_MERCHANTS_RATE*1000.0;
     wdmTimeOfLastWarring = wdmTimeOfLastWarring + dltTime*WDM_WARRING_RATE*1000.0;
     wdmTimeOfLastFollow = wdmTimeOfLastFollow + dltTime*WDM_FOLLOW_RATE*1000.0;
     //Вероятность от количества созданных
     float nump = 1.0 - numShips*0.15;
     //Идентификаторы энкоунтеров
     int i1 = -1;
     int i2 = -1;
     //Выбираем
     ref mc = GetMainCharacter();
     bool encoff = false;
     if(GetAttribute(mc,"worldmapencountersoff") == 1)   return;
     if(rand(1001) + 1 < wdmTimeOfLastMerchant)
     {
       wdmTimeOfLastMerchant = 0.0;
       if(GenerateMapEncounter(WDM_ETYPE_MERCHANT, wdmGetCurrentIsland(), &i1, &i2) == false) return;
And:
Code:
bool GenerateMapEncounter(int iMapEncounterType, string sIslandID, ref iEncounter1, ref iEncounter2)
{
   iEncounter1 = -1;
   iEncounter2 = -1;

   bool bReturn = false;
   int iNearIslandNation = PIRATE;

   // NK 05-06-27 actually get island nation.
   if(sIslandID != "")
   {
     if(!Island_IsEncountersEnable(sIslandID)) {   return false; }

     ref isl = GetIslandByID(worldMap.islands.(wdmCurrentIsland).name);
     iNearIslandNation = sti(isl.smuggling_nation);
   }
   // NK <--
Note how 'sIslandID' actually comes from wdmGetCurrentIsland(), but CAN return both "" and "WDM_NONE_ISLAND".
But then later, the code uses 'sIslandID' and 'wdmCurrentIsland' within two lines of each other, while they should be virtually interchangeable. :facepalm

'wdmCurrentIsland' is a global variable that appears to be set through the game engine:
Code:
void wdmEvent_InsideIsland()
{
   wdmDisableTornadoGen = true;
   wdmCurrentIsland = GetEventData();
   //Trace("Inside to "+wdmCurrentIsland);
   PlayLandHo(); // NK // added by KAM after build 11 // KK
}

void wdmEvent_OutsideIsland()
{
   wdmCurrentIsland = GetEventData();
   //Trace("Outside from "+wdmCurrentIsland);
   wdmCurrentIsland = WDM_NONE_ISLAND;
   worldMap.outsideIsland = true; // KK
}

I think technically this "fix" of mine should prevent the error.log entries and the potential lag resulting from that:
Code:
void wdmEvent_EncounterCreate()
{
   float dltTime = GetEventData();
   float playerShipX = GetEventData();
   float playerShipZ = GetEventData();
   float playerShipAY = GetEventData();
   //Save player ship position
   worldMap.playerShipX = playerShipX;
   worldMap.playerShipZ = playerShipZ;
   worldMap.playerShipAY = playerShipAY;
   if(wdmCurrentIsland !=WDM_NONE_ISLAND && wdmCurrentIsland !="") // PB: Prevent error.log entries

That being said, this COULD clearly do with being made a bit more uniform so that either the code ALWAYS uses 'wdmCurrentIsland' OR wdmGetCurrentIsland() but not both semi-interchangeably.
Then it can be made to return "WDM_NONE_ISLAND" whenever there is no island and do away with the potential for "" being returned.

This seems to play into @Levis' old and temporarily abandoned Medium Priority - Worldmap Encounter Enhancement (V 1.32) Updated | PiratesAhoy! feature from Build 14 Beta 3.2 .
So I'll consider this "Fixed" and any further changes can be made when we get round to restoring @Levis' previous related work.
 
Back
Top