Script & Config Changes for Editable Maps
9/10/02
Note the following changes and additions to support editable maps and course following boat functionality
(based on an 094 distro installation)
----
0.
New property for itemdesc.cfg Map{} entries: Editable, which defaults to 0
This determines if the user can plot points on the map gump. Note you can still edit the map
via the script methods listed under item 4.

1.
decodeMap.src, CreateTreasureMap:
  var map := CreateItemInBackpack( who, GetObjtypeByName("map_nonedit"), 1 );
and at the end of SetTreasureCoords:
  map.AppendPin(x,y);
 
2.
/pkg/skills/cartography/itemdesc.cfg
add:
Map 0x641E
{
    Name map_nonedit
    Desc Blank Map
    Graphic 0x14EB
    VendorSellsFor  10
    VendorBuysFor   5
	Editable		0
}
all the other entries add:
Editable 1 
to, except for "rolledmap" and "rolledmap2"

3.
/pkg/items/treasuremap/itemdesc.cfg
add to all:
Editable 0

4. 
New script methods for map obejcts:
GetPins(): returns an array of structs with .x and .y members for each pin point.
InsertPin( index, x, y ): inserts a pin with the passed x,y before the passed index. Valid range 0 to n-1
AppendPin( x, y ): appends a pin to the end of the course with the passed x,y
ErasePin( index ): erases the pin at the passed index (valid 0 to n-1), and shifts pins down.
isa(): returns true if the object is a map object

5. 
Tillerman course follow functionality: in this boat.src (edit from the 094 distro)
////////////////
//	boat.src 
//
//	control script for boats
//
//	Header replaced by Racalac 9/15/02
//
//	Todo/Fix: Course nav needs the "goto #" command implimented.
//			  Tillerman string recognition causes boat to pause
////////////////

use os;
use uo;
use util;
use boat;
use math;

include "include/eventID";
include "include/sysEvent";
include "include/myUtil";
include "include/client";
include "include/yesNo";
include "include/math";
include "util/key";
include "plankUtil";

const STATE_STATIONARY := 0;
const STATE_MOVING := 1;
const STATE_DRIFTING := 2;
const STATE_FOLLOWING_COURSE := 3;
const DELAY_DRIFTING := 15000;

const NEGONE := -1;
const ZERO := 0;
const POSONE := 1;

var state := STATE_STATIONARY;
var relative_direction;
var ms_delay_between_moves := 200;
var boat;
var tillerman;
var owner := GetObjProperty(boat.hold,"owner");
var decay := GetObjProperty(boat.tillerman, "decay");
var first_move_toward_waypoint := 1;

program autostart_boat(param)
  	boat := param;
  	if(!boat)
    	syslog( "[ERROR] [boat.src] Boat script running, but boat not found!" );
    	return;
  	endif
  	tillerman := boat.tillerman;
  	if(!tillerman)
    	syslog( "[ERROR] [boat.src] No tillerman for boat at: " +(boat.x-1) +","+ boat.y +","+ boat.z + "; destroying the boat..." );
    	DestroyMulti(boat);
    	return;
  	endif
  	EnableEvents(SYSEVENT_SPEECH, 15);
  	RegisterForSpeechEvents(tillerman, 15);
  	var nextencounter := ReadGameClock()+(RandomInt(120) + 120);
  	var nextsound := ReadGameClock()+5;
  	var driftcounter := 1;
  	//set_critical(1);
  	var x,y;
  	while(boat)
    	if(GetObjProperty(boat.hold, "#speed"))
      		state := STATE_MOVING;
      		ms_delay_between_moves := GetObjProperty(boat.hold, "#speed"); 
      		relative_direction := GetObjProperty(boat.hold, "#relativedir");
      		eraseobjproperty(boat.hold, "#speed");
      		eraseobjproperty(boat.hold, "#relativedir");    
    	endif     
    	decay := getobjproperty(boat.tillerman,"decay");
    	if((decay) and(!getobjproperty(tillerman,"nodecay")))
      		if(ReadGameClock() > decay)
        		PrintTextAbove(tillerman, "Arrrrrgh!  She's going down, Captain!  Abandon ship!");
        		sleep(4);
        		foreach item in EnumerateItemsInContainer( boat.hold )
          			DestroyItem(item);
        		endforeach
        		foreach mob in(boat.items)
          			DestroyItem(mob);
        		endforeach        
        		foreach mob in(boat.mobiles)
          			MoveCharacterToLocation(mob, 1487, 1869, 0,  MOVECHAR_FORCELOCATION );
          			SendSysMessage(mob, "Your ship, badly in need of maintenance, sinks beneath the waves!");
          			SendSysMessage(mob, "You awake on a distant shore...");
        		endforeach
        		if(boat.has_offline_mobiles)
          			foreach mob in(boat.offline_mobiles)
            			MoveCharacterToLocation(mob, 1487, 1869, 0,  MOVECHAR_FORCELOCATION );
          			endforeach
        		endif
        		DestroyMulti(boat);
      		endif
    	endif    
    	case(state)
      	STATE_MOVING:   if(ReadGameClock() > nextsound)
          		        	PlayBoatSounds();
                        	nextsound := ReadGameClock()+5;
                      	endif
                      	x := boat.x;
                      	y := boat.y;
                      	MoveBoatRelative(boat, relative_direction);
                      	if(boat.x = 6 or boat.x = 5097 or boat.y = 6 or boat.y = 4089)   
                        	WorldWrap();
                      	endif
                      	if((x == boat.x) &&(y == boat.y))
                        	ms_delay_between_moves := 1000;
                        	state := STATE_DRIFTING;
                        	PrintTextAbove(tillerman, "Aaargh!  We've stopped!");
                        	//SmackEveryone();
                      	endif
                      	sleepms(ms_delay_between_moves);
                      	if(ReadGameClock() > nextencounter)
                        	DoEncounter();
                        	nextencounter := ReadGameClock()+120;
                      	endif
                      	while(events_waiting())
                        	process_event(wait_for_event(0));
                      	endwhile
      	STATE_DRIFTING: if(ReadGameClock() > nextsound)
                        	PlayBoatSounds();
                          	nextsound := ReadGameClock()+5;
                        endif
                        if(driftcounter > 15)
                          	MoveBoatRelative(boat, RandomInt(8));
                          	driftcounter := 1;
                        else
                          	driftcounter := driftcounter + 1;
                        endif
                        sleepms(1000);
                        while(events_waiting())
                          	process_event(wait_for_event(0));
                        endwhile
      	STATE_STATIONARY: var ev := wait_for_event(120);
                        if(ev)
                          	process_event(ev);
                        endif
        STATE_FOLLOWING_COURSE:
        				x := boat.x;
        				y := boat.y;
        				BoatFollowCourse(boat);
        				
        				sleepms(ms_delay_between_moves);
                        while(events_waiting())
                          	process_event(wait_for_event(0));
                        endwhile        				
    	endcase
    	if(ReadGameClock() > nextencounter)
      		checkres();
      		nextencounter := ReadGameClock()+120;
    	endif
  	endwhile
endprogram

function handle_speech(event)
  	var text := lower(event.text);
  	if(text["drift"] || text["raise anchor"])
    	ms_delay_between_moves := 1000;
    	state := STATE_DRIFTING;
  	elseif(text["forward"])
    	if(state = STATE_STATIONARY)
      		PrintTextAbove(tillerman, "The Anchor Is Down Captain!");
    	else
      		PrintTextAbove(tillerman, "Aye Aye Captain!");
      		state := STATE_MOVING;
      		if(text["left"])
        		relative_direction := 7;
      		elseif(text["right"])
        		relative_direction := 1;
      		else
        		relative_direction := 0;
      		endif
    	endif
  	elseif(text["back"])
    	if(state = STATE_STATIONARY)
      		PrintTextAbove(tillerman, "The Anchor Is Down Captain!");
    	else
      		PrintTextAbove(tillerman, "Aye Aye Captain!");
      		state := STATE_MOVING;
      		if(text["left"])
        		relative_direction := 5;
      		elseif(text["right"])
        		relative_direction := 3;
      		else
        		relative_direction := 4;
      		endif
    	endif
  	elseif(text["stop"] || text["furl sail"] || text["furl"])
    	PrintTextAbove(tillerman, "Aye Aye Captain!");
    	ms_delay_between_moves := 1000;
    	state := STATE_DRIFTING;
  	elseif(text["drop anchor"])
    	PrintTextAbove(tillerman, "Aye Aye Captain! Anchor Dropped.");
    	ms_delay_between_moves := 1000;
    	state := STATE_STATIONARY;
  	elseif(text["turn right"] || text["starboard"])
    	if(state = STATE_STATIONARY)
      		PrintTextAbove(tillerman, "The Anchor Is Down Captain!");
    	else
      		PrintTextAbove(tillerman, "Aye Aye Captain!");
      		TurnBoat(boat, 1);
    	endif
  	elseif(text["right"])
    	if(state = STATE_STATIONARY)
      		PrintTextAbove(tillerman, "The Anchor Is Down Captain!");
    	else
      		PrintTextAbove(tillerman, "Aye Aye Captain!");
      		state := STATE_MOVING;
      		relative_direction := 2;
    	endif
  	elseif(text["turn left"] || text["port"])
    	if(state = STATE_STATIONARY)
      		PrintTextAbove(tillerman, "The Anchor Is Down Captain!");
    	else
      		PrintTextAbove(tillerman, "Aye Aye Captain!");
      		TurnBoat(boat, 3);
    	endif
  	elseif(text["left"])
    	if(state = STATE_STATIONARY)
      		PrintTextAbove(tillerman, "The Anchor Is Down Captain!");
    	else
      		PrintTextAbove(tillerman, "Aye Aye Captain!");
      		state := STATE_MOVING;
      		relative_direction := 6;
    	endif
  	elseif(text["come about"] || text["turn around"])
    	if(state = STATE_STATIONARY)
      		PrintTextAbove(tillerman, "The Anchor Is Down Captain!");
    	else
      		PrintTextAbove(tillerman, "Aye Aye Captain! Coming About.");
      		TurnBoat(boat, 2);
      		sleep(1);
    	endif
    	/*NEW MAP COURSE COMMANDS*/
    elseif(text["set course"])
      	PrintTextAbove(tillerman, "Select a map for me to follow.");   	
      	var map := Target(event.source);
      	if(!map.isa(POLCLASS_MAP))
      		PrintTextAbove(tillerman, "Are ye dense? I need a map!"); 
      	else
      		SetObjProperty(tillerman, "course", map.GetPins());
      		SetObjProperty(tillerman, "nextwaypoint", 1);
      		PrintTextAbove(tillerman, "Arr, course plotted."); 
      	endif
    elseif(text["clear course"])
    	PrintTextAbove(tillerman, "Arr, course cleared Cap'n.");
    	EraseObjProperty(tillerman, "course");
    	EraseObjProperty(tillerman, "nextwaypoint");
    elseif(text["nav"])
    	if(state != STATE_FOLLOWING_COURSE)
    		PrintTextAbove(tillerman, "We are not currently following a course.");
    	else
    		PrintTextAbove(tillerman, "We are following our course.");
    		PrintTextAbove(tillerman, "The next waypoint is #" + GetObjProperty(tillerman,"nextwaypoint"));
    	endif
    elseif(text["start"])
    	if( (GetObjProperty(tillerman,"course")==error) || (GetObjProperty(tillerman,"nextwaypoint")==error))
    		PrintTextAbove(tillerman,"Arr, set course first Cap'n.");
    	else    		
     		state := STATE_FOLLOWING_COURSE;
     		SetObjProperty(tillerman, "nextwaypoint", 1);
    		PrintTextAbove(tillerman,"Aye, sailing to first waypoint.");
    	endif
    elseif(text["continue"])
    	if( (GetObjProperty(tillerman,"course")==error) || (GetObjProperty(tillerman,"nextwaypoint")==error))
    		PrintTextAbove(tillerman,"Arr, set course first Cap'n.");
    	else
    	    state := STATE_FOLLOWING_COURSE;
    		PrintTextAbove(tillerman,"Aye, moving on.");
    	endif
  	endif
  	/*END NEW MAP COURSE COMMANDS*/
  	if(text["full"])
    	if(state != STATE_STATIONARY)
      		PrintTextAbove(tillerman, "Aye Aye Captain! Full Speed Ahead.");
      		ms_delay_between_moves := 100;
    	endif
  	elseif(text["slow"])
    	if(state != STATE_STATIONARY)
      		PrintTextAbove(tillerman, "Aye Aye Captain! ");
      		ms_delay_between_moves := 1000;
    	endif
  	endif
  	
  	sleep(1);
  	if(text["one"])
    	if(state != STATE_STATIONARY)
      		MoveBoatRelative(boat, relative_direction);
      		state := STATE_STATIONARY;
    	endif
  	endif
endfunction

function process_event(event)
  	if(event.type == SYSEVENT_SPEECH)
		if(CanCommandMe(event.source))
      		var text := lower(event.text);
      		if(text["drydock"])
        		PrintTextAbove(tillerman, "Aye Aye Captain! Docking.");
        		drydock(event);
        		sleep(1);
      		elseif(text["status"])
        		decay := GetObjProperty(boat.tillerman, "decay");
        		if((decay - ReadGameClock() ) > 861000 )
          			PrintTextAbove( tillerman, "Arrh, the ship's in excellent shape! She's a fine vessel indeed, Captain!" );
        		elseif((decay - ReadGameClock() ) > 604800 )
          			PrintTextAbove( tillerman, "She's slightly worn, Captain.");
        		elseif((decay - ReadGameClock() ) > 259200 )
          			PrintTextAbove(tillerman, "She's fairly worn, Captain.");
        		elseif((decay - ReadGameClock() ) > 86400 )
          			PrintTextAbove(tillerman, "Arrr, she's greatly worn and in dire need of repair, Captain!");
        		else 
          			PrintTextAbove(tillerman, "Arrr, she's going to sink within the day if she don't get repairs soon, Captain!");
        		endif
      		else
        		handle_speech(event);
      		endif
    	endif
  	endif
endfunction

function CanCommandMe(who)
  	owner := GetObjProperty(boat.tillerman, "owner");
  	if(who.serial == owner)
    	return 1;
  	else
    	var packkey;
    	var lokid := GetObjProperty(boat.tillerman, "lockid");
    	foreach thing in EnumerateItemsInContainer(who.backpack)
      		if(GetObjProperty(thing, "lockid") = lokid)
        		packkey := 1;
        		break;
      		endif
    	endforeach
    	if(packkey = 1)
      		return 1;
    	else
      		return 0;
    	endif
  	endif
endfunction

function DoEncounter()
  	foreach who in  ListMobilesNearLocationEx(boat.x, boat.y, GetMapInfo(boat.x, boat.y).z, 4, LISTEX_FLAG_GHOST)
    	if(YesNo(who,"Ressurect?"))
      		PlaySoundEffect(who, SFX_SPELL_RESSURECTION);
      		PlaySoundEffect(who, SFX_SPELL_RESSURECTION);
      		Resurrect(who);
    	endif
  	endforeach
  	var who :=(boat.mobiles);
  	who := who[1];
  	if(!who)
    	return;
  	endif
  	var x;
  	var y;
  	var z;
  	x := RandomInt(15)-5;
  	y := RandomInt(15)-5;
  	x := x + boat.x;
  	y := y + boat.y;
  	if(x > boat.x)
 		x := x + 5;
  	else
	    x := x - 5;
  	endif
  	if(y > boat.y)
    	y := y + 5;
  	else
    	y := y - 5;
  	endif
  	z := GetMapInfo(x, y).z;
  	if(z >= who.z)
    	return;
  	endif
  	var it := CreateNpcFromTemplate(getcritter(), x, y, z, 0);
  	if(it)
    	SetObjProperty(it,"killme",1);
  	endif
  	var ev := {};
  	ev.+type := EVID_ENTEREDAREA;
  	ev.+source := who;
  	SendEvent(it, ev);
endfunction

function checkres()
  	foreach who in  ListMobilesNearLocationEx(boat.x, boat.y, GetMapInfo(boat.x, boat.y).z, 4, LISTEX_FLAG_GHOST)
    	if(who in boat.mobiles)
      		if(YesNo(who,"Resurrect?"))
        		PlaySoundEffect(who, SFX_SPELL_RESSURECTION);
        		PlaySoundEffect(who, SFX_SPELL_RESSURECTION);
        		Resurrect(who);
      		endif
    	endif
  	endforeach
endfunction

function getcritter()
  	case(RandomInt(9))
	    0: return "walrus";
    	1: return "walrus";
    	2: return "walrus";
    	3: return "walrus";
    	4: return "alligator";
    	5: return "alligator";
    	6: return "waterelemental";
    	7: return "seaserpent";
    	8: return "airelemental";
  	endcase
endfunction

function drydock(event)
  	if(GetObjProperty(event.source, "#DryDocking"))
    	SendSysMessage(event.source, "You are already doing something else.");
    	return;
  	endif
  	SetObjProperty(event.source, "#DryDocking", 1);
  	EraseObjProperty(event.source, "#DryDocking");
  	var text := lower(event.text);
  	if(!text["drydock"])
    	EraseObjProperty(event.source, "#DryDocking");
    	return;
  	endif
  	var me := event.source;
  	var items := boat.items;
  	if(len(items) > 0)
    	PrintTextAbovePrivate(boat.tillerman, "You cannot drydock the boat while items are on deck.", me);
    	EraseObjProperty(me, "#DryDocking");
    	return;
  	endif
  	items := EnumerateItemsInContainer(boat.hold);
  	if(len(items) > 0)
    	PrintTextAbovePrivate(boat.tillerman, "You cannot drydock the boat while items are in the hold.", me);
    	EraseObjProperty(me, "#DryDocking");
    	return;
  	endif
  	if(CanDockMe(boat, me))
    	if(!YesNo(me,"Drydock?"))
      		EraseObjProperty(me, "#DryDocking");
      		return;
    	endif
    	
    	var shiptype;
    	case(boat.objtype)
      		0x6040: shiptype := 0x6027;
      		0x6041: shiptype := 0x6028;
      		0x6042: shiptype := 0x6029;
      		0x6043: shiptype := 0x602a;
      		0x6044: shiptype := 0x602b;
      		0x6045: shiptype := 0x602c;
    	endcase
    	
    	if(!shiptype)
      		SendSysMessage(me, "Your boat seems to be malfunctioning. Please page a GM.");
      		return;
    	endif
    	
    	var newboat := CreateItemInContainer(me.backpack, shiptype, 1);
    	var key := FindKey(me, boat.tillerman);
    	if(key)
      		DestroyItem(key);
    	endif
    	if(!newboat)
      		SendSysMessage(me,"Could not create boat deed in your backpack!");
      		EraseObjProperty(me, "#DryDocking");
      		return;
    	endif
    	if(ReserveItem(newboat))
      		newboat.graphic := 0x14f3;
      		if(!boat.tillerman.name)
        		newboat.name := "a toy boat";
      		else
      	  		newboat.name := boat.tillerman.name;
      		endif
      		
      		if(!DestroyBoat(boat))
        		DestroyItem(newboat);
      		endif
    	else
      		DestroyItem(newboat);
    	endif
  	endif
  	EraseObjProperty(me, "#DryDocking");
endfunction

function DestroyBoat(who)
  	var res := DestroyMulti(boat);
  	if(!res)
    	return 0;
  	endif
  	return 1;
endfunction

function SmackEveryone()
  	foreach mob in(boat.mobiles)
    	ApplyDamage(mob,RandomInt(10));
    	PerformAction(mob,0x14);
    	PlaySoundEffect(mob,0x110);
  	endforeach
endfunction

function PlayBoatSounds()
  	var who := RandomInt(len(boat.mobiles)+1);
  	var mobs :=(boat.mobiles);
  	if(RandomInt(2) == 1)
    	PlaySoundEffect(mobs[who],0x13);
  	else
    	PlaySoundEffect(mobs[who],0x14);
  	endif
endfunction

function WorldWrap()
  	var newx := boat.x;
  	var newy := boat.y;
  	if(boat.y <= 6)
    	newy := 4088;
  	elseif(boat.y >= 4089)
    	newy := 6;
  	endif
  	if(boat.x <= 6)
    	newx := 5096;
  	elseif(boat.x >= 5097)
    	newx := 6;
  	endif
  	var lockid := GetObjProperty(boat.hold, "lockid");
  	var owner := GetObjProperty(boat.hold, "owner");
  	var shiptype := boat.objtype;
  	var shipfacing, created;
  	case(tillerman.graphic)
    	0x3e4e: shipfacing := CRMULTI_FACING_NORTH;
    	0x3e55: shipfacing := CRMULTI_FACING_EAST;
    	0x3e4b: shipfacing := CRMULTI_FACING_SOUTH;
    	0x3e50: shipfacing := CRMULTI_FACING_WEST;
    	default: shipfacing := "Error!";
  	endcase
  	if(shipfacing="Error!")
    	syslog( "[ERROR] [boat.src] Couldn't tell which way the ship was facing for the worldwrap!");
  	endif    
  	created := CreateMultiAtLocation( newx, newy, -5, shiptype, shipfacing);
  	if(!created)
    	syslog( "[ERROR] [boat.src] New ship couldn't be created for worldwrap!");
    	return;
  	endif
  	var oldshiphold := boat.hold;
  	var newshiphold := created.hold;
  	foreach item in EnumerateItemsInContainer( oldshiphold )
    	if(item.container = oldshiphold)
      		MoveItemToContainer(item, newshiphold);
    	endif
  	endforeach
  	foreach item in EnumerateItemsInContainer( oldshiphold )
    	MoveItemToContainer(item, newshiphold);
  	endforeach
  	foreach mob in(boat.mobiles)
    	MoveCharacterToLocation(mob,(mob.x-boat.x)+created.x,(mob.y-boat.y)+created.y, -2,  MOVECHAR_FORCELOCATION );
  	endforeach
  	if(boat.has_offline_mobiles)
    	foreach mob in(boat.offline_mobiles)
      		MoveCharacterToLocation(mob,(mob.x-boat.x)+created.x,(mob.y-boat.y)+created.y, -2,  MOVECHAR_FORCELOCATION );
    	endforeach
  	endif
  	foreach mob in(boat.items)
    	MoveitemToLocation(mob,(mob.x-boat.x)+created.x,(mob.y-boat.y)+created.y, -2,0  );
  	endforeach
  	var newtillerman := created.tillerman;
  	newtillerman.name := tillerman.name;
  	SetObjProperty( created.starboardplank, "lockid", lockid);
  	SetObjProperty( created.portplank, "lockid", lockid);
  	SetObjProperty( created.ship.starboardplank, "owner", owner );
  	SetObjProperty( created.ship.portplank, "owner", owner );
  	SetObjProperty( created.ship.starboardplank, "tillermanid", newtillerman.serial );
  	SetObjProperty( created.ship.portplank, "tillermanid", newtillerman.serial );
  	SetObjProperty( created.hold, "lockid", lockid);
  	SetObjProperty( created.hold, "owner", owner);
  	SetObjProperty( created.tillerman, "owner", owner);
  	SetObjProperty( created.tillerman, "shipserial", created.serial);
  	SetObjProperty( created.tillerman, "lockid", lockid);
  	SetObjProperty( created.tillerman, "decay", decay);
  	if(boat.starboardplank.locked = 1)
    	created.starboardplank.locked := 1;
  	endif
  	if(boat.portplank.locked = 1)  
    	created.portplank.locked := 1;
  	endif
  	if(boat.hold.locked = 1)
    	created.hold.locked := 1;
  	endif
  	SetObjProperty( created.hold, "#relativedir", relative_direction);
  	SetObjProperty( created.hold, "#speed", ms_delay_between_moves);
  	created.tillerman.usescript := ":boat:tillerman";
  	if(!DestroyMulti(boat))
    	DestroyItem(boat.tillerman);
    	syslog( "[ERROR] [boat.src] Old ship at " +(boat.x-1) +","+ boat.y +","+ boat.z + " couldn't be destroyed during worldwrap!");
  	endif
endfunction

function CanDockMe(boat, who)
  	if(GetObjProperty(boat.hold,"owner") == who.serial)
    	return 1;
  	else
    	var packkey;
    	var lokid := GetObjProperty(boat.tillerman, "lockid");
    	foreach thing in EnumerateItemsInContainer(who.backpack)
      		if(GetObjProperty(thing, "lockid") = lokid)
        		packkey := 1;
        		break;
      		endif
    	endforeach
    	if(packkey = 1)
      		return 1;
    	else	
      		return 0;
    	endif
  	endif
endfunction

/*NEW MAP COURSE FUNCTIONS*/
//follows the current course
function BoatFollowCourse(boat)
	var startx, starty, destx, desty;
	var course, nextwaypoint;
	var newpoint, newfacing, prevfacing;
	
	course := GetObjProperty(boat.tillerman,"course");
	nextwaypoint := GetObjProperty(boat.tillerman,"nextwaypoint");
	startx := boat.x;
	starty := boat.y;
	prevfacing := boat.facing;
	
	if( (course==error) || (nextwaypoint==error))
		return;
	endif
	
	destx := course[nextwaypoint].x;
	desty := course[nextwaypoint].y;
	
	newpoint := lineBresenham(startx,starty,destx,desty);
	newfacing := GetNewFacing(startx,starty,newpoint[1],newpoint[2]);

	if(newfacing == error) //reached waypoint/end
		nextwaypoint := nextwaypoint + 1;
		first_move_toward_waypoint := 1; //so we turn toward next waypoint (includes next course follow if we're done with this one)
		if(nextwaypoint > course.size()) //done
			state := STATE_DRIFTING;
			PrintTextAbove(boat.tillerman,"Arr, we've reached our destination.");
			return;
		else
			SetObjProperty(boat.tillerman,"nextwaypoint",nextwaypoint);
		endif
		
	else

		if(first_move_toward_waypoint == 1)
			if( prevfacing != newfacing )
				TurnBoat(boat,Cint(DetermineTurnCode(boat,newfacing)) );
			endif
			first_move_toward_waypoint := 0;
		endif
		MoveBoat(boat,newfacing);
		
		if((startx == boat.x) &&(starty == boat.y)) //we didn't move. must be blocked
    		state := STATE_DRIFTING;
    		PrintTextAbove(tillerman, "Aaargh!  We've stopped!");
  		endif
  	endif
	
endfunction

//convert facing 0-7 to turn code 0-3
function DetermineTurnCode( boat, desired )

    var current := CInt(boat.facing / 2);
    desired := CInt(desired / 2);

	
    if ( desired < current ) 
    	desired := desired + 4; 
    endif
    return (desired - current);
endfunction
	
//returns the direction (x1,y1) lies from (x0,y0) in the range 0-7.
// returns error if the 2 points are equal.
function GetNewFacing(x0,y0,x1,y1)
	var dx, dy;
	var facing;
	dx := x1 - x0;
	dy := y1 - y0;
	
	case(dx)
		NEGONE: 
			case(dy)
				 NEGONE: facing := 7;
				 ZERO: facing := 6;
				 POSONE: facing := 5;
				 default: facing := error;
			endcase
		 ZERO:
		 	case(dy)
		 		 NEGONE: facing := 0;
		 		 ZERO: facing := error;
		 		 POSONE: facing := 4;
		 		 default: facing := error;
		    endcase
		 POSONE:
		 	case(dy)
		 		 NEGONE: facing := 1;
		 		 ZERO: facing := 2;
		 		 POSONE: facing := 3;
		 		 default: facing := error;
		 	endcase
		 default: facing := error;
	endcase
	
	return facing;
endfunction

//Bresenham's line drawing algorithm. determines a line between two waypoints.
function lineBresenham(x0, y0, x1, y1)
    var dy;
    dy := y1 - y0;
    var dx;
    dx := x1 - x0;
    var stepx, stepy;

    if (dy < 0)
      	dy := -dy;  
       	stepy := -1;
    else
      	stepy := 1;
    endif
        
    if (dx < 0) 
       	dx := -dx;
       	stepx := -1;
    else 
      	stepx := 1;
    endif
        
    dy := 2*dy;   
    dx := 2*dx;    

    if (dx > dy)
        var fraction;
        fraction := 2*dy - dx;
        while (x0 != x1)
        	if (fraction >= 0)
            	y0 := y0 + stepy;
                fraction := fraction - dx;
           	endif
            x0 := x0 + stepx;
            fraction := fraction + dy;
            return {x0, y0};
        endwhile
    else
        var fraction;
        fraction := 2*dx - dy;
        while (y0 != y1)
            if (fraction >= 0)
                x0 := x0 + stepx;
                fraction := fraction - dy;
            endif
            y0 := y0 + stepy;
            fraction := fraction + dx;
            return {x0, y0};
        endwhile
    endif
endfunction
/*END NEW MAP COURSE FUNCTIONS*/

