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: vehicles.c                                                       *
 *                                                                        *
 * Vehicles code                                                          *
 *                                                                        *
 **************************************************************************/

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

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

/* external functions */
char	*make_bar(long val, long max, long len, int color);
void	free_room(ROOM_DATA *room);
void	rent_vehicle( CHAR_DATA *ch, VEHICLE_DATA *vehicle );
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 );
void	list_obj_to_char(OBJ_DATA *list, CHAR_DATA *ch, int mode, int show, bool RDR);
void	list_char_to_char(CHAR_DATA *list, CHAR_DATA *ch);

/* globals */
VEHICLE_DATA	*first_vehicle	= NULL;
VEHICLE_DATA	*last_vehicle	= NULL;
VEHICLE_INDEX	*vehicle_types[MAX_VEH_TYPE];

/* locals */
bool	can_see_vehicle( CHAR_DATA *ch, VEHICLE_DATA *vehicle );
void	mob_from_yoke( CHAR_DATA *mob, VEHICLE_DATA *vehicle );


/* *************************************************************************** */
/* Create / Destroy Vehicles Routines                                          */
/* *************************************************************************** */

void clear_vehicle( VEHICLE_DATA *pVeh )
{
	if ( pVeh->name )
		STRFREE(pVeh->name);
	if ( pVeh->short_description )
		STRFREE(pVeh->short_description);
	if ( pVeh->description )
		STRFREE(pVeh->description);

	if (pVeh->veh_room)
	{
		free_room(pVeh->veh_room);
		DISPOSE(pVeh->veh_room);
	}

	pVeh->next			= NULL;
	pVeh->prev			= NULL;
	pVeh->in_room		= NULL;
	pVeh->last_room		= NULL;
	pVeh->first_content	= NULL;
	pVeh->last_content	= NULL;
	pVeh->people		= NULL;
	pVeh->wagoner		= NULL;
	pVeh->next_in_room	= NULL;
	pVeh->veh_room		= NULL;
	pVeh->first_yoke	= NULL;
	pVeh->last_yoke		= NULL;
	pVeh->type			= NULL;

	pVeh->flags			= 0;
}


void new_vehicle_room( VEHICLE_DATA *pVeh )
{
	pVeh->veh_room = new_room();

	pVeh->veh_room->sector_type		= SECT_INSIDE;
	pVeh->veh_room->zone			= NOWHERE;

	switch (pVeh->type->vnum)
	{
	case VEH_CART:
		/* set up vehicle's room descriptions */
		pVeh->veh_room->name		= str_dup("Inside a cart");
		pVeh->veh_room->description	= str_dup("You are inside a cart.\r\n");
		break;
	case VEH_WAGON:
		/* set up vehicle's room descriptions */
		pVeh->veh_room->name		= str_dup("Inside a wagon");
		pVeh->veh_room->description	= str_dup("You are inside a four-wheeled wagon.\r\n");
		break;
	default:
		log("SYSERR: new_vehicle_room() - unknown vehicle type %d.", pVeh->type->vnum);
		DISPOSE(pVeh->veh_room);
	}
}


VEHICLE_DATA *new_vehicle(void)
{
	VEHICLE_DATA *pVeh;

	CREATE(pVeh, VEHICLE_DATA, 1);

	clear_vehicle(pVeh);

	return (pVeh);
}

/* create a new vehicle of the given type */
VEHICLE_DATA *init_vehicle( VEHICLE_INDEX *vType )
{
	VEHICLE_DATA *pVeh;

	if (!vType)
	{
		log("SYSERR: init_vehicle() - unused vehicle proto.");
		return (NULL);
	}

	if ( vType->vnum < 0 || vType->vnum >= MAX_VEH_TYPE )
	{
		log("SYSERR: init_vehicle() - wrong vehicle type %d.", vType->vnum);
		return (NULL);
	}

	pVeh = new_vehicle();

	pVeh->type = vType;

	// copy strings
	pVeh->name					= str_dup(vType->name);
	pVeh->short_description		= str_dup(vType->short_description);
	pVeh->description			= str_dup(vType->description);

	// copy value
	pVeh->max_val = vType->value;

	// init some current values, others must be 0
	pVeh->curr_val.health		= pVeh->max_val.health;
	pVeh->curr_val.speed		= pVeh->max_val.speed;

	// if needed, setup inside rooms
	if ( pVeh->max_val.passengers > 0 )
		new_vehicle_room( pVeh );

	/* add to the list */
	LINK(pVeh, first_vehicle, last_vehicle, next, prev);

	return (pVeh);
}

/*
 * extract a vehicle from world
 * mode = 0 - leave mobs and objs in the room it was
 * mode = 1 - extract mobs and objs too
 * mode = 2 - from destroy_vehicle()
 */
void extract_vehicle( VEHICLE_DATA *vehicle, int mode )
{
	// destroyed vehicles don't loose theirs freights
	if (mode != 2)
	{
		if ( vehicle->first_content )
		{
			OBJ_DATA *obj, *next_obj = NULL;
			
			for ( obj = vehicle->first_content; obj; obj = next_obj )
			{
				next_obj = obj->next_content;
				if (mode == 1)
					extract_obj(obj);
				else
				{
					obj_from_vehicle(obj);
					obj_to_room(obj, vehicle->in_room);
				}
			}
		}
	}

	if ( vehicle->people )
	{
		CHAR_DATA *ppl, *next_ppl = NULL;

		for ( ppl = vehicle->people; ppl; ppl = next_ppl )
		{
			next_ppl = ppl->next_in_vehicle;
			char_from_vehicle(ppl);
			char_from_room(ppl);
			char_to_room(ppl, vehicle->in_room);
		}
	}

	if ( vehicle->first_yoke )
	{
		YOKE_DATA *yoke, *next_yoke = NULL;

		for ( yoke = vehicle->first_yoke; yoke; yoke = next_yoke )
		{
			next_yoke = yoke->next;

			if (mode == 1)
				extract_char_final(yoke->mob);
			else
				mob_from_yoke(yoke->mob, vehicle);
		}
	}

	if ( vehicle->veh_room )
	{
		free_room( vehicle->veh_room );
		DISPOSE(vehicle->veh_room);
	}

	if ( vehicle->wagoner )
		stop_be_wagoner( vehicle->wagoner );

	// destroyed vehicles must remain in place....
	if (mode == 2)
		return;

	if ( vehicle->in_room )
		vehicle_from_room(vehicle);

	UNLINK(vehicle, first_vehicle, last_vehicle, next, prev);

	clear_vehicle(vehicle);

	DISPOSE(vehicle);
}

/* *************************************************************** */
/* Finding Vehicles Routines                                       */
/* *************************************************************** */

/* find the vehicle searching the entire world */
VEHICLE_DATA *find_vehicle(char *name)
{
	VEHICLE_DATA *pVeh;
	int fnum;

	if (!name)
		return (NULL);

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

	for (pVeh = first_vehicle; pVeh; pVeh = pVeh->next)
	{
		if (isname(name, pVeh->name) || !str_cmp(name, "vehicle"))
			if (--fnum == 0)
				break;
	}

	return (pVeh);

}

/* find the vehicle in room */
VEHICLE_DATA *find_vehicle_in_room_by_name( CHAR_DATA *ch, char *name )
{
	VEHICLE_DATA *pVeh;
	int fnum;

	if ( !ch->in_room->vehicles )
		return (NULL);

	if ( !name )
		return (NULL);

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

	for ( pVeh = ch->in_room->vehicles; pVeh; pVeh = pVeh->next_in_room )
	{
		if ( isname( name, pVeh->name ) || !str_cmp( name, "vehicle" ) )
			if ( can_see_vehicle(ch, pVeh) )
				if ( --fnum == 0 )
					break;
	}

	return (pVeh);
}

bool empty_vehicle( VEHICLE_DATA *vehicle, bool bQuick )
{
	if ( vehicle->wagoner )			return (FALSE);
	if ( vehicle->people )			return (FALSE);

	if ( bQuick )					return (TRUE);

	if ( vehicle->first_yoke )		return (FALSE);
	if ( vehicle->first_content )	return (FALSE);

	return (TRUE);
}

/* *************************************************************** */
/* Enter/Exit Vehicle Routines                                     */
/* *************************************************************** */

void char_to_vehicle( CHAR_DATA *ch, VEHICLE_DATA *vehicle )
{
	if ( !ch || !vehicle )
	{
		log("SYSERR: char_to_vehicle() - NULL pointer.");
		return;
	}

	ch->in_vehicle		= vehicle;

	char_to_room(ch, vehicle->veh_room);

	/* add to the ppl list */
	ch->next_in_vehicle	= vehicle->people;
	vehicle->people		= ch;

	vehicle->curr_val.passengers++;
}

void char_from_vehicle( CHAR_DATA *ch )
{
	CHAR_DATA *temp;

	if ( !ch )
	{
		log("SYSERR: char_from_vehicle() - NULL pointer.");
		return;
	}

	if ( !ch->in_vehicle )
		return;

	ch->in_vehicle->curr_val.passengers--;

	REMOVE_FROM_LIST(ch, ch->in_vehicle->people, next_in_vehicle);
	ch->in_vehicle		= NULL;
	ch->next_in_vehicle	= NULL;
}

void char_enter_vehicle( CHAR_DATA *ch, VEHICLE_DATA *vehicle )
{
	if ( !ch || !vehicle )
	{
		log("SYSERR: char_to_vehicle() - NULL pointer.");
		return;
	}

	if ( RIDING(ch) )
	{
		send_to_char("You cannot enter a vehicle while mounted.\r\n", ch);
		return;
	}

	if ( WAGONER(ch) )
	{
		send_to_char("You cannot enter a vehicle while driving a vehicle.\r\n", ch);
		return;
	}

	if ( ch->in_vehicle )
	{
		send_to_char("You are already inside a vehicle.\r\n", ch);
		return;
	}

	if (IS_SET(vehicle->flags, VEH_DESTROYED))
	{
		send_to_char("You cannot enter a destroyed vehicle.\r\n", ch);
		return;
	}

	if ( vehicle->owner_id != GET_IDNUM(ch) && !IS_IMMORTAL(ch) )
	{
		ch_printf(ch, "%s is not yours that you can use it.\r\n",
			vehicle->short_description);
		return;
	}

	if ( !vehicle->max_val.passengers )
	{
		ch_printf(ch, "%s cannot have passengers.\r\n", vehicle->short_description);
		return;
	}

	if ( vehicle->curr_val.passengers >= vehicle->max_val.passengers )
	{
		send_to_char("It's full.\r\n", ch);
		return;
	}

	// sanity check
	if ( !vehicle->veh_room )
	{
		ch_printf(ch, "Sorry, right now you cannot go inside %s.\r\n", vehicle->short_description);
		return;
	}

	act("You enter $v.", FALSE, ch, NULL, vehicle, TO_CHAR);
	act("$n enters $v.", FALSE, ch, NULL, vehicle, TO_ROOM);

	char_from_room(ch);
	char_to_vehicle(ch, vehicle);
	look_at_room(ch, 0);
}

void char_exit_vehicle( CHAR_DATA *ch )
{
	ROOM_DATA *pRoom;

	if ( !ch )
	{
		log("SYSERR: char_exit_vehicle() - NULL pointer.");
		return;
	}

	if ( !ch->in_vehicle )
		return;

	pRoom = ch->in_vehicle->in_room;

	// TODO - check if char can move in to the room..

	act("You exit from $v.", FALSE, ch, NULL, ch->in_vehicle, TO_CHAR);
	act("$n exits from $v.", FALSE, ch, NULL, ch->in_vehicle, TO_ROOM);

	char_from_vehicle(ch);
	char_from_room(ch);
	char_to_room(ch, pRoom);
	look_at_room(ch, 0);
}

/* *************************************************************** */
/* Wagoner Routines                                                */
/* *************************************************************** */

void start_be_wagoner( CHAR_DATA *ch, VEHICLE_DATA *vehicle )
{
	if ( !ch || !vehicle )
	{
		log("SYSERR: start_be_wagoner() - NULL pointer.");
		return;
	}

	if ( WAGONER(ch) )
	{
		ch_printf(ch, "You're already driving %s.\r\n", WAGONER(ch)->short_description);
		return;
	}

	if ( RIDING(ch) )
	{
		send_to_char("You cannot drive a vehicle while mounted.\r\n", ch);
		return;
	}

	if (IS_SET(vehicle->flags, VEH_DESTROYED))
	{
		send_to_char("You cannot drive a destroyed vehicle.\r\n", ch);
		return;
	}

	if ( vehicle->wagoner )
	{
		ch_printf(ch, "%s is already drived by %s.\r\n",
			vehicle->short_description, PERS(vehicle->wagoner, ch));
		return;
	}

	if ( vehicle->owner_id != GET_IDNUM(ch) && !IS_IMMORTAL(ch) )
	{
		ch_printf(ch, "%s is not yours that you can use it.\r\n",
			vehicle->short_description);
		return;
	}

	WAGONER(ch)				= vehicle;
	WAGONER(ch)->wagoner	= ch;

	act("You climb up and start driving $v.", FALSE, ch, NULL, vehicle, TO_CHAR);
	act("$n climbs up and start driving $v.", FALSE, ch, NULL, vehicle, TO_ROOM);
}

/*
 * messages to char are not inside here because
 * this function is called also by extract_char()
 * and from check inside do_simple_move()
 */
void stop_be_wagoner( CHAR_DATA *ch )
{
	if ( !ch )
	{
		log("SYSERR: stop_be_wagoner() - NULL pointer.");
		return;
	}

	if ( !WAGONER(ch) )
	{
		log("SYSERR: ch is not driving any vehicle.");
		return;
	}

	WAGONER(ch)->wagoner	= NULL;
	WAGONER(ch)				= NULL;
}

/* *************************************************************** */
/* Yokeing Mobs Routines                                           */
/* *************************************************************** */

bool hitched_mob( CHAR_DATA *mob )
{
	if ( !IS_NPC(mob) )
		return (FALSE);

	if ( !mob->mob_specials.hitched_to )
		return (FALSE);

	/* this should never happen!! */
	if ( mob->in_room != mob->mob_specials.hitched_to->in_room )
		return (FALSE);

	return (TRUE);
}

void mob_to_yoke( CHAR_DATA *mob, VEHICLE_DATA *vehicle )
{
	YOKE_DATA *yoke;

	if (!mob || !vehicle)
		return;

	if (IS_SET(vehicle->flags, VEH_DESTROYED))
		return;

	CREATE(yoke, YOKE_DATA, 1);
	yoke->mob						= mob;

	LINK(yoke, vehicle->first_yoke, vehicle->last_yoke, next, prev);

	mob->mob_specials.hitched_to	= vehicle;
	mob->mob_specials.yoke			= yoke;

	vehicle->curr_val.draft_mobs++;
}

void mob_from_yoke( CHAR_DATA *mob, VEHICLE_DATA *vehicle )
{
	YOKE_DATA *yoke;

	if ( !mob->mob_specials.hitched_to )
		return;

	if ( !(yoke = mob->mob_specials.yoke) )
		return;
	
	vehicle->curr_val.draft_mobs--;

	UNLINK(yoke, vehicle->first_yoke, vehicle->last_yoke, next, prev);
	yoke->next						= NULL;
	yoke->prev						= NULL;
	yoke->mob						= NULL;
	DISPOSE(yoke);

	mob->mob_specials.hitched_to	= NULL;
	mob->mob_specials.yoke			= NULL;
}

/* *************************************************************** */
/* Vehicle Movement Routines                                       */
/* *************************************************************** */

void vehicle_to_room( VEHICLE_DATA *pVeh, ROOM_DATA *pRoom )
{
	if ( !pVeh || !pRoom )
	{
		log("SYSERR: vehicle_to_room() - invalid pointer.");
		return;
	}

	/* add to the room list */
	pVeh->next_in_room	= pRoom->vehicles;
	pRoom->vehicles		= pVeh;

	pVeh->in_room		= pRoom;

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

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

void vehicle_from_room( VEHICLE_DATA *pVeh )
{
	VEHICLE_DATA *temp;

	if ( !pVeh )
	{
		log("SYSERR: NULL vehicle pointer in vehicle_from_room");
		exit(1);
	}

	pVeh->last_room		= pVeh->in_room;

	REMOVE_FROM_LIST(pVeh, pVeh->in_room->vehicles, next_in_room);
	pVeh->in_room		= NULL;
	pVeh->next_in_room	= NULL;

	wild_check_for_remove(pVeh->last_room);
}

bool vehicle_can_move( VEHICLE_DATA *vehicle )
{
	if (!vehicle)								return (FALSE);
	if (!vehicle->first_yoke)					return (FALSE);
	if (!vehicle->wagoner)						return (FALSE);
	if (IS_SET(vehicle->flags, VEH_DESTROYED))	return (FALSE);

	return (TRUE);
}

bool vehicle_can_go( VEHICLE_DATA *pVeh, ROOM_DATA *to_room )
{
	/*
	 * that's an hack.. in future must be handled
	 * in do_simple_move()
	 */
	if ( ROOM_FLAGGED(to_room, ROOM_DEATH) )
		return (FALSE);

	if ( IS_SET(pVeh->flags, VEH_FLY) )
		return (TRUE);

	if ( to_room->sector_type == SECT_ROAD		|| 
	     to_room->sector_type == SECT_FIELD		||
	     to_room->sector_type == SECT_PLAIN		||
	     to_room->sector_type == SECT_CITY		||
	     to_room->sector_type == SECT_LAND		||
	     to_room->sector_type == SECT_BRIDGE	||
	     to_room->sector_type == SECT_FORD		||
	     to_room->sector_type == SECT_PORT)
		return (TRUE);

	return (FALSE);
}


/* *************************************************************** */
/* Goods Storing in Vehicle Routines                               */
/* *************************************************************** */

OBJ_DATA *obj_to_vehicle( OBJ_DATA *obj, VEHICLE_DATA *vehicle )
{
	OBJ_DATA *oret, *tmp_obj;
	
	if (!obj || !vehicle)
	{
		log("SYSERR: obj_to_vehicle() - NULL pointer");
		return (NULL);
	}

	vehicle->curr_val.capacity += get_real_obj_weight(obj);

	for ( tmp_obj = vehicle->first_content; tmp_obj; tmp_obj = tmp_obj->next_content )
	{
		if ( (oret = group_object(tmp_obj, obj)) == tmp_obj )
			return (oret);
	}

	LINK(obj, vehicle->first_content, vehicle->last_content, next_content, prev_content);

	obj->in_vehicle		= vehicle;
	obj->in_room		= NULL;
	obj->in_obj			= NULL;
	obj->carried_by		= NULL;

	return (obj);
}

/* Take a goods object from a vehicle */
void obj_from_vehicle( OBJ_DATA *obj )
{
	if (!obj || !obj->in_vehicle)
	{
		log("SYSERR: obj_from_vehicle() - NULL pointer");
		return;
	}

	obj->in_vehicle->curr_val.capacity -= get_real_obj_weight(obj);

	UNLINK(obj, obj->in_vehicle->first_content, obj->in_vehicle->last_content,
		next_content, prev_content);

	obj->in_obj			= NULL;
	obj->in_room		= NULL;
	obj->carried_by		= NULL;
	obj->in_vehicle		= NULL;
	obj->next_content	= NULL;
	obj->prev_content	= NULL;
}

void vehicle_load_goods( CHAR_DATA *ch, OBJ_DATA *goods, VEHICLE_DATA *vehicle )
{
	if ( !ch || !goods || !vehicle )
	{
		log("SYSERR: vehicle_load_goods() - NULL pointer.");
		return;
	}

	if (IS_SET(vehicle->flags, VEH_DESTROYED))
	{
		send_to_char("You cannot put items in a destroyed vehicle.\r\n", ch);
		return;
	}

	if ( vehicle->curr_val.capacity + get_real_obj_weight(goods) > vehicle->max_val.capacity )
	{
		ch_printf(ch, "%s cannot contain that much.\r\n", vehicle->short_description);
		return;
	}

	obj_from_room(goods);
	obj_to_vehicle(goods, vehicle);

	ch_printf(ch, "You charge %s into %s.\r\n", goods->short_description, vehicle->short_description);
}

void vehicle_unload_goods( CHAR_DATA *ch, OBJ_DATA *goods, VEHICLE_DATA *vehicle )
{
	if ( !ch || !goods || !vehicle )
	{
		log("SYSERR: vehicle_unload_goods() - NULL pointer.");
		return;
	}

	obj_from_vehicle(goods);
	obj_to_room(goods, ch->in_room);

	ch_printf(ch, "You discharge %s from %s.\r\n", goods->short_description, vehicle->short_description);
}

/* *************************************************************** */
/* Vehicle Display Routines                                        */
/* *************************************************************** */

/* True if char can see vehicle */
bool can_see_vehicle( CHAR_DATA *ch, VEHICLE_DATA *vehicle )
{
	if ( !IS_NPC(ch) && PRF_FLAGGED(ch, PRF_HOLYLIGHT) )
		return (TRUE);
	
	if ( AFF_FLAGGED( ch, AFF_BLIND ) )
		return (FALSE);
	
	if ( IS_DARK(vehicle->in_room) && !AFF_FLAGGED(ch, AFF_INFRAVISION) )
		return (FALSE);

	if ( IS_SET(vehicle->flags, VEH_INVISIBLE) && !AFF_FLAGGED(ch, AFF_DETECT_INVIS) )
		return (FALSE);
	
	return (TRUE);
}

void list_vehicle_to_char( VEHICLE_DATA *vlist, CHAR_DATA *ch )
{
	VEHICLE_DATA *pVeh;
	char sbaf[MAX_STRING_LENGTH];

	for ( pVeh = vlist; pVeh; pVeh = pVeh->next_in_room )
	{
		if ( WAGONER(ch) && WAGONER(ch) == pVeh )
			continue;

		// needed because of "look out" command..
		if ( ch->in_vehicle && ch->in_vehicle == pVeh )
			continue;

		if ( can_see_vehicle(ch, pVeh) )
		{
			if (IS_SET(pVeh->flags, VEH_DESTROYED))
				sprintf(sbaf, "&b&5You see a destroyed %s", pVeh->name);
			else
				sprintf(sbaf, "&b&5You see %s", pVeh->short_description);

			if (pVeh->wagoner)
				sprintf(sbaf+strlen(sbaf), ", drived by %s", PERS(pVeh->wagoner, ch));

			if (pVeh->first_yoke)
			{
				YOKE_DATA *yoke;
				
				strcat(sbaf, ", dragged by ");

				for ( yoke = pVeh->first_yoke; yoke; yoke = yoke->next )
					sprintf(sbaf+strlen(sbaf), "%s%s",
						GET_NAME(yoke->mob),
						(yoke->next ? ", " : " ")
						);
			}
			strcat(sbaf, ".&0\r\n");

			send_to_char(sbaf, ch);
		}
	}
}


int look_at_vehicle(CHAR_DATA *ch, VEHICLE_DATA *vehicle)
{
	char sbaf[MAX_STRING_LENGTH];

	if (!can_see_vehicle(ch, vehicle))
		return (0);

	sprintf(sbaf,
		"%s%s\r\n"
		"Capacity : %s (%5d/%5d)\r\n"
		"Health   : %s (%5d/%5d)\r\n"
		,
		vehicle->description,
		(IS_SET(vehicle->flags, VEH_DESTROYED) ? " [destroyed]" : ""),
		make_bar(vehicle->curr_val.capacity, vehicle->max_val.capacity, 40, 6),
		vehicle->curr_val.capacity, vehicle->max_val.capacity,
		make_bar(vehicle->curr_val.health, vehicle->max_val.health, 40, 6),
		vehicle->curr_val.health, vehicle->max_val.health
		);

	send_to_char(sbaf, ch);

	if (vehicle->people)
		list_char_to_char(vehicle->people, ch);

	if (vehicle->first_content)
	{
		send_to_char("When you look inside you see:\r\n", ch);
		list_obj_to_char(vehicle->first_content, ch, 7, 1, FALSE);
	}

	return (1);
}


const char *veh_flags_descr[] =
{
  "FLYING",
  "INVISIBLE",
  "DESTROYED",
  "\n"
};

void stat_vehicle(CHAR_DATA *ch, VEHICLE_DATA *vehicle)
{
	char sbaf[MAX_STRING_LENGTH], dflags[128];

	if (!can_see_vehicle(ch, vehicle))
		return;

	sprintbit(vehicle->flags, veh_flags_descr, dflags);
	sprintf(sbaf,
		"%s%s\r\n"
		"Flags    : %s\r\n"
		"Capacity : %s (%5d/%5d)\r\n"
		"Health   : %s (%5d/%5d)\r\n"
		,
		vehicle->description,
		(IS_SET(vehicle->flags, VEH_DESTROYED) ? " [destroyed]" : ""),
		dflags,
		make_bar(vehicle->curr_val.capacity, vehicle->max_val.capacity, 40, 6),
		vehicle->curr_val.capacity, vehicle->max_val.capacity,
		make_bar(vehicle->curr_val.health, vehicle->max_val.health, 40, 6),
		vehicle->curr_val.health, vehicle->max_val.health
		);

	send_to_char(sbaf, ch);

	if (vehicle->people)
		list_char_to_char(vehicle->people, ch);

	if (vehicle->first_content)
	{
		send_to_char("When you look inside you see:\r\n", ch);
		list_obj_to_char(vehicle->first_content, ch, 7, 1, FALSE);
	}
}

/* *************************************************************** */
/* Vehicle Proto                                                   */
/* *************************************************************** */

#if defined(KEY)
#undef KEY
#endif

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


VEHICLE_INDEX *new_veh_proto(void)
{
	VEHICLE_INDEX *vType = NULL;

	CREATE( vType, VEHICLE_INDEX, 1 );
	vType->name					= NULL;
	vType->short_description	= NULL;
	vType->description			= NULL;
	vType->vnum					= NOTHING;

	return (vType);
}

VEHICLE_INDEX *fread_one_veh_proto( FILE *fp )
{
	VEHICLE_INDEX *vType = new_veh_proto();
	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':
			KEY("Capacity",			vType->value.capacity,		fread_number(fp));
			break;

		case 'D':
			KEY("Descr",			vType->description,			fread_string_nospace(fp));
			KEY("Draft_mobs",		vType->value.draft_mobs,	fread_number(fp));
			break;

		case 'E':
			if (!str_cmp(word, "End"))
			{
				return (vType);
			}
			break;

		case 'H':
			KEY("Health",			vType->value.health,		fread_number(fp));
			break;

		case 'N':
			KEY("Name",				vType->name,				str_dup(fread_word(fp)));
			break;

		case 'P':
			KEY("Passengers",		vType->value.passengers,	fread_number(fp));
			break;

		case 'S':
			KEY("Short_descr",		vType->short_description,	fread_string_nospace(fp));
			KEY("Speed",			vType->value.speed,			fread_number(fp));
			break;

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

void LoadVehiclesTable(void)
{
	FILE *fp;
	char fname[128];
	char letter;
	char *word;
	int num_of_veh_proto;

	sprintf(fname, "%svehicles.data", LIB_DATA);
	if (!(fp = fopen(fname, "r")))
		return;

	for (num_of_veh_proto = 0; num_of_veh_proto < MAX_VEH_TYPE; num_of_veh_proto++)
		vehicle_types[num_of_veh_proto] = NULL;

	num_of_veh_proto = 0;

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

		if (!str_cmp(word, "VEHICLE"))
		{
			if (num_of_veh_proto >= MAX_VEH_TYPE)
			{
				log("SYSERR: LoadVehiclesTable() - more prototypes than MAX_VEH_TYPE %d", MAX_VEH_TYPE);
				fclose(fp);
				return;
			}
			vehicle_types[num_of_veh_proto] = fread_one_veh_proto(fp);
			num_of_veh_proto++;
		}
		else if (!str_cmp(word, "END"))
			break;
		else
		{
			log("SYSERR: LoadVehiclesTable() - bad section %s.", word);
			continue;
		}
	}

	fclose(fp);
}


/* *************************************************************** */
/* Commands                                                        */
/* *************************************************************** */

/* wiz only */
ACMD(do_newvehicle)
{
	VEHICLE_DATA *pVeh;
	char arg[MAX_INPUT_LENGTH];
	int vnum;

	argument = one_argument(argument, arg);

	if ( !*arg )
	{
		send_to_char("Possible vehicles are:\r\n", ch);
		for (vnum = 0; vnum < MAX_VEH_TYPE; vnum++)
		{
			if (!vehicle_types[vnum])
				continue;
			ch_printf(ch, " (%d) %s\r\n", vnum, vehicle_types[vnum]->name);
		}
		send_to_char("Which type of vehicle?\r\n", ch);
		return;
	}

	if ( is_number(arg) )
		vnum = atoi(arg);
	else
	{
		for (vnum = 0; vnum < MAX_VEH_TYPE; vnum++)
		{
			if (!vehicle_types[vnum])
				continue;
			if (is_abbrev(arg, vehicle_types[vnum]->name))
				break;
		}
	}

	if (vnum < 0 || vnum >= MAX_VEH_TYPE || !vehicle_types[vnum])
	{
		ch_printf(ch, "Invalid vehicle type '%s'.\r\n", arg);
		return;
	}

	if ( ( pVeh = init_vehicle( vehicle_types[vnum] ) ) )
	{
		pVeh->owner_id = GET_IDNUM(ch);
		vehicle_to_room(pVeh, ch->in_room);
		ch_printf(ch, "You create %s out of the blue.\r\n", vehicle_types[vnum]->short_description);
	}
}

/*
 * quick exit from a vehicle
 */
ACMD(do_out)
{
	one_argument(argument, arg);

	if ( !ch->in_vehicle )
	{
		send_to_char("You are not inside a vehicle.\r\n", ch);
		return;
	}

	if ( !str_cmp(arg, "save") )
	{
		rent_vehicle(ch, ch->in_vehicle);
		return;
	}

	char_exit_vehicle(ch);
}

/*
 * so she wanna be a wagoner, eh?
 */
ACMD(do_drive)
{
	VEHICLE_DATA *vehicle;
	char arg1[MAX_INPUT_LENGTH];

	argument = one_argument(argument, arg1);

	if ( !*arg1 )
	{
		send_to_char(
			"Usage:\r\n"
			"drive <vehicle> - you start drive a vehicle.\r\n"
			"drive stop      - you stop drive a vehicle.\r\n", ch);
		return;
	}

	if ( !str_cmp(arg1, "stop") )
	{
		if ( !( vehicle = WAGONER(ch) ) )
		{
			send_to_char("You are not driving any vehicle.\r\n", ch);
			return;
		}
		
		act("You stop driving $v.", FALSE, ch, NULL, vehicle, TO_CHAR);
		act("$n stops driving $v.", FALSE, ch, NULL, vehicle, TO_ROOM);
		stop_be_wagoner(ch);
	}
	else
	{
		if ( !( vehicle = find_vehicle_in_room_by_name(ch, arg1) ) )
		{
			ch_printf(ch, "You don't see %s %s here..\r\n", AN(arg1), arg1);
			return;
		}

		if (IS_SET(vehicle->flags, VEH_DESTROYED))
		{
			send_to_char("You cannot drive a destroyed vehicle.\r\n", ch);
			return;
		}

		start_be_wagoner(ch, vehicle);
	}
}

/* attach a mob to the vehicle */
ACMD(do_yoke)
{
	CHAR_DATA *mob;
	VEHICLE_DATA *vehicle;
	char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];

	if ( RIDING(ch) )
	{
		send_to_char("You cannot yoke draft animal to a vehicle while mounted.\r\n", ch);
		return;
	}

	if ( WAGONER(ch) )
	{
		send_to_char("You cannot yoke draft animal to a vehicle while driving the vehicle.\r\n", ch);
		return;
	}

	argument = two_arguments(argument, arg1, arg2);

	if ( !*arg1 || !*arg2 )
	{
		send_to_char("Usage: yoke <mob> <vehicle>\r\n", ch);
		return;
	}

	if ( !( mob = get_char_room_vis(ch, arg1, NULL) ) )
	{
		ch_printf(ch, "You don't see %s %s here.\r\n", AN(arg1), arg1);
		return;
	}

	if ( !IS_NPC(mob) || !MOB_FLAGGED(mob, MOB_DRAFT_ANIMAL))
	{
		send_to_char("You can yoke only draft animal to a vehicle.\r\n", ch);
		return;
	}

	if ( !( vehicle = find_vehicle_in_room_by_name(ch, arg2) ) )
	{
		ch_printf(ch, "You don't see %s %s here.\r\n", AN(arg2), arg2);
		return;
	}

	if (IS_SET(vehicle->flags, VEH_DESTROYED))
	{
		send_to_char("You cannot yoke animals to a destroyed vehicle.\r\n", ch);
		return;
	}

	if ( vehicle->owner_id != GET_IDNUM(ch) && !IS_IMMORTAL(ch) )
	{
		ch_printf(ch, "%s is not yours that you can yoke animals.\r\n",
			vehicle->short_description);
		return;
	}

	if ( !vehicle->max_val.draft_mobs  )
	{
		ch_printf(ch, "You cannot yoke draft animal to %s.\r\n", vehicle->short_description);
		return;
	}

	if ( vehicle->curr_val.draft_mobs >= vehicle->max_val.draft_mobs  )
	{
		ch_printf(ch, "You cannot yoke any more draft animal to %s.\r\n", vehicle->short_description);
		return;
	}

	mob_to_yoke(mob, vehicle);

	ch_printf(ch, "You yoke %s to %s.\r\n", GET_NAME(mob), vehicle->short_description);
	sprintf(buf, "$n yoke $N to %s.", vehicle->short_description);
	act(buf, FALSE, ch, NULL, mob, TO_ROOM);

}

ACMD(do_unyoke)
{
	CHAR_DATA *mob;
	VEHICLE_DATA *vehicle;
	char arg1[MAX_INPUT_LENGTH];

	if ( RIDING(ch) )
	{
		send_to_char("You cannot unyoke draft animal from a vehicle while mounted.\r\n", ch);
		return;
	}

	if ( WAGONER(ch) )
	{
		send_to_char("You cannot unyoke draft animal from a vehicle while driving the vehicle.\r\n", ch);
		return;
	}

	argument = one_argument(argument, arg1);

	if ( !*arg1 )
	{
		send_to_char("Usage: unyoke <mob>\r\n", ch);
		return;
	}

	if ( !( mob = get_char_room_vis(ch, arg1, NULL) ) )
	{
		ch_printf(ch, "You don't see %s %s here.\r\n", AN(arg1), arg1);
		return;
	}

	if ( !IS_NPC(mob) || !MOB_FLAGGED(mob, MOB_DRAFT_ANIMAL))
	{
		send_to_char("You can unyoke only draft animal from a vehicle.\r\n", ch);
		return;
	}

	if ( !( vehicle = mob->mob_specials.hitched_to ) )
	{
		ch_printf(ch, "%s isn't yoked to any vehicle.\r\n", GET_NAME(mob));
		return;
	}

	if ( !mob->mob_specials.yoke )
	{
		log("SYSERR: do_unyoke() - yoked mob does not have yoke data.");
		send_to_char("There is a problem, call an Immortal.\r\n", ch);
		return;
	}

	if (IS_SET(vehicle->flags, VEH_DESTROYED))
	{
		send_to_char("You cannot unyoke animals from a destroyed vehicle.\r\n", ch);
		return;
	}

	if ( vehicle->owner_id != GET_IDNUM(ch) && !IS_IMMORTAL(ch) )
	{
		ch_printf(ch, "%s is not yours that you can unyoke animals.\r\n",
			vehicle->short_description);
		return;
	}

	ch_printf(ch, "You unyoke %s from %s.\r\n", GET_NAME(mob), vehicle->short_description);
	sprintf(buf, "$n unyoke $N from %s.", vehicle->short_description);
	act(buf, FALSE, ch, NULL, mob, TO_ROOM);

	mob_from_yoke(mob, vehicle);
}

/* ******************************************************* */
/* Code for repairing and damaging vehicles                */
/* ******************************************************* */

/* make some reparations */
void repair_vehicle(CHAR_DATA *ch, VEHICLE_DATA *pVeh)
{
	if (IS_SET(pVeh->flags, VEH_DESTROYED))
	{
		act("$v: you cannot repair destroyed vehicles.", FALSE, ch, NULL, pVeh, TO_CHAR);
		return;
	}

	if (pVeh->max_val.health == pVeh->curr_val.health)
	{
		act("$v does not need reparations.", FALSE, ch, NULL, pVeh, TO_CHAR);
		return;
	}

	if (!roll(GET_DEX(ch)))
	{
		send_to_char("You fail.\r\n", ch);
		return;
	}

	pVeh->curr_val.health++;
	act("You make sone reparations to $v.", FALSE, ch, NULL, pVeh, TO_CHAR);
	act("$n makes sone reparations to $v.", FALSE, ch, NULL, pVeh, TO_ROOM);
}

void destroy_vehicle(VEHICLE_DATA *pVeh)
{
	SET_BIT(pVeh->flags, VEH_DESTROYED);
	extract_vehicle(pVeh, 2);
}

/*
 * return 0 if vehicle is destroyed, >0 otherwise
 */
int damage_vehicle(VEHICLE_DATA *pVeh, int dam)
{
	if (dam < 0) dam = 0;

	pVeh->curr_val.health	-= dam;

	if (pVeh->curr_val.health <= 0)
	{
		pVeh->curr_val.health = 0;
		destroy_vehicle(pVeh);
		return (0);
	}

	return (1);
}

void hit_vehicle(CHAR_DATA *ch, VEHICLE_DATA *pVeh)
{
	OBJ_DATA *weapon;
	char dbuf[MAX_STRING_LENGTH];
	int dam;

	if (!ch || !pVeh)
		return;

	if (!(weapon = GET_EQ(ch, WEAR_WIELD)))
	{
		send_to_char("You cannot damage a building with your bare hands.\r\n", ch);
		return;
	}

	if (GET_OBJ_TYPE(weapon) != ITEM_WEAPON)
	{
		send_to_char("Using a weapon would surely help.\r\n", ch);
		return;
	}

	// calc damage
	dam = GET_REAL_DAMROLL(ch);
	dam += dice(GET_OBJ_VAL(weapon, 1), GET_OBJ_VAL(weapon, 2));

	// vehicle absorb damage..
	dam -= pVeh->max_val.health;

	// damage goes from 0 to 100
	dam = URANGE(0, dam, 100);

	// check for damaging the weapon used
	check_damage_obj(ch, weapon, 8);

	// no damage done
	if (!dam)
	{
		// message to attacker
		act("You strike $v, but make no damages.", FALSE, ch, NULL, pVeh, TO_CHAR);
		// message to room
		act("$n strikes $v but nothing happens.", FALSE, ch, NULL, pVeh, TO_ROOM);
		return;
	}

	// vehicle has been destroyed..
	if (!damage_vehicle(pVeh, dam))
	{
		// message to attacker
		act("Your attack DESTROY $v.", FALSE, ch, NULL, pVeh, TO_CHAR);
		// message to room
		act("$n DESTROYS $v with $s attack.", FALSE, ch, NULL, NULL, TO_ROOM);
		return;
	}

	// message to attacker
	sprintf(dbuf, "You strike $v, causing %d damages.", dam);
	act(dbuf, FALSE, ch, NULL, pVeh, TO_CHAR);
	// message to room
	act("$n strike $v, causing some damage.", FALSE, ch, NULL, pVeh, TO_ROOM);
}

bool attack_vehicle(CHAR_DATA *ch, char *arg)
{
	VEHICLE_DATA *pVeh;

	if (!ch || !*arg)
		return (FALSE);

	if (!(pVeh = find_vehicle_in_room_by_name(ch, arg)))
		return (FALSE);

	if (IS_SET(pVeh->flags, VEH_DESTROYED))
	{
		ch_printf(ch, "%s has already been destroyed.\r\n", pVeh->short_description);
		return (TRUE);
	}

	hit_vehicle(ch, pVeh);
	return (TRUE);
}