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

Capsizing Ships

Pieter Boelen

Navigation Officer
Administrator
Storm Modder
Hearts of Oak Donator
Look what I just found in PROGRAM\SEA_AI\AICannon.c:
Code:
float GetRollAngle(ref mchr, ref echr)
{
float X = stf(mchr.ship.pos.x) - stf(echr.ship.pos.x);
float Y = stf(mchr.ship.pos.z) - stf(echr.ship.pos.z);
float Pitch = stf(mchr.ship.ang.x);
float Roll = stf(mchr.ship.ang.z);
int Quad = GetCannonQuadrant(clampangle(atan2(X, Y) - (stf(mchr.ship.ang.y) + PI)));
float RollAngle = 0.0;
switch(Quad)
{
case 0:		// front
RollAngle = -Pitch;		// Pitch increases rolling to front
break;
case 1:		// right
RollAngle = Roll;		// Roll increases rolling to left
break;
case 2:		// back
RollAngle = Pitch;
break;
case 3:		// left
RollAngle = -Roll;
break;
}
//	float Angle1 = Radian2Degree(RollAngle); if (Angle1 > 180.0) Angle1 -= 360;				// testing only
//	if (mchr.id == "Blaze") traceandlog("Quadrant " + Quad + ", Roll Angle = " + Angle1);	// testing only
return RollAngle;
}
Does anyone else think "capsize"? :rolleyes:
 
I think capsize cause we were talking about this before... The code however is definatly a bonus :woot
 
I've got the concept of this working in the game! If a ship's roll angle exceeds 5 degrees, she will capsize.
However, this occurs quite quickly for large ships in storms and for small ships all the time.
We'd need to come up with some good formula to not make it too easy to capsize a ship, but to ensure it DOES occur in intended instances.

All the code for this goes in PROGRAM\SEA_AI\AIShip.c:
Code:
	if(!CheckAttribute(rDead, "Ship.Capsize"))
{
// Halfman -->
aSink.Speed.x = 0.021 * (frnd() * 2.0 - 1.0); // speed sink angle rotate around x
aSink.Speed.z = 0.04 * (frnd() * 2.0 - 1.0); // speed sink angle rotate around z
// Halfman <--
}
else
{
if(rDead.Ship.Capsize > 0) // Capsizing to Starboard
{
LogIt("The " + rDead.ship.name +", captained by " + GetMySimpleName(rDead) + ", capsized to starboard because her roll angle exceeded 5 degrees: " + rDead.Ship.Capsize);
aSink.Speed.x =  0.025 - 0.05 * frnd(); // speed sink angle rotate around x
aSink.Speed.z = -0.05; // speed sink angle rotate around z
}
else // Capsizing to Port
{
LogIt("The " + rDead.ship.name +", captained by " + GetMySimpleName(rDead) + ", capsized to port because her roll angle exceeded 5 degrees: " + rDead.Ship.Capsize);
aSink.Speed.x =  0.025 - 0.05 * frnd(); // speed sink angle rotate around x
aSink.Speed.z =  0.05; // speed sink angle rotate around z
}
}
And:
Code:
	// PB: Capsizing -->
float RollAngle = -Radian2Degree(stf(GetAttribute(rCharacter, "ship.ang.z")));
if(RollAngle < -180) RollAngle = RollAngle + 360;
if(IsMainCharacter(rCharacter)) LogIt("Player roll angle = " + RollAngle);
if(GetSeaTime() > 10 && abs(RollAngle) > 5)
{
rCharacter.Ship.Capsize = RollAngle;
ShipDead(sti(rCharacter.Index), KILL_BY_OWNER, sti(rCharacter.Index));
}
// PB: Capsizing <--
With this code in place, you can also trigger a manual capsize through the console with this:
Code:
	float RollAngle = -Radian2Degree(stf(GetAttribute(pchar, "ship.ang.z")));
if(RollAngle < -180) RollAngle = RollAngle + 360;
pchar.Ship.Capsize = RollAngle;
ShipDead(sti(pchar.Index), KILL_BY_OWNER, sti(pchar.Index));
 
The code for this is included in my code folders of today:
http://www.pyratesahoy.com/build/Build%2014%20Beta%202%20WIP/PB%20Code%202010-06-22.zip

People would be welcome to download this and test.
Make a backup of your PROGRAM\SEA_AI\AIShip.c file so you can return to the original,
because at the moment, capsizing happens too frequently for gameplay purposes.

Question to everybody: Does anyone have any thoughts on when a ship should and shouldn't capsize?
We need to come up with a formula that works well gameplay-wise.
I do know about how this works in real life (stability and such), but that's a bit too complex to work into the game.
 
Yesterday I ended up in a storm in a Tartane, which was pretty cool.
I had changed the capsize limit to 10 degrees and by adjusting my course to the direction the waves were coming from,
I was able to hold out for quite some time without capsizing.
Still, eventually I sank anyway, but it still proves that this could make for a cool modification that makes storms more interesting.
IF we can figure out a good "capsize limit" formula that is challenging, but not too difficult, that is.
And we might need to add the "roll angle" to the on-sea display, so you can see if you're in danger or not.

Some thoughts:

- Realistically, the loading of the ship should influence your stability and therefore the risk of capsizing
.Ships without a full cargo hold (low center of gravity) would have a higher risk of capsizing.
.Ships with heavy cannons on high decks would have a higher risk of capsizing
("simulate" by making the capsize risk dependent on installed calibre?)

- Maybe we should limit the risk of capsizing to only take place in storms and not in regular seas?
Though theoretically, if we make capsizing hard enough, you can only manage it in storms anyway.
 
Yesterday I ended up in a storm in a Tartane, which was pretty cool.
I had changed the capsize limit to 10 degrees and by adjusting my course to the direction the waves were coming from,
I was able to hold out for quite some time without capsizing.
Still, eventually I sank anyway, but it still proves that this could make for a cool modification that makes storms more interesting.
IF we can figure out a good "capsize limit" formula that is challenging, but not too difficult, that is.
And we might need to add the "roll angle" to the on-sea display, so you can see if you're in danger or not.

Some thoughts:

- Realistically, the loading of the ship should influence your stability and therefore the risk of capsizing
.Ships without a full cargo hold (low center of gravity) would have a higher risk of capsizing.
.Ships with heavy cannons on high decks would have a higher risk of capsizing
("simulate" by making the capsize risk dependent on installed calibre?)

- Maybe we should limit the risk of capsizing to only take place in storms and not in regular seas?
Though theoretically, if we make capsizing hard enough, you can only manage it in storms anyway.

This sounds good and would indeed add a more realistic feature to the storms and sailing in general, however i think perhaps a tuggle would work wounders here, then you could have the limit for say expert player's to be more challanging than for that of new player's. As for ship types, you could try to make the line of code work with each ship so you can set the ships capsize limit different to each other, that would quickly fix the problem with larger ships capsizing easier than smaller ones. Then you can add the tuggle where say a Tartan has a limit of 10 for new player's it could be higher say 25.

There is only one problem, in COAS when your ship sinks its game over from what i have seen anyway. If that could be changed then this would be something to add for COAS too. :will
 
Original PotC had game over as soon as you sank/got slain either; that's something we added and I'm sure you could do it too.

I always thought that storms were too easy to survive, since they require you to wait, rather than require you to ACT.
That's the main thing I'd like to be able to change with this idea.

It's the smaller ships that capsize easier (because they get bigger roll angles), which actually is very annoying.
After all, if you sink with a large ship and survive in a tartane, the tartane is bound to capsize also.

Ship-specific roll angle limits would be possible, but would also require us to define them for 200+ ships. :facepalm

At the moment, I set it to 15, which in the PotC Build Mod can happen occasionally, but isn't common at all.

Another potential issue is that companion ships are NOT going to care about wave direction,
so you might very quickly lose your entire fleet.

In short, although I reckon there's some opportunity here, it could also very well be a can of worms. :modding
 
As a rule the companions follow you so turning to the waves should allow them to do the same however the problem is they can take a long time because of the distance from you before they will turn. If only there was a way to increase the AI's intellegance so that they react to set events like storms, wind directions etc. We know that if you tell a companion to sail away they will alway's sail with the wind behind them, so perhaps it is possible to add some kind of command that can be added for storms where the compaions will sail into the waves. :shrug
 
This here might be a better idea:
Code:
	// PB: Capsizing -->
if(Whr_IsStorm() && IsMainCharacter(rCharacter) && CheckAttribute(rCharacter,"Capsize") && GetCharacterShipClass(rCharacter) < 8)
// Limit roll-dependent damage to larger player ships in storms only
{
float RollAngle = -Radian2Degree(stf(GetAttribute(rCharacter, "ship.ang.z")));
if(RollAngle < -180) RollAngle = RollAngle + 360;
LogIt("Roll Angle = " + RollAngle); // TEMP
switch(sti(rCharacter.Capsize.Warning))
{
case 0: // First Warning
if(GetSeaTime() > 10 && abs(RollAngle) > 9)
{
LogIt("Captain, the crew is getting seasick due to the ship's rolling");
rCharacter.Capsize.Warning  = 1;
}
break;

case 1: // Second Warning
if(GetSeaTime() > 25 &&  abs(RollAngle) > 11)
{
LogIt("Captain, some of the cargo has started shifting due to the rolling and went overboard");
rCharacter.Capsize.Warning  = 2;
}
break;

case 2: // Third Warning
if(GetSeaTime() > 40 && abs(RollAngle) > 13)
{
LogIt("Captain, the shifting cargo due to the rolling has done damage to the hull");
rCharacter.Capsize.Warning  = 3;
}
break;

case 3: // Fourth Warning
if(GetSeaTime() > 50 && abs(RollAngle) > 15)
{
if(RollAngle > 0)	LogIt("Captain, we're capsizing to starboard because of excessive rolling");
else				LogIt("Captain, we're capsizing to port because of excessive rolling");
rCharacter.Capsize.Angle = RollAngle;
ShipDead(sti(rCharacter.Index), KILL_BY_OWNER, sti(rCharacter.Index));
}
break;

}
}
// PB: Capsizing <--
You get three warnings and you'll capsize only if after those, you're still getting large roll angles.
At the moment, the warning don't actually DO anything, but I intend them to eventually do what they suggest.

For fun's sake, I might even randomize things and add different sorts of events that could happen.
So far I've got these ideas:

Crew getting seasick > morale decreases
Captain's blade fell out of the weaponslocker > decrease in quality
Cargo shifting and going overboard > loss of some cargo
Officer falling overboard > loss of tavern-hired officer
Shifting cargo in the ship > hull damage

Does anyone else have any thoughts of what else could happen in a storm?
 
Officer falling overboard... new quest open (FIND ....) :woot

Might I suggest instead of "Captain, we're capsizing to starboard because of excessive rolling" maybe having as something like "Captain, we're capsizing to starboard... ABANDON SHIP!!!"
 
Sure; can do. :yes

As for FINDING your officer again, that does make things rather more complicated... :facepalm
 
I'm working on putting the actual events in:
Code:
	// PB: Capsizing -->
if(Whr_IsStorm() && IsMainCharacter(rCharacter) && CheckAttribute(rCharacter,"Capsize") && GetCharacterShipClass(rCharacter) < 8)
// Limit roll-dependent damage to larger player ships in storms only
{
int RollDamageValue;
int i;
float RollAngle = -Radian2Degree(stf(GetAttribute(rCharacter, "ship.ang.z")));
if(RollAngle < -180) RollAngle = RollAngle + 360;
LogIt("Roll Angle = " + RollAngle); // Show the roll angle
switch(sti(rCharacter.Capsize.Warning))
{
case 0: // First Warning
if(GetSeaTime() > 10 && abs(RollAngle) > 9)
{
RollDamageValue = sti(rCharacter.Ship.Crew.Morale) - 10;
LogIt("Captain, the morale of the crew decreased from " + GetMoraleName(rCharacter.Ship.Crew.Morale) + " to " + GetMoraleName(RollDamageValue) + " because the ship's rolling is making them seasick");
rCharacter.Ship.Crew.Morale = RollDamageValue; // Decrease morale one level
if (sti(rCharacter.Ship.Crew.morale)<MORALE_MIN) rCharacter.Ship.Crew.morale=MORALE_MIN;

rCharacter.Capsize.Warning  = 1;
}
break;

case 1: // Second Warning
if(GetSeaTime() > 25 &&  abs(RollAngle) > 11)
{
//	switch(rand(1))
//	{
//		case 0:
for(i=GOOD_GOLD;i<GOODS_QUANTITY-1;i++) // Try to dump cargo first, rather than consumables
{
if(GetCargoGoods(rCharacter, i) <= 0) continue; // Don't dump cargo that isn't in the hold
if (CheckAttribute(rCharacter, "quest.generate_trade_quest.iQuantityGoods")) 
{
if ( i == sti(rCharacter.quest.generate_trade_quest.iTradeGoods) ) continue; // Don't dump quest cargo (not realistic, but less frustrating)
}
}
if(GetCargoGoods(rCharacter, i) <= 0) // If the previous loop didn't return anything
{
for(i=0;i<GOOD_GOLD;i++) // Find some consumables to dump
{
if(GetCargoGoods(rCharacter, i) <= 0) continue; // Don't dump cargo that isn't in the hold
if (CheckAttribute(rCharacter, "quest.generate_trade_quest.iQuantityGoods")) 
{
if ( i == sti(rCharacter.quest.generate_trade_quest.iTradeGoods) ) continue; // Don't dump quest cargo (not realistic, but less frustrating)
}
}
}
if(GetCargoGoods(rCharacter, i) > 0)
{
RollDamageValue = 1 + makeint(0.1*GetCargoGoods(rCharacter, i));
LogIt("Captain, the cargo has started shifting due to the rolling and " + RollDamageValue + " " + Goods[i].Name + " went overboard");
RemoveCharacterGoods(rCharacter, i, RollDamageValue);
}
else // The cargo hold is empty
{
RollDamageValue = makeint(0.1*GetCrewQuantity(rCharacter));
LogIt("*" + RollDamageValue + " crewmembers are heard yelling from inside the empty cargo hold*");
RemoveCharacterCrew(rCharacter, RollDamageValue); // 10% crew damage
}
/*						break;

case 1:
// Add blade damage
break;
}*/
rCharacter.Capsize.Warning  = 2;
}
break;

case 2: // Third Warning
if(GetSeaTime() > 40 && abs(RollAngle) > 13)
{
switch(rand(1))
{
case 0:
RollDamageValue = makeint(0.1*sti(rCharacter.Ship.HP));
LogIt("Captain, shifting cargo did " + RollDamageValue + " damage to the hull");
Ship_ApplyHullHitpoints(rCharacter, RollDamageValue, KILL_BY_TOUCH, -1); // 10% hull damage
break;

case 1:
RollDamageValue = makeint(0.1*GetCrewQuantity(rCharacter));
LogIt("Captain, that last violent roll washed " + RollDamageValue + " crewmembers overboard");
RemoveCharacterCrew(rCharacter, RollDamageValue); // 10% crew damage
break;
}
rCharacter.Capsize.Warning  = 3;
}
break;

case 3: // Fourth Warning
if(GetSeaTime() > 50 && abs(RollAngle) > 15)
{
if(RollAngle > 0)	LogIt("Captain, we're capsizing to starboard... ABANDON SHIP!");
else				LogIt("Captain, we're capsizing to port... ABANDON SHIP!");
rCharacter.Capsize.Angle = RollAngle;
ShipDead(sti(rCharacter.Index), KILL_BY_OWNER, sti(rCharacter.Index));
}
break;

}
}
// PB: Capsizing <--
 
Actually, that DIDN'T look good, because it didn't do what I wanted, especially when it came to dumping cargo.
I rewrote things, resulting in this:
Code:
	// PB: Capsizing -->
if(Whr_IsStorm() && IsMainCharacter(rCharacter) && CheckAttribute(rCharacter,"Capsize") && GetCharacterShipClass(rCharacter) < 8)
// Limit roll-dependent damage to larger player ships in storms only
{
int RollDamageValue;
int i;
float RollAngle = -Radian2Degree(stf(GetAttribute(rCharacter, "ship.ang.z")));
if(RollAngle < -180) RollAngle = RollAngle + 360;
LogIt("Roll Angle = " + RollAngle); // Show the roll angle
switch(sti(rCharacter.Capsize.Warning))
{
case 0: // First Warning
if(GetSeaTime() > 10 && abs(RollAngle) > 9)
{
RollDamageValue = sti(rCharacter.Ship.Crew.Morale) - 10;
LogIt("Captain, the morale of the crew decreased from " + GetMoraleName(rCharacter.Ship.Crew.Morale) + " to " + GetMoraleName(RollDamageValue) + " because the ship's rolling is making them seasick");
rCharacter.Ship.Crew.Morale = RollDamageValue; // Decrease morale one level
if (sti(rCharacter.Ship.Crew.morale)<MORALE_MIN) rCharacter.Ship.Crew.morale=MORALE_MIN;

rCharacter.Capsize.Warning  = 1;
rCharacter.Capsize.Time = GetSeaTime();
}
break;

case 1: // Second Warning
if(GetSeaTime() - sti(rCharacter.Capsize.Time) > 10 &&  abs(RollAngle) > 11)
{
for(i=GOODS_QUANTITY-1;i>=0;i--)
{
if(GetCargoGoods(rCharacter, i) <= 0) continue; // Don't dump cargo that isn't in the hold
if (CheckAttribute(rCharacter, "quest.generate_trade_quest.iQuantityGoods")) 
{
if ( i == sti(rCharacter.quest.generate_trade_quest.iTradeGoods) ) continue; // Don't dump quest cargo (not realistic, but less frustrating)
}
break;
}
if(GetCargoGoods(rCharacter, i) > 0)
{
RollDamageValue = 1 + makeint(0.1*GetCargoGoods(rCharacter, i));
LogIt("Captain, the cargo has started shifting due to the rolling and " + RollDamageValue + " " + Goods[i].Name + " went overboard");
RemoveCharacterGoods(rCharacter, i, RollDamageValue);
}
else // The cargo hold is empty
{
RollDamageValue = makeint(0.1*GetCrewQuantity(rCharacter));
LogIt("*" + RollDamageValue + " crewmembers are heard yelling from inside the empty cargo hold*");
RemoveCharacterCrew(rCharacter, RollDamageValue); // 10% crew damage
}
rCharacter.Capsize.Warning  = 2;
rCharacter.Capsize.Time = GetSeaTime();
}
break;

case 2: // Third Warning
if(GetSeaTime() - sti(rCharacter.Capsize.Time) > 10 && abs(RollAngle) > 13)
{
switch(rand(1))
{
case 0:
RollDamageValue = makeint(0.1*sti(rCharacter.Ship.HP));
LogIt("Captain, shifting cargo did " + RollDamageValue + " damage to the hull");
Ship_ApplyHullHitpoints(rCharacter, RollDamageValue, KILL_BY_TOUCH, -1); // 10% hull damage
break;

case 1:
RollDamageValue = makeint(0.1*GetCrewQuantity(rCharacter));
LogIt("Captain, that last violent roll washed " + RollDamageValue + " crewmembers overboard");
RemoveCharacterCrew(rCharacter, RollDamageValue); // 10% crew damage
break;
}
rCharacter.Capsize.Warning  = 3;
}
break;

case 3: // Fourth Warning
if(GetSeaTime() - sti(rCharacter.Capsize.Time) > 10 && abs(RollAngle) > 15)
{
if(RollAngle > 0)	LogIt("Captain, we're capsizing to starboard... ABANDON SHIP!");
else				LogIt("Captain, we're capsizing to port... ABANDON SHIP!");
rCharacter.Capsize.Angle = RollAngle;
ShipDead(sti(rCharacter.Index), KILL_BY_OWNER, sti(rCharacter.Index));
}
break;

}
}
// PB: Capsizing <--
Can you please do a sense-check on this one?

Also, I changed the triggers to take into account the time at which the previous event happened.
If you were able to delay warning 1 a long time, then the next events will be delayed too.
That'll make it harder to capsize, though still not impossible.
 
One note on this whole thing: I added some code to prevent throwing quest cargo overboard.
However, if you get quest cargo in a storyline or sidequest, the required attributes are not set to prevent this.
This could potentially be a problem in the Assassin and Bartolomeu storylines especially,
though could be circumvented by setting the same attributes as the random cargo quests add.
 
Interesting, if I understand in some of the Bartolomeu's cargo missions, we could lose some goods and fail the mission.. :woot
 
Yep but all that needs doing is making the cargo actually be quest cargo and it should be fine :yes
 
Hi,
This capsizing lark is a pain.....have'nt managed to survive a single storm yet.
15 degrees is not much,I'd have thought 40-45 degrees you might expect a bit of trouble,sinking while almost verticle seems pretty unlikely.
It's not like you can steer your way out of the storm realy.The ship seems to do it's own thing then sinks.

damski.
 
Back
Top