1. Dismiss Notice
  2. GOG.com logo

    Thanks to YOUR votes, GOG.com now sells:
    - Sea Dogs - Sea Dogs: Caribbean Tales
    - Sea Dogs: City of Abandoned Ships

    Vote now to add Pirates of the Caribbean to the list!

    Dismiss Notice
  3. Under the Crossbones Podcast

    A Pirate Podcast with Interviews
    Music, Comedy and all things Pirate!

    - Episode Guide - About - Subscribe -
    - Twitter - Facebook - iTunes - Android -
    - Youtube - Fill the Coffers -

    Dismiss Notice
  4. New Horizons logo

    Quick links for PotC: New Horizons
    - Download latest version
    - Wiki - FAQ - Report bugs here
    - ModDB profile

  5. GOF logo

    Quick links for AoP2: Gentlemen of Fortune 2
    - Downloads and info
    - Historical Immersion Supermod
    - ModDB Profile

Dismiss Notice
New to the forum?
Please take a moment to read our Welcome Message and Forum Rules.

Confirmed Bug Peaceful sailing music plays in battle after spawning from worldmap

Discussion in 'Build Mod Bug Tracker' started by Grey Roger, May 15, 2017.

  1. Grey Roger

    Grey Roger Sea Dog Staff Member Storm Modder

    Joined:
    Feb 12, 2007
    Messages:
    6,172
    You're sailing around on worldmap, then go to 3D sailing straight into a battle - perhaps someone attacked you and you couldn't refuse, perhaps you chased an enemy ship, or perhaps you joined in a battle in progress. When you go to 3D mode, although you're in combat, it's one of the peaceful sailing music tracks which plays.

    Here's why. In "PROGRAM\sound\sound.c", function 'SetSchemeForSea()':
    Code:
               if (sti(pchar.Ship.POS.Mode) == SHIP_WAR) // PB: !bMapEnter doesn't have the same effect
                   SetMusic("music_sea_battle");
               else
               {
                   if(Whr_IsNight())       SetMusicAlarm("music_night_sailing");
                   else
                   {
                       if(Whr_IsFog())       SetMusicAlarm("music_fog_sailing");
                           else               SetMusicAlarm("music_day_sailing");
                   }
               }
    I put a 'traceandlog' line in there to see how "pchar.Ship.POS.Mode" is set, and also had console do a 'dumpattributes(PChar)'. The 'traceandlog' line produced this:
    Code:
    pchar.ship.pos.mode = 0, SHIP_WAR = 1, playing peaceful sailing music
    And 'dumpattributes' said this:
    Code:
      pos =
        x = -0.0952811
        z = 0.185853
        y = 0.837455
        mode = 1
    This is a problem I've seen before in a different context. Attribute "PChar.ship.pos" and its sub-attributes aren't set right away after spawning to sea. So the code here is getting the wrong value of "PChar.ship.pos.mode").
     
    Jones likes this.
  2. Pieter Boelen

    Pieter Boelen (Not So) Old Seadog Staff Member Administrator Storm Modder Hearts of Oak Donator

    Joined:
    Nov 11, 2004
    Messages:
    66,322
    Gender:
    Male
    Occupation:
    Maritime Research: Project Engineer (Analysis)
    Location:
    Wageningen, The Netherlands
    @Grey Roger: Do I understand correctly that the code is technically correct, but is executed too early?
    It might be possible to solve that then by adding a PostEvent delay.
    Those are not technically too complicated, so if you would like to learn how to use those, I'll be happy to explain it.
    They can be a pretty powerful tool, also for quests. :yes
     
  3. Grey Roger

    Grey Roger Sea Dog Staff Member Storm Modder

    Joined:
    Feb 12, 2007
    Messages:
    6,172
    I would be happy to learn about PostEvent, yes. I'm not sure I could use it to fix this issue, but it may have uses at some point in the future, and learning new tricks never does any harm.

    And yes, the issue here is that the code is executed too early - it's trying to read the attribute "PChar.ship.pos.mode" before the attribute has been set. I ran into exactly the same problem when I tried to calculate the distance to a fort right after spawning - the function which calculates range uses "PChar.ship.pos.x/y/z", which also haven't been set right away, so it got the range wrong.
     
  4. Pieter Boelen

    Pieter Boelen (Not So) Old Seadog Staff Member Administrator Storm Modder Hearts of Oak Donator

    Joined:
    Nov 11, 2004
    Messages:
    66,322
    Gender:
    Male
    Occupation:
    Maritime Research: Project Engineer (Analysis)
    Location:
    Wageningen, The Netherlands
    Could you upload NK.c? I think there are some good (and hopefully simple) uses in there that may work as example.

    My thought would be to add a 1(?) second delay before starting the music, by which time the attribute should have been set.
    If not, the delay could be increased.
     
  5. Grey Roger

    Grey Roger Sea Dog Staff Member Storm Modder

    Joined:
    Feb 12, 2007
    Messages:
    6,172
    Here's "NK.c" from the 7th January installer. I don't have anything newer in the zip archive so this is probably the latest version.
     

    Attached Files:

    • NK.c
      File size:
      197.7 KB
      Views:
      12
  6. Pieter Boelen

    Pieter Boelen (Not So) Old Seadog Staff Member Administrator Storm Modder Hearts of Oak Donator

    Joined:
    Nov 11, 2004
    Messages:
    66,322
    Gender:
    Male
    Occupation:
    Maritime Research: Project Engineer (Analysis)
    Location:
    Wageningen, The Netherlands
    No worries; pretty much any version would do.
    I don't remember any recent changes to it anyway. I'm probably the only person to edit that file and I haven't done so in ages. ;)

    This seems to be a very simple example:
    Code:
     if(CheckAttribute(pchar, "KrakenAttack")) PostEvent("EnableKraken", 5*60*1000);
    }
    
    #event_handler("EnableKraken", "KrakenEnabled");
    void KrakenEnabled()
    {
     ref pchar = GetMainCharacter();
     DeleteAttribute(pchar, "KrakenAttack");
     if(KrakenAttackEnabled()) LogIt("Captain, the Kraken is ready for another attack!"); // Just in case you swap ships
    }
    
    First PostEvent("EnableKraken", 5*60*1000); is executed.
    This triggers a delay on "EnableKraken" in milliseconds, so 5*60*1000 equals a delay of 5 minutes.

    Then this line handles what to do when the delay expires:
    Code:
    #event_handler("EnableKraken", "KrakenEnabled");
    
    This means "if the delay expires on KrakenAttack (called earlier), then execute function KrakenEnabled".

    And then, of course, it relays to the KrakenEnabled function:
    Code:
    void KrakenEnabled()
    {
     ref pchar = GetMainCharacter();
     DeleteAttribute(pchar, "KrakenAttack");
     if(KrakenAttackEnabled()) LogIt("Captain, the Kraken is ready for another attack!"); // Just in case you swap ships
    }
    
    The purpose is to allow only one Kraken Attack to be called at the same time and to have a "cooldown period" before the next one.
    This prevents it from being too much like an "instant kill" cheat.

    The "#event_handler" doesn't necessarily need to be above the function declaration that it calls (it could be anywhere else in the code),
    but we usually do that so it is clear how it works and all related code is grouped together.

    This is the very basic use and should be enough to solve your music issue (I hope!).

    -------------------------------

    A more advanced example can be found a bit earlier:
    Code:
     PostEvent("KrakenAttackFinished", delay, "i", rCharacter);
    }
    
    #event_handler("KrakenAttackFinished", "FinishKrakenAttack");
    void FinishKrakenAttack()
    {
     ref pchar = GetMainCharacter();
     aref rCharacter = GetEventData();
    
    Here the "i" (for integer, I think) and 'rCharacter' are added.
    This allows 'GetEventData()' to read the value of 'rCharacter' with which the delay was called.

    Because a Kraken Attack can be called on different ships, passing the target character along with the event allows this:
    Code:
    LogIt("Captain, the Kraken has finished its attack on the " + rCharacter.ship.name + "!");
    
    Otherwise the 'FinishKrakenAttack()' function would have no clue who was the target of the attack that just finished.


    I hope this explains things for you. :doff
     
  7. Grey Roger

    Grey Roger Sea Dog Staff Member Storm Modder

    Joined:
    Feb 12, 2007
    Messages:
    6,172
    Two different 'PostEvent' commands have two different lists of parameters. One just has the event name and a delay; the other has event name, delay, string "i" and a reference.

    'PostEvent("EnableKraken", 5*60*1000)' is easy enough to understand - delay is in milliseconds, so a 5 minute delay is 5 x seconds/minute x milliseconds/second. If I were doing one of those, I'd then need to declare a whole new function somewhere.

    'PostEvent("KrakenAttackFinished", delay, "i", rCharacter)' is less easy to understand. "delay" is again the delay in milliseconds. "i" can't be "integer", or if it is, what's it referring to? Not "rCharacter" - that's an aref, not an integer. What's the difference between aref and ref anyway?
     
  8. Pieter Boelen

    Pieter Boelen (Not So) Old Seadog Staff Member Administrator Storm Modder Hearts of Oak Donator

    Joined:
    Nov 11, 2004
    Messages:
    66,322
    Gender:
    Male
    Occupation:
    Maritime Research: Project Engineer (Analysis)
    Location:
    Wageningen, The Netherlands
    Correct, but that is easy enough. Doesn't even require a new game to take effect.

    It is even possible to add more than one variable to pass on. The "i" marks the type.
    If you pass two of type "i", then you can use "ii", variable1, variable2) .

    I honestly can't quite remember; that confused me just now as well. But I do know it works.
    If I recall, the "Manuals & History" PDF file in the mod's "Documentation" subfolder contains a list of those.
    You could have a look through that and see if there is a description to be found about Events.

    What I do remember is that this is weird and confusing. There was also "l" and "s", I think.
    "l" might be a number then and "i" a reference of sorts?

    I never had that clear either, but @Levis explained it to me here:
    New Horizons Wiki
     
  9. Grey Roger

    Grey Roger Sea Dog Staff Member Storm Modder

    Joined:
    Feb 12, 2007
    Messages:
    6,172
    According to that, 'ref' is a reference to an object and 'aref' is a reference to an attribute.

    So 'PostEvent("KrakenAttackFinished", delay, "i", rCharacter)' triggers event "KrakenAttackFinished", whose event handler triggers function 'FinishKrakenAttack()', which tries to read aref 'rCharacter' from event data.

    The 'PostEvent' is in function 'KrakenAttack' and supplies parameter also called 'rCharacter', which was declared as an aref parameter to 'void KrakenAttack(aref rCharacter, int iSwimQuantity)'.

    Function 'KrakenAttack' is called from within "BattleInterface.c":
    Code:
    KrakenAttack(Characters[targetNum], GetCharacterShipHP(Characters[targetNum])/100 );
    And array "Characters" is an array of objects declared in "globals.c". So by the definition in the wiki I'd expect anything referring to its elements to be ref, not aref.

    Now you see why I am having trouble figuring out the difference between ref and aref. o_O
     
  10. Pieter Boelen

    Pieter Boelen (Not So) Old Seadog Staff Member Administrator Storm Modder Hearts of Oak Donator

    Joined:
    Nov 11, 2004
    Messages:
    66,322
    Gender:
    Male
    Occupation:
    Maritime Research: Project Engineer (Analysis)
    Location:
    Wageningen, The Netherlands
    I know the feeling, because it still confuses me as well.
    As far as I can tell, they're used pretty much interchangeably in the code. :facepalm
    The way I figure it: As long as it works, I don't care so much about the specifics.

    I have a vague memory that 'aref' is quite common in combination with 'GetEventData()'.
    You could try replacing it with 'ref' and see what happens.

    Sometimes the game engine does an automatic type conversion if you use the "wrong" type for whatever reason.
    Just like this actually works fine:
    Code:
    int number = 1;
    string text = "Number is " + number;
    LogIt(text);
    
    In most programming languages that I am familiar with, you would need to use an 'int2string' function on 'number' if it is to be used in a string context.
    This makes the PotC code relatively forgiving and somewhat easier to work with for beginning coders.

    On the other hand, when it SHOULD do a data conversion but doesn't, it can become massively confusing.
    It took me forever to figure out a bug caused by mixing 'int' and 'float' in the same formula. :modding
     
  11. Grey Roger

    Grey Roger Sea Dog Staff Member Storm Modder

    Joined:
    Feb 12, 2007
    Messages:
    6,172
    I have no intention of messing with general game code that currently works!

    Automatic conversion of numbers to strings is something I've seen before, especially if it's just going to something like a "print" or "trace". Going the other way usually requires a function, though.

    It's worth noting that nobody, myself included, ever noticed this before. The only reason I noticed it now is that I've been paying more attention to music as a result of getting my end theme to work. Also, when the game picks a piece of music to play during peaceful sailing, it cycles through all the options which were set in "music_standard.c", but I've set another one in "StartStoryline.c" so there's another track which can play if you're sailing peacefully in daytime while playing "Ardent". It just so happened that the game had got to the end of the cycle, which means my new track was the one which played. I'd been listening to the music to make sure it really was picking a different track each time I went to sailing mode, and it wasn't supposed to pick that one during a battle!
     
  12. Pieter Boelen

    Pieter Boelen (Not So) Old Seadog Staff Member Administrator Storm Modder Hearts of Oak Donator

    Joined:
    Nov 11, 2004
    Messages:
    66,322
    Gender:
    Male
    Occupation:
    Maritime Research: Project Engineer (Analysis)
    Location:
    Wageningen, The Netherlands
    I just meant as an experiment to see if it makes a difference.
    If it does make a difference, you'll probably notice soon enough because the Kraken functionality would no longer work properly.

    Indeed it's not a big bug and nobody bothered reporting it before.
    But it is still wrong (I vaguely recall noticing it before), so if a fix can be attempted with only a few lines of code, it might be worth to at least try. :doff
     
  13. Grey Roger

    Grey Roger Sea Dog Staff Member Storm Modder

    Joined:
    Feb 12, 2007
    Messages:
    6,172
    Do you reckon this would work?
    Code:
    void SetSchemeForSea()
    {
       PostEvent("SeaSchemeSet", 500);   // GR: delay setting music so that PChar.ship.pos attributes have time to be set
    }
    
    #event_handler("SeaSchemeSet", "Delayed_SetSchemeForSea");
    void Delayed_SetSchemeForSea()
    {
    <entire contents of original 'SetSchemeForSea'>
    }
    The entire original function 'SetSchemeForSea' is renamed 'Delayed_SetSchemeForSea' and called by the event handler. New 'SetSchemeForSea' just sets off the event, delayed by 0.5 second. My guess is that it will either crash horribly, actually work, or not have any effect because 0.5 second isn't enough...
     
  14. Pieter Boelen

    Pieter Boelen (Not So) Old Seadog Staff Member Administrator Storm Modder Hearts of Oak Donator

    Joined:
    Nov 11, 2004
    Messages:
    66,322
    Gender:
    Male
    Occupation:
    Maritime Research: Project Engineer (Analysis)
    Location:
    Wageningen, The Netherlands
    @Grey Roger: I think in theory that should do exactly what it is intended to do. :onya

    You may want to add some LogIt/Trace statements to check it actually executes as you'd expect.
    And indeed the value for the delay may require tweaking; but that is something you'll find out soon enough. :doff
     
  15. Grey Roger

    Grey Roger Sea Dog Staff Member Storm Modder

    Joined:
    Feb 12, 2007
    Messages:
    6,172
    Ever get the idea the system hates you? Now that I want it to play the wrong music, it refuses to do so. Still using the version of "sound.c" without the 'PostEvent' fix, I went to worldmap, sailed around looking for something to fight, spawned right on top of it, and got battle music. "compile.log" showed that it had started with normal sailing music and then switched to battle music, and a few 'trace' lines in "sound.c" showed the same - 'SetSchemeForSea()' started playing a "music_day_sailing" theme, then 'Sound_OnSeaAlarm' detected a battle in progress and played a "music_sea_battle" theme. I tried several times reloading from different savegames in port, going to worldmap, then exiting worldmap straight into a fight, and it always did the same. :modding

    In another thread, someone had a similar problem when playing "Hoist the Colours" and going into battle with the Interceptor, so I've asked for a savegame which will hopefully play the wrong music, then I can test the fix. Meanwhile, I've started "Hoist the Colours" myself, partly in the hope of getting the same problem at the same point, and partly because it's about time I tried that storyline.
     
  16. Pieter Boelen

    Pieter Boelen (Not So) Old Seadog Staff Member Administrator Storm Modder Hearts of Oak Donator

    Joined:
    Nov 11, 2004
    Messages:
    66,322
    Gender:
    Male
    Occupation:
    Maritime Research: Project Engineer (Analysis)
    Location:
    Wageningen, The Netherlands
    Yes!
     

Share This Page