empiremud/cnf/
empiremud/doc/
empiremud/lib/boards/
empiremud/lib/etc/
empiremud/lib/misc/
empiremud/lib/plralias/F-J/
empiremud/lib/plralias/K-O/
empiremud/lib/plralias/P-T/
empiremud/lib/plralias/U-Z/
empiremud/lib/plrobjs/
empiremud/lib/plrobjs/F-J/
empiremud/lib/plrobjs/K-O/
empiremud/lib/plrobjs/P-T/
empiremud/lib/plrobjs/U-Z/
empiremud/lib/world/
empiremud/lib/world/mob/
empiremud/lib/world/obj/
empiremud/log/
/* ************************************************************************
*   File: ships.c                                        EmpireMUD AD 1.0 *
*  Usage: code related to boating                                         *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Code base by Paul Clarke.  EmpireMUD Project, a tbgMUD Production.     *
*  Based upon CircleMUD 3.0, beta patch level 17, by Jeremy Elson.        *
*                                                                         *
*  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */

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

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "skills.h"
#include "vnums.h"
#include "empire.h"

/* externs */
extern const char *dirs[];
extern const int room_capacity[];
extern const int building_capacity[];
extern int rev_dir[];
extern int last_action_rotation;


void create_exit(room_rnum from, room_rnum to, int dir, bool back) {
	CREATE(world[from].dir_option[dir], struct room_direction_data, 1);
	world[from].dir_option[dir]->to_room = to;
	world[from].dir_option[dir]->exit_info = 0;
	world[from].dir_option[dir]->keyword = NULL;

	if (back) {
		CREATE(world[to].dir_option[rev_dir[dir]], struct room_direction_data, 1);
		world[to].dir_option[rev_dir[dir]]->to_room = from;
		world[to].dir_option[rev_dir[dir]]->exit_info = 0;
		world[to].dir_option[rev_dir[dir]]->keyword = NULL;
		}
	}


Object setup_ship_object(obj_vnum vnum, room_rnum main_room) {
	Object obj;

	obj = read_object(vnum, VIRTUAL);
	GET_OBJ_VAL(obj, 2) = world[main_room].number;

	return obj;
	}


#define SHIP_PINNACE		0
#define SHIP_GALLEY			1
#define SHIP_BRIGANTINE		2
#define SHIP_ARGOSY			3
#define SHIP_GALLEON		4

Object create_ship(obj_vnum vnum, long owner, room_rnum to_room) {
	extern room_rnum create_room(room_vnum vnum);
	extern room_vnum find_free_vnum(void);

	Object ship;
	room_rnum main_room;
	room_rnum spare[8];
	int type, i;


	for (i = 0; i < 8; i++)
		spare[i] = NOWHERE;

	ship = read_object(vnum, VIRTUAL);
	type = GET_OBJ_VAL(ship, 0);

	/* The main room */
	main_room = create_room(find_free_vnum());
	GET_OBJ_VAL(ship, 2) = world[main_room].number;
	world[main_room].owner = owner;
	SECT(main_room) = SECT_INSIDE;
	world[main_room].home_room = NOWHERE;
	world[main_room].type = RTYPE_B_ONDECK;
	world[main_room].boat = ship;

	switch (type) {
		case SHIP_PINNACE:
			/* Helm - 0 */
			spare[0] = create_room(find_free_vnum());
			world[spare[0]].owner = owner;
			world[spare[0]].home_room = main_room;
			SECT(spare[0]) = SECT_INSIDE;
			world[spare[0]].type = RTYPE_B_HELM;

			create_exit(main_room, spare[0], EAST, TRUE);
			break;
		case SHIP_GALLEY:
			/* Helm - 0 */
			spare[0] = create_room(find_free_vnum());
			world[spare[0]].owner = owner;
			world[spare[0]].home_room = main_room;
			SECT(spare[0]) = SECT_INSIDE;
			world[spare[0]].type = RTYPE_B_HELM;

			/* On Deck - 1 */
			spare[1] = create_room(find_free_vnum());
			world[spare[1]].owner = owner;
			world[spare[1]].home_room = main_room;
			SECT(spare[1]) = SECT_INSIDE;
			world[spare[1]].type = RTYPE_B_ONDECK;

			create_exit(main_room, spare[0], EAST, TRUE);
			create_exit(main_room, spare[1], WEST, TRUE);
			break;
		case SHIP_BRIGANTINE:
			/* Helm - 0 */
			spare[0] = create_room(find_free_vnum());
			world[spare[0]].owner = owner;
			world[spare[0]].home_room = main_room;
			SECT(spare[0]) = SECT_INSIDE;
			world[spare[0]].type = RTYPE_B_HELM;

			/* Storage - 1 */
			spare[1] = create_room(find_free_vnum());
			world[spare[1]].owner = owner;
			world[spare[1]].home_room = main_room;
			SECT(spare[1]) = SECT_INSIDE;
			world[spare[1]].type = RTYPE_B_STORAGE;

			create_exit(main_room, spare[0], EAST, TRUE);
			create_exit(main_room, spare[1], DOWN, TRUE);
			break;
		case SHIP_ARGOSY:
			/* Helm - 0 */
			spare[0] = create_room(find_free_vnum());
			world[spare[0]].owner = owner;
			world[spare[0]].home_room = main_room;
			SECT(spare[0]) = SECT_INSIDE;
			world[spare[0]].type = RTYPE_B_HELM;

			/* On Deck - 1 */
			spare[1] = create_room(find_free_vnum());
			world[spare[1]].owner = owner;
			world[spare[1]].home_room = main_room;
			SECT(spare[1]) = SECT_INSIDE;
			world[spare[1]].type = RTYPE_B_ONDECK;

			/* Storage - 2-3 */
			for (i = 2; i <= 3; i++) {
				spare[i] = create_room(find_free_vnum());
				world[spare[i]].owner = owner;
				world[spare[i]].home_room = main_room;
				SECT(spare[i]) = SECT_INSIDE;
				world[spare[i]].type = RTYPE_B_STORAGE;
				}

			create_exit(main_room, spare[0], EAST, TRUE);
			create_exit(main_room, spare[1], WEST, TRUE);
			create_exit(spare[1], spare[2], DOWN, TRUE);
			create_exit(main_room, spare[3], DOWN, TRUE);
			break;
		case SHIP_GALLEON:
			/* Helm - 0 */
			spare[0] = create_room(find_free_vnum());
			world[spare[0]].owner = owner;
			world[spare[0]].home_room = main_room;
			SECT(spare[0]) = SECT_INSIDE;
			world[spare[0]].type = RTYPE_B_HELM;

			/* On Deck - 1 */
			spare[1] = create_room(find_free_vnum());
			world[spare[1]].owner = owner;
			world[spare[1]].home_room = main_room;
			SECT(spare[1]) = SECT_INSIDE;
			world[spare[1]].type = RTYPE_B_ONDECK;

			/* Below Deck - 2-3 */
			for (i = 2; i <= 3; i++) {
				spare[i] = create_room(find_free_vnum());
				world[spare[i]].owner = owner;
				world[spare[i]].home_room = main_room;
				SECT(spare[i]) = SECT_INSIDE;
				world[spare[i]].type = RTYPE_B_BELOWDECK;
				}

			/* Storage - 4-7 */
			for (i = 4; i < 8; i++) {
				spare[i] = create_room(find_free_vnum());
				world[spare[i]].owner = owner;
				world[spare[i]].home_room = main_room;
				SECT(spare[i]) = SECT_INSIDE;
				world[spare[i]].type = RTYPE_B_STORAGE;
				}

			create_exit(main_room, spare[0], EAST, TRUE);
			create_exit(main_room, spare[1], WEST, TRUE);
			create_exit(spare[1], spare[2], DOWN, TRUE);
			create_exit(spare[2], spare[3], EAST, TRUE);
			create_exit(spare[2], spare[4], NORTH, TRUE);
			create_exit(spare[2], spare[5], SOUTH, TRUE);
			create_exit(spare[3], spare[6], NORTH, TRUE);
			create_exit(spare[3], spare[7], SOUTH, TRUE);
			break;
		default:
			extract_obj(ship);
			return NULL;
		}

	/* Load all the important objects */
	obj_to_room(setup_ship_object(os_WINDOW, main_room), main_room);

	for (i = 0; i < 8; i++)
		if (spare[i] != NOWHERE)
			switch (world[spare[i]].type) {
				case RTYPE_B_HELM:
					obj_to_room(setup_ship_object(os_HELM, main_room), spare[i]);
				default:
					obj_to_room(setup_ship_object(os_WINDOW, main_room), spare[i]);
					break;
				}

	obj_to_room(ship, to_room);
	return (ship);
	}


ACMD(do_sail) {
	extern const char *from_dir[];

	Object obj, ship;
	int dir;
	room_rnum to_room;

	skip_spaces(&argument);

	if (!(obj = get_obj_in_list_num(real_object(os_HELM), world[ch->in_room].contents)))
		msg_to_char(ch, "You can't sail from here.\r\n");
	else if (!(ship = world[HOME_ROOM(obj->in_room)].boat))
		msg_to_char(ch, "Unable to find a ship associated with this helm!\r\n");
	else if (!CAN_USE_ROOM(ch, HOME_ROOM(ch->in_room), 1) && ww_dice(GET_DEXTERITY(ch) + GET_LARCENY(ch), 7) <= 0)
		msg_to_char(ch, "You don't have permission to sail this ship.\r\n");
	else if (!*argument)
		msg_to_char(ch, "Which direction would you like to sail?\r\n");
	else if ((dir = parse_direction(argument)) < 0)
		msg_to_char(ch, "You can't sail that direction.\r\n");
	else if (dir == UP || dir == DOWN)
		msg_to_char(ch, "How would that work?\r\n");
	else if (SECT(ship->in_room) == SECT_BUILDING) {
		if (!world[ship->in_room].dir_option[dir]) {
			msg_to_char(ch, "You can't sail that way!\r\n");
			return;
			}

		to_room = world[ship->in_room].dir_option[dir]->to_room;
		if (SECT(to_room) != SECT_OCEAN && SECT(to_room) != SECT_RIVER && (SECT(to_room) != SECT_ROAD || !IS_COMPLETE(to_room) || !world[to_room].type))
			msg_to_char(ch, "You can't sail that way!\r\n");
		else {
			if (world[ship->in_room].people) {
				sprintf(buf, "$p sails %s.", dirs[dir]);
				act(buf, TRUE, world[ship->in_room].people, ship, 0, TO_CHAR | TO_ROOM);
				}
			obj_to_room(ship, to_room);
			if (world[ship->in_room].people) {
				sprintf(buf, "$p sails in %s.", from_dir[dir]);
				act(buf, TRUE, world[ship->in_room].people, ship, 0, TO_CHAR | TO_ROOM);
				}
			look_at_room_by_rnum(ch, ship->in_room);
			WAIT_STATE(ch, 1 RL_SEC);
			}
		}
	else {
		to_room = real_shift(ship->in_room, shift_dir[dir][0], shift_dir[dir][1]);

		if (SECT(to_room) != SECT_OCEAN && SECT(to_room) != SECT_RIVER && BUILDING_TYPE(to_room) != BUILDING_DOCKS && (SECT(to_room) != SECT_ROAD || !IS_COMPLETE(to_room) || !world[to_room].type))
			msg_to_char(ch, "You can't sail that direction!\r\n");
		else if (!IS_COMPLETE(to_room))
			msg_to_char(ch, "You can't use an incomplete dock.\r\n");
		else if (SECT(to_room) == SECT_BUILDING && dir != world[to_room].building_entrance)
			msg_to_char(ch, "You can't enter from this side.\r\n");
		else if (SECT(to_room) == SECT_BUILDING && !CAN_USE_ROOM(ch, to_room, 0))
			msg_to_char(ch, "You don't have permission to dock.\r\n");
		else {
			if (world[ship->in_room].people) {
				sprintf(buf, "$p sails %s.", dirs[dir]);
				act(buf, TRUE, world[ship->in_room].people, ship, 0, TO_CHAR | TO_ROOM);
				}
			obj_to_room(ship, to_room);
			if (world[ship->in_room].people) {
				sprintf(buf, "$p sails in %s.", from_dir[dir]);
				act(buf, TRUE, world[ship->in_room].people, ship, 0, TO_CHAR | TO_ROOM);
				}
			look_at_room_by_rnum(ch, ship->in_room);
			WAIT_STATE(ch, 1 RL_SEC);
			}
		}
	}


ACMD(do_board) {
	struct follow_type *k;
	Object ship;
	Creature leading = NULL;
	room_rnum was_in = ch->in_room;

	one_argument(argument, arg);

	if (IS_NPC(ch))
		return;
	else if (!*arg)
		msg_to_char(ch, "Board what?\r\n");
	else if (!(ship = get_obj_in_list_vis(ch, arg, world[ch->in_room].contents)))
		msg_to_char(ch, "You don't see a %s here.\r\n", arg);
	else if (GET_OBJ_TYPE(ship) != ITEM_SHIP)
		msg_to_char(ch, "You can't board that!\r\n");
	else if (!CAN_USE_ROOM(ch, real_room(GET_OBJ_VAL(ship, 2)), 0) && ww_dice(GET_DEXTERITY(ch) + GET_LARCENY(ch), 7) <= 0)
		msg_to_char(ch, "You don't have permission to board it.\r\n");
	else if (GET_RIDING(ch))
		msg_to_char(ch, "You can't board while riding.\r\n");
	else if (GET_LEADING(ch) && GET_LEADING(ch)->in_room == ch->in_room && (leading = GET_LEADING(ch)) && BUILDING_TYPE(ch->in_room) != BUILDING_DOCKS)
		msg_to_char(ch, "You can't lead an animal on board from here.\r\n");
	else if (GET_OBJ_VAL(ship, 1))
		msg_to_char(ch, "You can't board the ship until it's finished!\r\n");
	else if (real_room(GET_OBJ_VAL(ship, 2)) == NOWHERE)
		msg_to_char(ch, "An error occurred in the ship: it has no inside.\r\n");
	else {
		act("$n boards $p.", TRUE, ch, ship, 0, TO_ROOM);
		msg_to_char(ch, "You board it.\r\n");
		if (leading)
			act("$n is lead on-board.", TRUE, leading, 0, ch, TO_NOTVICT);
		char_to_room(ch, real_room(GET_OBJ_VAL(ship, 2)));
		look_at_room(ch);
		act("$n boards the ship.", TRUE, ch, 0, 0, TO_ROOM);
		if (leading) {
			char_to_room(leading, real_room(GET_OBJ_VAL(ship, 2)));
			act("$n boards the ship.", TRUE, leading, 0, 0, TO_ROOM);
			look_at_room(leading);
			}

		for (k = ch->followers; k; k = k->next)
			if ((k->follower->in_room == was_in) && (GET_POS(k->follower) >= POS_STANDING)) {
				act("You follow $N.\r\n", FALSE, k->follower, 0, ch, TO_CHAR);
				act("$n boards $p.", TRUE, k->follower, ship, 0, TO_ROOM);
				char_to_room(k->follower, real_room(GET_OBJ_VAL(ship, 2)));
				act("$n boards the ship.", TRUE, k->follower, 0, 0, TO_ROOM);
				look_at_room(k->follower);
				}
		if (BUILDING_TYPE(was_in) != BUILDING_DOCKS)
			WAIT_STATE(ch, 2 RL_SEC);
		}
	}


ACMD(do_disembark) {
	Object ship = world[ch->in_room].boat;
	Creature leading = NULL;
	room_rnum was_in = ch->in_room;
	struct follow_type *k;

	if (!ship)
		msg_to_char(ch, "You can't disembark from here!\r\n");
	else if (GET_RIDING(ch))
		msg_to_char(ch, "You can't disembark while riding.\r\n");
	else if (GET_LEADING(ch) && GET_LEADING(ch)->in_room == ch->in_room && (leading = GET_LEADING(ch)) && BUILDING_TYPE(ship->in_room) != BUILDING_DOCKS)
		msg_to_char(ch, "You can't lead an animal off the ship unless it's at a dock.\r\n");
	else {
		act("$n disembarks from $p.", TRUE, ch, ship, 0, TO_ROOM);
		msg_to_char(ch, "You disembark.\r\n");
		if (leading)
			act("$n is lead off.", TRUE, leading, 0, ch, TO_NOTVICT);
		char_to_room(ch, ship->in_room);
		act("$n disembarks from $p.", TRUE, ch, ship, 0, TO_ROOM);
		look_at_room(ch);
		if (leading) {
			char_to_room(leading, ship->in_room);
			act("$n disembarks from $p.", TRUE, leading, ship, 0, TO_ROOM);
			look_at_room(leading);
			}

		for (k = ch->followers; k; k = k->next)
			if ((k->follower->in_room == was_in) && (GET_POS(k->follower) >= POS_STANDING)) {
				act("You follow $N.\r\n", FALSE, k->follower, 0, ch, TO_CHAR);
				act("$n disembarks from $p.", TRUE, k->follower, ship, 0, TO_ROOM);
				char_to_room(k->follower, ship->in_room);
				act("$n disembarks from $p.", TRUE, k->follower, ship, 0, TO_ROOM);
				look_at_room(k->follower);
				}
		if (BUILDING_TYPE(ch->in_room) != BUILDING_DOCKS)
			WAIT_STATE(ch, 2 RL_SEC);
		}
	}


struct ship_data_struct {
	char *name;
	int vnum;
	int type;
	int science;
	int intelligence;
	int resources;
	int advanced;
	} ship_data[] = {
		{ "pinnace",	os_PINNACE,		0,		1,	3,		60,		0	},
		{ "galley",		os_GALLEY,		1,		2,	3,		90,		0	},
		{ "brigantine",	os_BRIGANTINE,	2,		3,	3,		120,	0	},
		{ "argosy",		os_ARGOSY,		3,		4,	3,		240,	1	},
		{ "galleon",	os_GALLEON,		4,		5,	3,		480,	1	},

		{ "\n", -1, 0, 0, 0, 0 }
		};


/*
 * Like buildings, ships will take a long time to build.  However, they will
 * use ONLY logs and iron and will alternate every update between those two.
 */


void process_manufacturing(Creature ch) {
	Object ship;
	Creature c;
	Resource iron[] = { {o_IRON, 1}, END_RESOURCE_LIST };
	Resource logs[] = { {o_LOG, 1}, END_RESOURCE_LIST };


	for (ship = world[ch->in_room].contents; ship; ship = ship->next_content)
		if (GET_OBJ_TYPE(ship) == ITEM_SHIP && GET_OBJ_VAL(ship, 1))
			break;

	if (!ship) {
		msg_to_char(ch, "You try to work on a ship, but there isn't one here!\r\n");
		GET_ACTION(ch) = ACT_NONE;
		}
	else if ((GET_OBJ_VAL(ship, 1) % 2)) {
		if (!has_resources(ch, iron, TRUE))
			GET_ACTION(ch) = ACT_NONE;
		else {
			extract_resources(ch, iron, TRUE);
			msg_to_char(ch, "You nail the wood onto the structure.\r\n");
			act("$n nails the wood onto the structure.", FALSE, ch, 0, 0, TO_ROOM);
			GET_OBJ_VAL(ship, 1) -= 1;
			}
		}
	else {
		if (!has_resources(ch, logs, TRUE))
			GET_ACTION(ch) = ACT_NONE;
		else {
			extract_resources(ch, logs, TRUE);
			msg_to_char(ch, "You position some wood on the structure.\r\n");
			act("$n positions the wood on the structure.", FALSE, ch, 0, 0, TO_ROOM);
			GET_OBJ_VAL(ship, 1) -= 1;
			}
		}
	if (GET_OBJ_VAL(ship, 1) == 0) {
		msg_to_char(ch, "You finish the construction!\r\n");
		act("$n finishes the construction!", FALSE, ch, 0, 0, TO_ROOM);
		for (c = world[ch->in_room].people; c; c = c->next_in_room)
			if (!IS_NPC(c) && GET_ACTION(c) == ACT_MANUFACTURING)
				GET_ACTION(c) = ACT_NONE;
		}
	}


ACMD(do_manufacture) {
	Object ship;
	int i = 0;

	for (ship = world[ch->in_room].contents; ship; ship = ship->next_content)
		if (GET_OBJ_TYPE(ship) == ITEM_SHIP && GET_OBJ_VAL(ship, 1))
			break;

	skip_spaces(&argument);

	if (*argument)
		for (i = 0; str_cmp(ship_data[i].name, "\n") && (!is_abbrev(argument, ship_data[i].name) || GET_SCIENCE(ch) < ship_data[i].science || GET_INTELLIGENCE(ch) < ship_data[i].intelligence); i++);

	if ((BUILDING_TYPE(ch->in_room) != BUILDING_SHIPYARD && BUILDING_TYPE(ch->in_room) != BUILDING_SHIPYARD2) || !IS_COMPLETE(ch->in_room))
		msg_to_char(ch, "You can't do that here.\r\n");
	else if (GET_ACTION(ch) == ACT_MANUFACTURING) {
		msg_to_char(ch, "You stop working on the ship.\r\n");
		GET_ACTION(ch) = ACT_NONE;
		}
	else if (GET_ACTION(ch) != ACT_NONE)
		msg_to_char(ch, "You're busy doing something else right now.\r\n");
	else if (ship && *argument)
		msg_to_char(ch, "A shipyard may only build one ship at a time.\r\n");
	else if (!ship && (!*argument || !str_cmp(ship_data[i].name, "\n"))) {
		msg_to_char(ch, "What type of ship would you like to build:");
		for (i = 0; str_cmp(ship_data[i].name, "\n"); i++)
			if (GET_SCIENCE(ch) >= ship_data[i].science && GET_INTELLIGENCE(ch) >= ship_data[i].intelligence)
				msg_to_char(ch, " %s", ship_data[i].name);
		msg_to_char(ch, "\r\n");
		}
	else if (ship) {
		act("You begin working on $p.", FALSE, ch, ship, 0, TO_CHAR);
		act("$n begins working on $p.", FALSE, ch, ship, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_MANUFACTURING;
		GET_ACTION_TIMER(ch) = 1;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		}
	else if (!CAN_USE_ROOM(ch, ch->in_room, 0))
		msg_to_char(ch, "You don't have permission to manufacture ships here.\r\n");
	else if (ship_data[i].advanced && BUILDING_TYPE(ch->in_room) != BUILDING_SHIPYARD2)
		msg_to_char(ch, "You can't build that without an advanced shipyard.\r\n");
	else {
		ship = create_ship(ship_data[i].vnum, get_id_by_empire(ch), ch->in_room);
		msg_to_char(ch, "You begin working on a %s.\r\n", ship_data[i].name);
		act("$n begins to build a ship.", FALSE, ch, 0, 0, TO_ROOM);
		GET_ACTION(ch) = ACT_MANUFACTURING;
		GET_ACTION_TIMER(ch) = 1;
		GET_ACTION_ROOM(ch) = ch->in_room;
		GET_ACTION_ROTATION(ch) = last_action_rotation;
		GET_OBJ_VAL(ship, 1) = ship_data[i].resources;
		}
	}


/* Returns TRUE if it loads the object */
bool load_one_obj_to_boat(Creature ch, Object obj, room_rnum to_room, Object ship) {
	if (ROOM_WEIGHT(to_room) + GET_OBJ_WEIGHT(obj) < room_capacity[(int) world[to_room].type]) {
		obj_to_room(obj, to_room);
		act("You load $p into $P.", FALSE, ch, obj, ship, TO_CHAR);
		return TRUE;
		}

	return FALSE;
	}

/* Returns 1 if totally done, 0 if the room fills, and -1 if nothing could be loaded */
int load_all_objs_to_boat(Creature ch, room_rnum from, room_rnum to, Object ship) {
	Object o, next_o;
	bool done = TRUE, any = FALSE;

	for (o = world[from].contents; o; o = next_o) {
		next_o = o->next_content;

		/* We can never load a ship */
		if (GET_OBJ_TYPE(o) == ITEM_SHIP)
			continue;

		/* We can only load a !take item onto a Galleon (catapult, etc) */
		if (!CAN_WEAR(o, ITEM_WEAR_TAKE) && GET_OBJ_VAL(ship, 0) != SHIP_GALLEON)
			continue;

		if (ROOM_WEIGHT(to) + GET_OBJ_WEIGHT(o) < room_capacity[(int) world[to].type]) {
			obj_to_room(o, to);
			any = TRUE;
			}
		else
			done = FALSE;
		}

	/*
	 * done will still be TRUE if we never failed to load an item
	 * any will be TRUE if any item was loaded
	 * if not, this puppy is full!
	 */

	if (done)
		return 1;
	else if (any)
		return 0;
	else
		return -1;
	}


ACMD(do_load_boat) {
	Object ship, to_load = NULL;
	room_rnum i;
	int val;
	bool done = FALSE;

	two_arguments(argument, arg, buf);

	if (BUILDING_TYPE(ch->in_room) != BUILDING_DOCKS)
		msg_to_char(ch, "You can only load a ship at the dock.\r\n");
	else if (!IS_COMPLETE(ch->in_room))
		msg_to_char(ch, "The docks are incomplete.\r\n");
	else if (!*arg)
		msg_to_char(ch, "What ship would you like to load?\r\n");
	else if (!(ship = get_obj_in_list_vis(ch, arg, world[ch->in_room].contents)))
		msg_to_char(ch, "There's no ship by that name here.\r\n");
	else if (GET_OBJ_TYPE(ship) != ITEM_SHIP)
		msg_to_char(ch, "That's not a ship!\r\n");
	else if (*buf && !(to_load = get_obj_in_list_vis(ch, buf, ch->carrying)) && !(to_load = get_obj_in_list_vis(ch, buf, world[ch->in_room].contents)))
		msg_to_char(ch, "You don't seem to have a %s to load.\r\n", buf);
	else if (to_load && GET_OBJ_TYPE(to_load) == ITEM_SHIP)
		msg_to_char(ch, "You can't load a ship onto a ship!\r\n");
	else if (to_load && !CAN_WEAR(to_load, ITEM_WEAR_TAKE) && GET_OBJ_VAL(ship, 0) != SHIP_GALLEON)
		msg_to_char(ch, "You can't load that onto this ship!\r\n");
	else {
		for (i = MAP_SIZE; i <= top_of_world && !done; i++) {
			if (HOME_ROOM(i) != real_room(GET_OBJ_VAL(ship, 2)))
				continue;
			if (ROOM_TYPE(i) != RTYPE_B_STORAGE && ROOM_TYPE(i) != RTYPE_B_ONDECK)
				continue;

			if (to_load) {
				if (load_one_obj_to_boat(ch, to_load, i, ship))
					return;
				}
			else {
				if ((val = load_all_objs_to_boat(ch, ch->in_room, i, ship)) == 1)
					done = TRUE;
				if (val != -1) {
					act("You load some cargo into $p.", FALSE, ch, ship, 0, TO_CHAR);
					act("$n loads some cargo into $p.", FALSE, ch, ship, 0, TO_ROOM);
					}
				/* else nothing was loaded at all to this room */
				}
			}
		if (!to_load && !done)
			act("$p is full.", FALSE, ch, ship, 0, TO_CHAR);
		}
	}



/*
 * Returns:
 *  -2: nothing could be unloaded, docks are full
 *  -1: some stuff was unloaded, but docks were full
 *   0: room was empty
 *   1: room finished
 */
int perform_unload_boat(Creature ch, room_rnum from, room_rnum to, Object ship) {
	Object o, next_o;
	bool done = TRUE, any = FALSE;

	for (o = world[from].contents; o; o = next_o) {
		next_o = o->next_content;

		/* We can never load a ship */
		if (GET_OBJ_TYPE(o) == ITEM_SHIP || GET_OBJ_TYPE(o) == ITEM_HELM || GET_OBJ_TYPE(o) == ITEM_WINDOW)
			continue;

		/* It's not necessary to check for ITEM_WEAR_TAKE.. if it got here we'll take it out */

		if (ROOM_WEIGHT(to) + GET_OBJ_WEIGHT(o) < building_capacity[(int) world[to].type]) {
			obj_to_room(o, to);
			any = TRUE;
			}
		else
			done = FALSE;
		}

	if (!done && !any)
		return -2;
	else if (!done && any)
		return -1;
	else if (done && !any)
		return 0;
	else
		return 1;
	}


ACMD(do_unload_boat) {
	Object ship;
	room_rnum i;
	int val;
	bool more = FALSE;

	one_argument(argument, arg);

	if (BUILDING_TYPE(ch->in_room) != BUILDING_DOCKS)
		msg_to_char(ch, "You can only unload a ship at the dock.\r\n");
	else if (!IS_COMPLETE(ch->in_room))
		msg_to_char(ch, "The docks are incomplete.\r\n");
	else if (!*arg)
		msg_to_char(ch, "What ship would you like to load?\r\n");
	else if (!(ship = get_obj_in_list_vis(ch, arg, world[ch->in_room].contents)))
		msg_to_char(ch, "There's no ship by that name here.\r\n");
	else if (GET_OBJ_TYPE(ship) != ITEM_SHIP)
		msg_to_char(ch, "That's not a ship!\r\n");
	else {
		for (i = MAP_SIZE; i <= top_of_world; i++) {
			if (HOME_ROOM(i) != real_room(GET_OBJ_VAL(ship, 2)))
				continue;
			if (ROOM_TYPE(i) != RTYPE_B_STORAGE && ROOM_TYPE(i) != RTYPE_B_ONDECK)
				continue;

			if ((val = perform_unload_boat(ch, i, ch->in_room, ship)) < 0)
				more = TRUE;
			if (val != 0 && val != -2) {
				act("You unload some cargo from $p.", FALSE, ch, ship, 0, TO_CHAR);
				act("$n unloads some cargo from $p.", FALSE, ch, ship, 0, TO_ROOM);
				}
			}
		if (more)
			msg_to_char(ch, "You can't unload anything else, the docks are full!\r\n");
		}
	}