• 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 Unique Ship/Captain Encounters

Mere_Mortal

Free Like a Radical
Storm Modder
I am working on allowing certain unique ships to be encountered at sea. Two good examples here would be the Black Pearl and HMS Victory. So potentially, both ships could end up in the same battle (although time periods could prevent this). In addition, the former would always be captained by Hector Barbossa and the latter by Horatio Nelson.

This might allow for a bit of logic which applied to Sid Meier’s, whereby the player could hunt for known pirates and put them out of the game. While it is a way off right now, this could also give rise to other possibilities such as bounty prizes, unique loot and maybe even undead crew.

seadogs2_0001-tga-jpg.27136

I’m not sure if I properly have my head around how names are generated for ships, but I’ll use an example here to illustrate what I’m getting at.

RN_SuperiorWarship

This is the Arrogant-class 3rd Rate. Naturally, that will be the name given in icons but this is not what I’m concerned with. What I’m looking at is the individual name for a randomly-encountered ship.

I am assuming that a random name will be grabbed from the array for the relevant nation, English in this case - and therefore this ship could just happen to be called “HMS Victory”, which would be rather unfitting (even if they had the same designer). When it comes to generic ships it really doesn’t matter, but if we’re concerned with unique ships or a more-specific class of ships, particularly ships-of-the-line, then it might be better to limit the random names to historically-relevant ones.

In the case the Arrogant-class, we have a relevant bunch of names to use (with the HMS prefix of course)...
  • Arrogant
  • Audacious
  • Bellerophon
  • Cornwall
  • Edgar
  • Elephant
  • Excellent
  • Goliath
  • Illustrious
  • Saturn
  • Vanguard
  • Zealous
So instead of a randomly-generated Arrogant-class ship being given a generic name, it gets one of those.

On a side-note, is the Culloden-class pretty much the same as the Arrogant? If so, the array of names could be extended in this case to include the Culloden-class types.

What I’m essentially looking to do is to go through ships_init.c and set an array of unique names to some of them. Therefore, before a random ship name is applied a check could first be done to see if the particular ship already has a unique array. I feel this would be more immersive.
 
Last edited:
Relevant function is GetRandomShipNameForNation in PROGRAM\Ships\Ships.c .
That one makes use of the generic ship names from ships_name.c .

Indeed I don't think "Victory" is a very fitting name for small ships.
That is actually the reason why the Standard Storyline starting ship no longer has that name. ;)
 
That’s the function I was looking at. What I’ll probably do is stick a load of name arrays in the init file and leave them there, then maybe later I’ll see if I can understand the function a bit better. I’ll probably enjoy trawling through Wikipedia grabbing the relevant data, I’d learn stuff about the ships.
 
Based on your suggestions, my first thought was to add an extra optional attribute to ships in ships_init.c that can contain possible ship names.
Then if a ship has that attribute, use only names from that attribute. Otherwise use the default list.
That might work.

That might even provide a way for unique ships with name plates to actually get their correct names.
Because at the moment they don't, they don't appear in random encounters at all.
But it would be very cool if we could change that!

Only problem then is how to avoid there being two of the real HMS Victory at the same time?
 
Based on your suggestions, my first thought was to add an extra optional attribute to ships in ships_init.c that can contain possible ship names.
Then if a ship has that attribute, use only names from that attribute. Otherwise use the default list. That might work.
This was precisely my idea. There’s no harm in doing it, and as I said I’ll learn a thing or two along the way. :)
Only problem then is how to avoid there being two of the real HMS Victory at the same time?
“Victory” appears in the default array in the common text file, so any British ship could have the name and without regard as to its type. I suppose removing it from that array would prevent duplication. Or do you mean the ship itself being duplicated? I don’t know if HMS Victory can be encountered multiple times, potentially even two at the same time, but if they are the genuine ship then that’s not my concern here. No, the ship itself shouldn’t be duplicated (even if they have different names) but we’re just talking about the name. The way I see it is that the only place which should have “Victory” would be under that particular ship.

Oh, I get you now. Two ships could be generated using the HMS Victory template, but will have different names. Giving it just one possible name would mean two of them would have the same name. But should the ship even be generated twice at the same time anyway?
 
I mean that there is an "HMS_Victory" ship model with her real name showing on the stern.
At the moment she cannot ever be encountered at random and is only used as a Naval Officer promotion ship.
She can also be purchased from Vanderdecken of course (as many as you want), but he's a special case.

What I am getting at is that it would be nice if that model COULD show up in random encounters.
With your idea, she could be forced to actually have her real name in the game.
But random encounters might have the same ship model twice. :facepalm
 
Couldn’t we just set a bool on the ship init, and if a check for it comes back true then it is a unique ship which can only exist once at a time? Maybe something like creating an array of unique ships, which they are added to it if generated and removed if destroyed. So if the bool is defined for a ship and it is already on the array, it will be skipped.

Maybe something like this...
Code:
   int NameArray = AddStr2Array(&sRndShpEnName,NameArray,"Victory")
   bool isUnique = true
Hold on, it won’t work like that... they need to be set as an attribute.

So, if a ship already has this...
Code:
   refShip.CanEncounter = false
   refShip.CanBuy      = false
Then is it not unique anyway?
 
Last edited:
So, if a ship already has this...
Code:
   refShip.CanEncounter = false
   refShip.CanBuy      = false
Then is it not unique anyway?
That is currently the case for "HMS_Victory" and is what prevents her from showing up in random encounters.
And since she has no story use, in regular play you'll basically never see that model despite it existing.
Same for USS Constitution.

There is also the "refShip.unique" attribute. At the moment that just prevents the "nation stat modifiers" from affecting that ship though.

I would imagine the Force_GetShipType function to be able to generate only one "unique" ship in the game at the same time.
Probably wouldn't be so simple though. Might require a loop through the entire character array to see if that ship is already in use.
Still.... would be cool if possible. :yes

[SPOILER[
My next idea would be to have a hardcoded captain model and name for those ships as well.
That way the correct historical captain could be assigned.
Or the Black Pearl could appear only once in a pirate encounter and WITH Barbossa as captain.
Anyway.... too much complexity and probably not worth the effort.
[/SPOILER]
 
There’s a duplicate entry for Victory which means the ship can actually be encountered.
Strictly speaking it is not the Victory itself but just the same type of ship...
// HISTORICAL SHIP PACK -->
//-------------------------------------------------------------------------
// HMS Victory 1765 by pgargon (Generic version, no nameplate)
//-------------------------------------------------------------------------
// Armada -->

Alright, I’m having a couple of problems with this.

For one, it seems like when a random ship name is generated it is applied to the captain and at this point the ship itself doesn’t even exist. Checking the ship index for the character returns zero, which supports that notion.

Also, I’ve been changing a few random attributes in the init file and they are not changing in-game. I changed a ship’s default calibre, reloaded the game and then dumped its attributes, but the original calibre was still showing. Is the ship init being kept in the save game and unchangeable unless a new game is started? For testing purposes and via the console, I’m trying to change my own ship’s name based on its type but since the attribute isn’t being set then it won’t work. :unsure

No wait, I think a reinit might have fixed that.
 
Last edited:
There’s a duplicate entry for Victory which means the ship can actually be encountered.
Strictly speaking it is not the Victory itself but just the same type of ship...
Yep. "HMS_Victory" and "RN_FirstRate" have ever-so-slightly different paint schemes though.

For one, it seems like when a random ship name is generated it is applied to the captain and at this point the ship itself doesn’t even exist. Checking the ship index for the character returns zero, which supports that notion.
May depend on how the captain is generated. Should be possible to set the name AFTER generating the ship.
Probably best if this is done first though: Planned Feature - Use Generic Captain and Ship Generation Functions | PiratesAhoy!
Then at least ALL captains and ships could be generated in the SAME way. That would simplify things tremendously.

Also, I’ve been changing a few random attributes in the init file and they are not changing in-game. I changed a ship’s default calibre, reloaded the game and then dumped its attributes, but the original calibre was still showing. Is the ship init being kept in the save game and unchangeable unless a new game is started? For testing purposes and via the console, I’m trying to change my own ship’s name based on its type but since the attribute isn’t being set then it won’t work. :unsure
If you change ships_init.c, you have to press F11 to update the ships array in the game.
And because the stats of existing ships are stored with the character, you may need to execute this to update the stats on your own fleet too:
Code:
       for (i = 0; i <= GetCompanionQuantity(PChar); i++) {
         limit = GetCompanionIndex(PChar, i);
         if (limit < 0) continue;
         ch = GetCharacter(limit);
         GiveShip2Character(ch,ch.ship.type,ch.ship.name,-1,ch.ship.stats.nation,true,true);
       }
 
Setting the name after is certainly an option, but it’s one of those things which adds to the amount processing even if it’s minute.

Reinit sorted that out, no worries. Now I just need to figure out how to use a variable for an attribute name.

I mean, this for example is just not happening...
Code:
   // In ships_init entry...
   // refShip.UniqueNames = 1
   // refShip.UniqueNames.Name1 = "Test"

   int iShipType = GetCharacterShipType(MainChar)
   ref rShipType = GetShipByType(iShipType)
   int numNames  = rShipType.UniqueNames

   if (numNames > 0) {
     int random = rand(numNames-1)++
     string curName = "Name"+random
     MainChar.Ship.Name = rShipType.UniqueNames.curName
   }
 
Last edited:
Setting the name after is certainly an option, but it’s one of those things which adds to the amount processing even if it’s minute.
Looks like the generic quest character ship generation function does already have it in the correct order:
Code:
   GiveShip2Character(rFantom, GetShipID(iShipType), "Ship Name", -1, sti(rFantom.nation), true, true); // PB: Generic Function
   SetRandomNameToShip(rFantom);

Now I just need to figure out how to use a variable for an attribute name.
Try:
Code:
MainChar.Ship.Name = rShipType.UniqueNames.(curName);
Note the brackets.

You should also be aware that "rand(2)" can return the numbers 0, 1 and 2.
So you probably need an UniqueName.Name0 in ships_init.c as well.

Instead of that, you could also consider using the GetRandSubString functions.
I think then you could have a line like this in ships_init.c: refShip.UniqueNames = "Victory,St Lawrence,Royal Sovereign,Britannia";
That would make this functionality more similar to what is already in ships_name.c . :doff
 
Looks like the generic quest character ship generation function does already have it in the correct order:
Okay, I’ll have to see why it was returning zero then.
Try:
Code:
MainChar.Ship.Name = rShipType.UniqueNames.(curName);
Note the brackets.
That’ll probably work!
You should also be aware that "rand(2)" can return the numbers 0, 1 and 2.
So you probably need an UniqueName.Name0 in ships_init.c as well.
That’s why I did it like this, which works wonders: rand(numNames-1)++
Instead of that, you could also consider using the GetRandSubString functions.
I think then you could have a line like this in ships_init.c: refShip.UniqueNames = "Victory,St Lawrence,Royal Sovereign,Britannia";
That would make this functionality more similar to what is already in ships_name.c . :doff
Currently I’m putting a case-switch in place...
Code:
   string UniqueName
   int random = rand(4)
   switch(random)
   {
     case 0: UniqueName = "Test0"; break;
     case 1: UniqueName = "Test1"; break;
     case 2: UniqueName = "Test2"; break;
     case 3: UniqueName = "Test3"; break;
     case 4: UniqueName = "Test4"; break;
   }
   refShip.UniqueName = UniqueName
It certainly works, but seemingly the same name is being returned every time unless I reinit. So GetRandSubString() might be better.

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

Scrap that, this works...
Code:
   refShip.UniqueNames = 5
   refShip.UniqueNames.Name1 = "Test1"
   refShip.UniqueNames.Name2 = "Test2"
   refShip.UniqueNames.Name3 = "Test3"
   refShip.UniqueNames.Name4 = "Test4"
   refShip.UniqueNames.Name5 = "Test5"
Code:
   int iShipType = GetCharacterShipType(MainChar)
   ref rShipType = GetShipByType(iShipType)
   int numNames  = rShipType.UniqueNames

   if (numNames > 0) {
     int random = rand(numNames-1)++
     string curName = "Name"+random
     MainChar.Ship.Name = rShipType.UniqueNames.(curName)
     Trace(MainChar.Ship.Name)
   }
Test1
Test4
Test3
Test3
Test5
Test1
Test3
Test5
Test1
Test5
Test2
GetRandSubString() might still be better though because that way it isn’t being set in the attributes.
 
Last edited:
:cheeky
Code:
//-------------------------------------------------------------------------
//     HMS Bellerophon (1786) - Arrogant-class - by Mere Mortal
//-------------------------------------------------------------------------

   makeref(refShip,ShipsTypes[n]) ; n++

   refShip.unique     = 1 // maximum number of this ship which can exist at once
   refShip.unique.names = "Bellerophon"
   refShip.unique.captains = "Sir Thomas Pasley, Sir William Hope, James Cranstoun, Sir Henry Darby, John Cooke, Sir Frederick Maitland"

   refShip.Name     = "HMS_Bellerophon"
   refShip.SName     = "RN_SuperiorWarship"
   refShip.id       = refShip.Name
   refShip.All       = "PO_Warship"
   refShip.walk     = "Battleship1"
   refShip.Model     = "Third Rate"
   refShip.Class     = 3
   refShip.Weight     = Tonnes2CWT(3000)
   refShip.Capacity   = 3250
   refShip.MaxCrew     = 550
   refShip.MinCrew    = 110
   refShip.Price     = 500000
   refShip.HP       = 11000
   refShip.SP       = 200

   refShip.Cannon     = CANNON_TYPE_LONG_LBS24
   refShip.MaxCaliber           = 24
   refShip.CannonsQuantity         = 74
   refShip.Cannons.Borts.cannonf.qty   = 2
   refShip.Cannons.Borts.cannonb.qty   = 4

   refShip.BigPicTexName = "SHIPS4"
   refShip.BI.Tex     = 14
   refShip.BI.Pic     = 12
   refShip.CannonsDeck   = 2
   refShip.CargoHold   = 1
   refShip.QDeck     = "ShipDeck2"
   refShip.Cabin     = "Cabin1"
   refShip.Jetty     = false

   refShip.Flags.Mast2.Flag1 = FLAG_PENNANT
   refShip.Flags.Mast3.Flag1 = FLAG_ENSIGN
   refShip.Flags.Mast4.Flag1 = FLAG_PENNANT
   refShip.Flags.Mast4.Flag2 = FLAG_ENSIGN

   refShip.period.0   = 0
   refShip.period.1   = 0
   refShip.period.2   = 0
   refShip.period.3   = 0
   refShip.period.4   = 0.2
   refShip.period.5   = 0.3

   refShip.england     = 0.5
   refShip.france     = 0
   refShip.holland     = 0
   refShip.portugal   = 0
   refShip.pirate     = 0
   refShip.spain     = 0
   refShip.america     = 0

   refShip.Type.Trade     = false
   refShip.Type.War     = true
   refShip.CanEncounter   = true
   refShip.CanBuy       = false

   refship.WaterLine       = 0.9
   refship.SpeedDependWeight   = 0.3
   refship.SubSeaDependWeight   = 0.9

   refShip.GeraldSails.rey_a2   = 1
   refShip.GeraldSails.rey_a3   = 1
   refShip.GeraldSails.rey_b2   = 1
   refShip.GeraldSails.rey_b3   = 1

   refShip.RigType = "Bat"

   if (iRealismMode > 0 || REALISTIC_SHIP_INERTIA) {
     refShip.SpeedRate         = 11.0
     refShip.TurnRate         = 54.0
     refShip.InertiaAccelerationX   = 4.0
     refShip.InertiaBrakingX       = 0.5
     refShip.InertiaAccelerationY   = 1.75
     refShip.InertiaBrakingY       = 0.5
     refShip.InertiaAccelerationZ   = 2.5
     refShip.InertiaBrakingZ       = 2.0
   }
   else {
     refShip.SpeedRate         = 11.0
     refShip.TurnRate         = 27.0
     refShip.InertiaAccelerationX   = 0.2
     refShip.InertiaBrakingX       = 2.0
     refShip.InertiaAccelerationY   = 7.0
     refShip.InertiaBrakingY       = 6.0
     refShip.InertiaAccelerationZ   = 4.0
     refShip.InertiaBrakingZ       = 1.0
   }
I don’t understand how some of these numbers work, I must say.
 
Last edited:
refShip.unique: Just to make sure, that attribute also prevents any randomization to her stats.

refShip.unique.captains: I wonder if there is any way to split the captain names and titles and to define their models as well.
This is mainly in case we would want to make, say, the Black Pearl available in random encounters, with her correct name and captain (Jack Sparrow or Barbossa).
Ideally then also including "character pirate flag". Probably gets a bit too complicated though.
Easier to just make them pre-defined quest ships though if people would truly want that.

I don’t understand how some of these numbers work, I must say.
If you have any specific questions, feel free to ask.
 
Personally I don’t think the stats should be randomised for them, particularly considering they will be specific to a nation anyway. In the case of Bellerophon, she was actually noted for being particularly fast so I gave a small boost to the speed.

As for the captains, I need to look into how the ships are even generated in the first place since they just get a random captain - that’s something which would need to be forced if they are unique. I guess the best way to do it is to just do a random encounter and if the ship turns out to be unique, then run different logic for assigning its captain. I assume at present unique ships are either skipped or incidentally have encounter set to false?

Do unique ships get random upgrades?
 
Before getting too fancy with this, there should be a single function to handle all encounters.
Otherwise anything complicated is going to get very annoying.

At the moment the "unique" attribute only prevents the stats being randomised.
Other than that, they can show up just like any other ships do.
 
Well for starters I’m only configuring some historical ships, I’m not even thinking about looking at encounters until I can understand exactly how they work. There’s no point in me putting stuff in place, except for testing of course, unless it’s actually going to work otherwise encounters could get broken.

Anyway, what I’m looking at right now is something like this...
Code:
void GenerateUniqueShipNames(ref refShip)
{
   if (refShip.SName == "RN_SuperiorWarship") {   // Arrogant-class 3rd-Rate

     refShip.unique = 10

     refShip.unique.names  = "Arrogant, Audacious, Cornwall, Edgar, Excellent, Goliath, Illustrious, Saturn, Vanguard, Zealous"
     refShip.unique.captains  = "Sir Davidge Gould, Frederick Cornewall, John Elliot, George Murray, Sir Charles Knowles, Sir Thomas Foley, Robert Lambert, Sir Edward Berry, Sir Thomas Hardy, James Walker"
   }
}
What happens here is that if the ship being generated is an Arrogant-class then it will get one of these names. There are also a list of 10 people who captained those ships, but for simplicity they could end up on any of them. That is with the exception of Bellerophon (which gets a big list of people who commanded it) and Elephant (which was Horatio Nelson’s ship), both being unique in their own rights. Ideally, the above list would mean that only 10 of them could exist at a given time (12 including the other two) and the same goes for the captains.

Now, when a ship is generated in an encounter - say the ship survives then does the captain still exist once the player returns to the world map or is he effectively removed from the game? I wonder about this because it leads to the question of whether or not a unique ship/captain should stay gone forever if it is sunk/killed.
 
Last edited:
I think trying to control the captains and number of ships should wait until that can be linked together with a generic encounter generation function.
It sounds potentially quite complicated.

Maybe best to just do the specified ship names for starters?
That should be quite doable by reading the character ship attributes from inside the random name function.
 
Back
Top