void DragAndDrop::dropOnGround( cUOSocket* socket, P_ITEM pItem, const Coord_cl& pos )
{
	P_PLAYER pChar = socket->player();

	// Check if the destination is in line of sight
	if ( !pChar->lineOfSight( pos.losItemPoint(pItem->id()) ) )
	{
		socket->bounceItem( pItem, BR_OUT_OF_SIGHT );
		return;
	}

	if ( !pChar->canPickUp( pItem ) )
	{
		socket->bounceItem( pItem, BR_CANNOT_PICK_THAT_UP );
		return;
	}

	if ( pItem->onDropOnGround( pos ) )
	{
		// We're still dragging something
		if ( socket->dragging() )
			socket->bounceItem( socket->dragging(), BR_NO_REASON );

		return;
	}

	pItem->removeFromCont();
	pItem->moveTo( pos );
	pItem->update();

	// Play Sounds for non gold items
	if (pItem->id() != 0xEED) {
		pItem->soundEffect(0x42);
	}
}
// do one spawn and reset the timer
void cSpawnRegion::reSpawn( void )
{
	this->checkForDeleted();

	UI16 i = 0;
	for( i = 0; i < this->npcsPerCycle_; i++ )
	{
		if( this->npcSerials_.size() < this->maxNpcAmt_ )
		{
			// spawn a random npc
			// first find a valid position for the npc
			Coord_cl pos;
			if( this->findValidSpot( pos ) )
			{
				QString NpcSect = this->npcSections_[ RandomNum( 1, this->npcSections_.size() ) - 1 ];
				P_NPC pc = cCharStuff::createScriptNpc( NpcSect, pos );
				if( pc != NULL )
				{
					this->npcSerials_.push_back( pc->serial() );
					pc->setSpawnregion( this->name_ );
				}
			}
		}
	}

	for( i = 0; i < this->itemsPerCycle_; i++ )
	{
		if( this->itemSerials_.size() < this->maxItemAmt_ )
		{
			// spawn a random item
			// first find a valid position for the item
			Coord_cl pos;
			if( this->findValidSpot( pos ) )
			{
				QString ItemSect = this->itemSections_[ RandomNum( 1, this->itemSections_.size() ) - 1 ];
				P_ITEM pi = cItem::createFromScript( ItemSect );
				if( pi != NULL )
				{
					pi->moveTo( pos );
					this->itemSerials_.push_back( pi->serial() );
//					pi->setSpawnRegion( this->name_ );
				}
			}
		}
	}

	this->nextTime_ = uiCurrentTime + RandomNum( this->minTime_, this->maxTime_ ) * MY_CLOCKS_PER_SEC;
}
void cSpawnRegion::spawnSingleItem()
{
	Coord_cl pos;
	if ( findValidSpot( pos ) )
	{
		QString ItemSect = this->itemSections_[RandomNum( 1, this->itemSections_.size() ) - 1];
		P_ITEM pi = cItem::createFromScript( ItemSect );
		if ( pi )
		{
			pi->moveTo( pos, true );
			pi->setSpawnregion( this );
			pi->update();
			onSpawn( pi );
		}
	}
}
void cDragItems::dropOnGround( P_CLIENT client, P_ITEM pItem, const Coord_cl &pos )
{
	P_CHAR pChar = client->player();

	// Check if the destination is in line of sight
	if( !line_of_sight( client->socket(), pChar->pos, pos, TREES_BUSHES|WALLS_CHIMNEYS|DOORS|ROOFING_SLANTED|FLOORS_FLAT_ROOFING|LAVA_WATER ) )
	{
		client->sysMessage( "You cannot see the target." );
		bounceItem( client, pItem );
		return;
	}

	if( !pChar->canPickUp( pItem ) )
	{
		bounceItem( client, pItem );
		return;
	}

	pItem->setContSerial( INVALID_SERIAL );
	pItem->moveTo( pos );	
	pItem->setLayer( 0 );
	RefreshItem( pItem ); // Send it to all clients in range

	pChar->weight -= pItem->getWeight();
	statwindow( client->socket(), pChar ); // Update our weight-stats

	if( pItem->glow != INVALID_SERIAL )
	{
		pChar->removeHalo( pItem );
		pChar->glowHalo( pItem );
	}

	// Multi handling (Hm i don't like that...)
	if( pChar->multis > 0 )
	{
		P_ITEM pMulti = FindItemBySerial( pChar->multis );
		if( pMulti != NULL )
		{
			pMulti = findmulti( pItem->pos );
			if( pItem != NULL )
				pItem->SetMultiSerial( pMulti->serial );
		}
	}
}
void cDragItems::dropOnGround( cUOSocket *socket, P_ITEM pItem, const Coord_cl &pos )
{
	P_PLAYER pChar = socket->player();

	// Check if the destination is in line of sight
	if( !lineOfSight( pChar->pos(), pos, WALLS_CHIMNEYS|DOORS|LAVA_WATER ) )
	{
		socket->bounceItem( pItem, BR_OUT_OF_SIGHT );
		return;
	}

	if( !pChar->canPickUp( pItem ) )
	{
		socket->bounceItem( pItem, BR_CANNOT_PICK_THAT_UP );
		return;
	}

	if( pItem->onDropOnGround( pos ) )
	{
		// We're still dragging something
		if( socket->dragging() )
			socket->bounceItem( socket->dragging(), BR_NO_REASON );

		return;
	}

	pItem->removeFromCont();
	pItem->moveTo( pos );
	pItem->update();

	if( pItem->priv() & 0x01 )
		pItem->startDecay();

	// Multi handling
	// Has it been dropped into a multi
	cMulti* pMulti = cMulti::findMulti( pos );
	if( pMulti )
	{
		pMulti->addItem( pItem );
	}
}
void cBoat::Turn(P_ITEM pBoat, int turn)//Turn the boat item, and send all the people/items on the boat to turnboatstuff()
{
	if (pBoat == NULL) return; 
	
	int id2 = pBoat->id2 ,olddir = pBoat->dir;
	unsigned short int Send[MAXCLIENT];
	SERIAL serial;
	int a,dir, d=0;
	
	
	for (a = 0; a < now; ++a)
	{
		if (iteminrange(a, pBoat, BUILDRANGE) && perm[a])
		{
			Send[d]=a;
			Xsend(a,wppause,2);
			d++; 
		} 
		
	}
	
	//Of course we need the boat items!
	serial = calcserial(pBoat->moreb1,pBoat->moreb2,pBoat->moreb3,pBoat->moreb4);
	if(serial == INVALID_SERIAL) return;
	P_ITEM pTiller = FindItemBySerial( serial );
	if(pTiller == NULL) return;
	P_ITEM pi_p1 = FindItemBySerial( pBoat->morex );
	if(pi_p1 == NULL) return;
	P_ITEM pi_p2 = FindItemBySerial( pBoat->morey );
	if(pi_p2 == NULL) return;
	P_ITEM pi_hold = FindItemBySerial( pBoat->morez );
	if(pi_hold == NULL) return;
	
	if(turn)//Right
	{
		pBoat->dir+=2;
		id2++;
	} else {//Left
		pBoat->dir-=2;
		id2--;
	}
	if(pBoat->dir>7) pBoat->dir-=4;//Make sure we dont have any DIR errors
	//if(pBoat->dir<0) pBoat->dir+=4;
	if(id2<pBoat->more1) id2+=4;//make sure we don't have any id errors either
	if(id2>pBoat->more2) id2-=4;//Now you know what the min/max id is for :-)
	
	pBoat->id2=id2;//set the id
	
	if(pBoat->id2==pBoat->more1) pBoat->dir=0;//extra DIR error checking
	if(pBoat->id2==pBoat->more2) pBoat->dir=6;
	
	
	if( Block( pBoat, 0, 0, pBoat->dir ) )
	{
		pBoat->dir = olddir;
		for( a = 0; a < d; a++ )
		{
			Xsend( Send[a], restart, 2 );
			itemtalk( Send[a], pTiller, "Arr, something's in the way!" );
		}
		return;
	}
	pBoat->id2=id2;//set the id
	
	if(pBoat->id2==pBoat->more1) pBoat->dir=0;//extra DIR error checking
	if(pBoat->id2==pBoat->more2) pBoat->dir=6;    
	
	
	
	
    serial=pBoat->serial; // lb !!!

	unsigned int ci;	
	vector<SERIAL> vecEntries = imultisp.getData(serial);
    for (ci = 0; ci < vecEntries.size(); ci++)
	{
		P_ITEM pi = FindItemBySerial(vecEntries[ci]);
		if (pi != NULL)
			TurnStuff(pBoat, pi, turn);
	}
	
	vecEntries.clear();
	vecEntries = cmultisp.getData(serial);
	for (ci = 0; ci < vecEntries.size(); ci++)
	{
		P_CHAR pc = FindCharBySerial(vecEntries[ci]);
		if (pc != NULL)
			TurnStuff(pBoat, pc, turn);
	}
	
	//Set the DIR for use in the Offsets/IDs array
	dir = (pBoat->dir&0x0F)/2;
	
	//set it's Z to 0,0 inside the boat
	
	pi_p1->MoveTo(pBoat->pos.x,pBoat->pos.y,pi_p1->pos.z);
	pi_p1->id2 = cShipItems[dir][PORT_P_C];//change the ID
	
	pi_p2->MoveTo(pBoat->pos.x,pBoat->pos.y,pi_p2->pos.z);
	pi_p2->id2=cShipItems[dir][STAR_P_C];
	
	pTiller->MoveTo(pBoat->pos.x,pBoat->pos.y, pTiller->pos.z);
	pTiller->id2=cShipItems[dir][TILLERID];
	
	pi_hold->MoveTo(pBoat->pos.x,pBoat->pos.y, pi_hold->pos.z);
	pi_hold->id2=cShipItems[dir][HOLDID];
	
	switch(pBoat->more1)//Now set what size boat it is and move the specail items
	{
	case 0x00:
	case 0x04:
		pi_p1->moveTo(pi_p1->pos + Coord_cl(iSmallShipOffsets[dir][PORT_PLANK][X], iSmallShipOffsets[dir][PORT_PLANK][Y], 0));
		pi_p2->moveTo(pi_p2->pos + Coord_cl(iSmallShipOffsets[dir][STARB_PLANK][X], iSmallShipOffsets[dir][STARB_PLANK][Y], 0));
		pTiller->moveTo(pTiller->pos + Coord_cl(iSmallShipOffsets[dir][TILLER][X], iSmallShipOffsets[dir][TILLER][Y], 0));
		pi_hold->moveTo(pi_hold->pos + Coord_cl(iSmallShipOffsets[dir][HOLD][X], iSmallShipOffsets[dir][HOLD][Y], 0));
		break;
	case 0x08:
	case 0x0C:
		pi_p1->moveTo(pi_p1->pos + Coord_cl(iMediumShipOffsets[dir][PORT_PLANK][X], iMediumShipOffsets[dir][PORT_PLANK][Y], 0) );
		pi_p2->moveTo(pi_p2->pos + Coord_cl(iMediumShipOffsets[dir][STARB_PLANK][X], iMediumShipOffsets[dir][STARB_PLANK][Y], 0) );
		pTiller->moveTo(pTiller->pos + Coord_cl(iMediumShipOffsets[dir][TILLER][X], iMediumShipOffsets[dir][TILLER][Y], 0) );
		pi_hold->moveTo(pi_hold->pos + Coord_cl(iMediumShipOffsets[dir][HOLD][X], iMediumShipOffsets[dir][HOLD][Y], 0) );
		
		break;
	case 0x10:
	case 0x14:
		pi_p1->moveTo(pi_p1->pos + Coord_cl(iLargeShipOffsets[dir][PORT_PLANK][X], iLargeShipOffsets[dir][PORT_PLANK][Y], 0) );
        pi_p2->moveTo(pi_p2->pos + Coord_cl(iLargeShipOffsets[dir][STARB_PLANK][X], iLargeShipOffsets[dir][STARB_PLANK][Y], 0 ) );
		pTiller->moveTo(pTiller->pos + Coord_cl(iLargeShipOffsets[dir][TILLER][X], iLargeShipOffsets[dir][TILLER][Y], 0 ) );
		pi_hold->moveTo(pi_hold->pos + Coord_cl(iLargeShipOffsets[dir][HOLD][X], iLargeShipOffsets[dir][HOLD][Y], 0 ) );
		
		break;
		
	default: 
		{ 
			sprintf((char*)temp,"Turnboatstuff() more1 error! more1 = %c not found!\n",pBoat->more1); 
			LogWarning((char*)temp);
		}  
	}
	
	sendinrange(pi_p1);
	sendinrange(pi_p2);
	sendinrange(pi_hold);
	sendinrange(pTiller);
	
	for (a = 0; a < d; ++a) 
	{ 
		Xsend(Send[a],restart,2);
	}
}
void cBoat::Move(UOXSOCKET s, int dir, P_ITEM pBoat)
{//Move the boat and all it's items 1 square
	int tx=0,ty=0;
	int serial;
     
	if (pBoat == NULL)
		return;

	serial = calcserial(pBoat->moreb1, pBoat->moreb2, pBoat->moreb3, pBoat->moreb4);
	if (serial == INVALID_SERIAL) return;
	P_ITEM pTiller = FindItemBySerial( serial );
	if(pTiller == NULL)
		return;
	
	P_ITEM pi_p1 = FindItemBySerial( pBoat->morex );
	if(pi_p1 == NULL) 
		return;

	P_ITEM pi_p2 = FindItemBySerial( pBoat->morey );
	if(pi_p2 == NULL) 
		return;

	P_ITEM pHold = FindItemBySerial( pBoat->morez );
	if(pHold == NULL) 
		return;

	Xsend(s,wppause,2);

	switch(dir&0x0F)//Which DIR is it going in?
	{
	case '\x00' : 
		ty--;
		break;
	case '\x01' : 
		tx++; 
		ty--;
		break;
	case '\x02' :
		tx++;
		break;
	case '\x03' :
		tx++;
		ty++;
		break;
	case '\x04' : 
		ty++;
		break;
	case '\x05' :
		tx--;
		ty++;
		break;
	case '\x06' : 
		tx--;
		break;
	case '\x07' : 
		tx--; 
		ty--;
		break;
	default:
		{
		  sprintf((char*)temp,"warning: Boat direction error: %i int boat %i\n",pBoat->dir&0x0F,pBoat->serial);
		  LogWarning((char*)temp);
		  break;
		}
	}

	if((pBoat->pos.x+tx<=200 || pBoat->pos.x+tx>=6000) && (pBoat->pos.y+ty<=200 || pBoat->pos.y+ty>=4900)) //bugfix LB
	{
		pBoat->type2=0;
		itemtalk(s, pTiller, "Arr, Sir, we've hit rough waters!");
		Xsend(s,restart,2);
		return;
	}
	if(Block(pBoat,tx,ty,dir))
	{
		pBoat->type2=0;
		itemtalk(s, pTiller, "Arr, somethings in the way!");
		Xsend(s,restart,2);
		return;
	}

	//Move all the special items
	Coord_cl desloc(tx, ty, 0);
	pBoat->moveTo(pBoat->pos + desloc);
	pTiller->moveTo(pTiller->pos + desloc);
	pi_p1->moveTo(pi_p1->pos + desloc);
	pi_p2->moveTo(pi_p2->pos + desloc);
	pHold->moveTo(pHold->pos + desloc);

    serial = pBoat->serial;
	
	unsigned int a;
	vector<SERIAL> vecEntries = imultisp.getData(pBoat->serial);
	for (a = 0; a < vecEntries.size(); a++)
	{
		P_ITEM pi = FindItemBySerial(vecEntries[a]);
		if(pi != NULL)
		{
			pi->MoveTo(pi->pos.x+=tx, pi->pos.y+=ty, pi->pos.z);
			sendinrange(pi);
		}
	}

	vecEntries.clear();
	vecEntries = cmultisp.getData(pBoat->serial);
	for (a = 0; a < vecEntries.size(); a++)
	{
		P_CHAR pc_c = FindCharBySerial(vecEntries[a]);
		if (pc_c != NULL)
		{
			pc_c->moveTo(pc_c->pos + desloc);
			teleport((pc_c));
		}
	}
	Xsend(s,restart,2);
}
void cDragItems::dropOnItem( cUOSocket *socket, P_ITEM pItem, P_ITEM pCont, const Coord_cl &dropPos )
{
	P_PLAYER pChar = socket->player();
	
	if( pItem->isMulti() )
	{
		socket->sysMessage( tr( "You cannot put houses in containers" ) );
		cUOTxBounceItem bounce;
		bounce.setReason( BR_NO_REASON );
		socket->send( &bounce );
		Items->DeleItem( pItem );
		return;
	}
	
	if( pItem->onDropOnItem( pCont ) )
	{
		if( socket->dragging() )
			socket->bounceItem( socket->dragging(), BR_NO_REASON );

		return;
	}
	else if( pCont->onDropOnItem( pItem ) )
	{
		if( socket->dragging() )
			socket->bounceItem( socket->dragging(), BR_NO_REASON );

		return;
	}

	// If the target belongs to another character 
	// It needs to be our vendor or else it's denied
	P_CHAR packOwner = pCont->getOutmostChar();

	if( ( packOwner ) && ( packOwner != pChar ) && !pChar->isGM() )
	{
		// For each item someone puts into there 
		// He needs to do a snoop-check
		if( pChar->maySnoop() )
		{
			if( !pChar->checkSkill( SNOOPING, 0, 1000 ) )
			{

				socket->sysMessage( tr( "You fail to put that into %1's pack" ).arg( packOwner->name() ) );
				socket->bounceItem( pItem, BR_NO_REASON );
				return;
			}
		}

		if( packOwner->objectType() == enPlayer || 
			( packOwner->objectType() == enNPC && dynamic_cast<P_NPC>(packOwner)->owner() != pChar ) )
		{
			socket->sysMessage( tr("You cannot put that into the belongings of another player") );
			socket->bounceItem( pItem, BR_NO_REASON );
			return;
		}
	}

	// If we put the item into a trade-window
	// Reset the trade-status for both players
	if( pCont->layer() == 0 && pCont->id() == 0x1E5E &&	pChar->Wears( pCont ) )
	{
		// Trade window???
		P_ITEM tradeWindow = FindItemBySerial( calcserial( pCont->moreb1(), pCont->moreb2(), pCont->moreb3(), pCont->moreb4() ) );

		// If it *IS* a trade-window, replace the status
		if( tradeWindow && ( pCont->morez() || tradeWindow->morez() ) )
		{
			tradeWindow->setMoreZ(0);
			pCont->setMoreZ(0);
//			sendtradestatus( tradeWindow, pCont );
		}
	}
	
	if( !pChar->canPickUp( pItem ) )
	{
		socket->bounceItem( pItem, BR_CANNOT_PICK_THAT_UP );
		return;
	}

	// Trash can
	if( pCont->type()==87 )
	{
		Items->DeleItem( pItem );
		socket->sysMessage( tr( "As you let go of the item it disappears." ) );
		return;
	}

	// Spell Book
	cSpellBook *pBook = dynamic_cast< cSpellBook* >( pCont );
	if( pBook )
 	{
		SI08 spellId = NewMagic->calcSpellId( pItem->id() );

		if( pItem->type() != 1105 || spellId < 0 )
		{
			socket->sysMessage( tr( "You can only put scrolls into a spellbook" ) );
			socket->bounceItem( pItem, BR_NO_REASON );
			return;
		}		

		if( pBook->hasSpell( spellId ) )
		{
			socket->sysMessage( tr( "That spellbook already contains this spell" ) );
			socket->bounceItem( pItem, BR_NO_REASON );
			return;
		}

		if( pItem->amount() > 1 )
		{
			socket->sysMessage( tr( "You can only put 1 scroll into a spellbook at a time" ) );
			socket->bounceItem( pItem, BR_NO_REASON );
			return;
		}
		else
		{	
			pBook->addSpell( spellId );
			Items->DeleItem( pItem );
			pBook->update( socket );
			return;
		}
	}

	// We drop something on the belongings of one of our playervendors
/*	if( ( packOwner != NULL ) && ( packOwner->npcaitype() == 17 ) && packOwner->owner() == pChar )
	{
		socket->sysMessage( tr( "You drop something into your playervendor (unimplemented)" ) );
		socket->bounceItem( pItem, BR_NO_REASON );
		return;
	}*/

	// Playervendors (chest equipped by the vendor - opened to the client)

	/*if( !( pCont->pileable() && pItem->pileable() && pCont->id() == pItem->id() || ( pCont->type() != 1 && pCont->type() != 9 ) ) )
	{
		P_CHAR pc_j = GetPackOwner(pCont);
		if (pc_j != NULL)
		{
			if (pc_j->npcaitype() == 17 && pc_j->isNpc() && pChar->Owns(pc_j))
			{
				pChar->inputitem = pItem->serial;
				pChar->inputmode = cChar::enPricing;
				sysmessage(s, "Set a price for this item.");
			}
		}
	*/

	// We may also drop into *any* locked chest
	// So we can have post-boxes ;o)
	// Spellbooks are containers for us as well
	if( pCont->type() == 1 || pCont->type() == 8 || pCont->type() == 63 || pCont->type() == 65 || pCont->type() == 66 )
	{
		// If we're dropping it onto the closed container
		if( dropPos.distance( pCont->pos() ) == 0 )
		{
			pCont->addItem( pItem );
		}
		else
		{
			pCont->addItem( pItem, false );
			pItem->setPos( dropPos );
		}

		// Dropped on another Container/in another Container
		pChar->soundEffect( 0x57 );
		pItem->update();
		return;
	}
	// Item matching needs to be extended !!! at least Color! (for certain types)
	else if ( pCont->isPileable() && pItem->isPileable() && ( pCont->id() == pItem->id() ) )
	{
		if( pCont->amount() + pItem->amount() <= 65535 )
		{
			pCont->setAmount( pCont->amount() + pItem->amount() );
			
			Items->DeleItem( pItem );
			pCont->update(); // Need to update the amount
			return;
		}
		// We have to *keep* our current item
		else
		{
			pCont->setAmount( 65535 ); // Max out the amount
			pCont->update();

			// The delta between 65535 and pCont->amount() sub our Amount is the
			// new amount
			pItem->setAmount( pItem->amount() - ( 65535 - pCont->amount() ) );
		}
	}

	// We dropped the item NOT on a container
	// And were *un*able to stack it (!)
	// >> Set it to the location of the item we dropped it on and stack it up by 2
	pItem->moveTo( pCont->pos() );
	pItem->setPos( pItem->pos() + Coord_cl(0, 0, 2) );
	pItem->update();

/*	// This needs to be checked
	// It annoyingly shows the spellbook
	// whenever you add a scroll
	// << could it be that addItemToContainer is enough?? >>
	if( pCont->type() == 9 )
		Magic->openSpellBook( pChar, pCont );*/
}
void cDragItems::dropOnItem( P_CLIENT client, P_ITEM pItem, P_ITEM pCont, const Coord_cl &dropPos )
{
	P_CHAR pChar = client->player();
	
	if( pItem->isMulti() )
	{
		client->sysMessage( "You cannot put houses in containers" );
		bounceItem( client, pItem );
		return;
	}
	
	// If the target belongs to another character 
	// It needs to be our vendor or else it's denied
	P_CHAR packOwner = GetPackOwner( pCont );

	if( ( packOwner != NULL ) && ( packOwner != pChar ) )
	{
		// For each item someone puts into there 
		// He needs to do a snoop-check
		if( pChar->canSnoop() )
		{
			if( !Skills->CheckSkill( pChar, SNOOPING, 0, 1000 ) )
			{

				client->sysMessage( QString( "You fail to put that into %1's pack" ).arg( packOwner->name.c_str() ) );
				bounceItem( client, pItem );
				return;
			}
		}

		if( !packOwner->isNpc() || ( packOwner->npcaitype() != 17 ) || !pChar->Owns( packOwner ) )
		{
			client->sysMessage( "You cannot put that into the belongings of another player" );
			bounceItem( client, pItem );
			return;
		}
	}

	// If we put the item into a trade-window
	// Reset the trade-status for both players
	if( pCont->layer() == 0 && pCont->id() == 0x1E5E &&	pChar->Wears( pCont ) )
	{
		// Trade window???
		P_ITEM tradeWindow = FindItemBySerial( calcserial( pCont->moreb1(), pCont->moreb2(), pCont->moreb3(), pCont->moreb4() ) );

		// If it *IS* a trade-window, replace the status
		if( tradeWindow && ( pCont->morez || tradeWindow->morez ) )
			{
				tradeWindow->morez = 0;
				pCont->morez = 0;
				sendtradestatus( tradeWindow, pCont );
			}
	}
	
	if( !pChar->canPickUp( pItem ) )
	{
		bounceItem( client, pItem );
		return;
	}

	// Trash can
	if( pCont->type()==87 )
	{
		Items->DeleItem( pItem );
		client->sysMessage( "As you let go of the item it disappears." );
		return;
	}

	// Spell Book
	if( pCont->type() == 9 )
	{
		UI08 spellId = Magic->calcSpellId( pItem->id() );

		if( spellId < 0 )
		{
			client->sysMessage( "You can only put scrolls into a spellbook" );
			bounceItem( client, pItem );
			return;
		}		

		if( Magic->hasSpell( pCont, spellId )  )
		{
			client->sysMessage( "That spellbook already contains this spell" );
			bounceItem( client, pItem );
			return;
		}
	}

	// We drop something on the belongings of one of our playervendors
	if( ( packOwner != NULL ) && ( packOwner->npcaitype() == 17 ) && pChar->Owns( packOwner ) )
	{
		client->sysMessage( "You drop something into your playervendor" );
		bounceItem( client, pItem );
		return;
	}

	// Playervendors (chest equipped by the vendor - opened to the client)

	/*if( !( pCont->pileable() && pItem->pileable() && pCont->id() == pItem->id() || ( pCont->type() != 1 && pCont->type() != 9 ) ) )
	{
		P_CHAR pc_j = GetPackOwner(pCont);
		if (pc_j != NULL)
		{
			if (pc_j->npcaitype() == 17 && pc_j->isNpc() && pChar->Owns(pc_j))
			{
				pChar->inputitem = pItem->serial;
				pChar->inputmode = cChar::enPricing;
				sysmessage(s, "Set a price for this item.");
			}
		}
	*/
	
	// We may also drop into *any* locked chest
	// So we can have post-boxes ;o)
	// Spellbooks are containers for us as well
	if( pCont->type() == 9 || pCont->type() == 1 || pCont->type() == 8 || pCont->type() == 63 || pCont->type() == 65 || pCont->type() == 66 )
	{
		pItem->setContSerial( pCont->serial );
		pItem->setLayer( 0 ); // Remove it from our drag-layer

		// Huh ? - Make that random will you!
		pItem->pos = dropPos;
		
		SndRemoveitem( pItem->serial );
		RefreshItem( pItem );
		
		// Dropped on another Container/in another Container
		soundeffect2( pChar, 0x57 );

		return;
	}
	// Item matching needs to be extended !!! at least Color! (for certain types)
	else if ( pCont->isPileable() && pItem->isPileable() && ( pCont->id() == pItem->id() ) )
	{
		if( pCont->amount() + pItem->amount() <= 65535 )
		{
			pCont->setAmount( pCont->amount() + pItem->amount() );
			Items->DeleItem( pItem );

			RefreshItem( pCont ); // Need to update the amount
			return;
		}
		// We have to *keep* our current item
		else
		{
			pCont->setAmount( 65535 ); // Max out the amount
			RefreshItem( pCont );

			// The delta between 65535 and pCont->amount() sub our Amount is the
			// new amount
			pItem->setAmount( pItem->amount() - ( 65535 - pCont->amount() ) );
		}
	}

	// We dropped the item NOT on a container
	// And were *un*able to stack it (!)
	// >> Set it to the location of the item we dropped it on and stack it up by 1
	pItem->moveTo( pCont->pos );
	pItem->pos.z++; // Increase z by 1
	pItem->pos.y++; // To get it visualized do that with y as well
	pItem->setLayer( 0 );
	pItem->setContSerial( pCont->contserial );
	RefreshItem( pItem );
				
	// This needs to be checked
	// It annoyingly shows the spellbook
	// whenever you add a scroll
	if( pCont->type() == 9 )
		Magic->openSpellBook( pChar, pCont );

	// Glowing Objects moved between chars
	if( pItem->glow != INVALID_SERIAL )
	{
		pChar->removeHalo( pItem );
				
		if( packOwner != NULL )
		{
			packOwner->addHalo(pItem);
			packOwner->glowHalo(pItem);
		}
	}
}
void DragAndDrop::dropOnItem( cUOSocket* socket, P_ITEM pItem, P_ITEM pCont, const Coord_cl& dropPos )
{
	P_PLAYER pChar = socket->player();

	if ( pItem->isMulti() )
	{
		socket->sysMessage( tr( "You cannot put houses in containers" ) );
		cUOTxBounceItem bounce;
		bounce.setReason( BR_NO_REASON );
		socket->send( &bounce );
		pItem->remove();
		return;
	}

	if ( pItem->onDropOnItem( pCont ) )
	{
		if ( pItem->free )
			return;

		if ( socket->dragging() )
			socket->bounceItem( socket->dragging(), BR_NO_REASON );

		return;
	}
	else if ( pCont->onDropOnItem( pItem ) )
	{
		if ( pItem->free )
			return;

		if ( socket->dragging() )
			socket->bounceItem( socket->dragging(), BR_NO_REASON );

		return;
	}

	// If the target belongs to another character
	// It needs to be our vendor or else it's denied
	P_CHAR packOwner = pCont->getOutmostChar();

	if ( ( packOwner ) && ( packOwner != pChar ) && !pChar->isGM() )
	{
		// For each item someone puts into there
		// He needs to do a snoop-check
		if ( pChar->maySnoop() )
		{
			if ( !pChar->checkSkill( SNOOPING, 0, 1000 ) )
			{
				socket->sysMessage( tr( "You fail to put that into %1's pack" ).arg( packOwner->name() ) );
				socket->bounceItem( pItem, BR_NO_REASON );
				return;
			}
		}

		if ( packOwner->objectType() == enPlayer || ( packOwner->objectType() == enNPC && dynamic_cast<P_NPC>( packOwner )->owner() != pChar ) )
		{
			socket->sysMessage( tr( "You cannot put that into the belongings of another player" ) );
			socket->bounceItem( pItem, BR_NO_REASON );
			return;
		}
	}

	if ( !pChar->canPickUp( pItem ) )
	{
		socket->bounceItem( pItem, BR_CANNOT_PICK_THAT_UP );
		return;
	}

	// We drop something on the belongings of one of our playervendors
	/*	if( ( packOwner != NULL ) && ( packOwner->npcaitype() == 17 ) && packOwner->owner() == pChar )
	{
	socket->sysMessage( tr( "You drop something into your playervendor (unimplemented)" ) );
	socket->bounceItem( pItem, BR_NO_REASON );
	return;
	}*/

	// Playervendors (chest equipped by the vendor - opened to the client)

	/*if( !( pCont->pileable() && pItem->pileable() && pCont->id() == pItem->id() || ( pCont->type() != 1 && pCont->type() != 9 ) ) )
	{
	P_CHAR pc_j = GetPackOwner(pCont);
	if (pc_j != NULL)
	{
	if (pc_j->npcaitype() == 17 && pc_j->isNpc() && pChar->Owns(pc_j))
	{
	pChar->inputitem = pItem->serial;
	pChar->inputmode = cChar::enPricing;
	sysmessage(s, "Set a price for this item.");
	}
	}
	*/

	// We may also drop into *any* locked chest
	// So we can have post-boxes ;o)
	if ( pCont->type() == 1 )
	{
		// If we're dropping it onto the closed container
		if ( dropPos.x == 0xFFFF && dropPos.y == 0xFFFF )
		{
			pCont->addItem( pItem );
		}
		else
		{
			pCont->addItem( pItem, false );
			pItem->setPos( dropPos );
		}

		// Dropped on another Container/in another Container
		pChar->soundEffect( 0x57 );
		pItem->update();
		return;
	}
	else if ( pCont->canStack( pItem ) )
	{
		if ( pCont->amount() + pItem->amount() <= 65535 )
		{
			pCont->setAmount( pCont->amount() + pItem->amount() );

			pItem->remove();
			pCont->update(); // Need to update the amount
			pCont->resendTooltip();
			return;
		}
		else
		{
			// The delta between 65535 and pCont->amount() sub our Amount is the
			// new amount
			pItem->setAmount( pItem->amount() - ( 65535 - pCont->amount() ) );
			pItem->resendTooltip();

			pCont->setAmount( 65535 ); // Max out the amount
			pCont->update();
			pCont->resendTooltip();
		}
	}

	// We dropped the item NOT on a container
	// And were *un*able to stack it (!)
	// >> Set it to the location of the item we dropped it on and stack it up by 2
	if ( pCont->container() )
	{
		P_ITEM pNewCont = dynamic_cast<P_ITEM>( pCont->container() );

		if ( pNewCont )
		{
			pNewCont->addItem( pItem, false );
			pItem->setPos( pCont->pos() + Coord_cl( 0, 0, 2 ) );
		}
		else
		{
			pChar->getBackpack()->addItem( pItem );
		}
	}
	else
	{
		pItem->removeFromCont();
		pItem->moveTo( pCont->pos() + Coord_cl( 0, 0, 2 ) );
	}

	pItem->update();
}
// CombatHit now expects that LineOfSight has been checked before (Duke, 10.7.2001)
void cCombat::CombatHit(P_CHAR pc_attacker, P_CHAR pc_deffender, unsigned int currenttime, short los)
{
	char hit;

	if (pc_deffender == NULL || pc_attacker == NULL)
		return;

	UOXSOCKET s1=calcSocketFromChar(pc_attacker), s2=calcSocketFromChar(pc_deffender);
	unsigned short fightskill = Skills->GetCombatSkill(pc_attacker), bowtype = Combat->GetBowType(pc_attacker),splitnum,splitcount,hitin;
	unsigned int basedamage;
	int damage; // removed from unsigne by Magius(CHE)
	signed int x;
	// Magius(CHE) - For armour absorbtion system
	int maxabs, maxnohabs, tmpj;


	P_ITEM pWeapon=pc_attacker->getWeapon();// get the weapon item only once

	if (pWeapon && !(rand()%50)	// a 2 percent chance (Duke, 07.11.2000)
		&& pWeapon->type !=9)	// but not for spellbooks (Duke, 09/10/00)
	{
		pWeapon->hp--; //Take off a hit point
		if(pWeapon->hp<=0)
		{
			sysmessage(s1, tr("Your weapon has been destroyed"));
			if ((pWeapon->trigon==1) && (pWeapon->layer>0))// -Frazurbluu- Trigger Type 2 is my new trigger type *-
			{
				Trig->triggerwitem(s1, pWeapon, 1); // trigger is fired when item destroyed
			}				
			Items->DeleItem(pWeapon);
		}
	}

	// End here - Magius(CHE) - For armour absorbtion system



	pc_attacker->swingtarg=-1;

	if((chardist(pc_attacker, pc_deffender)>1 && fightskill!=ARCHERY) || !los) return;
	if(pc_deffender->isNpc() && pc_deffender->isInvul()) return; // ripper


	hit=Skills->CheckSkill(pc_attacker, fightskill, 0, 1000);  // increase fighting skill for attacker and defender
	if (!hit)
	{
		if (pc_attacker->isPlayer())
			doMissedSoundEffect(pc_attacker);
		if ((fightskill==ARCHERY)&&(los))
		{
			if (rand()%3-1)//-1 0 or 1
			{
				short id=0x1BFB;	// bolts
				if (bowtype==1)
					id=0x0F3F;		// arrows

				P_ITEM pAmmo=Items->SpawnItem(pc_deffender,1,"#",1,id,0,0);
				if(pAmmo)
				{
					pAmmo->moveTo(pc_deffender->pos);
					pAmmo->priv=1;
					RefreshItem(pAmmo);
				}
			}
		}
	}
	else
	{
		if (!pc_deffender->isInvul())
		{
			if (pc_deffender->xid==0x0191) soundeffect2(pc_deffender,0x014b);
			else if (pc_deffender->xid==0x0190) soundeffect2(pc_deffender,0x0156);
			playmonstersound(pc_deffender, pc_deffender->id(), SND_DEFEND);
			//AntiChrist -- for poisoned weapons
			if((pWeapon) && (pWeapon->poisoned>0))
			{
				   pc_deffender->poisoned=pWeapon->poisoned;
				   pc_deffender->poisontime=uiCurrentTime+(MY_CLOCKS_PER_SEC*(40/pc_deffender->poisoned)); // a lev.1 poison takes effect after 40 secs, a deadly pois.(lev.4) takes 40/4 secs - AntiChrist
			       pc_deffender->poisonwearofftime=pc_deffender->poisontime+(MY_CLOCKS_PER_SEC*SrvParams->poisonTimer()); //wear off starts after poison takes effect - AntiChrist
			}
			CheckPoisoning(s2, pc_attacker, pc_deffender);	// attacker poisons defender
			CheckPoisoning(s1, pc_deffender, pc_attacker); // and vice versa

			if ((pc_deffender->effDex()>0)) pc_deffender->priv2&=0xFD;	// unfreeze

			if (fightskill!=WRESTLING && los)
				Combat->ItemSpell(pc_attacker, pc_deffender);
			
			if (fightskill!=WRESTLING || pc_attacker->isNpc())
				basedamage=Combat->CalcAtt(pc_attacker); // Calc base damage
			else
			{
				if ((pc_attacker->skill[WRESTLING]/100) > 0) 
				{
					if (pc_attacker->skill[WRESTLING]/100!=0)
						basedamage=rand()%(pc_attacker->skill[WRESTLING]/100);
					else basedamage=0;
				}
				else basedamage=rand()%2;
			}

			if((pc_attacker->isPlayer()) && (fightskill!=WRESTLING))
			{ 
				if (pWeapon->racehate != 0 && pc_deffender->race != 0)//-Fraz- Racehating combat
				{
					if (pWeapon->racehate==pc_deffender->race)
					{
						basedamage *=2;
							if(pc_deffender->isPlayer())
							{
								sysmessage(s2, tr("You scream in agony from being hit by the accursed metal!"));
								if (pc_deffender->xid == 0x0191) soundeffect2(pc_deffender,0x0152);
								else if (pc_deffender->xid==0x0190) soundeffect2(pc_deffender,0x0157);
							}// can add a possible effect below here for npc's being hit
					}
							
				}
			}
			Skills->CheckSkill(pc_attacker, TACTICS, 0, 1000);
			damage=(int)(basedamage*((pc_attacker->skill[TACTICS]+500.0)/1000.0)); // Add Tactical bonus
			damage=damage+(int)((basedamage*(pc_attacker->st/500.0))); // Add Strength bonus

			//Adds a BONUS DAMAGE for ANATOMY
			//Anatomy=100 -> Bonus +20% Damage - AntiChrist (11/10/99)
			if (Skills->CheckSkill(pc_attacker, ANATOMY, 0, 1000))
			{
				float multiplier=(((pc_attacker->skill[ANATOMY]*20)/1000.0f)/100.0f)+1;
				damage=(int)  (damage * multiplier);
			}
			//Adds a BONUS DEFENCE for TACTICS
			//Tactics=100 -> Bonus -20% Damage - AntiChrist (11/10/99)
			float multiplier=1-(((pc_deffender->skill[TACTICS]*20)/1000.0f)/100.0f);
			damage=(int)  (damage * multiplier);
			P_ITEM pShield=pc_deffender->getShield();
			if(pShield)
			{
				if (Skills->CheckSkill(pc_deffender, PARRYING, 0, 1000))// chance to block with shield
				{
					if (pShield->def!=0) damage-=rand()%(pShield->def);// damage absorbed by shield
					if(rand()%2) pShield->hp--; //Take off a hit point
					if(pShield->hp<=0)
					{
						sysmessage(s2, tr("Your shield has been destroyed"));
						if ((pShield->trigon==1) && (pShield->layer >0))// -Frazurbluu- Trigger Type 2 is my new trigger type *-
						{
							Trig->triggerwitem(s2, pShield, 1); // trigger is fired when item destroyed
						}	
						Items->DeleItem(pShield);
					}
				}
			}
			// Armor destruction and sped up by hitting with maces should go in here somewhere 
			// According to lacation of body hit Id imagine -Frazurbluu- **NEEDS ADDED**
			x=rand()%100;// determine area of body hit
			if (!SrvParams->combatHitMessage())
			{
				if (x<=44) x=1; // body
				else if (x<=58) x=2; // arms
				else if (x<=72) x=3; // head
				else if (x<=86) x=4; // legs
				else if (x<=93) x=5; // neck
				else x=6; // hands
			}
			else
			{
				temp[0] = '\0';
				hitin = rand()%2;
				if (x<=44)
				{
					x=1;       // body
					switch (hitin)
					{
					case 1:
						//later take into account dir facing attacker during battle
						if (damage < 10) strcpy(temp, "hits you in your Chest!");
						else if (damage >=10) strcpy(temp, "lands a terrible blow to your Chest!");
						break;
					case 2:
						if (damage < 10) strcpy(temp, "lands a blow to your Stomach!");
						else if (damage >=10) strcpy(temp, "knocks the wind out of you!");
						break;
					default:
						if (damage < 10) strcpy(temp, "hits you in your Ribs!");
						else if (damage >=10) strcpy(temp, "broken your Rib?!");
					}
				}
				else if (x<=58)
				{
					if (damage > 1)
					{
						x=2;  // arms
						switch (hitin)
						{
						case 1:	strcpy(temp, "hits you in Left Arm!");	break;
						case 2:	strcpy(temp, "hits you in Right Arm!");	break;
						default:strcpy(temp, "hits you in Right Arm!");
						}
					}
				}
				else if (x<=72)
				{
					x=3;  // head
					switch (hitin)
					{
					case 1:
						if (damage < 10) strcpy(temp, "hits you you straight in the Face!");
						else if (damage >=10) strcpy(temp, "lands a stunning blow to your Head!");
						break;
					case 2:
						if (damage < 10) strcpy(temp, "hits you to your Head!"); //kolours - (09/19/98)
						else if (damage >=10) strcpy(temp, "smashed a blow across your Face!");
						break;
					default:
						if (damage < 10) strcpy(temp, "hits you you square in the Jaw!");
						else if (damage >=10) strcpy(temp, "lands a terrible hit to your Temple!");
					}
				}
				else if (x<=86) 
				{
					x=4;  // legs
					switch (hitin)
					{
					case 1:	strcpy(temp, "hits you in Left Thigh!");	break;
					case 2:	strcpy(temp, "hits you in Right Thigh!");	break;
					default:strcpy(temp, "hits you in Groin!");
					}
				}
				else if (x<=93)
				{
					x=5;  // neck
					strcpy(temp, "hits you to your Throat!");
				}
				else
				{
					x=6;  // hands
					switch (hitin)
					{
					case 1:
						if (damage > 1) strcpy(temp, tr("hits you in Left Hand!").latin1());
						break;
					case 2:
						if (damage > 1) strcpy(temp, tr("hits you in Right Hand!").latin1());
						break;
					default:
						if (damage > 1) strcpy(temp, tr("hits you in Right Hand!").latin1());
					}
				}

				sprintf((char*)temp2,"%s %s",pc_attacker->name.c_str(), temp);
				if (pc_deffender->isPlayer() && s2!=-1) sysmessage(s2, (char*)temp2); //kolours -- hit display
			}
			x = CalcDef(pc_deffender,x);
			
			// Magius(CHE) - For armour absorbtion system
			maxabs = 20; //
			           // there are monsters with DEF >20, this makes them undefeatable
			maxnohabs=100;
			if (SrvParams->maxAbsorbtion() > 0)
			{
				maxabs = SrvParams->maxAbsorbtion();
			}
			if (SrvParams->maxnohabsorbtion() > 0)
			{
				maxnohabs = SrvParams->maxnohabsorbtion();
			}		
			if (!ishuman(pc_deffender)) maxabs=maxnohabs;
			tmpj=(int) (damage*x)/maxabs; // Absorbtion by Magius(CHE)
			damage -= tmpj;
			if (damage<0) damage=0;
			if (pc_deffender->isPlayer()) damage /= SrvParams->npcdamage(); // Rate damage against other players
			// End Armour Absorbtion by Magius(CHE) (See alse reactive armour spell damage)

			if (pc_attacker->isPlayer())//Zippy
				ItemCastSpell(s1, pc_deffender,pWeapon);

			//AntiChrist - 26/10/99
			//when hitten and damage >1, defender fails if casting a spell!
			if(damage>1 && pc_deffender->isPlayer())//only if damage>1 and against a player
			{
				if(pc_deffender->casting && currentSpellType[s2]==0 )
				{//if casting a normal spell (scroll: no concentration loosen)
					Magic->SpellFail(s2);
					currentSpellType[s2]=0;
					pc_deffender->spell=-1;
					pc_deffender->casting=0;
					pc_deffender->spelltime=0;
					pc_deffender->priv2 &= 0xfd; // unfreeze, bugfix LB
				}
			}

			if(damage>0)
			{
				if (pc_deffender->ra) // For reactive armor spell
				{
					// -Frazurbluu- RA may need a rewrite to be more OSI standard here
					// Its said 80% deflected 10% to attacker / 10% defender gotta check special effects
					int damage1;
					damage1=(int)( damage*(pc_deffender->skill[MAGERY]/2000.0));
					pc_deffender->hp -= damage-damage1;
					if (pc_deffender->isNpc()) damage1 = damage1 * SrvParams->npcdamage(); // by Magius(CHE)
					pc_attacker->hp -= damage1;  // Remove damage from attacker
					staticeffect(pc_deffender, 0x37, 0x4A, 0, 15);//RA effect - AntiChrist (9/99)
					if ((fightskill==MACEFIGHTING) && (IsSpecialMace(pWeapon->id())))// Stamina Loss -Fraz-
					{ 
						//pc_attacker->stm-=3+(rand()%4);
					}
					if ((fightskill==FENCING) && (IsFencing2H(pWeapon->id())))// Paralyzing -Fraz-
					{ 
						//will call the combat caused paralyzation **NEED TO DO**
					}
					if ((fightskill==SWORDSMANSHIP) && (IsAxe(pWeapon->id())))// Concussion Hit -Fraz-
					{ 
						//will call the combat caused concussion (loss of int for 30 secs) **NEED TO DO**
						// for now make it subtract mana
						//pc_attacker->mn-=(pc_attacker->mn/2);
					}
					updatestats(pc_attacker, 0);
				}
				else 
				{	// -Fraz- Now needs adjusted to happen on a skill percentage 
					pc_deffender->hp-=damage; // Remove damage from defender only apply special hits to non-npc's
					if ((fightskill==MACEFIGHTING) && (IsSpecialMace(pWeapon->id())) && (pc_deffender->isPlayer()))// Stamina Loss -Fraz-
					{ 
						pc_deffender->stm-=3+(rand()%3);
					}
					if ((fightskill==FENCING) && (IsFencing2H(pWeapon->id())) && (pc_deffender->isPlayer()))// Paralyzing -Fraz-
					{ 
						tempeffect(pc_attacker, pc_deffender, 44, 0, 0, 0);
						sysmessage(s1, tr("You delivered a paralyzing blow"));
											}
					if ((fightskill==SWORDSMANSHIP) && (IsAxe(pWeapon->id())) && (pc_deffender->isPlayer()))// Concussion Hit -Fraz-
					{ 
						tempeffect(pc_attacker, pc_deffender, 45, 0, 0, 0);
						//pc_attacker->mn-=(pc_attacker->mn/2); //-Fraz- temp use of this for concussion
					}
					updatestats((pc_deffender), 0);
				}
				// blood shred by blackwind
				if (damage>10)
				{
	               short id = 0x122c;	
	               if (damage>50) id=0x122a;
				   else if (damage>40) id=0x122d;
	               else if (damage>30) id=0x122e;
	               else if (damage>20) id=0x122b;
				   P_ITEM pBlood = Items->SpawnItem(pc_deffender, 1, "#", 0, id, 0, 0);
				   if (pBlood)
				   {
					  pBlood->moveTo(pc_deffender->pos);
					  pBlood->priv = 1;
					  pBlood->setGMMovable(); //Moveable by GM
					  RefreshItem(pBlood);
					  pBlood->decaytime = (SrvParams->decayTime()/2)*MY_CLOCKS_PER_SEC+uiCurrentTime;
				   }
				}

				/////////  For Splitting NPCs ///  McCleod
				if ((pc_deffender->split>0)&&(pc_deffender->hp>=1))
				{
					if (rand()%100<=pc_deffender->splitchnc)
					{
						if (pc_deffender->split==1) splitnum=1;
						else splitnum=rand()%pc_deffender->split+1;
						
						for (splitcount=0;splitcount<splitnum;splitcount++)
							Npcs->Split(pc_deffender);
					}
				}
				////////      End of spliting NPCs
			}
			if (pc_attacker->isPlayer())
				if((fightskill==ARCHERY && los)|| fightskill!=ARCHERY)
					doSoundEffect(pc_attacker, fightskill, pWeapon);
			if (pc_deffender->hp<0) pc_deffender->hp=0;
			updatestats((pc_deffender), 0);
			x = pc_deffender->id();
			if (x>=0x0190)
			{
				if (!pc_deffender->onhorse) 
					npcaction(pc_deffender, 0x14);
			}
		}
	}
}