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

AI Sailing into the wind

irR4tiOn4L

Landlubber
Storm Modder
Hi everyone, ive been pondering a bit about the problem of AI sailing into the wind (stupid morons) and obviously getting stuck and going nowhere. Id like to change that. I was hoping to have someone with in depth knowledge of the game code point me in the right direction however, as i am not quite sure which of the files in Program/Sea_AI would be most appropriate. Heres what i want to do:

The AI ingame, I think its fair to say, is fairly basic. It will generally make an assessment on whether to engage or run away (which is largely the work of the Build mod team, well done on that!), will then sail straight for the player and once its close enough, will either turn to constantly keep its broadsides pointed at the enemy (tactical situation be damned) or sail right up to your ship and board. This works well enough for the most part, as even if it doesnt stick to windward or otherwise choose its engagements, age of sail battles are generally forgiving enough for the resulting combat to still be satisfying for the player.

It seems fair to say, however, that modding the AI to be aware of where the wind is coming from and at least do things like tack upwind would involve some fairly heavy rewrites of the AI routines, and thats not even considering stuff like keeping to windward.

I have a much simpler fix for this behaviour in mind. Rather than modify the AI itself, i would like to override the AI's rudder whenever it stupidly chooses to point itself into the wind. I envision this working something like this;

The game would run a check to determine whether the AI ship's sails have stalled. This is better than checking for specific angles off the wind, and hopefully is possible to do. Once it is detected that the AI's sails have stalled, which is only possible when sailing too far into the wind, the game will then either freeze the AI ship's rudder - effectively forcing it to keep turning at whatever rate it was already turning at - or will run a check to see what side of the wind the ship is on and force the rudder to some satisfactory amount - maybe 50% or full deflection. Either way the rudder will stay forced until the ship's sails are no longer stalled.

This should produce the following result - if the wind is coming from 0 and the AI ship is sailing at 270, but starts turning to starboard to follow a player (or for whatever reason), the moment its sails stall, at say 330 degrees, its rudder will be frozen in place and it will be forced to cross the wind all the way to 30 degrees, when it will be returned control of its rudder. The AI will, of course, most likely still want to pursue whatever is upwind, and so will probably try to turn into the wind again. But since it takes time to then counteract the turning motion and then begin turning the other way again, by the time the AI begins turning to port and crosses 30 degrees again, again freezing its rudder, it will hopefully have picked up some speed and sailed a short distance upwind. Its rudder will again be frozen, this time until it crosses to 330 degrees, and the process will repeat.

The result will hopefully be that although the AI isnt trying to tack, it will be performing a very crude tacking maneuver, hopefully making some (albeit slow) headway upwind.

Now its possible that simply freezing the rudder in place will not be satisfactory, because the ship will cross the wind too slowly and pick up negative speed, only to jam the rudder in the other direction once its crossed it and again lose whatever speed it built up. In this case, it may be better to, rather than locking the rudder at current deflection, to jam the rudder to full - either by checking for which side is closer to the wind and jamming it that way (eg if you are at 330 degrees, it should jam full starboard rudder) or by detecting the current DIRECTION of rudder (port or starboard) and jamming that to full. You can increase the rudder gradually rather than suddenly or not to full to make the ship obey the new realistic rudder mechanics in build mod but either way the result should be similar.

The result in this case should hopefully be this; if you jam the rudder to the direction of the wind, the ship will always be forced to cross the wind, albeit it will turn across the wind much quicker than just keeping rudder at whatever you found it. This should do 2 things - 1 - minimize the time spent facing the wind, thus minimizing speed loss and - 2 - when the ship regains rudder control at, say, 30 degrees, the inertia mechanics in build mod should be so high at full rudder deflection that it will take a good 30 seconds for the ship to stop turning (lets say at 45 degrees it gets to 0 rudder again) and start turning back toward the wind again. Hopefully this will mean that by the time it hits 30 degrees again (but now turning to port) it will have sailed a decent 30 seconds or more upwind, gained some speed, and with the rudder again being jammed to full, now to port, it will cross the wind and repeat the process on the other side - and hopefully make some headway into the wind.

If, instead, you jam the rudder to whatever direction it is currently facing, but to full, the result will largely be the same (in order to reach, say, 330 degrees from 270, the ship had to be turning to starboard, so jamming full starboard will force it to cross the wind) but in some very rare circumstances, like if the ship was turning to starboard to follow a ship, but that ship crossed its path and now the ship is trying to turn to port instead, the ship will not be forced to cross the wind but instead will return to whatever side of the wind it was on.


Now a few more things - in order to prevent spectacular failure, no matter what method is used the rudder must always be jammed at either port or starboard, to some minimum amount. Otherwise, a ship sailing normally with 0 rudder, but suddenly facing a shift in the wind from directly ahead (unlucky but happens) will have its rudder jammed at 0 (going nowhere fast) or the program might crash. So failsafe code will be needed to make sure the rudder is always either at port or starboard (one can be randomly selected if it isnt) at either full, whatever amount you desire, or a minimum of lets say 10%. This should mean that no matter what happens, the ship will ALWAYS steer out of the wind, eventually.

Second point - why insist on crossing the wind? Wouldnt it be better if the AI sailed on one side of the wind and crossed when it got close? Yes, but this is far more complicated to implement. If you force the ship to stay on one side of the wind, it also wont be able to cross to the other side. So for example, if i jammed the rudder of a ship at 30 degrees to port, it would never be able to pass 30 degrees - it might hit a cliff and be stuck, or the player might easily take advantage. To stop this, youd have to run complicated checks for where the enemy target is, what vector to cross the wind at, and how to avoid cliffs. The result would be faster tacking, but its needless complexity.

This brings me to the final point - its quite possible that, in part of its tacking, the AI will run up against a cliff and be unable to avoid it because we jammed its rudder. The situation is unlikely, but it can occur. However, this is a small price to pay - the ship will stop against the wall, suffer some damage, but it will keep turning (because weve jammed its rudder). Once its sails stop stalling, however, it will probably try turning back into the wind, and at this point it will be forced to cross it again - AWAY from the cliff. From this point on, the path of the ship will either lead away from the cliff altogether, or, by banging into the cliff the first time, the tacking will be close to but unlikely to hit the cliff again. Hopefully this wont be much of a problem.


The code to implement all this could be quite short and simple, its just a matter of being able to check for stalling sails, jam the rudder and know the right place in the files to insert the code so that its always running on all AI ships (and not player's ship).

Please let me know what you think and how i could get this ingame!

Oh, and ah - be gentle if this is a stupid idea!

Thanks!
 
Ive briefly simulated the concept ingame (manually) with 3 different ships - trade brig (full upgrades), Xebec (no upgrades), Fast war galleon (no upgrades).

Out of these, the concept worked very well for the Xebec, because in a wind of 16 it built up a lot of speed (6-7) but also a lot of turning momentum. It tended to swing from about 290-70 degrees and did make good headway, but importantly, never went backwards while crossing the wind.

The concept worked acceptably for the Trade Brig, because in a wind of 16 the brig built up some speed (3-4) and swung fairly quickly across the wind, while taking quite a long time to swing back again. It tended to swing from about 310-50 degrees and while it did go backwards while facing the wind, it only did so at about 0.5 kn (and easily made up for that out of the wind).

The concept at least didnt hinder the fast war galleon, because in a wind of 16 while it hardly built up any speed (2 kn) and its turning momentum surprisingly didnt give it as much time out of the wind as the other two, swinging from about 310-50 degrees, it didnt go back all that quickly either (-1.5kn or so). All in all, it tended to stay on the spot, which, ironically, is still better than the AI which goes backwards.


However, its fairly obvious to me now that the whole idea would work a lot better with two additions;
1) Force the AI to strike sails. Assuming you can still detect when sails are no longer stalled (and thus when to give the AI control again) this should be fairly easy to do and will obviously have a huge benefit in tacking. If you cant strike all sail, than at least strike to battle sails. Obviously sails will be unfurled again as soon as they are no longer stalling.

2) Introduce a time delay between the sails no longer stalling and control being returned to the AI. Sails should be unfurled before this delay, but the rudder should remain locked for this time delay before being given back to AI control. What is a good time delay depends on the type of ship - 15-20 seconds is good for that galleon, but for a Xebec this would see it swinging past 260-100 degrees (and introduce risk of collision). I propose a simple test based upon the time spent with sails stalled - ie as soon as the sails stall and this script kicks in, start timing and stop timing when the sails are no longer stalling. Then take that time - the time to cross the wind essentially - and depending on what works, maybe half or quarter it, and then make that the time delay before AI gets control again. This should tailor the script very well for any ship - eg, if a Xebec takes 20 seconds to cross the wind, a 5 second delay afterward will suffice, since it will take it maybe another 15 seconds to turn around and head back into the wind, during which time it has gained more speed than it will lose in the wind. But for a galleon that takes 60 seconds to cross the wind, 15 seconds might be the right delay.


Its still a fairly simple script and to be honest, if you can get 1) you wont really need 2) (although that time delay would improve the average speed the AI would tack with). Overall though, i think this concept would definitely work, and we could finally see the AI tacking upwind in a fairly convincing way. Just need to know the following;
1) Can a script read when a ship's sails would stall?
2) Can the AI's rudder be locked and manipulated by a script?
3) Can the AI's sails be locked and manipulated by a script?
4) Would the AI be able to recover properly after it was given control of its rudder again?
5) Which file would this script best be put in?

Thanks!
 
The game doesn't really work with a "rudder" per se, it's just the Realistic Game Mode's increased inertia values that simulate this.
Although I'm not sure about influencing a ship's course, I do know that forcing a certain speed onto a ship doesn't work.
At least, I've tried everything I could think of and never managed it. I'd expect it to be the same with course, though am not 100% sure.
I certainly hope I'm wrong, because if I'm right, that makes any attemps at this tricky to say the least. :facepalm

I think we CAN force the AI to raise/lower sails. At least we can for the player, so I's expect it to be the same for the AI.
 
Hmm is it possible to edit inertia values at will? Increasing inertia or z rotation to a very high level would also achieve the desired crossing of the wind.

Alternatively, is it possible to give the AI two more types of sail that change z rotation and thus keep the ship rotating - and then force them to hoist it?
 
Inertia is set per ship in ships_init.c; I'm not sure if that can be changed "on the fly" while in 3D sailing mode, though might be possible...
 
Anyone know how to work with "arCharShip.Impulse.Rotate.y"?
Ive got some code on this and its working to my satisfaction (testing on player ship atm and it pushes me out of the wind then stops pushing me) - but I can ONLY ever get the ship to spin to the RIGHT, no matter what I do!

Is there another coordinate for left or is it simply impossible to make the ship spin to the left? Its so frustrating to be so close yet so far

EDIT: Nevermind, a very simple test allowed me to rotate the ship to the left :) Must be some error in my code :rolleyes:

EDIT2: Almost there! My function now detects which way the ship is moving and forces me to keep moving until I am out of the wind - and it does it at a reasonable force! All I need now is to apply it only to AI (shouldnt be an issue) and to figure out how to lower AI's sails (not quite sure on this one)
 
function Ship_SetSailState(int iCharacterIndex, float fSailState) allows you to set it to a value as needed:
0 - no sails
1 - battle sails
2 - full sails

Is this what you were looking for?
 
function Ship_SetSailState(int iCharacterIndex, float fSailState) allows you to set it to a value as needed:
0 - no sails
1 - battle sails
2 - full sails

Is this what you were looking for?

Yes, thankyou!

I also VERY much need a way to set ship specific globals. A counter of sorts. Right now, my floats are getting wiped each time the thing runs and setting a global in sea.c is useless since it is not ship specific. This has very much limited the functionality because while i have been able and know how to get ships to, eg, sail OFF the wind for a while THEN cross it, because the process is direction specific I need a way to 'store' decisions about each ship. Without this, i have been able to get ships to cross the wind continually - aka 'tacking' - but its not nearly as effective as i could make it.

Still, heres a working version;

Code:
//TM --> Detect when non player ship is sailing into wind and use rotate function to turn it out of wind
float fTacking2 = 0;

if (iCharacterIndex != GetMainCharacterIndex()) {
if(fOffWind < fClosestPoint) {
if (fSailState > 0.1) {	
float fTacking = stf(stf(arCharShip.Speed.y));
if(fTacking <= 0) {fTacking2 = -0.001; arCharShip.Impulse.Rotate.y = fTacking2; Log_SetStringToLog("Tacking Left");}; //Left		
else {fTacking2 = 0.001; arCharShip.Impulse.Rotate.y = fTacking2; Log_SetStringToLog("Tacking Right");}; //Right				
} else {
arCharShip.Impulse.Rotate.y = 0
fTacking = 0
fTacking2 = 0
}
} else {
arCharShip.Impulse.Rotate.y = 0
fTacking = 0
fTacking2 = 0
}
}
// <--

The simplicity really does hide many hours of work learning how to get this to work, and i have many other variants if anyone is interested.

Insert this below the luffing sound code, in AIShips.c ->
Code:
// LDH --> sail luffing sounds
if (SAIL_LUFFING_SOUNDS) {
if(iCharacterIndex == GetMainCharacterIndex()) {
if(fOffWind < fClosestPoint + RS_CP_OFFSET) {
if (fSailState > 0.1) {		// don't play if sails are furled
for (int i=0 ; i<SAIL_LUFFING_SOUNDS; i++ )
{
PostEvent("luffing_event",Rand(999));
}
}
}
}
}
// LDH <--



<-----INSERT HERE



//	Log_SetStringToLog("Angle1: " + fOffWind*100);

arCharShip.MaxSpeedZ = fMaxSpeedZ * RS_CalcSpeedScalar(fClosestPoint, fBestPoint, rigtype, fOffWind, fWindPower) * fK * fSpeedPerk; // 04-09-19 now a function
arCharShip.speedmult = fMaxSpeedZ * fK * fSpeedPerk * GetSailPercent(rCharacter) / 100.0; // NK 04-09-21
//	Log_SetStringToLog("Trgt: " + arCharShip.MaxSpeedZ);

}
// NK <--
// ORZEL SECTION :), DELETE /*
/*if (bStrand) fK = fK * stf(rCharacter.TmpSkill.);
if (fWindDotShip < -0.98) arCharShip.MaxSpeedZ = 0.0;*/
// ORZEL SECTION :), DELETE */

// calculate MaxSpeedY
float	fTRFromWeight = Clampf(1.0 - stf(GetLocalShipAttrib(arCharShip, rShip, "TurnDependWeight")) * fLoad);
// NK -->
float	fTRFromSpeed = (1.0 - stf(GetLocalShipAttrib(arCharShip, rShip, "TurnDependSpeed"))) + stf(GetLocalShipAttrib(arCharShip, rShip, "TurnDependSpeed")) * (Clampf((abs(fCurrentSpeedZ) / fMaxSpeedZ))); // fixed 05-04-10 NK
// NK <--
Please note that I am not a programmer and this is all very early stuff. This also probably wont work in arcade mode since it relies on luff sound and closest point, which are part of realistic mode. Then again, in arcade mode the AI doesnt sail backwards in the wind! I should also warn people that until I learn to use character attributes (eg arCharShip.stormimp in the storm code; anyone know how to set these and have them keep their value?) or some other way of storing information about each ship people should not expect miraculous tacking performance from the AI, particularly in large ships, since it is difficult to keep them on one side of the wind for any length of time.

Also, I must give credit to NK and LDH whose storm rotate and luff sound functions I largely used to make this. I dont want to take any credit really, and of course if anyone wants to reuse/change/modify/use without permission any of this, for whatever purpose, feel free to do so.

I am really just dedicated to getting these damn AI ships not going backwards anymore! xD:

I havent tested this version too much, I have been making many changes but this simpler one works best until i resolve storing variables issues. Hopefully it all works and doesnt create new issues.

EDIT: Note updated version in posts below.
 
Thank you for this. :2up But! Flayed one has also just released a new AIShip.c file. Could you two gentlemen get together and merge your files? :whipa
 
Thank you for this. :2up But! Flayed one has also just released a new AIShip.c file. Could you two gentlemen get together and merge your files? :whipa

Sure, thats no problem. Ill just download his and insert the code in. Btw, a better version now with ships that furl their sails to 0.25 sail state (good balance between turning and speed loss).

Again, these are no nautical masters. But at least between the La Couronne, Single deck corvette, caravel, brig and 56 gun 4th rate that ive tried it they make actual headway against the wind, rather than sailing backwards. Ships with poor turning but good speed seem to do best because those with good turning quickly just face into the wind again. The caravel and la Couronne seemed to do better than the brig.

Code:
//TM --> Detect when non player ship is sailing into wind and use rotate function to turn it out of wind
float fTacking2 = 0;

if (iCharacterIndex != GetMainCharacterIndex()) {
if(fOffWind < fClosestPoint) {
if (fSailState > 0.0) {	
Ship_SetSailState(iCharacterIndex, 0.25);
Log_SetStringToLog("Sail State" + fSailState + ".");
float fTacking = stf(arCharShip.Speed.y);
if(fTacking <= 0) {fTacking2 = -0.001; arCharShip.Impulse.Rotate.y = fTacking2; Log_SetStringToLog("Tacking Left");}; //Left		
else {fTacking2 = 0.001; arCharShip.Impulse.Rotate.y = fTacking2; Log_SetStringToLog("Tacking Right");}; //Right				
} else {
arCharShip.Impulse.Rotate.y = 0
fTacking = 0
fTacking2 = 0
}
} else {
arCharShip.Impulse.Rotate.y = 0
fTacking = 0
fTacking2 = 0
}
}
// <--


Here it is - combined AIship.c with Flayed One's changes and Tacking ships (This is Flayed One's AIShip.c as a base with my code merely inserted in the luff sound code)

Please let me know how you go with the tacking ships, especially any problems. I havent tested much but with this version after having the ships furl sails and change sail states (thankyou for showing me how Flayed One) I would say the AI tacks quite well, at least considering the simplicity of this function. It is nothing more than a hack after all!
 
I also VERY much need a way to set ship specific globals. A counter of sorts. Right now, my floats are getting wiped each time the thing runs and setting a global in sea.c is useless since it is not ship specific. This has very much limited the functionality because while i have been able and know how to get ships to, eg, sail OFF the wind for a while THEN cross it, because the process is direction specific I need a way to 'store' decisions about each ship. Without this, i have been able to get ships to cross the wind continually - aka 'tacking' - but its not nearly as effective as i could make it.

If I understand you correctly, you mean you need an attribute - an object/value specific to a ship? If that is so, just do something like that:

arCharShip.MyAttribute = whatever you want it to hold.

Remember to use 'bool CheckAttribute(arCharShip, "MyAttribute")' before getting the value to avoid situations where a ship doesn't have MyAttribute yet. To delete it, use 'DeleteAttribute(arCharShip,"MyAttribute")'.

Hope this helps,

Good luck ;)

-Edit- I have one good news and one bad news;)
Good - It works! I saw some ships going against wind.... :dance
Bad - The game crashes if it tries to:
a) load a game saved when some ships tacked
b) Sail Ho while some ships tack
c) Land Ho if it spawns some ships going against the wind
Do you experience it too? Perhaps speed isn't yet assigned?
 
I also VERY much need a way to set ship specific globals. A counter of sorts. Right now, my floats are getting wiped each time the thing runs and setting a global in sea.c is useless since it is not ship specific. This has very much limited the functionality because while i have been able and know how to get ships to, eg, sail OFF the wind for a while THEN cross it, because the process is direction specific I need a way to 'store' decisions about each ship. Without this, i have been able to get ships to cross the wind continually - aka 'tacking' - but its not nearly as effective as i could make it.

If I understand you correctly, you mean you need an attribute - an object/value specific to a ship? If that is so, just do something like that:

arCharShip.MyAttribute = whatever you want it to hold.

Remember to use 'bool CheckAttribute(arCharShip, "MyAttribute")' before getting the value to avoid situations where a ship doesn't have MyAttribute yet. To delete it, use 'DeleteAttribute(arCharShip,"MyAttribute")'.

Hope this helps,

Good luck ;)

-Edit- I have one good news and one bad news;)
Good - It works! I saw some ships going against wind.... :dance
Bad - The game crashes if it tries to:
a) load a game saved when some ships tacked
b) Sail Ho while some ships tack
c) Land Ho if it spawns some ships going against the wind
Do you experience it too? Perhaps speed isn't yet assigned?

Ahh! Thanks. I was using archarship.myattribute = xx correctly, but it wasnt working! No matter what i did, my attribute read = 1 with log output. I guess i didnt initialize it. Ill try that

On the crashes - short answer is that ive even been able to load a savegame where ships were facing against the wind fine (but they struck sails as soon as loaded, and i had to tell them to follow me - they then tacked fine).

On sail ho while ships tack - i havent tried that, nor land ho. I will have to try testing this to see if i can reproduce those. Both of you seem to agree its a problem though

Its a fairly simple script so i wonder what it could be


EDIT: Ok i definitely get the crash on sail ho!

I am thinking it might be one of the variables i use, since i reuse them from the luff sound - so i am defining them all myself. Would the following do the trick? I just copied these from their respective functions.

Code:
float fTacking2 = 0;
float	fClosestPointtack = DEFAULT_CLOSESTPOINT;
if (CheckShipAttribute(arCharShip, rShip, "ClosestPoint")) fClosestPointtack = stf(GetLocalShipAttrib(arCharShip, rShip, "ClosestPoint"));
float	fWindAngletack = Whr_GetWindAngle();
float	fShipAngletack = stf(arCharShip.Ang.y);
float fOffWindtack = RS_CalcOffwind(fWindAngletack, fShipAngletack);

Thing is though - where is fsailstate defined? Is it acceptable to simply refer to the current value of fsailstate without defining it? I also found the following definition
Code:
float fSailState = GetEventData();

EDIT 2: Dont need to define all these variables actually, its a set sail issue.
 
Crashes CONFIRMED and MITIGATED - Crashes on Sail Ho, Land Ho and load at sea. New version without AI furling sails and thus crashes.

I also had crashes on load at sea, sail ho and land ho - all were solved by removing the following command:
Code:
Ship_SetSailState(iCharacterIndex, 0.25)
Im obviously not setting sails correctly. Can anyone look over how im using the set sail command and see anything wrong?

In the meantime, here is a version, based again on Flayed One's aiship.c, WITHOUT the set sail command. No crashes experienced but AI no longer furl sails into the wind - this hurts their performance somewhat (until i resolve the set sail issue) but they still make headway into the wind and perform acceptably.

EDIT: Did a bit of testing, sailed around the archipelago and often ordered my ships to fight upwind. I can say that even if limited because i have yet to implement sails furling and more advanced tacking, AI ships can still tack and this appreciable improves their performance overall - often their criss crossing of the wind opens an angle where they no longer need to tack; whereas in stock they would go backwards and never reach that angle. Tacking opens up new battle situations - i just recently cornered an upwind War Pinnace with 2 Heavy East Indiamen, a 56 Gun 4th rate and a corvette. Of these, the 56 gun seemed to tack best, but all made headway upwind (as did the Pinnace, who found himself wedged between us in a crescent and a cliff) - it was a slow but convincing battle, and thats the idea.

Ive deleted the old AIship.c attachments in the posts above to fit within the forum 500kb limit. Below is a link to the new AIship.c (without AI furling sails, using Flayed One's AIship.c) and ive also attached it
http://www.filedropper.com/aiship
 
Success! - Crash issues WORKAROUND FOUND, allows for version where AI furl sails again

By using fSeaExpTimer, which starts whenever a load occurs (including sail ho, land ho and load) and delaying my script until this timer reaches at least 5 (5 seconds), I have been able to continue using Ship_SetSailState without encountering the crash issues you guys reported (so far). Its a hack though, and the underlying issue with using Ship_SetSailState is still beyond me - all i know is that if this runs early enough in the load process, the value or something else it relies on must not be created or something, and a crash occurs. Trying to set sails through fSailState = xx doesnt seem to work., so its important to this script that I can use Ship_SetSailState.

With this I can now continue to have AI furl sails and yet avoid crashes. Ie the functionality is the same as the improved version i posted earlier.

So without further adieu; here is what i hope is a crash free, working first version. Much functionality is still to come but hopefully this will do for now.

Please report any crashes, and also any performance hits etc - again, I am not a programmer and dont know how often the script should run etc - I could probably build in a timer though.
 

Attachments

  • AIShip.c
    195.3 KB · Views: 127
That's one tough cookie to bite.

You're right that it's the setsailstate function. More precisely it's this line that causes crashes:
Code:
SendMessage(&AISea, "laf", AI_MESSAGE_SHIP_SET_SAIL_STATE, &Characters[iCharacterIndex], fSailState);

I though that maybe AISea isn't there yet, so I tried keeping track of it with a global bool, but the result was still the same - crashes. Still, I havent seen a crash when I ordered a change of sail state on my ship, so the function works correctly when used by BattleInterface.

A non elegant solution, but a solution nonetheless, would be to only call ship_setsailstate when a battle interface is on.
 
That's one tough cookie to bite.

You're right that it's the setsailstate function. More precisely it's this line that causes crashes:
Code:
SendMessage(&AISea, "laf", AI_MESSAGE_SHIP_SET_SAIL_STATE, &Characters[iCharacterIndex], fSailState);

I though that maybe AISea isn't there yet, so I tried keeping track of it with a global bool, but the result was still the same - crashes. Still, I havent seen a crash when I ordered a change of sail state on my ship, so the function works correctly when used by BattleInterface.

A non elegant solution, but a solution nonetheless, would be to only call ship_setsailstate when a battle interface is on.

Thanks for your help, its invaluable! Thats a very good workaround; I will put that in the next version for redundancy along with my current workaround. Per above my workaround right now is to use fSeaExpTimer to stop the script doing anything until 5 seconds have passed - in practice the issue occurs in the first 1 second when the game is loading, 5 seconds is overkill. Between a delayed start on load due to fSeaExpTimer and Battleinterface being loaded, the crashes should never rear their ugly heads again!

Im still stumped on character attributes, they just dont seem to work at all.
 
Thanks for your help, its invaluable! Thats a very good workaround; I will put that in the next version for redundancy along with my current workaround. Per above my workaround right now is to use fSeaExpTimer to stop the script doing anything until 5 seconds have passed - in practice the issue occurs in the first 1 second when the game is loading, 5 seconds is overkill. Between a delayed start on load due to fSeaExpTimer and Battleinterface being loaded, the crashes should never rear their ugly heads again!

The timer is a totally acceptable solution :yes
Still, you could only limit sail furling by the timer, and left everything else always active ;)

Im still stumped on character attributes, they just dont seem to work at all.

Could you post a piece of code when they don't work as you'd expect them to do?
 
The timer is a totally acceptable solution :yes
Still, you could only limit sail furling by the timer, and left everything else always active ;)

I definitely could have, and was about to make an if (fSeaExpTimer > 5) { Set_Sail_State ..) limit. However, I then stopped and thought - this script is loading right at the start in any load, and at least one function has already crapped out - what if its not the only one? Will 5 seconds of no tacking hurt the AI significantly, in battles or otherwise?

The conclusion I came to was that it was probably better and more redundant to make the whole script wait 5 seconds, especially as there is really no appreciable difference in how the AI performs.

Could you post a piece of code when they don't work as you'd expect them to do?

Yes, I have many attempts (and failures) at this. I will find one and post it.

EDIT: Heres an early attempt. Im not sure if i was testing here or if this was intended to work, but either way i just couldnt set that darn attribute to hold the direction (as 1 or 2).
Code:
//TM --> Detect when non player ship is sailing into wind and use rotate function to turn it out of wind
int ftackdirection = 0;

if (iCharacterIndex == GetMainCharacterIndex()) {
if(fOffWind < fClosestPoint) {
if (fSailState > 0.1) {	
if (ftackdirection == 0) {
if(frnd() <0.5) {ftackdirection = 1}
else {ftackdirection = 2}					
} else 
float fTacking = stf(arCharShip.Impulse.Rotate.y); 
if(frnd() < 0.5) {
if(CheckAttribute(arCharShip,"tackimp")) {
if (arCharShip.tackimp > 0.01) { arCharShip.tackimp = 0.01; }
if (arCharShip.tackimp < -0.01) { arCharShip.tackimp = -0.01; }
arCharShip.Impulse.Rotate.y = arCharShip.tackimp;
DeleteAttribute(arCharShip,"tackimp");
}
} else {			
if(!CheckAttribute(arCharShip,"tackimp")) {
if (ftackdirection == 1) {arCharShip.tackimp = fTacking; fTacking += -0.02;}
else if (ftackdirection == 2) {arCharShip.tackimp = fTacking; fTacking -= -0.02;} 
if (fTacking > 0.01) { fTacking = 0.01; }
if (fTacking < -0.01) { fTacking = -0.01; }
arCharShip.Impulse.Rotate.y = fTacking;
}
}	
} else {
arCharShip.Impulse.Rotate.y = 0
fTacking = 0
arCharShip.tackimp = 0
DeleteAttribute(arCharShip,"tackimp")
}
} else {
ftackdirection = 0
arCharShip.Impulse.Rotate.y = 0
fTacking = 0
arCharShip.tackimp = 0
DeleteAttribute(arCharShip,"tackimp")			
}
}
// <--

This is largely based off of NK's storm rotate code, also in AIship.c - which begs the question - if his works, why doesnt mine?
Code:
// NK -->
/*float fRotate = stf(arCharShip.Impulse.Rotate.y) + (frnd() * 0.08 - 0.04);
if (fRotate > 0.07) { fRotate = 0.07; }
if (fRotate < -0.07) { fRotate = -0.07; }*/
float fRotate = stf(arCharShip.Impulse.Rotate.y);
if(frnd() < 0.5)
{
if(CheckAttribute(arCharShip,"stormimp"))
{
arCharShip.Impulse.Rotate.y = arCharShip.stormimp;
DeleteAttribute(arCharShip,"stormimp");
}
}
else
{
if(!CheckAttribute(arCharShip,"stormimp"))
{
switch(rand(9))
{
case 0: arCharShip.stormimp = fRotate; fRotate -= 0.02; break;
case 1: arCharShip.stormimp = fRotate; fRotate -= 0.02; break;
}
arCharShip.Impulse.Rotate.y = fRotate;
}
}
// NK <--
 
Back
Top