Lyonesse/bin/
Lyonesse/doc/eng/
Lyonesse/doc/ita/
Lyonesse/lib/
Lyonesse/lib/buildings/
Lyonesse/lib/clans/
Lyonesse/lib/data/
Lyonesse/lib/etc/
Lyonesse/lib/house/
Lyonesse/lib/misc/
Lyonesse/lib/plralias/A-E/
Lyonesse/lib/plralias/F-J/
Lyonesse/lib/plralias/K-O/
Lyonesse/lib/plralias/P-T/
Lyonesse/lib/plralias/U-Z/
Lyonesse/lib/plralias/ZZZ/
Lyonesse/lib/plrobjs/A-E/
Lyonesse/lib/plrobjs/F-J/
Lyonesse/lib/plrobjs/K-O/
Lyonesse/lib/plrobjs/P-T/
Lyonesse/lib/plrobjs/U-Z/
Lyonesse/lib/plrobjs/ZZZ/
Lyonesse/lib/plrsave/A-E/
Lyonesse/lib/plrsave/F-J/
Lyonesse/lib/plrsave/K-O/
Lyonesse/lib/plrsave/P-T/
Lyonesse/lib/plrsave/U-Z/
Lyonesse/lib/plrsave/ZZZ/
Lyonesse/lib/ships/
Lyonesse/lib/stables/
Lyonesse/lib/text/help/
Lyonesse/lib/world/
Lyonesse/lib/world/bld/
Lyonesse/lib/world/ship/
Lyonesse/lib/world/shp/
Lyonesse/lib/world/wls/
Lyonesse/lib/world/wls/Life/
Lyonesse/lib/world/wls/Map/
Lyonesse/log/
/**************************************************************************
 * #   #   #   ##   #  #  ###   ##   ##  ###       http://www.lyonesse.it *
 * #    # #   #  #  ## #  #    #    #    #                                *
 * #     #    #  #  # ##  ##    #    #   ##   ## ##  #  #  ##             *
 * #     #    #  #  # ##  #      #    #  #    # # #  #  #  # #            *
 * ###   #     ##   #  #  ###  ##   ##   ###  #   #  ####  ##    Ver. 1.0 *
 *                                                                        *
 * -Based on CircleMud & Smaug-     Copyright (c) 2001-2002 by Mithrandir *
 *                                                                        *
 * ********************************************************************** *
 *                                                                        *
 * File: wild.ships.c                                                     *
 *                                                                        *
 * Ships and Navigation code                                              *
 *                                                                        *
 **************************************************************************/

#include "conf.h"
#include "sysdep.h"

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "db.h"
#include "constants.h"
#include "screen.h"
#include "interpreter.h"
#include "handler.h"
#include "spells.h"

/* external functions */
ROOM_DATA *create_wild_room( COORD_DATA *coord, bool Static );
OBJ_DATA *fread_one_obj( FILE *fp, int *location );
int		save_objs(OBJ_DATA *obj, FILE *fp, int mode, int location);
char	*make_bar(long val, long max, long len, int color);
void	put_sect( int y, int x, int nSect, bool upMap );
void	look_at_wild( CHAR_DATA *ch, bool quick );
void	check_wild_move(CHAR_DATA *ch, VEHICLE_DATA *vehicle, SHIP_DATA *ship, FERRY_DATA *ferry, ROOM_DATA *pRoomFrom, ROOM_DATA *pRoomTo);
void	wild_check_for_remove( ROOM_DATA *wRoom );
void	wild_remove_dequeue( ROOM_DATA *wRoom );
int		get_sail_dir( SHIP_DATA *ship, COORD_DATA *cto, bool silent );
int		calc_angle( int chX, int chY, int lmX, int lmY, int *ipDistan );
int		angle_to_dir( int iAngle );
long	event_time(EVENT_DATA *event);
bitvector_t asciiflag_conv(char *flag);


#if defined(KEY)
#undef KEY
#endif

#define KEY( literal, field, value )					\
				if ( !strcmp( word, literal ) )			\
				{										\
				    field  = value;						\
				    fMatch = TRUE;						\
				    break;								\
				}

/* globals */
SHIP_TYPE		*ship_table[NUM_SHIP_TYPE];
SHIP_DATA		*first_ship			= NULL;
SHIP_DATA		*last_ship			= NULL;
PORT_DATA		*port_table			= NULL;
COURSE_DATA		*course_table		= NULL;
COURSE_DATA		*rev_course_table	= NULL;
FERRY_DATA		*first_ferry		= NULL;
FERRY_DATA		*last_ferry			= NULL;

int				top_ship_num		= 0;
int				top_ship_table		= 0;
int				top_port			= 0;

/* local functions */
EVENTFUNC(ship_sail_event);
PORT_DATA *get_port_by_name( char *name );
PORT_DATA *get_port_by_coord( COORD_DATA *coord );
COURSE_DATA *get_course( COORD_DATA *orig, PORT_DATA *port_dest );
COURSE_DATA *get_course_by_vnum( int vnum );
COURSE_STEP *get_course_step( COURSE_DATA *course, COORD_DATA *coord );
void check_ship_speed( SHIP_DATA *ship );
void load_ship_table( void );
void load_ports( void );
void engage_course( CHAR_DATA *ch, COURSE_DATA *course, bool recover );
bool knows_course( CHAR_DATA *ch, int cnum );
char *get_port_name( COORD_DATA *coord );
/* local ferry functions */
EVENTFUNC(ferry_move_event);
void ferry_move(void);


/* ******************************************************************* */
/* Utilities for retrieving ship data                                  */
/* ******************************************************************* */
SHIP_DATA *find_ship( sh_int vnum )
{
	SHIP_DATA *ship;

	if ( vnum < 0 )
		return (NULL);

	for ( ship = first_ship; ship; ship = ship->next )
		if ( ship->vnum == vnum )
			return (ship);

	return (NULL);
}

SHIP_DATA *find_ship_in_room_by_name( ROOM_DATA *pRoom, char *name )
{
	SHIP_DATA *ship;
	int fnum;

	if ( !pRoom || !pRoom->ships )
		return (NULL);

	if ( !name )
		return (pRoom->ships);

	if ( !( fnum = get_number(&name) ) )
		return (NULL);

	for ( ship = pRoom->ships; ship; ship = ship->next_in_room )
		if ( isname( name, ship->name ) || !str_cmp( name, "ship" ) )
			if ( --fnum == 0 )
				break;

	return (ship);
}

ROOM_DATA *find_room_ship( SHIP_DATA *ship, int room )
{
	if ( room < 1 || room > ship->type->size )
		return (NULL);

	return ( ship->rooms[room] );
}


CHAR_DATA *find_char_in_ship( SHIP_DATA *ship, long idnum )
{
	CHAR_DATA *ppl;

	for ( ppl = ship->people; ppl; ppl = ppl->next_in_vehicle )
		if ( GET_IDNUM(ppl) == idnum )
			break;

	return (ppl);
}

/* ******************************************************************* */
/* Ship Authorization code                                             */
/* ******************************************************************* */
AUTH_DATA *get_ship_auth( SHIP_DATA *ship, long idnum )
{
	AUTH_DATA *pAuth;
	bool aboard = FALSE;

	if ( !ship || !ship->authorized)
		return (NULL);

	for (pAuth = ship->authorized; pAuth; pAuth = pAuth->next)
	{
		if ( pAuth->id_num == idnum )
			break;
	}

	return (pAuth);
}

bool can_go_aboard( CHAR_DATA *ch, SHIP_DATA *ship )
{
	if ( !ch || !ship )
		return (FALSE);

	/* immortals can go aboard */
	if ( IS_IMMORTAL(ch) )
		return (TRUE);

	/* owner of the ship */
	if ( GET_IDNUM(ch) == ship->idowner )
		return (TRUE);

	/* captain of the ship */
	if ( GET_IDNUM(ch) == ship->idcaptain )
		return (TRUE);

	/* ppl authorized to go aboard */
	if ( get_ship_auth( ship, GET_IDNUM(ch) ) )
		return (TRUE);

	return (FALSE);
}

void auth_aboard( SHIP_DATA *ship, CHAR_DATA *ch, CHAR_DATA *vict, int mode )
{
	AUTH_DATA *pAuth;

	if ( !ship || !vict )
		return;

	/* authorize */
	if ( mode == 1 )
	{
		if ( get_ship_auth( ship, GET_IDNUM(vict) ) )
		{
			ch_printf( ch, "%s is already authorized to go aboard.\r\n", PERS(vict, ch) );
			return;
		}

		CREATE(pAuth, AUTH_DATA, 1);
		pAuth->id_num		= GET_IDNUM(vict);
		pAuth->name			= str_dup(GET_NAME(vict));

		pAuth->next			= ship->authorized;
		ship->authorized	= pAuth;

		ch_printf(ch, "%s is now authorized to go aboard of '%s'.\r\n",
			PERS(vict, ch), ship->name);
		ch_printf(vict, "You are now authorized to go aboard of '%s'.\r\n",
			ship->name);
	}
	/* revoke authorization */
	else
	{
		AUTH_DATA *temp;

		if (!(pAuth = get_ship_auth(ship, GET_IDNUM(vict))))
		{
			ch_printf( ch, "%s is already not authorized to go aboard.\r\n", PERS(vict, ch) );
			return;
		}

		if ( find_char_in_ship( ship, GET_IDNUM(vict) ) )
		{
			send_to_char("You cannot revoke authorization to someone that's aboard.\r\n", ch);
			return;
		}

		REMOVE_FROM_LIST(pAuth, ship->authorized, next);
		pAuth->next	= NULL;
		STRFREE(pAuth->name);
		DISPOSE(pAuth);

		ch_printf(ch, "%s now is not authorized to go aboard of '%s'.\r\n",
			PERS(vict, ch), ship->name);
		ch_printf(vict, "Authorization to go aboard of '%s' has been revoked.\r\n",
			ship->name);
	}
}


/* ******************************************************************* */
/* Character/Ship/Room low-level interaction                           */
/* ******************************************************************* */

/* To be called when a pc enter a ship */
void char_to_ship( CHAR_DATA *ch, SHIP_DATA *ship, int room )
{
	int room_num = room;

	if ( room_num < 0 || room_num > ship->type->size)
		room_num = 0;

	ch->in_ship			= ship;

	char_to_room(ch, ship->rooms[room_num]);

	ch->next_in_vehicle	= ship->people;
	ship->people		= ch;

	/* add ch weight to the ship */
	ship->curr_val.loads	+= GET_WEIGHT(ch) + IS_CARRYING_W(ch);
	check_ship_speed( ship );
}

/* To be called when a pc exit a ship */
void char_from_ship( CHAR_DATA *ch )
{
	CHAR_DATA *temp;

	/* subtract ch weight from the ship */
	ch->in_ship->curr_val.loads -= GET_WEIGHT(ch) + IS_CARRYING_W(ch);
	check_ship_speed( ch->in_ship );

	REMOVE_FROM_LIST(ch, ch->in_ship->people, next_in_vehicle);
	ch->in_ship					= NULL;
	ch->next_in_vehicle			= NULL;
	ch->player_specials->athelm	= FALSE;
}

/* remove ship from room */
void ship_from_room( SHIP_DATA *ship )
{
	SHIP_DATA *temp;

	if ( !ship )
	{
		log("SYSERR: NULL character or NULL room in ship_from_room");
		exit(1);
	}

	ship->last_room		= ship->in_room;

	REMOVE_FROM_LIST(ship, ship->in_room->ships, next_in_room);
	ship->in_room		= NULL;
	ship->next_in_room	= NULL;

	wild_check_for_remove(ship->last_room);
}

/* place ship on room */
void ship_to_room( SHIP_DATA *ship, ROOM_DATA *room )
{
	if (room == NULL )
	{
		log("SYSERR: NULL is not a room pointer.  Sorry.");
		return;
	}
	if ( ship == NULL )
	{
		log("SYSERR: NULL is not a ship pointer.  Sorry.");
		return;
	}

	ship->next_in_room	= room->ships;
	room->ships			= ship;
	ship->in_room		= room;

	/* Handle Wild Sectors */
	if ( IS_WILD(room) )
	{
		check_wild_move( NULL, NULL, ship, NULL, ship->last_room, room );

		if (ROOM_FLAGGED(room, ROOM_WILD_REMOVE))
			wild_remove_dequeue(room);
	}
}


/* ******************************************************************* */
/*                      C O M M U N I C A T I O N S                    */
/* ******************************************************************* */
/* Send a message to everyone on the ship */
void send_to_ship( char *msg, SHIP_DATA *ship )
{
	CHAR_DATA *ch;

	if (!ship)
		return;

	for (ch = ship->people; ch; ch = ch->next_in_vehicle)
		send_to_char(msg, ch);
}

/* Send a message to everyone on the ship's deck */
void send_to_deck( char *msg, SHIP_DATA *ship )
{
	CHAR_DATA *ch;

	for ( ch = ship->people; ch; ch = ch->next_in_vehicle )
		if ( ON_DECK(ch) )
			send_to_char( msg, ch );
}

/* ship tell */
int is_stell_ok( CHAR_DATA *vict )
{
	if (!IS_NPC(vict) && !vict->desc)        /* linkless */
		return (FALSE);

	if (PLR_FLAGGED(vict, PLR_WRITING))
		return (FALSE);

	if ((!IS_NPC(vict) && PRF_FLAGGED(vict, PRF_NOTELL)) || ROOM_FLAGGED(vict->in_room, ROOM_SOUNDPROOF))
		return (FALSE);

	return (TRUE);
}

ACMD(do_stell)
{
	CHAR_DATA *ppl;

	skip_spaces(&argument);
	one_argument( argument, arg );

	if ( !ch->in_ship )
	{
		send_to_char( "You aren't embarked on a ship.\r\n", ch );
		return;
	}

	if (!*arg)
	{
		send_to_char("What do you want to tell to people on ship?\r\n", ch);
		return;
	}

	if (!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_NOTELL))
	{
		send_to_char("You can't tell other people while you have notell on.\r\n", ch);
		return;
	}
	
	if (ROOM_FLAGGED(ch->in_room, ROOM_SOUNDPROOF))
	{
		send_to_char("The walls seem to absorb your words.\r\n", ch);
		return;
	}

	if (PRF_FLAGGED(ch,PRF_NOREPEAT))
		sprintf(buf, OK);
	else
		sprintf(buf, "&1You tell to everyone on ship, '%s'&0\r\n", arg );
	send_to_char(buf, ch);

	sprintf( buf, "&1$n tells to ship, '%s'&0", arg);

	for ( ppl = ch->in_ship->people; ppl; ppl = ppl->next_in_vehicle )
	{
		if ( ppl == ch )
			continue;

		if ( !is_stell_ok(ppl) )
			continue;

		act(buf, FALSE, ch, 0, ppl, TO_VICT | TO_SLEEP);
	}
}


/* ******************************************************************* */
/*                        I N F O R M A T I O N S                      */
/* ******************************************************************* */

void list_one_ship( SHIP_DATA *ship, CHAR_DATA *ch )
{
	if ( !ship || !ch )
		return;

	ch_printf( ch, "&b&6%s %s named '&7%s&6' is here at anchor.&0\r\n",
		UAN(ship->type->name), ship->type->name, ship->name );
}

void list_ship_to_char( SHIP_DATA *shiplist, CHAR_DATA *ch )
{
	SHIP_DATA *ship;

	if ( !shiplist || !ch || IS_NPC(ch) )
		return;

	for ( ship = shiplist; ship; ship = ship->next_in_room )
		list_one_ship( ship, ch );
}

/* ******************************************************************* */
/*                           C O M M A N D S                           */
/* ******************************************************************* */
ACMD(do_embark)
{
	SHIP_DATA *ship;

	one_argument(argument, arg);

	if ( !*arg )
	{
		send_to_char( "Embark on which ship?\r\n", ch );
		return;
	}

	if ( ch->in_ship )
	{
		send_to_char( "But you are currently embarked on a ship.. disembark first.\r\n", ch );
		return;
	}

	if ( !ch->in_room->ships )
	{
		send_to_char( "No ships here.\r\n", ch );
		return;
	}

	if ( !( ship = find_ship_in_room_by_name( ch->in_room, arg ) ) )
	{
		ch_printf( ch, "No ship named '%s' around.\r\n", arg );
		return;
	}

	if ( RIDING(ch) )
	{
		send_to_char( "No way you can go aboard while riding.\r\n", ch );
		return;
	}

	if ( WAGONER(ch) )
	{
		send_to_char( "No way you can go aboard while driving a vehicle.\r\n", ch );
		return;
	}

	if ( !can_go_aboard( ch, ship ) )
	{
		ch_printf( ch, "You are not authorized to go aboard of '%s'.\r\n",
			ship->name );
		return;
	}

	char_from_room(ch);
	char_to_ship(ch, ship, 0);

	ch_printf( ch, "You go aboard of '%s'.\r\n", ship->name );
}


ACMD(do_disembark)
{
	ROOM_DATA *pRoom;

	if ( !ch->in_ship )
	{
		send_to_char( "But you aren't aboard a ship.\r\n", ch );
		return;
	}

	if ( !SHIP_FLAGGED(ch->in_ship, SHIP_IN_PORT) )
	{
		send_to_char( "You cannot disembark from a ship that's not in a port.\r\n", ch );
		return;
	}

	if ( SECT(ch->in_room) != SECT_SHIP_STERN )
	{
		send_to_char( "You must be astern to disembark.\r\n", ch );
		return;
	}

	if ( !( pRoom = get_wild_room( ch->in_ship->port ) ) )
	{
		log( "SYSERR: no wilderness room as harbour at coord %d %d.",
			ch->in_ship->port->y, ch->in_ship->port->x );
		send_to_char( "Sorry, at the moment you cannot disembark.\r\n", ch );
		return;
	}

	ch_printf( ch, "You disembark from '%s'.\r\n", ch->in_ship->name );

	char_from_ship(ch);
	char_from_room(ch);
	char_to_room(ch, pRoom);
}

ACMD(do_courselist)
{
	COURSE_DATA *course;
	COURSE_STEP *cstep;
	PORT_DATA *port_orig, *port_dest;
	char cbuf[MAX_STRING_LENGTH];

	if ( !course_table )
	{
		send_to_char("No course loaded.\r\n", ch);
		return;
	}

	strcpy( cbuf, "List of Sea Course:\r\n"
		"---------------------------------------------\r\n" );

	for (course = course_table; course; course = course->next)
	{
		port_orig = get_port_by_coord( course->port_orig );
		port_dest = get_port_by_coord( course->port_end );

		sprintf(cbuf + strlen(cbuf),
			"From Port: %s\r\nTo Port  : %s\r\n",
			port_orig->name, port_dest->name );

		for ( cstep = course->first_step; cstep; cstep = cstep->next )
			sprintf(cbuf + strlen(cbuf),
				"  Step: %d %d\r\n",
				cstep->coord.y, cstep->coord.x );
	}

	strcat( cbuf, "---------------------------------------------\r\n" );
	page_string( ch->desc, cbuf, 1 );
}

/*
 * commands for ships:
 *
 * load		- carica merce a bordo
 * unload	- scarica merce
 * drive	- si mette al timone / lascia il timone
 * anchor	- butta l'ancora / recupera l'ancora
 * sail <porto>	- fa rotta verso un porto
 * repair	- mette in cantiere la nave per riparare i danni
 * auth <ch>	- autorizza un giocatore a salire a bordo
 */
ACMD(do_ship)
{
	SHIP_DATA *ship;
	argument = one_argument(argument, arg);

	if (!ch->in_ship)
	{
		send_to_char( "To be aboard a ship would surely help.. he he\r\n", ch );
		return;
	}

	if ( !*arg )
	{
		send_to_char( 
			"Available commands for ships are:\r\n"
			"-------------------------------------------------------------------"
			"<status>            print informations about the ship\r\n"
			"<drive>             grab/let go the helm to drive the ship\r\n"
			"*<charge>           list goods aboard\r\n"
			"*<load 'good'>      load good aboard\r\n"
			"*<unload 'good'>    unload good from ship\r\n"
			"<anchor>            place/remove anchor\r\n"
			"<sail 'port'>       automatic sailing to the given port destination\r\n"
			"<stop>              stops automatic sailing\r\n"
			"<captain 'name'>    nominate 'name' captain of the ship\r\n"
			"<captain>           revoke captaincy\r\n"
			"-------------------------------------------------------------------\r\n"
			"* = Uninmplemented.\r\n"
			, ch );	
		return;
	}

	ship = ch->in_ship;

	if (is_abbrev(arg, "drive"))
	{
		if ( SECT(ch->in_room) != SECT_SHIP_STERN )
		{
			send_to_char("You must be at the stern to drive a ship.\r\n", ch );
			return;
		}

		if ( GET_IDNUM(ch) != ship->idcaptain )
		{
			send_to_char("You must be the captain to drive the ship.\r\n", ch );
			return;
		}

		if ( !GET_SKILL(ch, SKILL_NAVIGATION) )
		{
			send_to_char( "You have no idea of how to drive a ship.\r\n", ch );
			return;
		}

		if ( GET_SKILL(ch, SKILL_NAVIGATION) < ship->type->skill_min )
		{
			send_to_char("You aren't skilled enough to drive a ship of this size.\r\n", ch);
			return;
		}

		/* first of all, check if ship helmperson and ch are the same */
		if ( ship->helmperson && ship->helmperson != ch )
		{
			send_to_char( "Someone else is driving the ship.\r\n", ch );
			return;
		}

		if ( ch->player_specials->athelm )
		{
			ch->player_specials->athelm	= FALSE;
			ship->helmperson			= NULL;
			send_to_char( "You let off the helm.\r\n", ch );
		}
		else
		{
			ch->player_specials->athelm	= TRUE;
			ship->helmperson			= ch;
			send_to_char( "You grab the helm and start to drive the ship.\r\n", ch );
		}
	}
	else if (is_abbrev(arg, "status"))
	{
		char sflags[128];

		sprintbit(ship->flags, ship_flags, sflags);
		sprintf(buf,
			"---------------------------------------------------------------\r\n"
			"Name   : '%s'\r\nType   : %s\r\nFlags  : %s\r\n",
			ship->name, ship->type->name, sflags );
		send_to_char(buf, ch);
		sprintf(buf,
			"---------------------------------------------------------------\r\n"
			"Power  : %s (%5d/%5d)\r\n"
			"Defense: %s (%5d/%5d)\r\n"
			"Health : %s (%5d/%5d)\r\n"
			"Speed  : %s (%5d/%5d)\r\n"
			"Loads  : %s (%5d/%5d)\r\n"
			"---------------------------------------------------------------\r\n",
			make_bar( ship->curr_val.power, ship->max_val.power, 40, 6 ),
			ship->curr_val.power,	ship->max_val.power,
			make_bar( ship->curr_val.defense, ship->max_val.defense, 40, 6 ),
			ship->curr_val.defense, ship->max_val.defense,
			make_bar( ship->curr_val.health, ship->max_val.health, 40, 6 ),
			ship->curr_val.health,	ship->max_val.health,
			make_bar( ship->curr_val.speed, ship->max_val.speed, 40, 6 ),
			ship->curr_val.speed,	ship->max_val.speed,
			make_bar( ship->curr_val.loads, ship->max_val.loads, 40, 6 ),
			ship->curr_val.loads,	ship->max_val.loads
			);
		send_to_char(buf, ch);
		if ( SHIP_FLAGGED(ship, SHIP_IN_COURSE) )
		{
			sprintf(buf, "&b&2In course for %s harbour, heading to the %s.&0\r\n"
				"Next move in %ld ticks.\r\n",
				get_port_name( ship->course->port_end), dirs[ship->direction],
				event_time(ship->action) );
			send_to_char(buf, ch);
		}
	}
	else if (is_abbrev(arg, "sail"))
	{
		COURSE_DATA *course;
		PORT_DATA *port_dest;
		char arg2[MAX_STRING_LENGTH];

		argument = one_argument( argument, arg2 );

		if ( SHIP_FLAGGED(ship, SHIP_AT_ANCHOR) )
		{
			send_to_char("Anchored ships cannot move.\r\n", ch );
			return;
		}

		if ( SHIP_FLAGGED(ship, SHIP_IN_COURSE) )
		{
			send_to_char("You are already following a course, use <ship stop> to stop the ship.\r\n", ch );
			return;
		}

		/* first of all, check if ship helmperson and ch are the same */
		if ( ship->helmperson && ship->helmperson != ch )
		{
			send_to_char( "Someone else is driving the ship.\r\n", ch );
			return;
		}

		if ( !GET_SKILL(ch, SKILL_NAVIGATION) )
		{
			send_to_char( "You have no idea of how set a course.\r\n", ch );
			return;
		}

		if ( GET_SKILL(ch, SKILL_NAVIGATION) < ship->type->skill_min )
		{
			send_to_char("You aren't skilled enough to drive a ship of this size.\r\n", ch);
			return;
		}

		if ( !*arg2 )
		{
			if ( ship->course )
			{
				ch_printf( ch, "You recover the sailing to '%s' harbour.\r\n",
					get_port_name(ship->course->port_end) );
				engage_course( ch, ship->course, TRUE );
				return;
			}
			send_to_char( "Sail to which port?\r\n", ch );
			return;
		}

		if ( !( port_dest = get_port_by_name( arg2 ) ) )
		{
			ch_printf( ch, "No port called %s is known.\r\n", arg2 );
			return;
		}

		if ( SHIP_FLAGGED(ship, SHIP_IN_PORT) )
		{
			if ( ship->port->y == port_dest->coord->y &&
			     ship->port->x == port_dest->coord->x )
			{
				send_to_char( "The shorter history sail. There you are.\r\n", ch );
				return;
			}
		}

		if ( SHIP_FLAGGED(ship, SHIP_IN_PORT) )
			course = get_course( ship->port, port_dest );
		else
		{
			send_to_char( "You can follow sea courses only when leaving a port.\r\n", ch );
			return;
		}

		if ( !course )
		{
			ch_printf( ch, "There is not a direct course to '%s' harbour.\r\n", arg2 );
			return;
		}

		if ( !knows_course( ch, course->vnum ) )
		{

			ch_printf( ch, "You don't know a course to '%s' harbour.\r\n", arg2 );
			return;
		}

		engage_course( ch, course, FALSE );
	}
	else if (!str_cmp(arg, "stop"))
	{
		if ( !SHIP_FLAGGED(ship, SHIP_IN_COURSE))
		{
			send_to_char( "You aren't following a course.\r\n", ch );
			return;
		}

		event_cancel( ship->action );
		ship->action = NULL;
		REMOVE_BIT(SHIP_FLAGS(ship), SHIP_IN_COURSE);
		send_to_char( "You stop follow the sea course.\r\n", ch );
		send_to_ship("The ship stops.\r\n", ship);
	}
	else if (is_abbrev(arg, "captain"))
	{
		CHAR_DATA *vict;
		ROOM_DATA *pRoom;
		char arg2[MAX_STRING_LENGTH];

		argument = one_argument( argument, arg2 );

		if ( !*arg2 )
		{
			if ( ship->idcaptain != -1 )
			{
				ship->idcaptain = -1;
				send_to_char("The ship is now without a captain.\r\n", ch);
			}
			else
				send_to_char( "Nominate captain who?\r\n", ch );
			return;
		}

		if ( ship->idcaptain != -1 )
		{
			send_to_char("The ship already has a captain.\r\n", ch);
			return;
		}

		if ( !SHIP_FLAGGED(ship, SHIP_IN_PORT) || !( pRoom = get_wild_room(ship->port) ) )
		{
			send_to_char("You can nominate only when ship is at one port.\r\n", ch);
			return;
		}

		if ( !( vict = get_char_room_vis( ch, arg2, NULL ) ) )
		{
			ch_printf( ch, "No one called '%s' around.\r\n", arg2 );
			return;
		}

		/* new introduction system */
		if ( !is_char_known(ch, vict) )
		{
			send_to_char("You cannot nominate captain someone you don't know.\r\n", ch );
			return;
		}

		/* assume command! */
		ship->idcaptain = GET_IDNUM(vict);

		act("$N is now the captain of the ship.", FALSE, ch, NULL, vict, TO_CHAR);
		act("$N is now the captain of the ship.", FALSE, ch, NULL, vict, TO_ROOM);
		ch_printf(vict, "You have been nominated captain of '&7%s&0'. Congratulations!!\r\n",
			ship->name);
	}
	// toggle anchor
	else if (is_abbrev(arg, "anchor"))
	{
		if (ship->idcaptain != GET_IDNUM(ch))
		{
			send_to_char("You must be the captain to place/remove the anchor.\r\n", ch );
			return;
		}

		if (SHIP_FLAGGED(ship, SHIP_AT_ANCHOR))
		{
			REMOVE_BIT(SHIP_FLAGS(ship), SHIP_AT_ANCHOR);
			send_to_char("You remove the anchor.\r\n", ch);
		}
		else
		{
			if ( SHIP_FLAGGED(ship, SHIP_IN_COURSE))
			{
				event_cancel( ship->action );
				ship->action = NULL;
				REMOVE_BIT(SHIP_FLAGS(ship), SHIP_IN_COURSE);
				send_to_char( "You stop follow the sea course.\r\n", ch );
				send_to_ship("The ship stops.\r\n", ship);
			}
			SET_BIT(SHIP_FLAGS(ship), SHIP_AT_ANCHOR);
			send_to_char("You place the anchor.\r\n", ch);
		}
	}
	else
		send_to_char( "Not yet implemented, sorry.\r\n", ch );
}


/* ******************************************************************** *
 *                         N A V I G A T I O N							*
 * ******************************************************************** *
 *																		*
 * Legenda of Ship Movement Functions									*
 *																		*
 * check_ship_speed:	calculate ship speed							*
 * ship_can_sail:		check to see if the ship can sail				*
 * engage_course:		inits the automatic sailing						*
 * ship_to_port:		handle the arrival in a port					*
 * ship_move:			called from perform_move() - manual sailing		*
 * create_sail_event:	setup the delayed sail event					*
 * ship_sail_event:		the delayed sail event							*
 *																		*
 * ******************************************************************** */

/*
 * When ch and goods are loaded into ship, they affect ship speed as:
 *
 * - no affection if < of 50% max_val.loads
 * - reduction of 1 pt speed between 51-70% (Min speed is always 1)
 * - reduction of 2 pt speed between 71-80% (Min speed is always 1)
 * - reduction of 3 pt speed between 81-90% (Min speed is always 1)
 * - reduction of 4 pt speed between 91-99% (Min speed is always 1)
 * - 100% speed is always 1
 * - > 100% speed is 0 -- ship can't move
 */
void check_ship_speed( SHIP_DATA *ship )
{
	int plusw, malus, orig;
	char msg[MAX_STRING_LENGTH];

	if ( !ship )
		return;

	orig = ship->curr_val.speed;
	plusw = percentage( ship->curr_val.loads, ship->max_val.loads );

	/* calculate speed penality */
	if		( plusw < 50 )		malus = 0;
	else if ( plusw < 70 )		malus = 1;
	else if ( plusw < 80 )		malus = 2;
	else if ( plusw < 90 )		malus = 3;
	else if ( plusw < 99 )		malus = 4;
	else if ( plusw == 100 )	malus = ship->max_val.speed - 1;
	else	// > 100%
	{
		send_to_ship( "The ship cannot move.. too weight aboard!\r\n", ship );
		ship->curr_val.speed = 0;
		return;
	}

	/* set up current speed */
	ship->curr_val.speed = MAX( 1, ship->max_val.speed - malus );

	if ( orig == ship->curr_val.speed )
		return;

	sprintf(msg, "The ship speed capacity is now %d nodes.\r\n", ship->curr_val.speed );
	send_to_ship( msg, ship );
}


bool ship_can_sail( CHAR_DATA *ch, SHIP_DATA *ship )
{
	/* check speed */
	if ( ship->curr_val.speed <= 0 )
	{
		send_to_char( "The ship cannot sail, too weight aboard.\r\n", ch );
		return (FALSE);
	}

	/* check health status (but only before leaving a port) */
	if ( percentage(ship->curr_val.health, ship->max_val.health) < 10 &&
		SHIP_FLAGGED(ship, SHIP_IN_PORT) )
	{
		send_to_char( "The ship is too damaged to leave the port.\r\n", ch );
		return (FALSE);
	}

	if (SHIP_FLAGGED(ship, SHIP_AT_ANCHOR))
	{
		send_to_char("Anchored ships cannot move.\r\n", ch);
		return (FALSE);
	}

	return (TRUE);
}


void ship_to_port( SHIP_DATA *ship, ROOM_DATA *pRoom )
{
	PORT_DATA *port;
	
	if ( !( port = get_port_by_coord( pRoom->coord ) ) )
	{
		log( "SYSERR: no port found at coord %d %d.", GET_RY(pRoom), GET_RX(pRoom) );
		send_to_ship( "Argh.. there is a problem, call an Immortal.\r\n", ship );
		return;
	}
	
	SET_BIT(SHIP_FLAGS(ship), SHIP_IN_PORT);
	REMOVE_BIT(SHIP_FLAGS(ship), SHIP_SAIL);

	if ( SHIP_FLAGGED(ship, SHIP_IN_COURSE) )
	{
		REMOVE_BIT(SHIP_FLAGS(ship), SHIP_IN_COURSE);
		ship->course = NULL;
		DISPOSE(ship->cstep);
	}

	ship->action = NULL;

	CREATE(ship->port, COORD_DATA, 1);
	ship->port->y = GET_RY(pRoom);
	ship->port->x = GET_RX(pRoom);
	
	sprintf( buf, "%s sails to %s.\r\n", ship->name, port->name );
	send_to_room( buf, ship->in_room );
	
	ship_from_room( ship );
	ship_to_room( ship, pRoom );
	
	sprintf( buf, "%s sails from the %s.\r\n", ship->name, dirs[rev_dir[ship->direction]] );
	send_to_room( buf, ship->in_room );
	
	sprintf( buf, "You enter %s harbour.\r\n", port->name );
	send_to_ship( buf, ship );
}

void create_sail_event( SHIP_DATA *ship, sh_int type )
{
	SAIL_EVENT *sail;
	int delay;

	if ( !ship )
		return;

	if ( ship->curr_val.speed <= 0 )
		return;

	if ( ship->action )
		return;

	CREATE( sail, SAIL_EVENT, 1 );
	sail->ship	= ship;
	sail->type	= type;

	delay = SHIP_SPEED_BASE - ship->curr_val.speed;

	ship->action = event_create(ship_sail_event, sail, delay);
}

void engage_course( CHAR_DATA *ch, COURSE_DATA *course, bool recover )
{
	SHIP_DATA *ship;
	EXIT_DATA *pexit;
	int dir;

	if ( !ch || !course )
		return;

	ship = ch->in_ship;

	if ( ship->action )
	{
		send_to_char( "You can't sail at the moment.\r\n", ch );
		return;
	}

	if ( !ship_can_sail( ch, ship ) )
		return;

	if ( !recover )
	{
		ship->course	= course;
		ship->cstep		= course->first_step;
	}

	dir = get_sail_dir( ship, &ship->cstep->coord, FALSE );

	if ( !( pexit = get_exit( ship->in_room, dir ) ) )
	{
		send_to_char("Alas, you cannot go that way...\r\n", ch);
		ship->course	= NULL;
		ship->cstep		= NULL;
		return;
	}

	// here.. if ship recover course immediately out of a port, we must handle..
	/* the ship enter a port */
	if ( SECT(pexit->to_room) == SECT_PORT )
	{
		ship_to_port( ship, pexit->to_room );
		return;		
	}

	if ( !water_sector(SECT(pexit->to_room)) )
	{
		send_to_char("You can't go on landmasses with a ship.\r\n", ch);
		ship->course	= NULL;
		ship->cstep		= NULL;
		return;
	}

	SET_BIT(SHIP_FLAGS(ship), SHIP_IN_COURSE);

	/* first move out of the port */
	if ( SECT(ship->in_room) == SECT_PORT )
	{
		REMOVE_BIT(SHIP_FLAGS(ship), SHIP_IN_PORT);
		SET_BIT(SHIP_FLAGS(ship), SHIP_SAIL);
		DISPOSE(ship->port);
	}

	/*
	dir = get_sail_dir( ship, &ship->cstep->coord, FALSE );

	if ( !( pexit = get_exit( ship->in_room, dir ) ) )
	{
		send_to_char("Alas, you cannot go that way...\r\n", ch);
		return;
	}

	if ( !water_sector(SECT(pexit->to_room)) )
	{
		send_to_char("You can't go on landmasses with a ship.\r\n", ch);
		return;
	}
	*/
	
	/* save the direction for automatic sailing */
	ship->direction = dir;

	create_sail_event(ship, SAIL_COURSE);
}


void ship_move( CHAR_DATA *ch, int dir, int special_check )
{
	SHIP_DATA *ship;
	EXIT_DATA *pexit;

	if ( !ch || !ch->in_ship || !ch->player_specials->athelm )
		return;

	ship = ch->in_ship;

	if ( dir < 0 || dir >= NUM_OF_DIRS )
	{
		send_to_char("Invalid direction.\r\n", ch );
		return; 
	}

	if ( SHIP_FLAGGED(ship, SHIP_AT_ANCHOR) )
	{
		send_to_char("Anchored ships cannot move.\r\n", ch );
		return;
	}

	/* hmmm.. to check again later */
	if ( ship->action )
	{
		if ( SHIP_FLAGGED(ship, SHIP_IN_COURSE ) )
		{
			event_cancel( ship->action );
			ship->action = NULL;
			REMOVE_BIT(SHIP_FLAGS(ship), SHIP_IN_COURSE);
			send_to_char( "You stop the course sailing.\r\n", ch );
		}
		else
		{
			send_to_char("The ship is currently performing another action.\r\n", ch );
			return;
		}
	}

	if ( !ship_can_sail( ch, ship ) )
		return;

	if ( !( pexit = get_exit( ship->in_room, dir ) ) )
	{
		send_to_char("Alas, you cannot go that way...\r\n", ch);
		return;
	}

	/* the ship enter a port */
	if ( SECT(pexit->to_room) == SECT_PORT )
	{
		ship_to_port( ship, pexit->to_room );
		return;		
	}
	
	if ( !water_sector(SECT(pexit->to_room)) )
	{
		send_to_char("You can't go on landmasses with a ship.\r\n", ch);
		return;
	}
	
	/* first move out of the port */
	if ( SECT(ship->in_room) == SECT_PORT )
	{
		REMOVE_BIT(SHIP_FLAGS(ship), SHIP_IN_PORT);
		SET_BIT(SHIP_FLAGS(ship), SHIP_SAIL);
		DISPOSE(ship->port);
	}

	/* save the direction for automatic sailing */
	ship->direction = dir;

	create_sail_event(ship, SAIL_MANUAL);
}


/* Ship movement.. */
EVENTFUNC(ship_sail_event)
{
	SAIL_EVENT *sail = (SAIL_EVENT *) (event_obj);
	SHIP_DATA *ship;
	EXIT_DATA *pexit;
	CHAR_DATA *ppl;

	ship = sail->ship;

	if ( !ship || !ship->action)
	{
		DISPOSE(event_obj);
		return (0);
	}

	if ( sail->type == SAIL_MANUAL )
		ship->action	= NULL;		/* clear ship action */

	// check for speed
	if ( ship->curr_val.speed <= 0 )
	{
		DISPOSE(event_obj);
		return (0);
	}

	// check for exit
	if ( !( pexit = get_exit( ship->in_room, ship->direction ) ) )
	{
		log("SYSERR: sail_event() -- invalid exit to %d for ship %s.",
			ship->direction, ship->name );
		DISPOSE(event_obj);
		return (0);
	}

	/* the ship enter a port */
	if ( SECT(pexit->to_room) == SECT_PORT )
	{
		ship_to_port( ship, pexit->to_room );
		DISPOSE(event_obj);
		return (0);
	}

	// check for water
	if (!water_sector(SECT(pexit->to_room)))
	{
		log("SYSERR: sail_event() -- not sea exit to %s for ship %s.",
			dirs[ship->direction], ship->name );
		DISPOSE(event_obj);
		return (0);
	}

	sprintf( buf, "%s sails to the %s.\r\n", ship->name, dirs[ship->direction] );
	send_to_room( buf, ship->in_room );

	ship_from_room( ship );
	ship_to_room( ship, pexit->to_room );

	sprintf( buf, "%s sails from the %s.\r\n", ship->name, dirs[rev_dir[ship->direction]] );
	send_to_room( buf, ship->in_room );

	for ( ppl = ship->people; ppl; ppl = ppl->next_in_vehicle )
	{
		if ( ON_DECK(ppl) && !FIGHTING(ppl) )
			look_at_wild(ppl, TRUE);
	}

	if ( sail->type == SAIL_COURSE )
	{
		int dir;
		
		/* have we reached the step? */
		if ( ship->in_room->coord->y == ship->cstep->coord.y &&
		     ship->in_room->coord->x == ship->cstep->coord.x )
		{
			/* check for last step */
			if ( !ship->cstep->next )
			{
				COURSE_STEP *csnew;

				CREATE(csnew, COURSE_STEP, 1);

				csnew->next		= NULL;
				csnew->prev		= NULL;
				csnew->coord.y	= ship->course->port_end->y;
				csnew->coord.x	= ship->course->port_end->x;

				/* next step will be the port itself */
				ship->cstep	= csnew;
			}
			else
				ship->cstep	= ship->cstep->next;
		}
		
		dir = get_sail_dir( ship, &ship->cstep->coord, FALSE );

		if ( dir == NOWHERE )
		{
			log("SYSERR: ship_sail_event() - cannot get a direction for next step %d %d.",
				ship->cstep->coord.y, ship->cstep->coord.x);
			REMOVE_BIT(SHIP_FLAGS(ship), SHIP_IN_COURSE);
			REMOVE_BIT(SHIP_FLAGS(ship), SHIP_SAIL);
			DISPOSE(event_obj);
			return (0);
		}

		ship->direction = dir;

		return (SHIP_SPEED_BASE - ship->curr_val.speed);
	}
	else
	{
		DISPOSE(event_obj);
		return (0);
	}
}

/* ******************************************************************* */
/* Ship making                                                         */
/* ******************************************************************* */

void free_ship( SHIP_DATA *ship )
{
	SHIP_DATA *temp;

	/* cut off from the list, if needed */
	if ( ship->next )
		UNLINK(ship, first_ship, last_ship, next, prev);

	if ( ship->next_in_room && ship->in_room )
		REMOVE_FROM_LIST( ship, ship->in_room->ships, next_in_room );

	if ( ship->people )
	{
		/* transfer people to limbo */
		CHAR_DATA *ppl;

		while (ship->people)
		{
			ppl = ship->people;
			char_from_ship(ppl);
			char_from_room(ppl);
			char_to_room(ppl, get_room(1));
		}
	}

	if ( ship->rooms[0] )
	{
	}

	ship->action		= NULL;
	ship->helmperson	= NULL;
	ship->in_room		= NULL;
	ship->last_room		= NULL;
	ship->next			= NULL;
	ship->next_in_room	= NULL;
	ship->people		= NULL;
	ship->storeroom		= NULL;

	if ( ship->name )
		free(ship->name );
	if ( ship->port )
		free(ship->port );

	ship->authorized	= NULL;
	ship->type			= NULL;

	DISPOSE(ship);
}


SHIP_DATA *new_ship( void )
{
	SHIP_DATA *ship;

	CREATE( ship, SHIP_DATA, 1 );
	ship->name			= NULL;
	ship->in_room		= NULL;
	ship->last_room		= NULL;
	ship->people		= NULL;
	ship->next			= NULL;
	ship->prev			= NULL;
	ship->next_in_room	= NULL;
	ship->action		= NULL;
	ship->storeroom		= NULL;
	ship->helmperson	= NULL;
	ship->port			= NULL;
	ship->type			= NULL;
	ship->authorized	= NULL;
	ship->course		= NULL;
	ship->cstep			= NULL;
	ship->flags			= 0;
	ship->direction		= NOWHERE;
	ship->idowner		= 0;
	ship->idcaptain		= 0;
	ship->vnum			= NOTHING;

	return (ship);
}


/* make the ship inside */
bool create_ship_rooms(SHIP_DATA *ship)
{
	FILE *fp;
	EXTRA_DESCR *new_descr;
	char fname[128];
	char letter, *flags;
	int room, door;

	sprintf(fname, "%s%d.ship", SHIP_PREFIX, ship->type->vnum);
	if (!(fp = fopen(fname, "r")))
	{
		log("SYSERR: Unable to open ship template file %s.", fname);
		return (FALSE);
	}

	CREATE(ship->rooms, ROOM_DATA *, ship->type->size);
	for (room = 0; room < ship->type->size; room++)
	{
		CREATE(ship->rooms[room], ROOM_DATA, 1);
		ship->rooms[room]->number = room;
		ship->rooms[room]->zone = SHIP_ZONE;

		CREATE(ship->rooms[room]->extra_data, ROOM_EXTRA, 1);
		ship->rooms[room]->extra_data->vnum = ship->vnum;
	}

	for ( ;; )
	{
		letter = fread_letter(fp);

		if (feof(fp))
			break;

		if (letter == '$')
			break;

		if (letter == '*')
		{
			fread_to_eol(fp);
			continue;
		}
		
		if (letter != '#')
		{
			log("create_ship_rooms(): # not found.");
			break;
		}
		
		room = fread_number(fp);

		if (room < 0 || room > ship->type->size)
			break;

		ship->rooms[room]->number		= room;
		ship->rooms[room]->name			= fread_string_nospace(fp);
		ship->rooms[room]->description	= fread_string_nospace(fp);
		flags							= fread_word(fp);
		ship->rooms[room]->room_flags	= asciiflag_conv(flags);
		ship->rooms[room]->sector_type	= fread_number(fp);

		if (ship->rooms[room]->sector_type == SECT_SHIP_HOLD)
			ship->storeroom = ship->rooms[room];

		for ( ;; )
		{
			letter = fread_letter(fp);
			if (letter == '*')
			{
				fread_to_eol(fp);
				continue;
			}
			if (letter == 'S' || letter == '$')
				break;

			switch (letter)
			{
			case 'D':
				door = fread_number(fp);
				if ( door < 0 || door > NUM_OF_DIRS )
				{
					log( "create_ship_rooms: vnum %d has bad door number %d.", room, door );
					break;
				}
				else
				{
					EXIT_DATA *pexit;
					char flags[128];
					char *ln;
					int x1, x2;
					
					pexit = make_exit(ship->rooms[room], NULL, door);
					pexit->description	= fread_string_nospace(fp);
					pexit->keyword		= fread_string_nospace(fp);
					ln					= fread_line(fp);
					x1=x2=0;
					sscanf( ln, "%s %d %d ", &flags, &x1, &x2 );
					
					pexit->key			= x1;
					pexit->vnum			= x2;
					pexit->vdir			= door;
					pexit->exit_info	= asciiflag_conv(flags);
					
					pexit->to_room		= ship->rooms[pexit->vnum];

					/* sanity check */
					if (pexit->exit_info && !EXIT_FLAGGED(pexit, EX_ISDOOR))
						SET_BIT(pexit->exit_info, EX_ISDOOR);
				}
				break;

			case 'E':
				CREATE(new_descr, EXTRA_DESCR, 1);
				new_descr->keyword		= fread_string(fp, buf2);
				new_descr->description	= fread_string(fp, buf2);
				new_descr->next			= ship->rooms[room]->ex_description;
				ship->rooms[room]->ex_description = new_descr;
				break;
			}
		}

		if (letter == '$')
			break;
	}

	fclose(fp);
	return (TRUE);
}


SHIP_DATA *create_ship( CHAR_DATA *ch, SHIP_TYPE *tShip, PORT_DATA *port, char *arg)
{
	SHIP_DATA *ship;
	int hp;

	if ( !tShip || !port )
	{
		log( "SYSERR: Create_ship() - no proto or port data" );
		return (NULL);
	}

	ship = new_ship();

	ship->name				= ( *arg ? str_dup(arg) : str_dup(tShip->name) );
	ship->idowner			= GET_IDNUM(ch);
	ship->idcaptain			= GET_IDNUM(ch);
	/* hmmmmm... */
	ship->vnum				= ++top_ship_num;

	ship->type				= tShip;

	CREATE( ship->port, COORD_DATA, 1 );
	ship->port->y			= port->coord->y;
	ship->port->x			= port->coord->x;

	hp = tShip->value.health * tShip->size;
	ship->curr_val.health	= ship->max_val.health	= hp;

	ship->curr_val.defense	= ship->max_val.defense	= tShip->value.defense;
	ship->curr_val.equip	= ship->max_val.equip	= tShip->value.equip;
	ship->curr_val.power	= ship->max_val.power	= tShip->value.power;
	ship->curr_val.speed	= ship->max_val.speed	= tShip->value.speed;
	ship->max_val.loads		= tShip->value.loads;
	ship->curr_val.loads	= 0;

	create_ship_rooms( ship );

	SET_BIT(SHIP_FLAGS(ship), SHIP_IN_PORT);

	/* add to the list */
	LINK(ship, first_ship, last_ship, next, prev);

	return (ship);
}

/* Imms command */
ACMD(do_shipsetup)
{
	SHIP_DATA *ship;
	COORD_DATA *coord;
	ROOM_DATA *pRoom;
	PORT_DATA *port;
	char arg1[MAX_STRING_LENGTH], arg2[MAX_STRING_LENGTH];
	char *shipname;
	int sh_type = 0;

	argument = one_argument( argument, arg );
	argument = two_arguments( argument, arg1, arg2 );

	shipname = strtok(argument, "'");
	
	if (shipname == NULL)
	{
		send_to_char("Which name?\r\n", ch);
		return;
	}
	
	shipname = strtok(NULL, "'");
	if (shipname == NULL)
	{
		send_to_char("Ship names must be enclosed in the Holy Magic Symbols: '\r\n", ch);
		return;
	}

	if ( !*arg || !*arg1 || !*arg2 || !*shipname )
	{
		send_to_char(
			"Usage: shipsetup <type> <y> <x> 'name'\r\n"
			" <type>   = type of ship\r\n"
			" <y> <x>  = port coordinates\r\n"
			" 'name'   = name of the ship\r\n",
			ch );
		return;
	}

	sh_type = atoi(arg);

	if ( sh_type < 0 || sh_type > top_ship_table - 1 )
	{
		ch_printf( ch, "Valid ship types goes from 0 to %d.\r\n", top_ship_table );
		return;
	}

	CREATE(coord, COORD_DATA, 1);
	coord->y = atoi(arg1);
	coord->x = atoi(arg2);

	if ( !check_coord( coord->y, coord->x ) )
		return;

	if ( !( port = get_port_by_coord( coord ) ) )
	{
		ch_printf( ch, "No port found at coord %d %d.\r\n", coord->y, coord->x );
		return;
	}

	pRoom = get_wild_room( coord );
	if ( !pRoom || SECT(pRoom) != SECT_PORT )
	{
		send_to_char("Invalid coordinates.\r\n", ch );
		return;
	}

	ship = create_ship( ch, ship_table[sh_type], port, shipname );
	ship_to_room( ship, pRoom );
}

/* ******************************************************************* */
/* Ship Files handling                                                 */
/* ******************************************************************* */

/* write on file the building inside */
void fwrite_ship_rooms(SHIP_DATA *ship, FILE *fp)
{
	ROOM_DATA *room;
	EXIT_DATA *pexit;
	char dflags[128];
	int num;

	for (num = 0; num < ship->type->size; num++)
	{
		room = ship->rooms[num];

		if (!room) break;

		/* Copy the description and strip off trailing newlines */
		strcpy(buf, room->description ? room->description : "Empty room.");
		strip_cr(buf);

		sprintascii(buf2, room->room_flags);

		fprintf(fp,
			"#%d\n"
			"%s~\n"
			"%s~\n"
			"%s %d\n",
			room->number,
			room->name ? room->name : "Untitled",
			buf, buf2, room->sector_type
			);

		/* Now you write out the exits for the room */
		for (pexit = room->first_exit; pexit; pexit = pexit->next)
		{
			/* check for non-building exits */
			if (!IS_SHIP(pexit->to_room))
				continue;
			
			if (pexit->description)
			{
				strcpy(buf, pexit->description);
				strip_cr(buf);
			}
			else
				*buf = '\0';
			
			if (pexit->keyword)
				strcpy(buf1, pexit->keyword);
			else
				*buf1 = '\0';
			
			/* New door flags handling -- Fab 2000 */
			REMOVE_BIT(EXIT_FLAGS(pexit), EX_LOCKED);
			REMOVE_BIT(EXIT_FLAGS(pexit), EX_CLOSED);
			REMOVE_BIT(EXIT_FLAGS(pexit), EX_HIDDEN);

			/* sanity check */
			if (EXIT_FLAGS(pexit) && !EXIT_FLAGGED(pexit, EX_ISDOOR))
				SET_BIT(EXIT_FLAGS(pexit), EX_ISDOOR);
			
			sprintascii(dflags, pexit->exit_info);
			
			fprintf(fp,
				"D%d\n"
				"%s~\n"
				"%s~\n"
				"%s %d %d\n", pexit->vdir, buf, buf1, dflags,
				pexit->key, ( pexit->to_room != NULL ? pexit->to_room->number : NOWHERE ) );
		}

		if (room->ex_description)
		{
			EXTRA_DESCR *xdesc;
			
			for (xdesc = room->ex_description; xdesc; xdesc = xdesc->next)
			{
				strcpy(buf, xdesc->description);
				strip_cr(buf);
				fprintf(fp,
					"E\n"
					"%s~\n"
					"%s~\n", xdesc->keyword, buf);
			}
		}
		
		fprintf(fp, "S\n");
	}

	/* Write the final line */
	fprintf(fp, "$\n\n");
}

void fwrite_ship( SHIP_DATA *ship )
{
	FILE *fp;
	char fname[128];
	int num;

	sprintf(fname, "%s%d.ship", LIB_SHIPS, ship->vnum);
	if ( !(fp = fopen( fname, "w" ) ) )
	{
		log( "Cannot open ship file %s", fname );
		return;
	}

	fprintf( fp, "#SHIP\n" );
	fprintf( fp, "Vnum           %d\n",	ship->vnum );
	fprintf( fp, "Name           %s~\n",	ship->name );
	fprintf( fp, "Type           %s~\n",	ship->type->name );
	fprintf( fp, "Flags          %d\n",	ship->flags );

	fprintf( fp, "Val_Defense    %hd %hd\n",	ship->curr_val.defense, ship->max_val.defense );
	fprintf( fp, "Val_Equip      %hd %hd\n",	ship->curr_val.equip, ship->max_val.equip );
	fprintf( fp, "Val_Health     %hd %hd\n",	ship->curr_val.health, ship->max_val.health );
	fprintf( fp, "Val_Loads      %hd %hd\n",	ship->curr_val.loads, ship->max_val.loads );
	fprintf( fp, "Val_Power      %hd %hd\n",	ship->curr_val.power, ship->max_val.power );
	fprintf( fp, "Val_Speed      %hd %hd\n",	ship->curr_val.speed, ship->max_val.speed );

	fprintf( fp, "IdOwner        %ld\n",	ship->idowner );
	fprintf( fp, "IdCaptain      %ld\n",	ship->idcaptain );
	if ( ship->authorized )
	{
		AUTH_DATA *pAuth;

		for (pAuth = ship->authorized; pAuth; pAuth = pAuth->next)
			fprintf(fp, "IdAuth         %ld %s\n", pAuth->id_num, pAuth->name);
	}

	if ( SHIP_FLAGGED(ship, SHIP_IN_PORT) )
		fprintf( fp, "Port           %hd %hd\n", ship->port->y, ship->port->x );
	else if ( SHIP_FLAGGED(ship, SHIP_AT_ANCHOR) )
		fprintf( fp, "Anchored       %hd %hd\n", GET_Y(ship), GET_X(ship) );
	else
		fprintf( fp, "Sailing        %hd %hd\n", GET_Y(ship), GET_X(ship) );

	if ( SHIP_FLAGGED(ship, SHIP_IN_COURSE) )
	{
		fprintf( fp, "Course         %d\r\n", ship->course );
		fprintf( fp, "CStep          %hd %hd\r\n", ship->cstep->coord.y, ship->cstep->coord.x );
	}

	fprintf( fp, "End\n\n" );

	fprintf(fp, "#ROOMS\n");
	
	fwrite_ship_rooms(ship, fp);
		
	for (num = 0; num < ship->type->size; num++)
	{
		if (ship->rooms[num] && ship->rooms[num]->first_content)
		{
			fprintf(fp, "#ROOMCONTENT\n");
			fprintf(fp, "Room          %d\n", num);
			save_objs( ship->rooms[num]->last_content, fp, 0, 0);
			fprintf(fp, "End\n\n");
		}
	}

	fprintf( fp, "#END\n" );
	fclose(fp);
}


void SaveShips( void )
{
	FILE *fp;
	SHIP_DATA *ship;
	char fname[128];

	if ( !first_ship )
		return;

	sprintf( fname, "%sindex", LIB_SHIPS );
	if ( !( fp = fopen( fname, "w" ) ) )
	{
		log( "SYSERR: unable to write index file for ships." );
		return;
	}

	for ( ship = first_ship; ship; ship = ship->next )
	{
		if ( ship->vnum == NOTHING )
			continue;

		fprintf( fp, "%d.ship\n", ship->vnum );
		fwrite_ship( ship );
	}

	fprintf( fp, "$\n" );
	fclose(fp);
}


/* READING */

SHIP_TYPE *parse_ship_type( char *tname )
{
	int y;

	for ( y = 0; y < top_ship_table; y++ )
	{
		if ( !ship_table[y] || !ship_table[y]->name )
			continue;

		if ( !str_cmp( tname, ship_table[y]->name ) )
			return ( ship_table[y] );
	}

	return (NULL);
}


bool fread_one_ship( SHIP_DATA *ship, FILE *fp )
{
	char *word;
	bool fMatch;

	for ( ; ; )
	{
		word   = feof( fp ) ? "End" : fread_word( fp );
		fMatch = FALSE;
		
		switch ( UPPER(word[0]) )
		{
		case '*':
			fMatch = TRUE;
			fread_to_eol( fp );
			break;

		case 'A':
			if ( !str_cmp( word, "Anchored" ) )
			{
				ROOM_DATA *pRoom;

				CREATE( ship->port, COORD_DATA, 1 );
				ship->port->y	= fread_number(fp);
				ship->port->x	= fread_number(fp);

				pRoom = create_wild_room( ship->port, FALSE );

				fMatch = TRUE;
				break;
			}
			break;

		case 'C':
			if ( !str_cmp( word, "Course" ) )
			{
				COURSE_DATA *course;
				int vnum = fread_number(fp);

				course = get_course_by_vnum( vnum );

				ship->course = course;
				/* should set on the course flag?? hmmmm */
				SET_BIT(SHIP_FLAGS(ship), SHIP_IN_COURSE);

				fMatch = TRUE;
				break;
			}
			if ( !str_cmp( word, "CStep" ) )
			{
				COORD_DATA coord;

				coord.y = fread_number(fp);
				coord.x = fread_number(fp);

				if ( ship->course )
					ship->cstep = get_course_step( ship->course, &coord );

				fMatch = TRUE;
				break;
			}
			break;

		case 'E':
			if ( !str_cmp( word, "End" ) )
			{
				if ( ship->vnum == NOTHING || !ship->type )
				{
					if ( ship->port )
						DISPOSE(ship->port);
					DISPOSE(ship);
					return (FALSE);
				}

				create_ship_rooms( ship );
				return (TRUE);
			}
			break;


		case 'F':
			KEY( "Flags",		ship->flags,			fread_number(fp) );
			break;

		case 'I':
			KEY( "IdOwner",		ship->idowner,			fread_number(fp) );
			KEY( "IdCaptain",	ship->idcaptain,		fread_number(fp) );
			if ( !str_cmp(word, "IdAuth"))
			{
				AUTH_DATA *pAuth;

				CREATE(pAuth, AUTH_DATA, 1);
				pAuth->id_num		= fread_number(fp);
				pAuth->name			= str_dup(fread_word(fp));

				// add to the list
				pAuth->next		= ship->authorized;
				ship->authorized	= pAuth;
				
				fMatch = TRUE;
				break;
			}
			break;

		case 'N':
			KEY( "Name",		ship->name,				fread_string_nospace(fp) );
			break;

		case 'P':
			if ( !str_cmp( word, "Port" ) )
			{
				CREATE( ship->port, COORD_DATA, 1 );
				ship->port->y	= fread_number(fp);
				ship->port->x	= fread_number(fp);

				fMatch = TRUE;
				break;
			}
			break;

		case 'S':
			if ( !str_cmp( word, "Sailing" ) )
			{
				ROOM_DATA *pRoom;

				CREATE( ship->port, COORD_DATA, 1 );
				ship->port->y	= fread_number(fp);
				ship->port->x	= fread_number(fp);

				pRoom = create_wild_room( ship->port, FALSE );

				fMatch = TRUE;
				break;
			}
			break;

		case 'T':
			if ( !str_cmp( word, "Type" ) )
			{
				ship->type = parse_ship_type( fread_string_nospace(fp) );
				fMatch = TRUE;
				break;
			}
			break;

		case 'V':
			KEY( "Vnum",		ship->vnum,				fread_number(fp) );
			if ( !str_cmp( word, "Val_Defense" ) )
			{
				ship->curr_val.defense	= fread_number(fp);
				ship->max_val.defense	= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			if ( !str_cmp( word, "Val_Equip" ) )
			{
				ship->curr_val.equip	= fread_number(fp);
				ship->max_val.equip	= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			if ( !str_cmp( word, "Val_Health" ) )
			{
				ship->curr_val.health	= fread_number(fp);
				ship->max_val.health	= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			if ( !str_cmp( word, "Val_Loads" ) )
			{
				ship->curr_val.loads	= fread_number(fp);
				ship->max_val.loads	= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			if ( !str_cmp( word, "Val_Power" ) )
			{
				ship->curr_val.power	= fread_number(fp);
				ship->max_val.power	= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			if ( !str_cmp( word, "Val_Speed" ) )
			{
				ship->curr_val.speed	= fread_number(fp);
				ship->max_val.speed	= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			break;
		}

		if ( !fMatch )
			log( "fread_ship_type: no match: %s", word );
	}

	return (FALSE);
}

#define MAX_BAG_ROWS	5

/* load contents of a ship room from file */
void fread_ship_contents( SHIP_DATA *ship, FILE *fp )
{
	OBJ_DATA *obj, *obj2, *cont_row[MAX_BAG_ROWS];
	int location, j, roomnum;
	char *word;
	bool fMatch;

	/* Empty all of the container lists (you never know ...) */
	for (j = 0; j < MAX_BAG_ROWS; j++)
		cont_row[j] = NULL;

	for ( ; ; )
	{
		word   = feof( fp ) ? "End" : fread_word( fp );
		fMatch = FALSE;
		
		switch ( UPPER(word[0]) )
		{
		case '*':
			fMatch = TRUE;
			fread_to_eol( fp );
			break;

		case 'E':
			if ( !str_cmp( word, "End" ) )
				return;

		case 'R':
			KEY( "Room",		roomnum,				fread_number(fp) );
			break;

		case '#':
			// skip #OBJECT line..
			fread_to_eol( fp );
			
			if ( ( obj = fread_one_obj( fp, &location ) ) == NULL )
				continue;
			
			obj_to_room( obj, ship->rooms[roomnum] );
			
			for (j = MAX_BAG_ROWS - 1; j > -location; j--)
			{
				if (cont_row[j])	/* No container, back to inventory. */
				{
					for (; cont_row[j]; cont_row[j] = obj2)
					{
						obj2 = cont_row[j]->next_content;
						obj_to_room( cont_row[j], ship->rooms[roomnum] );
					}
					cont_row[j] = NULL;
				}
			}
			if (j == -location && cont_row[j])	/* Content list exists. */
			{
				if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER ||
					GET_OBJ_TYPE(obj) == ITEM_MISSILE_CONT )
				{
					/* Take the item, fill it, and give it back. */
					obj_from_room(obj);
					obj->first_content = NULL;
					obj->last_content = NULL;
					for (; cont_row[j]; cont_row[j] = obj2)
					{
						obj2 = cont_row[j]->next_content;
						obj_to_obj(cont_row[j], obj);
					}
					obj_to_room( obj, ship->rooms[roomnum] );
				}
				else	/* Object isn't container, empty content list. */
				{
					for (; cont_row[j]; cont_row[j] = obj2)
					{
						obj2 = cont_row[j]->next_content;
						obj_to_room( cont_row[j], ship->rooms[roomnum] );
					}
					cont_row[j] = NULL;
				}
			}
			if (location < 0 && location >= -MAX_BAG_ROWS)
			{
				obj_from_room(obj);
				if ((obj2 = cont_row[-location - 1]) != NULL)
				{
					while (obj2->next_content)
						obj2 = obj2->next_content;
					obj2->next_content = obj;
				}
				else
					cont_row[-location - 1] = obj;
			}
			break;
		}
	}
}

/* load from file the building inside */
void fread_ship_rooms(SHIP_DATA *ship, FILE *fp)
{
	EXTRA_DESCR *new_descr;
	char letter, *flags;
	int room, door;

	CREATE(ship->rooms, ROOM_DATA *, ship->type->size);

	for (room = 0; room < ship->type->size; room++)
	{
		CREATE(ship->rooms[room], ROOM_DATA, 1);
		ship->rooms[room]->zone = SHIP_ZONE;

		CREATE(ship->rooms[room]->extra_data, ROOM_EXTRA, 1);
		ship->rooms[room]->extra_data->vnum = ship->vnum;
	}

	for ( ;; )
	{
		letter = fread_letter(fp);

		if (feof(fp))
			break;

		if (letter == '$')
			break;

		if (letter == '*')
		{
			fread_to_eol(fp);
			continue;
		}
		
		if (letter != '#')
		{
			log("fread_ship_rooms(): # not found.");
			break;
		}
		
		room = fread_number(fp);

		if (room < 0 || room > ship->type->size)
			break;

		ship->rooms[room]->number		= room;

		ship->rooms[room]->name			= fread_string_nospace(fp);
		ship->rooms[room]->description	= fread_string_nospace(fp);
		flags							= fread_word(fp);
		ship->rooms[room]->room_flags	= asciiflag_conv(flags);
		ship->rooms[room]->sector_type	= fread_number(fp);

		for (;;)
		{
			letter = fread_letter(fp);
			if (letter == '*')
			{
				fread_to_eol(fp);
				continue;
			}
			if (letter == 'S' || letter == '$')
				break;

			switch (letter)
			{
			case 'D':
				door = fread_number(fp);
				if ( door < 0 || door > NUM_OF_DIRS )
				{
					log("fread_ship_rooms: vnum %d has bad door number %d.", room, door);
					break;
				}
				else
				{
					EXIT_DATA *pexit;
					char flags[128];
					char *ln;
					int x1, x2;
					
					pexit = make_exit( ship->rooms[room], NULL, door );
					pexit->description	= fread_string_nospace(fp);
					pexit->keyword		= fread_string_nospace(fp);
					ln					= fread_line(fp);
					x1=x2=0;
					sscanf( ln, "%s %d %d ", &flags, &x1, &x2 );
					
					pexit->key			= x1;
					pexit->vnum			= x2;
					pexit->vdir			= door;
					pexit->exit_info	= asciiflag_conv( flags );
					
					pexit->to_room		= ship->rooms[pexit->vnum];

					/* sanity check */
					if ( pexit->exit_info && !EXIT_FLAGGED( pexit, EX_ISDOOR ) )
						SET_BIT( pexit->exit_info, EX_ISDOOR );
				}
				break;

			case 'E':
				CREATE(new_descr, EXTRA_DESCR, 1);
				new_descr->keyword		= fread_string(fp, buf2);
				new_descr->description	= fread_string(fp, buf2);
				new_descr->next			= ship->rooms[room]->ex_description;
				ship->rooms[room]->ex_description = new_descr;
				break;
			}
		}

		if (letter == '$')
			break;
	}
}

SHIP_DATA *fread_ship( char *sname ) 
{
	FILE *fp;
	SHIP_DATA *ship;
	char fname[128];
	char letter;
	char *word;

	sprintf( fname, "%s%s", LIB_SHIPS, sname );
	if ( !( fp = fopen(fname, "r") ) )
		return (NULL);

	ship = new_ship();

	for ( ;; )
	{
		letter = fread_letter( fp );
		if ( letter == '*' )
		{
			fread_to_eol( fp );
			continue;
		}
		
		if ( letter != '#' )
		{
			log( "fread_ship: # not found." );
			break;
		}
		
		word = fread_word( fp );
		if ( !str_cmp( word, "SHIP" ) )
		{
			if ( !fread_one_ship( ship, fp ) )
			{
				fclose(fp);
				return (NULL);
			}
		}
		else if (!strcmp(word, "ROOMS"))
			fread_ship_rooms(ship, fp);
		else if ( !str_cmp( word, "ROOMCONTENT" ) )
			fread_ship_contents( ship, fp );
		else if ( !str_cmp( word, "END" ) )
			break;
		else
		{
			log( "fread_ship: bad section." );
			continue;
		}
	}

	fclose( fp );

	return (ship);
}

/* load all saved rooms */
void LoadShips( void )
{
	FILE *fp;
	SHIP_DATA *ship;
	char fname[128];

	sprintf( fname, "%sindex", LIB_SHIPS );

	if ( !( fp = fopen(fname, "r") ) )
		return;

	while (!feof(fp))
	{
		char *shipname = fread_word( fp );

		if ( shipname[0] == '$' )
			break;

		if ( !( ship = fread_ship( shipname ) ) )
			log( "SYSERR: LoadShips() unable to read ship file %s.", shipname );
		else
		{
			ROOM_DATA *pRoom;

			/* add to ship list */
			LINK(ship, first_ship, last_ship, next, prev);

			top_ship_num++;

			/* place in the world */
			if ( SHIP_FLAGGED(ship, SHIP_IN_PORT ) )
				pRoom = get_wild_room( ship->port );
			else if ( SHIP_FLAGGED(ship, SHIP_SAIL) || SHIP_FLAGGED(ship, SHIP_AT_ANCHOR) )
			{
				pRoom = get_wild_room( ship->port );
				DISPOSE( ship->port );			// not a port, destroy
			}
			else
			{
				log( "SYSERR: no coord to place ship %d.", ship->vnum );
				if ( !port_table )
				{
					log( "SYSERR: no ports loaded." );
					free_ship(ship);
					continue;
				}
				pRoom = get_wild_room( port_table->coord );
			}
			
			ship_to_room( ship, pRoom );
		}
	}

	fclose (fp);
}

ACMD(do_shipread)
{
	SHIP_DATA *ship;
	ROOM_DATA *pRoom;

	one_argument(argument, arg);

	if ( !*arg )
		return;

	ship = fread_ship( arg );

	if ( !ship )
	{
		log("SYSERR: unable to load ship file %s.", arg );
		return;
	}

	if ( SHIP_FLAGGED(ship, SHIP_IN_PORT ) )
		pRoom = get_wild_room( ship->port );
	else if ( SHIP_FLAGGED(ship, SHIP_SAIL) || SHIP_FLAGGED(ship, SHIP_AT_ANCHOR) )
	{
		pRoom = get_wild_room( ship->port );
		DISPOSE( ship->port );			// not a port, destroy
	}
	else
	{
		log( "SYSERR: no coord to place ship %d.", ship->vnum );
		if ( !port_table )
		{
			log( "SYSERR: no ports loaded." );
			return;
		}
		pRoom = get_wild_room( port_table->coord );
	}

	ship_to_room( ship, pRoom );
}


/* ******************************************************************* */
/* Ship prototypes                                                     */
/* ******************************************************************* */

SHIP_TYPE *fread_ship_type( FILE *fp )
{
	SHIP_TYPE *tShip;
	char *word;
	bool fMatch;
	
	CREATE( tShip, SHIP_TYPE, 1 );
	tShip->name				= NULL;
	tShip->short_descr		= NULL;
	tShip->vnum				= NOTHING;
	tShip->skill_min		= 1;
	tShip->size				= 0;
	
	tShip->value.defense	= 0;
	tShip->value.equip		= 0;
	tShip->value.health		= 0;
	tShip->value.loads		= 0;
	tShip->value.power		= 0;
	tShip->value.speed		= 0;
	
	for ( ; ; )
	{
		word   = feof( fp ) ? "End" : fread_word( fp );
		fMatch = FALSE;
		
		switch ( UPPER(word[0]) )
		{
		case '*':
			fMatch = TRUE;
			fread_to_eol( fp );
			break;
			
		case 'D':
			KEY( "Defense",		tShip->value.defense,	fread_number(fp) );
			KEY( "Descr",		tShip->short_descr,		fread_string_nospace(fp) );
			break;
			
		case 'E':
			KEY( "Equip",		tShip->value.equip,		fread_number(fp) );
			if ( !str_cmp( word, "End" ) )
			{
				if ( tShip->vnum == NOTHING )
				{
					log("SYSERR: ship prototipe has no vnum.");
					exit(1);
				}
				if ( !tShip->name )
					tShip->name = str_dup("ship error");
				if ( !tShip->short_descr )
					tShip->short_descr = str_dup("Ship Error");

				return (tShip);
			}
			break;
			
		case 'H':
			KEY( "Health",		tShip->value.health,	fread_number(fp) );
			break;
			
		case 'L':
			KEY( "Loads",		tShip->value.loads,		fread_number(fp) );
			break;
			
		case 'N':
			KEY( "Name",		tShip->name,			fread_string_nospace(fp) );
			break;
			
		case 'P':
			KEY( "Power",		tShip->value.power,		fread_number(fp) );
			break;

		case 'S':
			KEY( "Size",		tShip->size,			fread_number(fp) );
			KEY( "Speed",		tShip->value.speed,		fread_number(fp) );
			KEY( "Skill",		tShip->skill_min,		fread_number(fp) );
			break;

		case 'V':
			KEY("Vnum",			tShip->vnum,			fread_number(fp));
			break;
		}			

		if ( !fMatch )
			log( "fread_ship_type: no match: %s", word );
	}

	return (NULL);
}


void load_ship_table( void )
{
	FILE *fp;
	char indname[256];
	char letter;
	char *word;
	
	sprintf(indname, "%sships.txt", SHIP_PREFIX);
	if (!(fp = fopen(indname, "r")))
	{
		log( "Cannot open ship table file %s", indname );
		exit(0);
	}
	
	top_ship_table = 0;
	
	for ( ;; )
	{
		letter = fread_letter( fp );
		if ( letter == '*' )
		{
			fread_to_eol( fp );
			continue;
		}
		
		if ( letter != '#' )
		{
			log( "Load_ship_table: # not found." );
			break;
		}
		
		word = fread_word( fp );
		if ( !str_cmp( word, "SHIP" ) )
		{
			if ( top_ship_table >= NUM_SHIP_TYPE )
			{
				log( "load_ship_table: more ships than NUM_SHIP_TYPE %d", NUM_SHIP_TYPE );
				fclose( fp );
				return;
			}
			ship_table[top_ship_table++] = fread_ship_type( fp );
			continue;
		}
		else
			if ( !str_cmp( word, "END" ) )
				break;
			else
			{
				log( "Load_ship_table: bad section." );
				continue;
			}
	}

	fclose( fp );
}


/* ******************************************************************* */
/* Special Procedures for buying ships                                 */
/* ******************************************************************* */

SPECIAL(dock)
{
	if (CMD_IS("list"))
	{
		int y;

		send_to_char("You can buy the following ships:\r\n", ch);
		for ( y = 0; y < top_ship_table; y++ )
			ch_printf(ch, "  %-20s %d\r\n", ship_table[y]->name,
				ship_table[y]->size * ship_table[y]->value.loads * 20);
		return (1);
	}

	if (CMD_IS("buy"))
	{
		SHIP_TYPE *tShip;
		PORT_DATA *port;
		SHIP_DATA *pShip;
		char arg[MAX_STRING_LENGTH], *shipname;
		int price, money;

		argument = one_argument( argument, arg );
		shipname = strtok(argument, "'");
		
		if (shipname == NULL)
		{
			send_to_char("Which name?\r\n", ch);
			return (1);
		}
		
		shipname = strtok(NULL, "'");
		if (shipname == NULL)
		{
			send_to_char("Ship names must be enclosed in the Holy Magic Symbols: '\r\n", ch);
			return (1);
		}
		
		if ( !*arg || !*shipname )
		{
			send_to_char("Usage: buy <type> 'ship name'\r\n", ch);
			return (1);
		}

		if (!(port = get_port_by_coord(ch->in_room->coord)))
		{
			log( "SYSERR: SPECIAL(dock) - No port found at coord %d %d.\r\n", GET_Y(ch), GET_X(ch) );
			return (1);
		}

		/* get ship type */
		if (!(tShip = parse_ship_type(arg)))
		{
			int y, price;
			
			ch_printf(ch, "Unknown ship type '%s'.\r\nYou can buy the following ships:\r\n", arg);
			for ( y = 0; y < top_ship_table; y++ )
			{
				price = ship_table[y]->size * ship_table[y]->value.loads * 20;
				ch_printf(ch, "  %40s %d\r\n", ship_table[y]->short_descr, price);
			}
			return (1);
		}

		/* Calc price & pay ship */
		price = tShip->size * tShip->value.loads * 20;
		money = get_gold(ch) + GET_BANK_GOLD(ch);

		if ( price > money )
		{
			ch_printf(ch, "You need %d gold coins to buy %s %s.\r\n",
				price, AN(tShip->name), tShip->name);
			if (IS_IMMORTAL(ch))
				price = 0;
			else
				return (1);
		}

		if ( price > get_gold(ch) )
		{
			int diff = price - get_gold(ch);

			sub_gold(ch, get_gold(ch));
			GET_BANK_GOLD(ch) -= diff;
		}
		else
			sub_gold(ch, price);

		pShip = create_ship(ch, tShip, port, shipname);
		ship_to_room(pShip, ch->in_room);

		ch_printf(ch, "Now you are the owner and captain of %s %s named '%s'\r\n",
			AN(tShip->name), tShip->name, pShip->name);

		return (1);
	}

	return (0);
}


/* ******************************************************************* */
/* Ports & Courses                                                     */
/* ******************************************************************* */

PORT_DATA *get_port_by_name( char *name )
{
	PORT_DATA *port;

	if ( !port_table || !name )
		return (NULL);

	for ( port = port_table; port; port = port->next )
		if ( isname( name, port->name ) )
			break;

	return (port);
}

PORT_DATA *get_port_by_coord( COORD_DATA *coord )
{
	PORT_DATA *port;

	if ( !port_table || !coord )
		return (NULL);

	for ( port = port_table; port; port = port->next )
		if ( coord->y == GET_RY(port) && coord->x == GET_RX(port) )
			break;

	return (port);
}

char *get_port_name( COORD_DATA *coord )
{
	PORT_DATA *port = get_port_by_coord( coord );

	if ( !port )
		return ("None");

	return ( port->name );
}


/*
 * given port coordinates and the destination port, returns the course
 */
COURSE_DATA *get_course(COORD_DATA *orig, PORT_DATA *port_dest)
{
	COURSE_DATA *course;

	if ( !course_table )
		return (NULL);

	for ( course = course_table; course; course = course->next )
	{
		if ( course->port_orig->y == orig->y &&
		     course->port_orig->x == orig->x &&
		     course->port_end->y == port_dest->coord->y &&
		     course->port_end->x == port_dest->coord->x )
			break;
	}

	/* if not found, try the reverse course */
	if ( !course )
	{
		for ( course = rev_course_table; course; course = course->next )
		{
			if ( course->port_orig->y == orig->y &&
			     course->port_orig->x == orig->x &&
			     course->port_end->y == port_dest->coord->y &&
			     course->port_end->x == port_dest->coord->x )
				break;
		}
	}

	return (course);
}

COURSE_DATA *get_course_by_vnum( int vnum )
{
	COURSE_DATA *course;

	if ( !course_table )
		return (NULL);

	for ( course = course_table; course; course = course->next )
		if ( course->vnum == vnum )
			break;

	return (course);
}


COURSE_STEP *get_course_step( COURSE_DATA *course, COORD_DATA *coord )
{
	COURSE_STEP *cstep;

	if ( !course || !coord )
		return (NULL);

	for ( cstep = course->first_step; cstep; cstep = cstep->next )
		if ( cstep->coord.y == coord->y && cstep->coord.x == coord->x )
			break;

	return (cstep);
}


COURSE_DATA *fread_course( FILE *fp )
{
	COURSE_DATA *course;
	char *word;
	bool fMatch;

	CREATE( course, COURSE_DATA, 1 );
	course->name		= NULL;
	course->vnum		= NOTHING;
	course->first_step	= NULL;
	course->last_step	= NULL;
	course->next		= NULL;

	for ( ; ; )
	{
		word	= feof( fp ) ? "End" : fread_word( fp );
		fMatch	= FALSE;
		
		switch ( UPPER(word[0]) )
		{
		case '*':
			fMatch = TRUE;
			fread_to_eol( fp );
			break;

		case 'E':
			if ( !str_cmp( word, "End" ) )
			{
				/* add to the list */
				course->next	= course_table;
				course_table	= course;
				
				return (course);
			}
			break;
	
		case 'N':
			KEY( "Name",		course->name,			fread_string_nospace(fp) );
			break;

		case 'P':
			if ( !str_cmp( word, "Port_Dest" ) )
			{
				CREATE( course->port_end, COORD_DATA, 1 );
				course->port_end->y		= fread_number(fp);
				course->port_end->x		= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			if ( !str_cmp( word, "Port_Orig" ) )
			{
				CREATE( course->port_orig, COORD_DATA, 1 );
				course->port_orig->y	= fread_number(fp);
				course->port_orig->x	= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'S':
			if ( !str_cmp( word, "Step" ) )
			{
				COURSE_STEP *cstep;

				CREATE( cstep, COURSE_STEP, 1 );

				cstep->coord.y			= fread_number(fp);
				cstep->coord.x			= fread_number(fp);

				LINK( cstep, course->first_step, course->last_step, next, prev );

				fMatch = TRUE;
				break;
			}
			break;

		case 'V':
			KEY( "Vnum",		course->vnum,			fread_number(fp) );
			break;
		}
	}

	return (NULL);
}

void ReverseCourse( void )
{
	COURSE_DATA *cnew, *course;
	COURSE_STEP *cstep, *newstep;

	if ( !course_table )
		return;

	log( "Inverting Courses table" );

	for ( course = course_table; course; course = course->next )
	{
		CREATE( cnew, COURSE_DATA, 1 );
		cnew->name				= str_dup(course->name);
		cnew->vnum				= course->vnum;

		CREATE( cnew->port_orig, COORD_DATA, 1 );
		CREATE( cnew->port_end, COORD_DATA, 1 );
		cnew->port_orig->y		= course->port_end->y;
		cnew->port_orig->x		= course->port_end->x;
		cnew->port_end->y		= course->port_orig->y;
		cnew->port_end->x		= course->port_orig->x;

		for ( cstep = course->last_step; cstep; cstep = cstep->prev )
		{
			CREATE( newstep, COURSE_STEP, 1 );
			newstep->coord.y	= cstep->coord.y;
			newstep->coord.x	= cstep->coord.x;

			LINK( newstep, cnew->first_step, cnew->last_step, next, prev );
		}

		cnew->next				= rev_course_table;
		rev_course_table		= cnew;
	}
}


void load_ports( void )
{
	FILE *fp;
	PORT_DATA *port;
	ROOM_DATA *pRoom;
	char fname[128];
	char letter;
	char *word;

	sprintf(fname, "%sharbours.txt", SHIP_PREFIX);
	if (!(fp = fopen(fname, "r")))
	{
		log( "Cannot open ports file %s", fname );
		exit(0);
	}

	for ( ;; )
	{
		letter = fread_letter( fp );
		if ( letter == '*' )
		{
			fread_to_eol( fp );
			continue;
		}
		
		if ( letter != '#' )
		{
			log( "load_ports: # not found." );
			break;
		}
		
		word = fread_word( fp );

		if ( !str_cmp( word, "HARBOUR" ) )
		{
			CREATE(port, PORT_DATA, 1);
			CREATE(port->coord, COORD_DATA, 1);
			port->coord->y		= fread_number(fp);
			port->coord->x		= fread_number(fp);
			port->name			= fread_string_nospace(fp);

			port->next			= port_table;
			port_table			= port;
		}
		else if ( !str_cmp( word, "COURSE" ) )
		{
			fread_course( fp );
		}
		else if ( !str_cmp( word, "END"	) )
			break;
		else
		{
			log( "load_ports: bad section %s.", word );
			continue;
		}
	}

	fclose( fp );

	/* setup wild rooms as harbours */
	for ( port = port_table; port; port = port->next )
	{
		pRoom = create_wild_room(port->coord, TRUE);
		pRoom->func = dock;
		if ( SECT(pRoom) != SECT_PORT )
		{
			put_sect( GET_RY(pRoom), GET_RX(pRoom), SECT_PORT, TRUE );
			SECT(pRoom) = SECT_PORT;
		}
	}

	ReverseCourse();
}

/* ******************************************************************* */
/* Player courses handling                                             */
/* ******************************************************************* */
bool knows_course( CHAR_DATA *ch, int cnum )
{
	KNOWN_COURSE *kCourse;

	if (cnum < 0)
		return (FALSE);

	if (IS_IMMORTAL(ch) || IS_NPC(ch))
		return (TRUE);

	for (kCourse = ch->player_specials->courses; kCourse; kCourse = kCourse->next)
	{
		if (kCourse->coursenum == cnum)
			break;
	}

	return (kCourse ? TRUE : FALSE);
}


int learn_course(CHAR_DATA *ch, int cnum)
{
	KNOWN_COURSE *kCourse;

	if (cnum < 0)
		return (-1);

	if (knows_course(ch, cnum))
		return (0);

	CREATE(kCourse, KNOWN_COURSE, 1);
	kCourse->coursenum	= cnum;

	// add to the list
	kCourse->next					= ch->player_specials->courses;
	ch->player_specials->courses	= kCourse;

	return (1);
}



/* ******************************************************************* */
/*                      F E R R Y B O A T S                           */
/* ******************************************************************* */

FERRY_DATA *get_ferry(int vnum)
{
	FERRY_DATA *pFerry;

	if (!first_ferry)
		return (NULL);

	for (pFerry = first_ferry; pFerry; pFerry = pFerry->next)
	{
		if (pFerry->vnum == vnum)
			break;
	}

	return (pFerry);
}


void ferry_to_port(FERRY_DATA *pFerry)
{
	EXIT_DATA *fexit, *rexit;

	// link ferry to port
	fexit = make_exit(pFerry->room, NULL, DOWN);
	fexit->to_room = pFerry->in_room;
	rexit = make_exit(pFerry->in_room, NULL, UP);
	rexit->to_room = pFerry->room;
}

/* ***************************************************** */

EVENTFUNC(ferry_move_event)
{
	FERRY_EVENT *fmove = (FERRY_EVENT *) (event_obj);
	FERRY_DATA *pFerry;
	EXIT_DATA *pexit;
	CHAR_DATA *ppl;
	ROOM_DATA *last_room;
	sh_int dir;

	pFerry	= fmove->ferry;
	dir	= fmove->dir;

	if (!pFerry || !pFerry->action)
	{
		free(event_obj);
		return (0);
	}

	pexit = get_exit(pFerry->in_room, dir);

	if (!pexit || !pexit->to_room)
	{
		free(event_obj);
		return (0);
	}

	send_to_room("The ferryboat has left.", pFerry->in_room); 

	pFerry->in_room->ferryboat	= NULL;
	last_room					= pFerry->in_room;
	// stanziamolo..
	pFerry->in_room				= pexit->to_room;
	pFerry->in_room->ferryboat	= pFerry;

	send_to_room("The ferryboat has arrived.", pFerry->in_room);

	/* Handle Wild Sectors */
	if ( IS_WILD(pFerry->in_room) )
	{
		check_wild_move( NULL, NULL, NULL, pFerry, last_room, pFerry->in_room );

		if (ROOM_FLAGGED(pFerry->in_room, ROOM_WILD_REMOVE))
			wild_remove_dequeue(pFerry->in_room);
		if (!ROOM_FLAGGED(pFerry->in_room, ROOM_WILD_STATIC))
			SET_BIT(ROOM_FLAGS(pFerry->in_room), ROOM_WILD_STATIC);
	}

	for ( ppl = pFerry->room->people; ppl; ppl = ppl->next_in_room )
	{
		if ( !FIGHTING(ppl) )
			look_at_wild(ppl, TRUE);
	}

	// check for port at next step
	if (SECT(pFerry->in_room) == SECT_PORT)
	{
		send_to_room("The ferryboat has arrived in port. Go down to disembark.", pFerry->room);
		
		// setup ferry data
		pFerry->timer	= FERRY_WAIT + number(-2, 3);
		pFerry->action	= NULL;
		if (dir == pFerry->dir)
			pFerry->place = 2;
		else
			pFerry->place = 1;

		ferry_to_port(pFerry);

		return (0);
	}
	else if (!water_sector(SECT(pexit->to_room)))
	{
		log("SYSERR: ferry %d moved in a non-water sector.", pFerry->vnum);
		free(event_obj);
		return(0);
	}

	send_to_room("The ferryboat moves...", pFerry->room);

	// yet another step
	return (FERRY_SPEED);
}


/*
 * called every minute from heartbeat() in comm.c
 */
void ferry_move(void)
{
	FERRY_DATA *pFerry;
	FERRY_EVENT *fmove;
	sh_int dir;

	if (!first_ferry)
		return;

	for (pFerry = first_ferry; pFerry; pFerry = pFerry->next)
	{
		// if action is not null, the ferry is moving...
		if (pFerry->action)
			continue;

		// countdown to departure
		if (--pFerry->timer > 0)
			continue;

		// reverse dir for sailing from site2 to site1
		if (pFerry->place == 2)
			dir = rev_dir[pFerry->dir];
		else
			dir = pFerry->dir;

		extract_exit(pFerry->room, get_exit(pFerry->room, DOWN));
		extract_exit(pFerry->in_room, get_exit(pFerry->in_room, UP));

		// initialize ferry move event
		CREATE(fmove, FERRY_EVENT, 1);
		fmove->ferry	= pFerry;
		fmove->dir		= dir;

		// create event
		pFerry->action = event_create(ferry_move_event, fmove, FERRY_SPEED);

		send_to_room("The ferryboat leave the port and begin to cross the river.\r\n", pFerry->room);
		send_to_room("The ferryboat leave the port and begin to cross the river.\r\n", pFerry->in_room);
	}
}


/* ********************************************************* */

FERRY_DATA *new_ferry(void)
{
	FERRY_DATA *pFerry;

	CREATE(pFerry, FERRY_DATA, 1);

	pFerry->action				= NULL;
	pFerry->in_room				= NULL;
	pFerry->mob					= NULL;
	pFerry->next				= NULL;
	pFerry->prev				= NULL;
	pFerry->vnum				= NOTHING;
	pFerry->cost				= 0;
	pFerry->dir					= NOWHERE;

	// default values
	pFerry->place				= 1;
	pFerry->timer				= FERRY_WAIT;

	// setup ferry room
	pFerry->room				= new_room();
	pFerry->room->name			= str_dup("Aboard the ferryboat");
	pFerry->room->sector_type	= SECT_FERRY_DECK;
	pFerry->room->zone			= FERRY_ZONE;
	pFerry->room->description	= str_dup("  You are aboard the ferryboat.\r\n");

	CREATE(pFerry->room->extra_data, ROOM_EXTRA, 1);
	pFerry->room->extra_data->max_hp	= 50;
	pFerry->room->extra_data->curr_hp	= 50;

	return (pFerry);
}


bool fread_one_ferry(FERRY_DATA *pFerry, FILE *fp)
{
	char *word;
	bool fMatch;

	for ( ; ; )
	{
		word   = feof( fp ) ? "End" : fread_word( fp );
		fMatch = FALSE;
		
		switch ( UPPER(word[0]) )
		{
		case '*':
			fMatch = TRUE;
			fread_to_eol( fp );
			break;

		case 'C':
			if (!strcmp(word, "Coord"))
			{
				fMatch = TRUE;

				CREATE(pFerry->coord, COORD_DATA, 1);

				pFerry->coord->y = fread_number(fp);
				pFerry->coord->x = fread_number(fp);

				if (!get_port_by_coord(pFerry->coord))
				{
					log("SYSERR: ferryboat must load in a port.");
					return (FALSE);
				}
				break;
			}
			KEY("Cost",			pFerry->cost,			fread_number(fp));
			break;

		case 'D':
			KEY("Dir",			pFerry->dir,			fread_number(fp));
			break;

		case 'E':
			if (!strcmp(word, "End"))
				return (TRUE);
			break;

		case 'M':
			if (!strcmp(word, "Mob"))
			{
				int vnum = fread_number(fp);

				if (!(pFerry->mob = read_mobile(vnum, VIRTUAL)))
					log("SYSERR: ferryboat's mob has invalid vnum %d.", vnum);

				fMatch = TRUE;
				break;	
			}
			break;

		case 'V':
			if (!strcmp(word, "Vnum"))
			{
				pFerry->vnum					= fread_number(fp);
				pFerry->room->extra_data->vnum	= pFerry->vnum;
				fMatch = TRUE;
				break;
			}
			break;
		}
	}

	return (FALSE);
}


FERRY_DATA *fread_ferry( char *sname ) 
{
	FILE *fp;
	FERRY_DATA *pFerry;
	char fname[128];
	char letter;
	char *word;

	sprintf( fname, "%s%s", LIB_SHIPS, sname );
	if ( !( fp = fopen(fname, "r") ) )
		return (NULL);

	pFerry = new_ferry();

	for ( ;; )
	{
		letter = fread_letter( fp );
		if ( letter == '*' )
		{
			fread_to_eol( fp );
			continue;
		}
		
		if ( letter != '#' )
		{
			log( "fread_ferry(): # not found." );
			break;
		}
		
		word = fread_word( fp );
		if ( !str_cmp( word, "FERRY" ) )
		{
			if ( !fread_one_ferry( pFerry, fp ) )
			{
				fclose(fp);
				return (NULL);
			}
		}
		else if (!str_cmp(word, "END"))
			break;
		else
		{
			log( "fread_ferry(): bad section." );
			continue;
		}
	}

	fclose( fp );

	return (pFerry);
}

/* load all saved rooms */
void LoadFerryboats( void )
{
	FILE *fp;
	FERRY_DATA *pFerry;
	char fname[128];

	sprintf(fname, "%sferryindex", LIB_SHIPS);

	if ( !( fp = fopen(fname, "r") ) )
		return;

	while (!feof(fp))
	{
		char ferryname[128];
		
		strcpy(ferryname, fread_word(fp));

		if (ferryname[0] == '$')
			break;

		if (!( pFerry = fread_ferry(ferryname)))
			log("SYSERR: LoadFerryboats() unable to read ferry file %s.", ferryname);
		else
		{
			ROOM_DATA *pRoom;

			// add to the list
			LINK(pFerry, first_ferry, last_ferry, next, prev);

			/* place in the world */
			if (!(pRoom = get_wild_room(pFerry->coord)))
			{
				log("SYSERR: LoadFerryboats() - port room must exist!");
				exit(1);
			}

			pRoom->ferryboat	= pFerry;
			pFerry->in_room		= pRoom;

			if (pFerry->mob)
				char_to_room(pFerry->mob, pFerry->room);

			ferry_to_port(pFerry);
		}
	}

	fclose (fp);
}