
// ccc Nov 06 directsailing without worldmap

// ----------------------- Tweaks and settings --------------------------------------
#define DIRECTENCOUNTERCHANCE 50   // chance in percent that an encounter fleet appears every hour
#define DIRECTENCOUNTERDISTANCE 1000 // distance from player at which random ships appear 

#define ACCURATE_NAVIGATION 0 // Jan07: Screwface found a method to determine the worldmapcoords of a ship sailing in 3D seaview.
// That makes accurate navigation possible. However, due to the confined worldmap voyages are rather short, and sometimes you appear BEHIND a coast.
// To try this set this to 1, with 0 you'll use the old "islandcell" islandhopping navigation

#define ISLANDSWITCHDISTANCE 5000.0 // for islandcell mode only: distance from island at which transfer to neighbour island starts
// distance set for testing, should be at least 10000 for playing !!!

#define ENCOUNTERBREAK 1 // 2 will trigger the flotsam/shipencounters only every second hour (3 every third...). 
// so you can let the clock run faster (by increasing #define TIMESCALAR_SEA in internalSettings.h ), meaning longer voyagetimes, without getting too many encounters.

#define MINES 1	// set to 0 to disable enconters with random mines


//------------------------ Initial checks and trigger -----------------------------

bool DirectsailCheck()  // called hourly by Whr_UpdateWeather
// ever growing list of conditions when Directsail shall NOT run
{
	ref pchar = GetMainCharacter();

	if( CheckAttribute(pchar,"nodirectsail") ){Traceandlog("Directsail toggled, press 0 to enable"); return false;} // ccc Jan07, Directsail can be toggled

	//MAXIMUS -[reload to the WorldMap not needed anymore]->
	if(!CheckQuestAttribute("StartAdventure", "begin"))
	{
		if(!CheckAttribute(pchar,"quest.StartAdventure.counter")) pchar.quest.StartAdventure.counter = 1;
		else pchar.quest.StartAdventure.counter = sti(pchar.quest.StartAdventure.counter)+1;
		if(sti(pchar.quest.StartAdventure.counter)==2)
		{
			pchar.quest.StartAdventure.win_condition.l1 = "Timer";
			pchar.quest.StartAdventure.win_condition.l1.date.min = GetMinute()+Rand(sti(50-GetMinute())+10);
			pchar.quest.StartAdventure.win_condition.l1.date.hour = GetHour();
			pchar.quest.StartAdventure.win_condition.l1.date.day = GetDataDay();
			pchar.quest.StartAdventure.win_condition.l1.date.month = GetDataMonth();
			pchar.quest.StartAdventure.win_condition.l1.date.year = GetDataYear();
			pchar.quest.StartAdventure.win_condition = "StartAdventure";
		}
	}

	if(CheckAttribute(pchar,"quest.Story_OxbayCaptured"))
	{
		aref cQuest = GetQuestData("Story_OxbayCaptured");
		if(!CheckQuestRecord(cQuest, "2"))
		{
			if(!CheckAttribute(pchar,"quest.Story_InvasionVideoAfterLeaveOxbay.counter")) pchar.quest.Story_InvasionVideoAfterLeaveOxbay.counter = 1;
			else pchar.quest.Story_InvasionVideoAfterLeaveOxbay.counter = sti(pchar.quest.Story_InvasionVideoAfterLeaveOxbay.counter)+1;
			if(sti(pchar.quest.Story_InvasionVideoAfterLeaveOxbay.counter)==2)
			{
				pchar.quest.Story_InvasionVideoAfterLeaveOxbay.win_condition.l1 = "Timer";
				pchar.quest.Story_InvasionVideoAfterLeaveOxbay.win_condition.l1.date.min = GetMinute()+Rand(sti(50-GetMinute())+10);
				pchar.quest.Story_InvasionVideoAfterLeaveOxbay.win_condition.l1.date.hour = GetHour();
				pchar.quest.Story_InvasionVideoAfterLeaveOxbay.win_condition.l1.date.day = GetDataDay();
				pchar.quest.Story_InvasionVideoAfterLeaveOxbay.win_condition.l1.date.month = GetDataMonth();
				pchar.quest.Story_InvasionVideoAfterLeaveOxbay.win_condition.l1.date.year = GetDataYear();
				pchar.quest.Story_InvasionVideoAfterLeaveOxbay.win_condition = "Story_InvasionVideoAfterLeaveOxbay";
			}
		}
	}
	//MAXIMUS <-[reload to the WorldMap not needed anymore]-

	// Navigation point "Battle Rocks" is added to worldmap if not yet done
	if(!CheckAttribute(worldMap,"islands.Battle_Rocks.name") ) DirectsailAddBattle_Rocks();

	if(pchar.location=="") // Jan07 open sea without island
	{
		Logit("No island within reach");  // Jan07, hint added
		Logit("Enter the map to sail to some island");
		return false;	// aborts function if location is open sea without island
	}

	// Jan07, aborts if you are too close to the coast 
	float coastzone = GetIslandSize(pchar.location)+1000;
	if( abs(stf(pchar.Ship.Pos.x))<coastzone && abs(stf(pchar.Ship.Pos.z))<coastzone )
	{Trace("Directsail aborted, too close to coast"); return false;}


  // aborts function if enemyships near, so that you aren't teleported out of an engagement
  int enemydist = 0;
  int nextenemy = 0;
  nextenemy = FindClosestShipofRel(GetMainCharacterIndex(), &enemydist, RELATION_ENEMY);
  Trace("DirectsailCheck; next enemy: "+nextenemy + " dist: "+enemydist);
  if(nextenemy!= -1 && enemydist<1000 ) {Trace("Directsail aborted due to hostile ship"); return false;}

  // Jan 07, same for neutral ships
  nextenemy = FindClosestShipofRel(GetMainCharacterIndex(), &enemydist, RELATION_NEUTRAL);
  Trace("DirectsailCheck; next neutral ship: "+nextenemy + " dist: "+enemydist);
  if(nextenemy!= -1 && enemydist<1000 ) {Trace("Directsail aborted due to neutral ship"); return false;}
  
  // looks like this doesn't always work, so I added another check for being in battle
  if(!bMapEnter) {Trace("Directsail aborted in battle"); return false;}

  if(CheckAttribute(pchar, "sailtostring") ){Trace("Directsail aborted during sail to"); return false;}
    // ccc Dec17, aborts Directsail if you use the "sail to" interfaceoption

	if(CheckAttribute(pchar,"IsOnDeck") && pchar.IsOnDeck==true) {Trace("Directsail aborted below decks"); return false;}
	// Maximus Jan07; aborts Directsail if you are below decks 

  // once all those conditions are checked we trigger an event that runs DS some seconds later
	SetEventHandler("DirectsailRun", "DirectsailRun", 1);  // Jan 07,
	int delay = 5000 + rand(60000);  // Jan 07, random delaytime in millisec
	if(CheckAttribute(GetMainCharacter(),"mapEnter")) { delay = 500; }//MAXIMUS: check added into BattleInterface for islands search after enable MapEnter
	PostEvent("DirectsailRun", delay);  // Jan 07, calls DirectsailRun() after delay to create break

	return true;
}



void DirectsailRun()  // Jan 07, taken out of DirectsailCheck() to create break
{
	DelEventHandler("DirectsailRun", "DirectsailRun");

	ref pchar = GetMainCharacter();

	// check if islandchange takes place
	bool islandswitch;
	if(ACCURATE_NAVIGATION){islandswitch = DirectIslandCoordCheck();} // use Screwface's mapcoord method
	else {islandswitch = DirectIslandCellCheck();} // use islandcell method

	if(islandswitch)
	{
		// islandchange
		// some eyecandy
		CreateEntity(&SeaFader, "fader");
		SendMessage(&SeaFader, "ls", FADER_PICTURE0, FindReloadPicture("LandHo.tga")); // KK
		SendMessage(&SeaFader, "lfl", FADER_IN, 0.5, true);
		PlaySound("land_ho");
		pchar.directsail.encounter = 1; // for new ships at new island

		// Jan 07 time advanced to simulate longer voyage
		int traveldays = (GetIslandRemoteness(pchar.location)+GetIslandRemoteness(pchar.directsail.toisland))/1000 +2;
		WaitDate("", 0, 0, traveldays, 0, 0);

		Sea_ReloadStartDirect();	// reloads Sea with new island at horizon
	}
	else
	{
		if(CheckAttribute(GetMainCharacter(),"mapEnter")) { DeleteAttribute(GetMainCharacter(),"mapEnter"); return; }//MAXIMUS: prevents from uninterrupted encounters (check added into BattleInterface for islands search after enable MapEnter)
		// no islandchange, instead random check if an shipencounter or flotsam shall appear
    
		// Jan07 counter that allows us to set encounterfrequency independent of clockspeed/hourly weatherchange
		if(!CheckAttribute(pchar,"directsail.count")) pchar.directsail.count = 0;
		pchar.directsail.count = sti(pchar.directsail.count) + 1;
		if(sti(pchar.directsail.count)<ENCOUNTERBREAK) { Trace("Directsail aborted, encounterbreak"+pchar.directsail.count); return; }


		if(DIRECTENCOUNTERCHANCE>rand(100)) // chance check for shipencounter
		{
			// encounter markerattribute is attached to player
			pchar.directsail.encounter = 1;

			CreateEntity(&SeaFader, "fader");
			SendMessage(&SeaFader, "ls", FADER_PICTURE0, FindReloadPicture("SailHo.tga")); // KK
			SendMessage(&SeaFader, "lfl", FADER_IN, 0.5, true);
			PlaySound("sail_ho");
			Sea_ReloadStartDirect();	// reloads Sea with new ships at horizon
		}
	}
	else { Randomshipevent(); }	// random shiplife events //MAXIMUS: was missed ";"
	if(CheckAttribute(GetMainCharacter(),"mapEnter")) { DeleteAttribute(GetMainCharacter(),"mapEnter"); }//MAXIMUS: check added into BattleInterface for islands search after enable MapEnter
}

// ----------------------------- Island check using worldmap coords --------------------

bool DirectIslandCoordCheck()  // called by DirectsailCheck()
//  Jan07 , uses Screwface's new worldmap coord check
{
	ref pchar = GetMainCharacter();
	Trace("directsail runs at " +  pchar.location );

	string toislandname = GetMapIslandzone(pchar.location); // the islandzone you may have sailed into

	if(toislandname != "")
	// if there is a neighbouring island in the leaving direction	
	{
		pchar.directsail.toisland = toislandname; // neighbour island stored as playerattribute for Sea_ReloadDirect function
		// Sea_ReloadDirect & SeaLoginDirect will later recognize from that player attribute that you left OldIsland
    // and delete the current island from the sea environment. The neighbour island is added
    // and you will be placed so that it looks as if you had arrived from the OldIsland.

		Traceandlog("Captain, " + toislandname + " in sight ");

		return true;	// returns that islandchange has taken place
	}
	else
	{
		Trace("DirectIslandCoordCheck false ");	
		return false;	// returns that NO islandchange has taken place
	}
}

string GetMapIslandzone(string Island)
// Screwface Jan07, converts your Seaview coords to mapcoords and checks if you are near some island
{
	ref Pchar = GetMaincharacter();

	float psX = MakeFloat(pchar.Ship.Pos.x);
	float psZ = MakeFloat(pchar.Ship.Pos.z);

	float ix = MakeFloat(worldMap.islands.(Island).position.rx);
	float iz = MakeFloat(worldMap.islands.(Island).position.rz);


	//REAL CONVERTION OF YOUR SEAVIEW COORDS IN WORLD MAP COORDS
	worldMap.playerShipX = (psX/WDM_MAP_TO_SEA_SCALE) + ix;
	worldMap.playerShipZ = (psZ/WDM_MAP_TO_SEA_SCALE) + iz;

	float pmX = MakeFloat(worldMap.playerShipX);
	float pmZ = MakeFloat(worldMap.playerShipZ);

	// AFTER THAT YOU CHECK IF YOU ARE IN THE RANGE OF ANOTHER ISLAND ON THE WORLDMAP
	for(int i=0; i<ISLANDS_QUANTITY; i++)
	{
		string isName = Islands[i].id;
		if(!CheckAttribute(worldMap,"islands."+isName)) continue;
		float isX = stf(worldMap.islands.(isName).position.x);
		float isY = stf(worldMap.islands.(isName).position.y);
		float isZ = stf(worldMap.islands.(isName).position.z);
		float isR = stf(worldMap.islands.(isName).radius);
		if(pmX >= isX - isR && pmX <= isX + isR && pmZ >= isZ - isR && pmZ <= isZ + isR && Island != isName) { return isName; }
	}

	trace("At mapcoords x " + pmX + " z "+pmZ+" you are in no new islandzone")
	return ""; // default
}



// ----------------------------- Island check using island cells --------------------

bool DirectIslandCellCheck()  // called by DirectsailCheck(), based on islandcells
// checks if we are so far from an island that location switches to the next island
// if yes playerattributes for new island and new coords are set
{
	ref pchar = GetMainCharacter();
	Trace("DirectIslandCellCheck runs at " +  pchar.location );

	string dir = "";
	string attr;

	// info on the Build13 ship coordsystem: positive z coords mean South, -z North, +x is West, -x East
	// the center of the coordsystem  (x=0, z=0) is also the center of the islandmodel
	float psX = MakeFloat(pchar.ship.pos.x);
	float psZ = MakeFloat(pchar.ship.pos.Z);

	float swdist = ISLANDSWITCHDISTANCE + GetIslandSize(pchar.location) + GetIslandRemoteness(pchar.location);  // ccc Jan07
	// GetIslandSize is the aprox size(coastradius) of the island
	// GetIslandRemoteness increases that for remote islands so that sailing to them takes longer
	// we add the distance after which we want to switch to the next island and get the swdist from islandmodelcenter (x=0, z=0)

	// several checks if the player left the vicinity of the island, and in which direction
	if(psX < -swdist) //east
	{
		dir="east";	// you leave the OLD island eastbound

		if(psZ < -(swdist*3/5)) // AND north
		{dir="northeast";}	// you are ALSO north, i.e. northeast

		if(psZ > swdist*3/5) // AND south
		{dir="southeast";}
	}

	if(psX > swdist) //west
	{
		dir="west";

		if(psZ < -(swdist*3/5)) // AND north
		{dir="northwest";}

		if(psZ > swdist*3/5) // AND south
		{dir="southwest";}
	}

	if(psZ < -swdist) //north
	{
		dir="north";

		if(psX < -(swdist*3/5)) // AND east
		{dir="northeast";}

		if(psX > swdist*3/5) // AND west
		{dir="northwest";}
	}

	if(psZ > swdist) //south
	{
		dir="south";

		if(psX < -(swdist*3/5)) // AND east
		{dir="southeast";}

		if(psX > swdist*3/5) // AND west
		{dir="southwest";}
	}
	Trace("DirectIslandCellCheck dir is " +  dir );

	if(dir=="")return false;	// aborts if no leaving anywhere was detected

	string toislandname = GetNeighbourIsland(pchar.location, dir); // neighbour island in DIRection

	if(toislandname != "")
	// if there is a neighbouring island in the leaving direction	
	{
		pchar.directsail.toisland = toislandname; // neighbour island stored as playerattribute for Sea_ReloadDirect function
		int NewDist = ISLANDSWITCHDISTANCE + GetIslandSize(toislandname) - 1000.0;
    // distance at which you appear from the CENTER of the neighbour island,
    // minus 1000 so that you don't switch back right away

		// with that we determine the coords at which you appear at the neighbour island
		// (the opposite of the leaving dir: if you leave old island southbound you appear north of new island)
		switch(dir)
		{
		case "North": pchar.directsail.tox= 0; pchar.directsail.toz= +NewDist; break;
		case "Northeast": pchar.directsail.tox= +NewDist; pchar.directsail.toz= +NewDist; break;
		case "Northwest": pchar.directsail.tox= -NewDist; pchar.directsail.toz= +NewDist; break;

		case "South": pchar.directsail.tox= 0; pchar.directsail.toz= -NewDist; break;
		case "Southeast": pchar.directsail.tox= +NewDist; pchar.directsail.toz= -NewDist; break;
		case "Southwest": pchar.directsail.tox= -NewDist; pchar.directsail.toz= -NewDist; break;

		case "East": pchar.directsail.tox= +NewDist; pchar.directsail.toz= 0; break;
		case "West": pchar.directsail.tox= -NewDist; pchar.directsail.toz= 0; break;
		}

		Trace("pchar.directsail.tox " + pchar.directsail.tox + " pchar.directsail.toz " + pchar.directsail.toz );

		// those data are now stored as playerattributes.
		// Sea_ReloadDirect & SeaLoginDirect will recognize from those player attributes that you left OldIsland to the leavingdirection dir
    // and delete the current island from the sea environment. The neighbour island is added
    // and you will be placed so that it looks as if you had arrived from the OldIsland.

		Traceandlog("Captain, " + toislandname + " to the " + dir );

		// Change worldmap coordinates accordingly
		// Some strange spelling in worldmap_init must be taken into account:

		// this converts the seaview position into map coords (still inaccurate)
 		worldMap.playerShipX = stf(worldMap.islands.(toislandname).position.x)+(stf(pchar.directsail.tox)/60); //divisor needs adjusting for final version
		worldMap.playerShipZ = stf(worldMap.islands.(toislandname).position.z)+(stf(pchar.directsail.toz)/60); // increase to get closer to the island
		Trace("Player map coord set to x: " + worldMap.playerShipX + " , z : " + worldMap.playerShipZ );

		return true;	// returns that islandchange has taken place
	}
	else
	{
		Trace("DirectIslandCellCheck false ");	
		return false;	// returns that NO islandchange has taken place
	}
}

string GetNeighbourIsland(string Island, string dir)
    // returns which neighbour island lies in the DIRection you leave an Island
    // could also be done in Islands_init.c, but this way I can leave that file undisturbed
{
	//MAXIMUS: translation -->
	//int tmpLangFileID = LanguageOpenFile("interface_strings.txt");
	string cptHint = TranslateString("","Captain, a hint:")+" ";
	string direcTo = TranslateString("",dir)+" ";
	string drcFrom = TranslateString("","of"+"_island")+" ";
	string direcIs = TranslateString("",Island)+" ";
	string drcLies = TranslateString("","lies nothing but open sea!");
	//LanguageCloseFile(tmpLangFileID);
	//MAXIMUS: translation <--

	switch(Island)
	{

		case "FalaiseDeFleur":
  		switch(dir)
  		{
  		case "North": return "Battle_Rocks"; break;
  		case "Northeast": return "Guadeloupe"; break;
  		case "Northwest": return "Battle_Rocks"; break;
  		case "East": return "Antigua"; break;
  		case "West": return "Oxbay"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "Redmond":
  		switch(dir)
  		{
  		case "North": return "Cuba"; break;
  		case "Northeast": return "Hispaniola"; break;
  		case "Northwest": return "Cayman"; break;

  		case "South": return "Battle_Rocks"; break;
  		case "Southeast": return "IslaMuelle"; break;
  		case "Southwest": return "Douwesen"; break;

  		case "East": return "Hispaniola"; break;
  		case "West": return "Cayman"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "IslaMuelle":
  		switch(dir)
  		{
  		case "North": return "Hispaniola"; break;
  		case "Northwest": return "Redmond"; break;

  		case "South": return "QuebradasCostillas"; break;
  		case "Southeast": return "SaintMartin"; break;
  		case "Southwest": return "Battle_Rocks"; break;

  		case "West": return "Battle_Rocks"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "Oxbay":
  		switch(dir)
  		{
  		case "North": return "Battle_Rocks"; break;
  		case "Northeast": return "Battle_Rocks"; break;
  		case "Northwest": return "Conceicao"; break;
   		case "East": return "FalaiseDeFleur"; break;
  		case "West": return "Conceicao"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "Conceicao":
  		switch(dir)
  		{
  		case "North": return "Douwesen"; break;
  		case "Northeast": return "Battle_Rocks"; break;
  		case "Northwest": return ""; break;
  		case "Southeast": return "Oxbay"; break;
  		case "East": return "Oxbay"; break;
  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "Douwesen":
  		switch(dir)
  		{
  		case "North": return "KhaelRoa"; break;
  		case "Northeast": return "Redmond"; break;
  		case "South": return "Conceicao"; break;
  		case "Southeast": return "Oxbay"; break;
  		case "East": return "Battle_Rocks"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "QuebradasCostillas":
  		switch(dir)
  		{
  		case "North": return "IslaMuelle"; break;
  		case "Northeast": return "IslaMuelle"; break;
  		case "Northwest": return "Battle_Rocks"; break;

  		case "South": return "Antigua"; break;
  		case "Southwest": return "Guadeloupe"; break;

  		case "East": return "SaintMartin"; break;
  		case "West": return "Guadeloupe"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "KhaelRoa":
  		switch(dir)
  		{
  		case "South": return "Douwesen"; break;
  		case "Southeast": return "Battle_Rocks"; break;
  		case "East": return "Cayman"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "Cayman":
  		switch(dir)
  		{
  		case "Northeast": return "Cuba"; break;
  		case "South": return "Battle_Rocks"; break;
  		case "Southeast": return "Redmond"; break;
  		case "Southwest": return "Douwesen"; break;
  		case "East": return "Cuba"; break;
  		case "West": return "KhaelRoa"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "Hispaniola":
  		switch(dir)
  		{
  		case "North": return "Turks"; break;
  		case "Northwest": return "Cuba"; break;
  		case "South": return "IslaMuelle"; break;
  		case "Southeast": return "IslaMuelle"; break;
  		case "Southwest": return "Battle_Rocks"; break;
  		case "West": return "Redmond"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "Cuba":
  		switch(dir)
  		{
  		case "South": return "Redmond"; break;
  		case "Southeast": return "Hispaniola"; break;
  		case "Southwest": return "Cayman"; break;

  		case "East": return "Turks"; break;
  		case "West": return "Cayman"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "Guadeloupe":
  		switch(dir)
  		{
  		case "North": return "Battle_Rocks"; break;
  		case "Northeast": return "QuebradasCostillas"; break;
  		case "Northwest": return "Battle_Rocks"; break;

  		case "Southeast": return "Antigua"; break;
  		case "Southwest": return "FalaiseDeFleur"; break;

  		case "East": return "QuebradasCostillas"; break;
  		case "West": return "FalaiseDeFleur"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "SaintMartin":
  		switch(dir)
  		{
  		case "North": return "IslaMuelle"; break;
  		case "Northwest": return "IslaMuelle"; break;
  		case "Southwest": return "Antigua"; break;
  		case "West": return "QuebradasCostillas"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "Antigua":
  		switch(dir)
  		{
  		case "North": return "QuebradasCostillas"; break;
  		case "Northeast": return "SaintMartin"; break;
  		case "Northwest": return "Guadeloupe"; break;
  		case "East": return ""; break;
  		case "West": return "FalaiseDeFleur"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "Turks":
  		switch(dir)
  		{
  		case "South": return "Hispaniola"; break;
  		case "Southwest": return "Hispaniola"; break;
  		case "West": return "Cuba"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;

		case "Battle_Rocks":
  		switch(dir)
  		{
  		case "North": return "Redmond"; break;
  		case "Northeast": return "Hispaniola"; break;
  		case "Northwest": return "Redmond"; break;

  		case "South": return "FalaiseDeFleur"; break;
  		case "Southeast": return "QuebradasCostillas"; break;
  		case "Southwest": return "Oxbay"; break;

  		case "East": return "IslaMuelle"; break;
  		case "West": return "Douwesen"; break;

  		traceandlog(cptHint + direcTo + drcFrom + direcIs + drcLies);//MAXIMUS: changed for translation
  		return "";  // default, no neighbour island in this dir
  		}
    break;
    trace("GetNeighbourIsland couldnt find " + Island)
  	return "";  // default
  }

  trace("GetNeighbourIsland for" + Island + " direction "+dir+" went wrong")
  return "";  // default
}


// -------------------------------- Reloading of sea environment -----------------------------------------

void Sea_ReloadStartDirect()  // called by DirectsailCheck, structure like original Sea_ReloadStart
{
	if (!bSeaActive) { return; }
	DeleteSeaEnvironment();
	
	SetEventHandler("Sea_ReloadDirect", "Sea_ReloadDirect", 0);
	PostEvent("Sea_ReloadDirect", 1.0);
}



void Sea_ReloadDirect() // Jan 07, new version by Screwface that works also with worldmapcoords
// called by Sea_ReloadStartDirect(), structure like original Sea_Reload
{
  DelEventHandler("Sea_ReloadDirect", "Sea_ReloadDirect");

  ref rPlayer = GetMainCharacter();

  object Login;
  Login.PlayerGroup.ay = stf(rPlayer.Ship.Ang.y);
  Login.PlayerGroup.x = MakeFloat(rPlayer.ship.pos.x); 
  Login.PlayerGroup.y = 0.0;
  Login.PlayerGroup.z = MakeFloat(rPlayer.ship.pos.z);
  Login.Island = Characters[0].location;

  if(CheckAttribute(rPlayer,"directsail.toisland"))
  // if Directsail() calls islandchange island and playerposition are changed
  {
    Login.Island = rPlayer.directsail.toisland;
    string toislandname = rPlayer.directsail.toisland;

  	if(ACCURATE_NAVIGATION)  // if worldmapcoords are to be used 
  	{
      // SECTION TO APPEAR IN THE NEW REAL POSITION AT THE NEW ISLAND 
      float psX = MakeFloat(worldMap.playerShipX);
      float psZ = MakeFloat(worldMap.playerShipZ);

      float ix = MakeFloat(worldMap.islands.(toislandname).position.rx);
      float iz = MakeFloat(worldMap.islands.(toislandname).position.rz);
      Login.playerGroup.x = (psX - ix)*WDM_MAP_TO_SEA_SCALE;
      Login.playerGroup.z = (psZ - iz)*WDM_MAP_TO_SEA_SCALE;
    }
    else
    {
      // old islandcell method
  		Login.PlayerGroup.x = stf(rPlayer.directsail.tox);
	   	Login.PlayerGroup.z = stf(rPlayer.directsail.toz);
		}

    Login.PlayerGroup.ay = stf(rPlayer.Ship.Ang.y); // your old heading
  }
  Trace("Sea_reloadDirect at "+ Login.Island );

  SeaLoginDirect(&Login);
}


void SeaLoginDirect(ref Login)  // called by Sea_ReloadDirect(), structure like original SeaLogin
	// reloads Sea with new ships at horizon, and with new island if DirectIslandCheck() was true
{
	int		i, j, iShipType;
	float	x, y, z, ay;
	ref		rCharacter, rGroup, rEncounter;
	aref	rRawGroup;
	aref	arQCGroups;
	string	sGName;
	int		iNumQCGroups;

	int iRDTSC = RDTSC_B();

	// clear load groups now object
	DeleteAttribute(&LoginGroupsNow, "");

	iStormLockSeconds = 0;
	iNumFantoms = 0;
	bSkipSeaLogin = false;
	bSeaReloadStarted = false;
	bSeaQuestGroupHere = false;
	bIslandLoaded = false;

	fSeaExp = 0.0;
	fSeaExpTimer = 0.0;
	// NK 04-09-21 timeupdate
	if(TIMEUPDATE_BLOCK_LAND)
	{
		AddTimeToCurrent(0, (locTmpTime * TIMESCALAR_LAND)/60);
	}
	locTmpTime = 0.0;
	// NK <--

	Sea_FreeTaskList();
	Encounter_DeleteDeadQuestMapEncounters();

	// Island
		int iIslandIndex = FindIsland(Login.Island);
		//Trace("Island id = " + Login.Island + ", Island index = " + iIslandIndex);
		string sIslandID = "";
		if (iIslandIndex != -1) { sIslandID = Islands[iIslandIndex].id; }

	// main character
		ref rPlayer = GetMainCharacter();
		rPlayer.Ship.Stopped = false;
		DeleteAttribute(rPlayer,"LastSailState"); // NK 05-04-20 so it doesn't say it during loading
		rPlayer.Ship.POS.Mode = SHIP_SAIL;
		rPlayer.location = sIslandID;

	// clear old fantom relations in our character
		if (CheckAttribute(rPlayer, "Relation"))
		{
			aref	arRelations; makearef(arRelations, rPlayer.Relation);
			int		iNumRelations = GetAttributesNum(arRelations);
			for (i=0; i<iNumRelations; i++)
			{
				aref arRelation = GetAttributeN(arRelations, i);
				string sRName = GetAttributeName(arRelation);
				if (sti(sRName) >= FANTOM_CHARACTERS)
				{
					DeleteAttribute(arRelations, sRName);
					iNumRelations--;
					i--;
				}
			}
		}

	// Quest check
		Event(EVENT_SEA_LOGIN, "");
		if (bSkipSeaLogin) return;

	// create all sea modules
		CreateSeaEnvironment();

	// delete our group
		Group_DeleteGroup(PLAYER_GROUP);

	// set commander to group
		Group_SetGroupCommander(PLAYER_GROUP, Characters[GetMainCharacterIndex()].id);

	// set our group position
		Group_SetXZ_AY(PLAYER_GROUP, stf(Login.PlayerGroup.x), stf(Login.PlayerGroup.z), stf(Login.PlayerGroup.ay) );
		Trace("Set group : " + PLAYER_GROUP + ", x = " + Login.PlayerGroup.x + ", z = " + Login.PlayerGroup.z + ", ay = " + Login.PlayerGroup.ay);

	Sea.MaxSeaHeight = 50.0;

	ReloadProgressUpdate();
	
	float wdmBaseX, wdmBaseZ; // WM base coords for fleets 05-05-02

	// login island if exist
		if (sIslandID != "")
		{
			trace("SealoginDirect loading island " + sIslandID);
			CreateEntity(&Island, "Island");
			Island.LightingPath = GetLightingPath();

			SendMessage(&SeaLighter, "ss", "ModelsPath", Islands[iIslandIndex].filespath.models);
			SendMessage(&SeaLighter, "ss", "LightPath", GetLightingPath());

			SendMessage(&Island, "lsss", MSG_ISLAND_LOAD_GEO, "islands", Islands[iIslandIndex].filespath.models, Islands[iIslandIndex].model);
			LayerAddObject(SEA_REALIZE, &Island, 65529);
			LayerAddObject("mast_island_trace", &Island, 1);
			LayerAddObject("sun_trace", &Island, 1);

			CreateEntity(&IslandReflModel, "MODELR");
			string sReflModel = Islands[iIslandIndex].filespath.models + "\\" + Islands[iIslandIndex].refl_model;
			SendMessage(&IslandReflModel, "ls", MSG_MODEL_SET_LIGHT_PATH, GetLightingPath());
			SendMessage(&IslandReflModel, "ls", MSG_MODEL_LOAD_GEO, sReflModel);
			LayerAddObject("sea_reflection", &IslandReflModel, -1);
			SendMessage(SeaLighter, "ssi", "AddModel", Islands[iIslandIndex].refl_model, &IslandReflModel);

			bIslandLoaded = true;

			SendMessage(&SeaLocatorShow, "a", &Islands[iIslandIndex]);
			Fort_Login(iIslandIndex);

			Sea.MaxSeaHeight = 3.0;
			// WM base coords for fleets 05-05-02 -->
			// 05-05-03 get correct wdm name for island.
			string wdmisland = wdmGetIslandNameFromID(sIslandID);
			if(wdmisland != "" && CheckAttribute(worldMap,".islands."+wdmisland))
			{
				wdmBaseX = MakeFloat(worldMap.islands.(wdmisland).position.rx);
				wdmBaseZ = MakeFloat(worldMap.islands.(wdmisland).position.rz);
			}
			else { wdmBaseX = makefloat(worldMap.playerShipX); wdmBaseZ = makefloat(worldMap.playerShipZ); }
		}
		else { wdmBaseX = makefloat(worldMap.playerShipX); wdmBaseZ = makefloat(worldMap.playerShipZ); } // WM base coords for fleets 05-05-02 <--
		AISea.Island = sIslandID;

	// clear some of group attributes
		for (i=0; i<MAX_SHIP_GROUPS; i++)
		{
			rGroup = Group_GetGroupByIndex(i);
			if (CheckAttribute(rGroup, "AlreadyLoaded")) { DeleteAttribute(rGroup, "AlreadyLoaded"); }
		}

	ReloadProgressUpdate();

	// from coast check (move / stop)
		bFromCoast = false;
		if (CheckAttribute(&Login, "FromCoast")) { bFromCoast = sti(Login.FromCoast); }

	// login main player and his friends
		int iMainCharacter,iCompanionIndex;
		iMainCharacter = GetMainCharacterIndex();

		rPlayer.SeaAI.Group.Name = PLAYER_GROUP;
		rPlayer.Ship.Type = Characters[iMainCharacter].Ship.Type;
		rPlayer.Ship.Stopped = false;
		Ship_Add2Sea(iMainCharacter, bFromCoast, "");
		trace("added pchar to sea");
		Group_AddCharacter(PLAYER_GROUP, Characters[iMainCharacter].id);
		for (i=1; i<COMPANION_MAX; i++)
		{
			iCompanionIndex = GetCompanionIndex(&Characters[iMainCharacter],i);
			if (iCompanionIndex == -1) { continue; }
			Characters[iCompanionIndex].SeaAI.Group.Name = PLAYER_GROUP;
			Ship_Add2Sea(iCompanionIndex, bFromCoast, "");

			// add companion to player group
			Group_AddCharacter(PLAYER_GROUP, Characters[iCompanionIndex].id);
		}
		//trace("added comps to sea");

	// set ship for sea camera
	  // CCC SeaCameras.Camera = "SeaDeckCamera"; // so that you look FORWARD, spyglass ready
		SeaCameras_SetShipForSeaCamera(&rPlayer);

	// login encounters
		object oResult;
		int iFantomIndex;

	// login quest group if island exist 
		ReloadProgressUpdate();
		
	  if(CheckAttribute(rPlayer,"directsail.toisland"))
  	// if DirectsailCheck() calls islandchange coastal ships are created
	  {
	   	if (AUTOCREATE_CR == 1) { SetCoastTraffic(sIslandID); }
  	}

		if (sIslandID != "")
		{
			for (i=0; i<MAX_SHIP_GROUPS; i++)
			{
				rGroup = Group_GetGroupByIndex(i);
				if (!CheckAttribute(rGroup,"AlreadyLoaded")) { DeleteAttribute(rGroup,"AlreadyLoaded");	} // NK: HUH!?

				if (!CheckAttribute(rGroup, "id")) { continue; }
				if (!CheckAttribute(rGroup, "location")) { continue; }
				if (rGroup.location != sIslandID) { continue; }

				Sea_LoginGroup(rGroup.id);
			}
		}
		// trace("Did any groups");

	// login quest groups to sea
		if (CheckAttribute(&Login, "QuestGroups"))
		{
			arQCGroups; makearef(arQCGroups, Login.QuestGroups);
			iNumQCGroups = GetAttributesNum(arQCGroups);
			for (i=0; i<iNumQCGroups; i++)
			{
				Sea_LoginGroup(GetAttributeValue(GetAttributeN(arQCGroups, i)));
			}
		}

		ReloadProgressUpdate();

	// login quest groups to sea from LoginGroupsNow object
		if (CheckAttribute(&LoginGroupsNow, "QuestGroups"))
		{
			makearef(arQCGroups, LoginGroupsNow.QuestGroups);
			iNumQCGroups = GetAttributesNum(arQCGroups);
			for (i=0; i<iNumQCGroups; i++)
			{
				Sea_LoginGroup(GetAttributeValue(GetAttributeN(arQCGroups, i)));
			}
		}

		ReloadProgressUpdate();

	  if(CheckAttribute(rPlayer,"directsail.encounter")) // if set so in DirectsailCheck() random encounter ships are created
	  {
	    rPlayer.ship.pos.x = stf(Login.PlayerGroup.x); // so that DirectEncounter doesn't use OLD coords
	    rPlayer.ship.pos.z = stf(Login.PlayerGroup.z);
	    DirectEncounter( stf(Login.PlayerGroup.ay) );
	   	DeleteAttribute(rPlayer,"directsail");	// clears tags from player
  	}

	// set tasks 2 all groups
	int c; //NK bugfix 5-1
	for (i=0; i<GetArraySize(&sTaskList)-2; i++)
	{
		string sGroupID = sTaskList[i];

		rGroup = Group_GetGroupByID(sGroupID);

		// set task
		switch (sti(rGroup.Task))
		{
			case AITASK_RUNAWAY:
				Group_SetTaskRunAway(sGroupID);
			break;
			case AITASK_ATTACK:
				Group_SetTaskAttack(sGroupID, rGroup.Task.Target);
			break;
			case AITASK_MOVE:
				if (CheckAttribute(rGroup, "Task.Target.Pos"))
				{
					Group_SetTaskMove(sGroupID, stf(rGroup.Task.Target.Pos.x), stf(rGroup.Task.Target.Pos.z));
				}
				else
				{
					x = 10000.0 * sin(stf(rGroup.Pos.ay));
					z = 10000.0 * cos(stf(rGroup.Pos.ay));
					Group_SetTaskMove(sGroupID, x, z);
				}
			break;
		}

		rCharacter = Group_GetGroupCommanderR(rGroup);
		int iRelation = GetRelation(GetMainCharacterIndex(), sti(rCharacter.index));

		// set relations to all characters in this group
		c = 0;
		while (true)
		{
			int iCharacterIndex = Group_GetCharacterIndexR(rGroup, c); c++;
			if (iCharacterIndex < 0) { break; }
			SetCharacterRelationBoth(iCharacterIndex, GetMainCharacterIndex(), iRelation);
		}
	}
	 trace("Set tasks");

	// update AISea ----------------------------------
	AISea.isDone = "";

	InitBattleInterface();							ReloadProgressUpdate();
	StartBattleInterface();							ReloadProgressUpdate();
	RefreshBattleInterface();						ReloadProgressUpdate();

	CreateEntity(&Seafoam,"Seafoam");				ReloadProgressUpdate();
	LayerAddObject(SEA_EXECUTE, &Seafoam, -1);
	LayerAddObject(SEA_REALIZE, &Seafoam, -1);
	if (Whr_IsStorm()) { Seafoam.storm = "true"; }

	SendMessage(&Telescope, "leee", MSG_TELESCOPE_INIT_ARRAYS, &Nations, &ShipsTypes, &Goods);

	PostEvent(SHIP_CHECK_RELOAD_ENABLE, 100);

	SetSchemeForSea();								ReloadProgressUpdate();

	iRDTSC = RDTSC_E(iRDTSC);
	//Trace("SeaLogin RDTSC = " + iRDTSC);
	//Trace("iNumShips = " + iNumShips);

	PostEvent("Sea_FirstInit", 100);
	trace("Direct Sea_login done");
}




//-------------------------------- Encounters and events -----------------------------------

void DirectEncounter(float encay)  // called by SeaLoginDirect
// creates ships that appear in the direction encay (playerheading as currently called )
{
	int i, j;
	float x, z, ay;
	ref rGroup;
	ref rPlayer = GetMainCharacter();
	string groupname = "Directenc"+rand(100);
	int Encnation = rand(5); // RANDOM nation for encounterships
	// int Encnation = FindEnemyNation2Character(GetMainCharacterIndex()); // another choice: Only ENEMY encounters

	object oResult;
	int iFantomIndex;



	// ships shall appear ahead of player(aprox!), so we set coords according to player coords & heading
	encay = encay - 1.5 + rand(3); // random deviation from playerheading, so that ships are not always dead ahead
	z = stf(rPlayer.Ship.Pos.z) + (cos(encay)*DIRECTENCOUNTERDISTANCE); // add Z component
	x = stf(rPlayer.Ship.Pos.x) + (sin(encay)*DIRECTENCOUNTERDISTANCE); // add X component

	Traceandlog("Strange sail to the " + GetCompassDirFromAY( encay )  );
	ay = encay + 3.2; // heading to player
	ReloadProgressUpdate();

	Sea_AddGroup2TaskList(groupname);

	rGroup = Group_GetGroupByIndex(Group_CreateGroup(groupname));
	Group_SetXZ_AY(groupname, x, z, ay);
	Group_SetType(groupname, "war");
	Group_DeleteAtEnd(groupname);

	// encountertype ? Rand(6) for now
	int enctype = makeint(rand(6) );
	int iNumShips = Fantom_GenerateEncounter(groupname, &oResult, enctype, Encnation);

	Trace("Directsail encounter type: " + enctype + ", at x = " + x + ", z = " + z);

	// load ships to sea
	if (iNumShips)
	{
		for (j=0; j<iNumShips; j++)
		{
			iFantomIndex = FANTOM_CHARACTERS + iNumFantoms - iNumShips + j;
			ref rFantom = &Characters[iFantomIndex];

			rFantom.id = "fenc_" + iFantomIndex;

			// set commander to group
			if (j==0) { Group_SetGroupCommander(groupname, Characters[iFantomIndex].id); }

			// set random character and ship names, face id
			rFantom.sex = "man";
			rFantom.Nation = Encnation;  // change for battle enc ?

			SetRandomNameToCharacter(rFantom);
			SetRandomNameToShip(rFantom);
			SetRandomFaceToCharacter(rFantom);
// KK -->
			rFantom.location = "none";
			rFantom.location.group = "";
			rFantom.location.locator = "";
// <-- KK
			//MAXIMUS Jan07-->
			Fantom_SetRandomModel(rFantom, rFantom.fantomtype);
			Facemaker(rFantom);
			//MAXIMUS <--

			LAi_Create_Officer(0, rFantom);
			Fantom_SetRandomMoney(rFantom, rFantom.fantomtype);
			Ship_SetFantomData(rFantom); // KK
			Fantom_SetCannons(rFantom, rFantom.fantomtype);
			Fantom_SetSails(rFantom, rFantom.fantomtype);

			rFantom.SeaAI.Group.Name = groupname;

// KK -->
			if (CheckAttribute(rFantom, "after_1st_sailto")) DeleteAttribute(rFantom, "after_1st_sailto");
			RestoreCharacter(rFantom);
// <-- KK

			// add fantom
			Group_AddCharacter(groupname, rFantom.id);

			// add to sea
			Ship_Add2Sea(iFantomIndex, 0, rFantom.fantomtype);
			Trace("Directencounter adds " + rFantom.id);

		}
	}

	// task: ENEMY attacks you, others sail towards you
	if (GetRMRelationType(GetRMRelation(GetMainCharacter(),Encnation)) != RELATION_ENEMY)
	{
		Group_SetTaskMove(groupname, stf(rPlayer.ship.pos.x), stf(rPlayer.ship.pos.z));
		Trace("Directsail encounter nation: " + Encnation + ", task move ");
	}
  else
  {
		Group_SetTaskAttack(groupname, PLAYER_GROUP);
		Group_LockTask(groupname);
		Trace("Directsail encounter nation: " + Encnation + ", task attack ");
		UpdateRelations();
	}

	ReloadProgressUpdate();
			Trace("Directencounter done ");
}



void Randomshipevent()
// jan 07 creates random shiplife events
// for the time being only flotsam
{
 ref pchar = GetMainCharacter();
 float bearing = rand(3) - 1.5; // random deviation from playerheading, so that salvage is not always dead ahead
 int sp = 8 - stf(ShipsTypes[GetCharacterShipType(pchar)].Class); // factor that increases amount of goodies for bigger shipclasses

 String sound = "objects\shipcharge\CannonReload-000.wav";
 String message = "Captain, there is something drifting in the water";

 switch(rand( 22 ) ) // 22 greater than number of cases to create blanks, increase for more
 {

  case 1:
   Direct_AddGood(pchar, "Sailcloth", "roll_of_rolls", 400.0, (rand(20)+5)*sp, 200, bearing);
  break;

  case 2:
   Direct_AddGood(pchar, "Planks", "roll_of_planks", 400.0, (rand(20)+5)*sp, 200, bearing);
  break;

  case 3:
   Direct_AddGood(pchar, "Wheat", "sack", 400.0, (rand(10)+2)*sp, 200, bearing);
  break;

  case 4:
   Direct_AddGood(pchar, "Rum", "barrel", 400.0, (rand(10)+2)*sp, 200, bearing);
  break;

  case 5:
   Direct_AddGood(pchar, "Gunpowder", "barrel", 400.0, (rand(10)+2)*sp, 200, bearing);
  break;

  case 6:
   Direct_AddGood(pchar, "Ebony", "roll_of_planks", 400.0, (rand(10)+2)*sp, 200, bearing);
  break;

  case 7:
   Direct_AddGood(pchar, "Mahogany", "roll_of_planks", 400.0, (rand(20)+5)*sp, 200, bearing);
  break;

  case 8:
   Direct_AddGood(pchar, "Silk", "roll_of_rolls", 400.0, (rand(20)+5)*sp, 200, bearing);
  break;

  case 9:
   Direct_AddGood(pchar, "Sandal", "bale", 400.0, (rand(20)+5)*sp, 200, bearing);
  break;

  case 10:
   Direct_AddGood(pchar, "Cinnamon", "bale", 400.0, (rand(20)+5)*sp, 200, bearing);
  break;

  case 11:
   Direct_AddGood(pchar, "Oil", "log", 1000.0, 102, 100, 0.0); // 102 trunk, dead ahead
   sound = "objects\shipcharge\_Abandon3.wav";
   message = "Something is drifting there, DEAD AHEAD,";
  break;

  case 12:
   Direct_AddGood(pchar, "Oil", "log", 1000.0, 102, 100, 0.0); // 102 trunk, repeated to increase frequency
   sound = "objects\shipcharge\_Abandon3.wav";
   message = "Something is drifting there, DEAD AHEAD,";
  break;

  case 13:
    Direct_AddGood(pchar, "Oil", "boat", 3000.0, (rand(5)+2)*sp, 500, bearing); // lifeboat with survivors
    message = "Captain, a boat";
  break;

  case 14:
    Direct_AddGood(pchar, "Oil", "boat", 3000.0, (rand(5)+2)*sp, 500, bearing); // lifeboat with survivors
    message = "Captain, a boat";
  break;

  case 15:
   Direct_AddGood(pchar, "Oil", "indianraft", 3000.0, 103, 800, bearing); // 103 indian raft
    message = "Captain, a native raft";
  break;

  case 16:
   Direct_AddGood(pchar, "Oil", "hulk", 3000.0, 104, 800, bearing); // 104 abandoned hulk
    message = "Captain, a dismasted vessel";
  break;

  case 17:
   Direct_AddGood(pchar, "Oil", "hulk", 3000.0, 104, 800, bearing); // 104 abandoned hulk
    message = "Captain, a dismasted vessel";
  break;

  case 18:
   If(MINES) Direct_AddGood(pchar, "Oil", "mine", 1000.0, 101, 100, 0.0); // 101 mine, dead ahead
   sound = "objects\shipcharge\_Abandon3.wav";
   message = "Something is drifting there, DEAD AHEAD,";
  break;

  message = ""; // blank, nothing happens
 }

 If(message!="")
 { 
  Logit(message + " to the " + GetCompassDirFromAY( stf(pchar.Ship.Ang.y) + bearing ) );
  PlayStereoSound(sound);
 }

}

void Direct_AddGood(ref rCharacter, string sGood, string sModel, float fTime, int iQuantity, float dist, float bearing)
// ccc Jan 07, mostly like AISeaGoods_AddGood, but salvage is set at a certain dist from rCharacter
// bearing is the angle to the flotsam's position from the ship heading: 0= ahead, 1.6=right, 3.2=astern, 4.8=left 
{
	if (!bSeaActive) return;

	Trace("Add good direct : " + sGood + ", rCharacter.id = " + rCharacter.id + ", iQuantity = " + iQuantity);

	// calculate position from ship according to dist and bearing
	float ay = stf(rCharacter.Ship.Ang.y) + bearing; // get angle
	float z = cos(ay)*dist; // add Z component
	float x = sin(ay)*dist; // add X component

	AISeaGoods.Good = sGood;
	AISeaGoods.Pos.x = stf(rCharacter.Ship.Pos.x) + x;
	AISeaGoods.Pos.z = stf(rCharacter.Ship.Pos.z) + z;

	AISeaGoods.CharIndex = "9"; // must NOT be rCharacter.Index, cause AISeaGoods_ShipEatGood() returns false if salvage and salvager have the same index; assume that 9 is not used for any shipcaptain

	AISeaGoods.Time = fTime;
	AISeaGoods.Quantity = iQuantity;
	AISeaGoods.Model = sModel;
	AISeaGoods.Add = "";
}


void LaunchMine(ref rCharacter)
// ccc Jan 07, places mine "good" behind ship of rCharacter.
// a new check in AISeaGoods_ShipEatGood() explodes any ship that sails onto that mine
{
	if (!bSeaActive) return;

	if(GetCargoGoods(rCharacter, GOOD_GUNPOWDER)>300 ) // you need gunpowder for a mine
	{
		AddCharacterGoods(rCharacter, GOOD_GUNPOWDER, -300 );
		float dist = 51.0-(stf(ShipsTypes[GetCharacterShipType(rCharacter)].Class)*4); // drop distance from shipcenter, depending on shipsize

		// calculate dropping position astern of ship
		float ay = stf(rCharacter.Ship.Ang.y) + 3.0; // get angle astern
		float z = cos(ay)*dist + stf(rCharacter.Ship.Pos.z); // Z coord
		float x = sin(ay)*dist + stf(rCharacter.Ship.Pos.x); // X coord

		// place mine "good" behind ship
		AISeaGoods.Good = "oil";
		AISeaGoods.Pos.x = x;
		AISeaGoods.Pos.z = z;

		AISeaGoods.CharIndex = "9"; // must NOT be rCharacter.Index, cause AISeaGoods_ShipEatGood() returns false if salvage and salvager have the same index; assume that 9 is not used for any shipcaptain

		AISeaGoods.Time = 5000;
		AISeaGoods.Quantity = 101;
		AISeaGoods.Model = "mine";
		AISeaGoods.Add = "";

		// launch effects
		PlayStereoSound("objects\shipcharge\ball_splash1.wav");
		Logit("Mine launched");
		CreateParticleSystemX("ball_splash", x, 1.0, z, 0.0, 3.0, 0.0, 2);

	}
	else { Logit("You don't have enough gunpowder for a mine !"); }
}


void SwimGoodEvent(ref rCharacter, int iQuantity)
// ccc Jan 07, special events that happen if you sail over flotsam of a certain kind
// called by AISeaGoods_ShipEatGood() if you sail over "swim goods" of the oil type
{
	//int tmpLangFileID = LanguageOpenFile("interface_strings.txt");//MAXIMUS: moved here
	int n;
	ref pchar = GetMainCharacter();
	int sp = 8 - stf(ShipsTypes[GetCharacterShipType(pchar)].Class); // factor that increases amount of goodies for bigger shipclasses

	switch(iQuantity) // Direct_AddGood calls with certain quantities of oil determine encountertype
	// rest ensured, that is a safe method: the game will never ever create more that 100 "swim goods" of oil
	{
		case 101: // 101 mine
			AddPartyExp(PChar, 1000*sp ); // reward to player for successful minelaying, or compensation for bad exp ;)
			Log_SetStringToLog("A mine ! ");   //screenmessage
			Ship_Detonate(rCharacter, true, true);
		break;

		case 102: // 102 floating trunk
			Logit("Collision with a drifting trunk ! ");   //screenmessage
			rCharacter.ship.hp = makeint(sti(rCharacter.ship.hp) * (70+ rand(20)) / 100 ); // ship hp reduced to 70 - 90%
			PlaySound("objects\shipcharge\hit_torock.wav");
		break;

		case 103: // 103 indian raft
			if(!sti(rCharacter.index) == GetMainCharacterIndex() ) break;  //runs only for the player
			PlaySound("cargo_aboard");

			switch(rand(4) )
			{
				case 1:
					for(n=1; n<17; n++){TakenItems(rCharacter, "indian"+n, rand(1)*sp );}
					Logit("You find some votive items on an Indian ceremonial raft");
				break;

				case 2:
					for(n=1; n<5; n++){TakenItems(rCharacter, "potion"+n, rand(4)*sp );}
					Logit("You find some burial objects on a witch doctor's funeral raft");
				break;

				case 3:
					for(n=1; n<4; n++){TakenItems(rCharacter, "mineral"+n, rand(1)*sp );}
					for(n=7; n<10; n++){TakenItems(rCharacter, "mineral"+n, rand(1)*sp );}
					Logit("You find some burial objects on a voodoo priest's funeral raft");
				break;

				Logit("This Indian funeral raft has already been plundered..."); // default, bad luck event
			}
		break; // 103

		case 104: // 104 abandoned hulk
			if(!sti(rCharacter.index) == GetMainCharacterIndex() ) break;  //runs only for the player
			PlaySound("cargo_aboard");

			switch(rand(4) )
			{
				case 1:
					for(n=1; n<17; n++){TakenItems(rCharacter, "jewelry"+n, rand(1)*sp );}
					Logit("You find some jewels on an abandoned packet boat");
				break;

				case 2:
					AddMoneyToCharacter(rCharacter, (rand(10000)+1000)*sp );
					Logit("You find some money on an abandoned packet boat");
				break;

				case 3:
					AddCharacterGoods(pchar, GOOD_BOMBS, rand(200)*sp );
					AddCharacterGoods(pchar, GOOD_GUNPOWDER, rand(5000)*sp );
					TakenItems(rCharacter, "pistolgrenade", rand(20)*sp );
					Logit("You find some ordinance on an abandoned ammo tender");
				break;

				Logit("Someone has already emptied this abandoned hulk..."); // default, bad luck event
			}
		break; // 104

  // more events will be added in the future

		// Default: original rescue survivors code from AISeaGoods.c
		if (sti(rCharacter.index) == GetMainCharacterIndex() )   //runs only for the player
		{
			AddCharacterCrew(rCharacter,iQuantity);   //adds crew
			Log_SetStringToLog(iQuantity + " " + TranslateString("","survivors rescued"));   //screenmessage
			PlaySound("objects\abordage\abordage_loosing.wav");   //soundeffect
			ChangeCharacterReputation(rCharacter, 1);   //rescuer deserves praise :)
		}
		// ccc rescue survivors end

	}
	//LanguageCloseFile(tmpLangFileID);//MAXIMUS: moved here
}



// ------------------- Utilities ---------------------------------------------

float GetIslandSize(string island)   // ccc Jan07 returns the aprox radius of an islands coastline
{
	switch(island)
	{
		case "Redmond": return 2000.0; break;
		case "IslaMuelle": return 2000.0; break;
		case "Oxbay": return 2000.0; break;
		case "Conceicao": return 2000.0; break;
		case "Hispaniola": return 7000.0; break;
		case "Cuba": return 8000.0; break;

		return 1000.0;  // default size for average island
	}
}


float GetIslandRemoteness(string island)   // ccc Jan07 for remote islands so that direct sailing to them takes longer
{
	switch(island)
	{
		case "Redmond": return 1000.0; break;
		case "IslaMuelle": return 1000.0; break;
		case "Oxbay": return 1000.0; break;
		case "Conceicao": return 1000.0; break;
		case "Douwesen": return 5000.0; break; // remote
		case "KhaelRoa": return 5000.0; break; // remote
		case "Cayman": return 3000.0; break; // remote
		case "Battle_Rocks": return 5000.0; break; // long range navpoint

		return 0.0;  // default for average island
	}
}




string GetCompassDirFromAY(float ay)
{
	// Traceandlog("Compass Ay before " + ay );
	while(ay < 0) {ay += 6.3;}
	while(ay > 6.3) {ay -= 6.3;}
	// Traceandlog("Ay corrected " + ay );

	if(ay < 0.4) return "south";
	if(ay < 1.2) return "southwest";
	if(ay < 2.0) return "west";
	if(ay < 2.8) return "northwest";
	if(ay < 3.55) return "north";
	if(ay < 4.35) return "northeast";
	if(ay < 5.15) return "east";
	if(ay < 5.9) return "southeast";
	if(ay < 6.31) return "south";
	return "somewhere"; // just in case :)
}


    

void DirectsailAddBattle_Rocks()
{
	if(CheckAttribute(worldMap,"islands.Battle_Rocks.name") ) return;
	//int tmpLangFileID = LanguageOpenFile("interface_strings.txt");//MAXIMUS

	// Navigation point "Battle Rocks" is added to worldmap if not yet done
	// could also be done in worldmap_init.c, but this way I can leave that file undisturbed
	//Name
	worldMap.islands.Battle_Rocks.name = "Battle_Rocks";
	//Geometry parameters
	worldMap.islands.Battle_Rocks.isVisibleName = 0;
	worldMap.islands.Battle_Rocks.position.x = -100.0;
	worldMap.islands.Battle_Rocks.position.y = 20.0;
	worldMap.islands.Battle_Rocks.position.z = 200.0;
	worldMap.islands.Battle_Rocks.position.rx = -70.0;
	worldMap.islands.Battle_Rocks.position.rz = 230.0;
	worldMap.islands.Battle_Rocks.radius = 10.0;
	worldMap.islands.Battle_Rocks.kradius = 0.1;
	//Island label
	worldMap.islands.Battle_Rocks.label.text = TranslateString("","Battle Rocks (uncharted, deserted)");//MAXIMUS
	worldMap.islands.Battle_Rocks.label.icon = PIRATE;
	worldMap.islands.Battle_Rocks.label.visible = "true";


	// Navigation point "Battle Rocks" is added to the Islands[n] array
	// could also be done in Islands_init.c, but this way I can leave that file undisturbed
	// The Islands[n] array has a size up to Islands[24], but slots are OCCUPIED only up to Islands[14] (Turks)
	// So we can add the "Battle_Rocks" attributes to the empty Islands[15] slot
	Islands[15].id = "Battle_Rocks";
	Islands[15].model = "Terks";
	Islands[15].filespath.models = "islands\Terks";
	Islands[15].refl_model = "Terks_refl";
	Islands[15].locators = "Terks_locators";
	Islands[15].visible = false;
	Islands[15].Enc_enable = false;
	Islands[15].smuggling_nation = PIRATE;
	Islands[15].towns.quantity = 0;

	Islands[15].reload.l2.label = "Devil Creek";
	Islands[15].reload.l2.name = "reload_2";
	Islands[15].reload.l2.go = "Devil_Creek";
	Islands[15].reload.l2.emerge = "see";
	Islands[15].reload.l2.radius = 200.0;
	Islands[15].reload.l2.pic = 0;
	Islands[15].reload.l2.tex = "t1";
	Islands[15].reload.l2.ships.l0.x = -104.94;
	Islands[15].reload.l2.ships.l0.z = 196.16;
	Islands[15].reload.l2.ships.l0.ay = 0.0;
	Islands[15].reload.l2.enable = false;

	Islands[15].InterfaceTextures.t1 = "battle_interface\moor_5.tga";
	Islands[15].InterfaceTextures.t1.h = 1;
	Islands[15].InterfaceTextures.t1.v = 1;

	
	// That should be all attributes that the game needs, cause KhaelRoa hasn't more either 
	// But if you get any "missing attribute" reports in connection with islands add them here

	if(ISLANDS_QUANTITY<16) { ISLANDS_QUANTITY = 16; } // number of existing islands //MAXIMUS: prevents from errors while ISLANDS_QUANTITY>16 (To CCC - maybe we can replace 15 with ISLANDS_QUANTITY and place in the end of Battle_Rocks's creation ISLANDS_QUANTITY++?)

	trace("Battle_Rocks added");
	//LanguageCloseFile(tmpLangFileID);//MAXIMUS
}



void ProcessCCCkeys(string ControlName) // Jan07, called by Seadogs.c. Put the code here so that I don't have to mess with seadogs.c for every control change
{
 	if(bSeaActive && !bAbordageStarted) // checks if you are in sailing mode
	{
		switch(ControlName)
		{
			 case "BI_ChargeKey9": //"Minedrop":
				 LaunchMine(GetMainCharacter());
			 break;

			 case "BI_ChargeKey0": //Toggle directsail
				 if(CheckAttribute(GetMainCharacter(),"nodirectsail"))
				 {
					 DeleteAttribute(GetMainCharacter(),"nodirectsail");
					 Logit("Directsail mod active");
				 }
				 else
				 {
					 characters[GetMainCharacterIndex()].nodirectsail = 1;
					 Logit("Directsail mod deactivated");
				 }
			 break;

			 case "BI_ChargeKey8": //"Seaview position":
				 DisplaySeaviewCoords();
			 break;
		}
	}
}


void DisplaySeaviewCoords()
{
	if(bSeaActive && !bAbordageStarted)
	{
		string xtext, ztext;
		int pX = MakeInt(characters[GetMainCharacterIndex()].ship.pos.x);
		int pZ = MakeInt(characters[GetMainCharacterIndex()].ship.pos.Z);
		if(pX<0){xtext = "(east)";}else{xtext = "(west)";}
		if(pZ<0){ztext = "(north)";}else{ztext = "(south)";}
		LogIt("Our position from "+characters[GetMainCharacterIndex()].location+": "+pZ+" "+ztext+", "+pX+" "+xtext);
	}
}
