/*!
	Sends items which came in range and handles collisions with teleporters
	or damaging items.
*/
void handleItems( P_CHAR pChar, const Coord& oldpos )
{
	P_PLAYER player = dynamic_cast<P_PLAYER>( pChar );

	MapItemsIterator iter = MapObjects::instance()->listItemsInCircle( pChar->pos(), VISRANGE );
	for ( P_ITEM pItem = iter.first(); pItem; pItem = iter.next() )
	{
		// Check for item collisions here.
		if ( pChar->pos().x == pItem->pos().x && pChar->pos().y == pItem->pos().y )
		{
			if ( pItem->pos().z >= pChar->pos().z - 15 && pItem->pos().z <= pChar->pos().z + 15 )
			{
				if ( handleItemCollision( pChar, pItem ) )
				{
					break;
				}
			}
		}

		// If we are a connected player then send new items
		if ( player && player->socket() )
		{
			UI32 oldDist = oldpos.distance( pItem->pos() );
			if ( oldDist >= player->visualRange() )
			{
				// was out of range before and now is in range
				pItem->update( player->socket() );
			}
		}
	}
}
Q_UINT16 DynTile( const Coord_cl& pos )
{
	RegionIterator4Items ri( pos );
	for ( ri.Begin(); !ri.atEnd(); ri++ )
	{
		P_ITEM mapitem = ri.GetData();
		if ( mapitem )
		{
			if ( mapitem->isMulti() )
			{
				MultiDefinition* def = MultiCache::instance()->getMulti( mapitem->id() - 0x4000 );
				if ( !def )
					return 0;
				QValueVector<multiItem_st> multi = def->getEntries();
				for ( Q_UINT32 j = 0; j < multi.size(); ++j )
				{
					if ( ( multi[j].visible && ( mapitem->pos().x + multi[j].x == pos.x ) && ( mapitem->pos().y + multi[j].y == pos.y ) && ( abs( mapitem->pos().z + multi[j].z - pos.z ) <= 1 ) ) )
					{
						return multi[j].tile;
					}
				}
			}
			else if ( mapitem->pos() == pos )
				return mapitem->id();
		}
	}
	return ( Q_UINT16 ) - 1;
}
Q_UINT16 DynTile( const Coord& pos )
{
	MapItemsIterator ri = MapObjects::instance()->listItemsInCircle( pos, 18 );
	for ( P_ITEM mapitem = ri.first(); mapitem; mapitem = ri.next() )
	{
		if ( mapitem->isMulti() )
		{
			MultiDefinition* def = MultiCache::instance()->getMulti( mapitem->id() - 0x4000 );
			if ( !def )
				return 0;

			QValueVector<multiItem_st> multi = def->getEntries();
			for ( Q_UINT32 j = 0; j < multi.size(); ++j )
			{
				if ( ( multi[j].visible && ( mapitem->pos().x + multi[j].x == pos.x ) && ( mapitem->pos().y + multi[j].y == pos.y ) && ( abs( mapitem->pos().z + multi[j].z - pos.z ) <= 1 ) ) )
				{
					return multi[j].tile;
				}
			}
		}
		else if ( mapitem->pos() == pos )
		{
			return mapitem->id();
		}
	}
	return ( Q_UINT16 ) - 1;
}
bool cMovement::canLandMonsterMoveHere( const Coord_cl& pos ) const
{
	if ( pos.x >= ( Maps::instance()->mapTileWidth( pos.map ) * 8 ) || pos.y >= ( Maps::instance()->mapTileHeight( pos.map ) * 8 ) )
		return false;

	const signed char elev = Maps::instance()->mapElevation( pos );
	Coord_cl target = pos;
	target.z = elev;
	if ( ILLEGAL_Z == elev )
		return false;

	// get the tile id of any dynamic tiles at this spot
	Coord_cl mPos = pos;
	mPos.z = elev;
	const Q_INT32 dt = DynTile( mPos );

	// if there is a dynamic tile at this spot, check to see if its a blocker
	// if it does block, might as well Q_INT16-circuit and return right away
	if ( dt >= 0 )
	{
		tile_st tile = TileCache::instance()->getTile( dt );
		if ( tile.isBlocking() || tile.isWet() )
			return false;
	}

	// if there's a static block here in our way, return false
	StaticsIterator msi = Maps::instance()->staticsIterator( pos );
	while ( !msi.atEnd() )
	{
		tile_st tile = TileCache::instance()->getTile( msi->itemid );
		const Q_INT32 elev = msi->zoff + cTileCache::tileHeight( tile );
		if ( ( elev >= pos.z ) && ( msi->zoff <= pos.z ) )
		{
			if ( tile.isBlocking() || tile.isWet() )
				return false;
		}
		msi++;
	}

	RegionIterator4Items items( pos, 0 );
	for ( items.Begin(); !items.atEnd(); items++ )
	{
		P_ITEM item = items.GetData();
		tile_st tile = TileCache::instance()->getTile( item->id() );
		const Q_INT32 elev = item->pos().z + cTileCache::tileHeight( tile );
		if ( ( elev >= pos.z ) && ( item->pos().z <= pos.z ) )
		{
			if ( tile.isBlocking() || tile.isWet() )
				return false;
		}
	}

	return true;
}
Example #5
0
Coord Coord::losTargetPoint( cUORxTarget* target, unsigned char map )
{
	SERIAL serial = target->serial();
	P_ITEM pItem = World::instance()->findItem( serial );
	P_CHAR pChar = World::instance()->findChar( serial );

	if ( pItem )
	{
		pItem = pItem->getOutmostItem();

		if ( pItem->container() && pItem->container()->isChar() )
		{
			return pItem->container()->pos().losCharPoint();
		}
		else
		{
			return pItem->pos().losItemPoint( pItem->id() );
		}
	}
	else if ( pChar )
	{
		return pChar->pos().losCharPoint();
	}
	else
	{
		return Coord( target->x(), target->y(), target->z(), map );
	}
}
// Remove it from all in-range sockets
void cUObject::removeFromView( bool clean )
{
	// Get Real pos
	Coord_cl mPos = pos_;

	if( isItemSerial( serial_ ) )
	{
		P_ITEM pItem = dynamic_cast<P_ITEM>(this);
		P_ITEM pCont = pItem->getOutmostItem();
		if( pCont )
		{
			mPos = pCont->pos();
			P_CHAR pOwner = dynamic_cast<P_CHAR>( pCont->container() );
			if( pOwner )
				mPos = pOwner->pos();
		}
	}

	cUOTxRemoveObject remove;
	remove.setSerial(serial_);
	for (cUOSocket *socket = cNetwork::instance()->first(); socket; socket = cNetwork::instance()->next()) {
		if (socket->player() != this && (clean || socket->canSee(this))) {
			socket->send(&remove);
		}
	}
}
/*!
	Checks if the specified object is in given range
*/
bool cUObject::inRange( cUObject* object, quint32 range ) const
{
	if ( !object )
		return false;

	Coord pos = object->pos_;

	if ( object->isItem() )
	{
		P_ITEM pItem = dynamic_cast<P_ITEM>( object );

		if ( pItem )
		{
			P_ITEM pCont = pItem->getOutmostItem();
			P_CHAR pEquipped = pItem->getOutmostChar();

			if ( pEquipped )
			{
				pos = pEquipped->pos();
			}
			else if ( pCont )
			{
				pos = pCont->pos();
			}
		}
	}

	return pos_.distance( pos ) <= range;
}
void cSectorMaps::add( cUObject* object )
{
	// Very powerful statement. It completely
	// annihilates the need to check for
	// nullpointers in our object-map
	if ( !object )
		return;

	if ( isItemSerial( object->serial() ) )
	{
		P_ITEM pItem = dynamic_cast<P_ITEM>( object );
		if ( pItem )
		{
			Coord_cl pos = pItem->pos();

			std::map<unsigned char, cSectorMap*>::const_iterator it = itemmaps.find( pos.map );

			if ( it == itemmaps.end() )
				throw QString( "Couldn't find a map with the id %1." ).arg( pos.map );

			it->second->addItem( ( cUObject * ) pItem );

			Timing::instance()->addDecayItem( pItem );
		}
	}
	else if ( isCharSerial( object->serial() ) )
	{
		// This is a safety check to make sure that
		// stabled pets don't appear on our sectormap
		P_NPC npc = dynamic_cast<P_NPC>( object );

		if ( npc && npc->stablemasterSerial() != INVALID_SERIAL )
		{
			return;
		}

		// The same check for players
		/*P_PLAYER player = dynamic_cast<P_PLAYER>( object );

		if ( player && !player->socket() && !player->logoutTime() )
		{
			return;
		}*/

		P_CHAR pChar = dynamic_cast<P_CHAR>( object );
		if ( pChar )
		{
			Coord_cl pos = pChar->pos();

			std::map<unsigned char, cSectorMap*>::const_iterator it = charmaps.find( pos.map );

			if ( it == charmaps.end() )
				throw QString( "Couldn't find a map with the id %1." ).arg( pos.map );

			it->second->addItem( ( cUObject * ) pChar );
		}
	}
}
void cSectorMaps::remove( cUObject* object )
{
	// Very powerful statement. It completely
	// annihilates the need to check for
	// nullpointers in our object-map
	if ( !object )
		return;

	if ( isItemSerial( object->serial() ) )
	{
		P_ITEM pItem = dynamic_cast<P_ITEM>( object );

		if ( pItem )
		{
			Coord_cl pos = pItem->pos();

			std::map<unsigned char, cSectorMap*>::const_iterator it = itemmaps.find( pos.map );

			if ( it == itemmaps.end() )
				throw QString( "Couldn't find a map with the id %1." ).arg( pos.map );

			it->second->removeItem( ( cUObject * ) pItem );
			Timing::instance()->removeDecayItem( pItem );
		}
	}
	else if ( isCharSerial( object->serial() ) )
	{
		P_CHAR pChar = dynamic_cast<P_CHAR>( object );
		if ( pChar )
		{
			Coord_cl pos = pChar->pos();

			std::map<unsigned char, cSectorMap*>::const_iterator it = charmaps.find( pos.map );

			if ( it == charmaps.end() )
				throw QString( "Couldn't find a map with the id %1." ).arg( pos.map );

			it->second->removeItem( ( cUObject * ) pChar );
		}
	}
}
Example #10
0
// Remove it from all in-range sockets
void cUObject::removeFromView( bool clean )
{
	// Get Real pos
	Coord_cl mPos = pos_;

	if( isItemSerial( serial_ ) )
	{
		P_ITEM pItem = dynamic_cast<P_ITEM>(this);
		P_ITEM pCont = pItem->getOutmostItem();
		if( pCont )
		{
			mPos = pCont->pos();
			P_CHAR pOwner = dynamic_cast<P_CHAR>( pCont->container() );
			if( pOwner )
				mPos = pOwner->pos();
		}
	}

	for( cUOSocket *socket = cNetwork::instance()->first(); socket; socket = cNetwork::instance()->next() )
		if( clean || ( socket->player() && ( socket->player()->pos().distance( mPos ) <= socket->player()->visualRange() ) ) )
			socket->removeObject( this );
}
Example #11
0
// The highest items will be @ the beginning
// While walking we always will try the highest first.
vector< stBlockItem > getBlockingItems( P_CHAR pChar, const Coord& pos )
{
	vector<stBlockItem> blockList;
	make_heap( blockList.begin(), blockList.end(), compareTiles() );

	// Process the map at that position
	stBlockItem mapBlock;
	mapBlock.maptile = true;
	mapBlock.z = Maps::instance()->mapAverageElevation( pos );
	mapBlock.height = 0;

	// TODO: Calculate the REAL average Z Value of that Map Tile here! Otherwise clients will have minor walking problems.
	map_st mapCell = Maps::instance()->seekMap( pos );
	//mapBlock.z = mapCell.z;
	land_st mapTile = TileCache::instance()->getLand( mapCell.id );

	// If it's not impassable it's automatically walkable
	if ( !( mapTile.flag1 & 0x40 ) )
		mapBlock.walkable = true;
	else
		mapBlock.walkable = checkWalkable( pChar, mapCell.id );

	if ( mapCell.id != 0x02 )
	{
		blockList.push_back( mapBlock );
		push_heap( blockList.begin(), blockList.end(), compareTiles() );
	}

	// Now for the static-items
	StaticsIterator staIter = Maps::instance()->staticsIterator( pos, true );
	for ( ; !staIter.atEnd(); ++staIter )
	{
		tile_st tTile = TileCache::instance()->getTile( staIter->itemid );

		// Here is decided if the tile is needed
		// It's uninteresting if it's NOT blocking
		// And NOT a bridge/surface
		if ( !( ( tTile.flag2 & 0x02 ) || ( tTile.flag1 & 0x40 ) || ( tTile.flag2 & 0x04 ) ) )
			continue;

		stBlockItem staticBlock;
		staticBlock.z = staIter->zoff;

		// If we are a surface we can always walk here, otherwise check if
		// we are special
		if ( ( tTile.flag2 & 0x02 ) && !( tTile.flag1 & 0x40 ) )
			staticBlock.walkable = true;
		else
			staticBlock.walkable = checkWalkable( pChar, staIter->itemid );

		// If we are a stair only the half height counts (round up)
		if ( tTile.flag2 & 0x04 )
			staticBlock.height = ( Q_UINT8 ) ( ( tTile.height ) / 2 );
		else
			staticBlock.height = tTile.height;

		blockList.push_back( staticBlock );
		push_heap( blockList.begin(), blockList.end(), compareTiles() );
	}

	// We are only interested in items at pos
	// todo: we could impliment blocking for items on the adjacent sides
	// during a diagonal move here, but this has yet to be decided.

	MapItemsIterator iIter = MapObjects::instance()->listItemsAtCoord( pos );
	for ( P_ITEM pItem = iIter.first(); pItem; pItem = iIter.next() )
	{
		if ( pChar && pChar->isDead() )
		{
			// Doors can be passed by ghosts
			if ( pItem->hasScript( "door" ) )
			{
				continue;
			}
		}

		tile_st tTile = TileCache::instance()->getTile( pItem->id() );

		// See above for what the flags mean
		if ( !( ( tTile.flag2 & 0x02 ) || ( tTile.flag1 & 0x40 ) || ( tTile.flag2 & 0x04 ) ) )
			continue;

		stBlockItem blockItem;
		blockItem.height = ( tTile.flag2 & 0x04 ) ? ( tTile.height / 2 ) : tTile.height;
		blockItem.z = pItem->pos().z;

		// Once again: see above for a description of this part
		if ( ( tTile.flag2 & 0x02 ) && !( tTile.flag1 & 0x40 ) )
			blockItem.walkable = true;
		else
			blockItem.walkable = checkWalkable( pChar, pItem->id() );

		blockList.push_back( blockItem );
		push_heap( blockList.begin(), blockList.end(), compareTiles() );
	}


	// deal with the multis now, or not.
	// 18 has been tested with castle sides and corners...
	MapMultisIterator iter = MapObjects::instance()->listMultisInCircle( pos, 18 );
	for ( cMulti*pMulti = iter.first(); pMulti; pMulti = iter.next() )
	{
		MultiDefinition* def = MultiCache::instance()->getMulti( pMulti->id() - 0x4000 );
		if ( !def )
			continue;

		QValueVector<multiItem_st> multi = def->getEntries();

		for ( unsigned int j = 0; j < multi.size(); ++j )
		{
			if ( multi[j].visible && ( pMulti->pos().x + multi[j].x == pos.x ) && ( pMulti->pos().y + multi[j].y == pos.y ) )
			{
				tile_st tTile = TileCache::instance()->getTile( multi[j].tile );
				if ( !( ( tTile.flag2 & 0x02 ) || ( tTile.flag1 & 0x40 ) || ( tTile.flag2 & 0x04 ) ) )
					continue;

				stBlockItem blockItem;
				blockItem.height = ( tTile.flag2 & 0x04 ) ? ( tTile.height / 2 ) : tTile.height;
				blockItem.z = pMulti->pos().z + multi[j].z;

				if ( ( tTile.flag2 & 0x02 ) && !( tTile.flag1 & 0x40 ) )
					blockItem.walkable = true;
				else
					blockItem.walkable = checkWalkable( pChar, pMulti->id() );

				blockList.push_back( blockItem );
				push_heap( blockList.begin(), blockList.end(), compareTiles() );
			}
		}
		continue;
	}

	// Now we need to evaluate dynamic items [...] (later)  ??
	sort_heap( blockList.begin(), blockList.end(), compareTiles() );

	return blockList;
};
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 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();
}
Example #14
0
// The highest items will be @ the beginning
// While walking we always will try the highest first.
vector< stBlockItem > getBlockingItems( P_CHAR pChar, const Coord_cl& pos )
{
	vector<stBlockItem> blockList;
	make_heap( blockList.begin(), blockList.end(), compareTiles() );

	// Process the map at that position
	stBlockItem mapBlock;
	mapBlock.maptile = true;
	mapBlock.z = Maps::instance()->mapAverageElevation( pos );
	mapBlock.height = 0;

	// TODO: Calculate the REAL average Z Value of that Map Tile here! Otherwise clients will have minor walking problems.
	map_st mapCell = Maps::instance()->seekMap( pos );
	//mapBlock.z = mapCell.z;*/
	land_st mapTile = TileCache::instance()->getLand( mapCell.id );

	// If it's not impassable it's automatically walkable
	if ( !( mapTile.flag1 & 0x40 ) )
		mapBlock.walkable = true;
	else
		mapBlock.walkable = checkWalkable( pChar, mapCell.id );

	if ( mapCell.id != 0x02 )
	{
		blockList.push_back( mapBlock );
		push_heap( blockList.begin(), blockList.end(), compareTiles() );
	}

	// Now for the static-items
	StaticsIterator staIter = Maps::instance()->staticsIterator( pos, true );
	for ( ; !staIter.atEnd(); ++staIter )
	{
		tile_st tTile = TileCache::instance()->getTile( staIter->itemid );

		// Here is decided if the tile is needed
		// It's uninteresting if it's NOT blocking
		// And NOT a bridge/surface
		if ( !( ( tTile.flag2 & 0x02 ) || ( tTile.flag1 & 0x40 ) || ( tTile.flag2 & 0x04 ) ) )
			continue;

		stBlockItem staticBlock;
		staticBlock.z = staIter->zoff;

		// If we are a surface we can always walk here, otherwise check if
		// we are special
		if ( ( tTile.flag2 & 0x02 ) && !( tTile.flag1 & 0x40 ) )
			staticBlock.walkable = true;
		else
			staticBlock.walkable = checkWalkable( pChar, staIter->itemid );

		// If we are a stair only the half height counts (round up)
		if ( tTile.flag2 & 0x04 )
			staticBlock.height = ( Q_UINT8 ) ( ( tTile.height + 1 ) / 2 );
		else
			staticBlock.height = tTile.height;

		blockList.push_back( staticBlock );
		push_heap( blockList.begin(), blockList.end(), compareTiles() );
	}

	RegionIterator4Items iIter( pos );
	for ( iIter.Begin(); !iIter.atEnd(); iIter++ )
	{
		P_ITEM pItem = iIter.GetData();

		if ( !pItem )
			continue;

		if ( pItem->id() >= 0x4000 )
		{
			MultiDefinition* def = MultiCache::instance()->getMulti( pItem->id() - 0x4000 );
			if ( !def )
				continue;
			QValueVector<multiItem_st> multi = def->getEntries();
			unsigned int j;
			for ( j = 0; j < multi.size(); ++j )
			{
				if ( multi[j].visible && ( pItem->pos().x + multi[j].x == pos.x ) && ( pItem->pos().y + multi[j].y == pos.y ) )
				{
					tile_st tTile = TileCache::instance()->getTile( multi[j].tile );
					if ( !( ( tTile.flag2 & 0x02 ) || ( tTile.flag1 & 0x40 ) || ( tTile.flag2 & 0x04 ) ) )
						continue;

					stBlockItem blockItem;
					blockItem.height = tTile.height;
					blockItem.z = pItem->pos().z + multi[j].z;

					if ( ( tTile.flag2 & 0x02 ) && !( tTile.flag1 & 0x40 ) )
						blockItem.walkable = true;
					else
						blockItem.walkable = checkWalkable( pChar, pItem->id() );

					blockList.push_back( blockItem );
					push_heap( blockList.begin(), blockList.end(), compareTiles() );
				}
			}
			continue;
		}
		else if ( pChar->isDead() )
		{
			// Doors can be passed by ghosts
			if ( pItem->hasScript( "door" ) )
			{
				continue;
			}
		}

		// They need to be at the same x,y,plane coords
		if ( ( pItem->pos().x != pos.x ) || ( pItem->pos().y != pos.y ) || ( pItem->pos().map != pos.map ) )
			continue;

		tile_st tTile = TileCache::instance()->getTile( pItem->id() );

		// Se above for what the flags mean
		if ( !( ( tTile.flag2 & 0x02 ) || ( tTile.flag1 & 0x40 ) || ( tTile.flag2 & 0x04 ) ) )
			continue;

		stBlockItem blockItem;
		blockItem.height = tTile.height;
		blockItem.z = pItem->pos().z;

		// Once again: see above for a description of this part
		if ( ( tTile.flag2 & 0x02 ) && !( tTile.flag1 & 0x40 ) )
			blockItem.walkable = true;
		else
			blockItem.walkable = checkWalkable( pChar, pItem->id() );

		blockList.push_back( blockItem );
		push_heap( blockList.begin(), blockList.end(), compareTiles() );
	}

	// Now we need to evaluate dynamic items [...] (later)
	// TODO: Multis here
	sort_heap( blockList.begin(), blockList.end(), compareTiles() );

	return blockList;
};
Example #15
0
bool Coord_cl::lineOfSight( const Coord_cl &target, UI16 targetheight, bool touch )
{
	//Console::instance()->send( QString( "LOScheck: Source:%1,Target:%2,Targetheight:%3\n" ).arg( z ).arg( target.z ).arg( targetheight ) );
	if( target.map != map )
		return false;

	if( (x == target.x) && (y == target.y) && (z == target.z) )
		return true;		// if source and target are on the same position

	SI32 n = ( target.x - x ), m = ( target.y - y ), i = 0;
	SI08 sgn_x = ( x <= target.x ) ? 1 : (-1); // signum for x
	SI08 sgn_y = ( y <= target.y ) ? 1 : (-1); // signum for y
	SI08 sgn_z = ( z <= target.z ) ? 1 : (-1); // signum for z
	if( x == target.x )
		sgn_x = 0;
	if( y == target.y )
		sgn_y = 0;
	if( z == target.z )
		sgn_z = 0;

	QValueList< Coord_cl > collisions;

	//first we get our x-y-coordinates
	if( sgn_x == 0 && sgn_y == 0 && !sgn_z == 0 ) // should fix shooting through floor issues
		{
			collisions.push_back( Coord_cl( x, y, 0, map ) );
		}
	else if( sgn_x == 0 ) // if we are on the same x-level, just push every x/y coordinate in y-direction from src to trg into the array
		for( i = 0; i <= (sgn_y * m); ++i )
		{
			collisions.push_back( Coord_cl( x, y + (sgn_y * i), 0, map ) );
		}
	else if ( sgn_y == 0 ) // if we are on the same y-level, just push every x/y coordinate in x-direction from src to trg into the array
		for( i = 0; i <= (sgn_x * n); ++i )
		{
			collisions.push_back( Coord_cl( x + (sgn_x * i), y, 0, map ) );
		}
	else
	{
		SI32 oldpos = y;
		bool exaktpos = false;
		for( i = 0; (sgn_x * n >= sgn_y * m) && (i <= (sgn_x * n)); i++ )
		{
			//Console::instance()->send( QString( "x:%1\n" ).arg( i ) );
			SI32 gridx = x + (sgn_x * i);
			if( ( ( n == 0 ) && ( gridx == 0 ) ) ||
				( ( n + ( gridx * m ) == 0 ) ) )
				continue;
			else
			{
				if( exaktpos )
				{
					collisions.push_back( Coord_cl( gridx, oldpos-sgn_y, 0, map ) );
					//Console::instance()->send( QString( "add exaktpos coordinate %1,%2\n" ).arg( gridx ).arg( oldpos-sgn_y ) );
					exaktpos = false;
				}
					
				// linear evaluation of extended 2x2 matrix, abbreviated
				double t = (double)sgn_x * ((double)i+0.5) * (double)m / (double)n + (double)y;
				//Console::instance()->send( QString( "t:%1\n" ).arg( t ) );

				if( ((sgn_y>0) && (specialFloor(t)==oldpos+0.5)) || ((sgn_y<0) && (specialFloor(t)==oldpos-0.5)) )
				{
					exaktpos = true;
				}
				
				if( ((sgn_y>0) && (t<oldpos+0.5)) || ((sgn_y<0) && (t>oldpos-0.5)) || (oldpos==target.y) )
				{
					collisions.push_back( Coord_cl( gridx, oldpos, 0, map ) );
					//Console::instance()->send( QString( "add coordinate %1,%2\n" ).arg( gridx ).arg( oldpos ) );
				}
				// but if not, we have to take BOTH coordinates, which the calculated collision is between!
				else
				{ 
					collisions.push_back( Coord_cl( gridx, oldpos, 0, map ) );
					//Console::instance()->send( QString( "add coordinate %1,%2\n" ).arg( gridx ).arg( oldpos ) );
					oldpos += sgn_y;
					collisions.push_back( Coord_cl( gridx, oldpos, 0, map ) );
					//Console::instance()->send( QString( "add coordinate %1,%2\n" ).arg( gridx ).arg( oldpos ) );
				}
			}
		}
		
		oldpos = x;
		exaktpos = false;
		for( i = 0; (sgn_y * m >= sgn_x * n) && (i <= (sgn_y * m)); ++i )
		{
			//Console::instance()->send( QString( "y:%1\n" ).arg( i ) );
			SI32 gridy = y + (sgn_y * i);
			if( ( ( m == 0 ) && ( gridy == 0 ) ) ||
				( ( m + ( gridy * n ) == 0 ) ) )
				continue;
			else
			{
				if( exaktpos )
				{
					collisions.push_back( Coord_cl( oldpos-sgn_x, gridy, 0, map ) );
					//Console::instance()->send( QString( "add exaktpos coordinate %1,%2\n" ).arg( oldpos-sgn_x ).arg( gridy ) );
					exaktpos = false;
				}

				double t = (double)x + (double)n / (double)m * (double)sgn_y * ((double)i+0.5);
				//Console::instance()->send( QString( "t:%1\n" ).arg( t ) );

				if( ((sgn_x>0) && (specialFloor(t)==oldpos+0.5)) || ((sgn_x<0) && (specialFloor(t)==oldpos-0.5)) )
				{
					exaktpos = true;
				}

				if( ((sgn_x>0) && (t<oldpos+0.5)) || ((sgn_x<0) && (t>oldpos-0.5)) || (oldpos==target.x) )
				{
					collisions.push_back( Coord_cl( oldpos, gridy, 0, map ) );
					//Console::instance()->send( QString( "add coordinate %1,%2\n" ).arg( oldpos ).arg( gridy ) );

				}
				// but if not, we have to take BOTH coordinates, which the calculated collision is between!
				else
				{ 
					collisions.push_back( Coord_cl( oldpos, gridy, 0, map ) );
					//Console::instance()->send( QString( "add coordinate %1,%2\n" ).arg( oldpos ).arg( gridy ) );
					oldpos += sgn_x;;
					collisions.push_back( Coord_cl( oldpos, gridy, 0, map ) );
					//Console::instance()->send( QString( "add coordinate %1,%2\n" ).arg( oldpos ).arg( gridy ) );
				}
			}
		}
	}

	// the next will search for multis
	QPtrList< cItem > multis;
	RegionIterator4Items ri( *this );
	for( ri.Begin(); !ri.atEnd(); ri++ )
	{
		P_ITEM pi = ri.GetData();
		if( pi && pi->id() >= 0x4000 )
		{
			multis.append( pi );
		}
	}

	//touch wird von notouch getrennt, da der notouch algorithmus wesentlich aufwändiger
	//ist (touch benötigt die erste for-schleife nicht), macht den code zwar bisserl
	//unübersichtlicher, aber ist wesentlich effizienter

	//touch is separated from notouch, because the notouch calculation is much more to do
	//( one additional for-loop )

	if( targetheight > 0 )
	{
		--targetheight;
	}

	if( !touch )
	{
		for( i = target.z+targetheight; i >= target.z; --i )
		{
			bool blocked = false;
			//Console::instance()->send( QString( "i:%1\n" ).arg( i ) );
			double dz;
			double gradient;
			double offset;
			if( (sgn_x == 0) && (sgn_y == 0) )
			{
				dz = (double)i - (double)z;
				gradient = 1; //only to avoid problems, isnt used
				offset = 1;
			}
			else
			{
				dz = ( (double)i -(double)z ) / sqrt( ((double)target.x - (double)x)*((double)target.x - (double)x) + ((double)target.y - (double)y)*((double)target.y - (double)y) );
				gradient = (double)m / (double)n;
				offset = 0.5*( (double)y + (double)target.y - gradient*( x + target.x ) );
			}
	
			map_st map1, map2;
			SI32 j;
	
			bool posHigherThanMap;
			map1 = Map->seekMap( (*this) );
			if( map1.z > z )
			{
				posHigherThanMap = false;
			}
			else
			{
				posHigherThanMap = true;
			}
			
			//Console::instance()->send( QString( "after first things\n" ) );
			QValueList< Coord_cl >::iterator pit = collisions.begin();
			while( pit != collisions.end() )
			{
				//Console::instance()->send( QString( "coordinate:%1,%2 dz:%3\n" ).arg( (*pit).x ).arg( (*pit).y ).arg( dz ) );
				//lets see what z-coordinates we have to test
				//we do our calculations exakt, because its the only way to solve all problems
				//of floor
				//"minimum" ist am anfang der platte, "maximum" am ende
				//our line is y = gradient * x + offset
				//or x = ( y - offset ) / gradient
				//we now have to test, where the line cuts one position
				//start and endposition has to be done alone
				
				double z1 = -300;
				double z2 = -300;
				SI08 zmin, zmax;

				if( (sgn_x == 0) && (sgn_y == 0) )
				{
					if( dz > 0 )
					{
						zmin = z+1;
						zmax = i;
					}
					else
					{
						zmin = i+1;
						zmax = z;
					}
				}
				else if( sgn_x == 0 )
				{
					//gradient only in y-direction
					z1 = (double)i - dz*( fabs( (double)target.y - (double)((*pit).y) ) -0.5 ); 
					z2 = (double)i - dz*( fabs( (double)target.y - (double)((*pit).y) ) +0.5 );
					//Console::instance()->send( QString( "i:%1,ty:%2,cy:%3\n" ).arg( i ).arg( target.y ).arg( (*pit).y ) );
					//Console::instance()->send( QString( "z1:%1,z2:%2\n" ).arg( z1 ).arg( z2 ) );

					if( z1 > z2 )
					{
						zmin = (SI08)floor( z2 );
						zmax = (SI08)ceilf( z1 );
					}
					else
					{
						zmin = (SI08)floor( z1 );
						zmax = (SI08)ceilf( z2 );
					}

					/*another try, but i think its needed for all positions, not only start and end...
					//target 
					if( (*pit).y == target.y )
					{
						if( dz > 0 )
						{
							zmax = QMIN( zmax, i );
							//Console::instance()->send( QString( "TargetY, zmax:%1, i:%2\n" ).arg( zmax ).arg( i ) );
						}
						else
						{
							zmin = QMAX( zmin, i+1 );
							//Console::instance()->send( QString( "TargetY, zmin:%1, i:%2\n" ).arg( zmin ).arg( i ) );
						}
					}

					//source
					if( (*pit).y == y )
					{
						if( dz > 0 )
						{
							zmin = QMAX( zmin, z );
							//Console::instance()->send( QString( "SourceY, zmin:%1, i:%2\n" ).arg( zmax ).arg( i ) );
						}
						else
						{
							zmax = QMIN( zmax, z );
							//Console::instance()->send( QString( "SourceY, zmax:%1, i:%2\n" ).arg( zmax ).arg( i ) );
						}
					}*/
					if( dz > 0 )
					{
						zmax = QMIN( zmax, i );
						zmin = QMAX( zmin, z );
					}
					else
					{
						zmin = QMAX( zmin, i+1 );
						zmax = QMIN( zmax, z );
					}

				}
				else if( sgn_y == 0 )
				{
					//gradient only in y-direction
					z1 = (double)i - dz*( fabs( (double)target.x - (double)((*pit).x) ) -0.5 ); 
					z2 = (double)i - dz*( fabs( (double)target.x - (double)((*pit).x) ) +0.5 );
					//Console::instance()->send( QString( "i:%1,tx:%2,cx:%3\n" ).arg( i ).arg( target.x ).arg( (*pit).x ) );
					//Console::instance()->send( QString( "z1:%1,z2:%2\n" ).arg( z1 ).arg( z2 ) );

					if( z1 > z2 )
					{
						zmin = (SI08)floor( z2 );
						zmax = (SI08)ceilf( z1 );
					}
					else
					{
						zmin = (SI08)floor( z1 );
						zmax = (SI08)ceilf( z2 );
					}
					
					if( dz > 0 )
					{
						zmax = QMIN( zmax, i );
						zmin = QMAX( zmin, z );
					}
					else
					{
						zmin = QMAX( zmin, i+1 );
						zmax = QMIN( zmax, z );
					}

				}
				else
				{
					//4 lines to test
					double gradx = ( (double)target.y*(double)z - (double)y*(double)i ) / ( (double)target.y*(double)x - (double)y*(double)target.x );
					double grady = ( (double)target.x*(double)z - (double)x*(double)i ) / ( (double)y*(double)target.x - (double)target.y*(double)x );

					//Console::instance()->send( QString( "gradx:%1,grady:%2\n" ).arg( gradx ).arg( grady ) );
					//Console::instance()->send( QString( "Gradient:%1,Offset:%2\n" ).arg( gradient ).arg( offset ) );
					double temp;
					temp = specialFloor( gradient*( (double)((*pit).x) - 0.5 ) + offset );
					//Console::instance()->send( QString( "temp1:%1\n" ).arg( temp ) );
					if( ( temp >= ((double)((*pit).y)-0.5) ) && ( temp <= ((double)((*pit).y)+0.5) ) )
					{
						if( z1 > -300 )
						{
							z2 = gradx * ( (double)((*pit).x)-0.5 ) + grady * temp;
						}
						else
						{	
							z1 = gradx * ( (double)((*pit).x)-0.5 ) + grady * temp;
						}
						//Console::instance()->send( QString( "1:i:%1,tx:%2,ty:%3,cy:%4,cy:%5\n" ).arg( i ).arg( target.x ).arg( target.y ).arg( (*pit).x ).arg( (*pit).y ) );
						//Console::instance()->send( QString( "z1:%1,z2:%2\n" ).arg( z1 ).arg( z2 ) );
					}
					temp = specialFloor( gradient*( (double)((*pit).x) + 0.5 ) + offset );
					//Console::instance()->send( QString( "temp2:%1\n" ).arg( temp ) );
					if( ( temp >= ((double)((*pit).y)-0.5) ) && ( temp <= ((double)((*pit).y)+0.5) ) )
					{
						if( z1 > -300 )
						{
							z2 = gradx * ( (double)((*pit).x)+0.5 ) + grady * temp;
						}
						else
						{	
							z1 = gradx * ( (double)((*pit).x)+0.5 ) + grady * temp;
						}
						//Console::instance()->send( QString( "2:i:%1,tx:%2,ty:%3,cy:%4,cy:%5\n" ).arg( i ).arg( target.x ).arg( target.y ).arg( (*pit).x ).arg( (*pit).y ) );
						//Console::instance()->send( QString( "z1:%1,z2:%2\n" ).arg( z1 ).arg( z2 ) );
					}
					temp = specialFloor( (double)((*pit).y) - 0.5 - offset ) / gradient;
					//Console::instance()->send( QString( "temp3:%1\n" ).arg( temp ) );
					if( ( temp > ((double)((*pit).x)-0.5) ) && ( temp < ((double)((*pit).x)+0.5) ) )
					{
						if( z1 > -300 )
						{
							z2 = gradx * temp + grady * ((double)((*pit).y)-0.5);
						}
						else
						{
							z1 = gradx * temp + grady * ((double)((*pit).y)-0.5);
						}
						//Console::instance()->send( QString( "3:i:%1,tx:%2,ty:%3,cy:%4,cy:%5\n" ).arg( i ).arg( target.x ).arg( target.y ).arg( (*pit).x ).arg( (*pit).y ) );
						//Console::instance()->send( QString( "z1:%1,z2:%2\n" ).arg( z1 ).arg( z2 ) );
					}
					temp = specialFloor( (double)((*pit).y) + 0.5 - offset ) / gradient;
					//Console::instance()->send( QString( "temp4:%1\n" ).arg( temp ) );
					if( ( temp > ((double)((*pit).x)-0.5) ) && ( temp < ((double)((*pit).x)+0.5) ) )
					{
						if( z1 > -300 )
						{
							z2 = gradx * temp + grady * ((double)((*pit).y)+0.5);
						}
						else
						{
							z1 = gradx * temp + grady * ((double)((*pit).y)+0.5);
						}
						//Console::instance()->send( QString( "4:i:%1,tx:%2,ty:%3,cy:%4,cy:%5\n" ).arg( i ).arg( target.x ).arg( target.y ).arg( (*pit).x ).arg( (*pit).y ) );
						//Console::instance()->send( QString( "z1:%1,z2:%2\n" ).arg( z1 ).arg( z2 ) );
					}
					
					//Console::instance()->send( QString( "z1:%1,z2:%2\n" ).arg( z1 ).arg( z2 ) );
					if( z1 > z2 )
					{
						zmin = (SI08)floor( z2 );
						zmax = (SI08)ceilf( z1 );
					}
					else
					{
						zmin = (SI08)floor( z1 );
						zmax = (SI08)ceilf( z2 );
					}

					if( z2 == -300 )
					{
						zmin = (SI08)floor( z1 );
					}

					if( dz > 0 )
					{
						zmax = QMIN( zmax, i );
						zmin = QMAX( zmin, z );
					}
					else
					{
						zmin = QMAX( zmin, i+1 );
						zmax = QMIN( zmax, z );
					}
					//Console::instance()->send( QString( "zmin:%1,zmax:%2\n" ).arg( zmin ).arg( zmax ) );
				}

				/*SI08 zmin = (SI08)floor( i - dz*sqrt( ((double)target.x - (double)(*pit).x)*((double)target.x - (double)(*pit).x) + ((double)target.y - (double)(*pit).y)*((double)target.y - (double)(*pit).y) ) );
				SI08 zmax;
				//Console::instance()->send( QString( "zmin:%1,dz:%2\n" ).arg( zmin ).arg( dz ) );
				if( dz > 0 )
				{
					zmin = QMAX( (SI08)floor( zmin - dz/2 ), z+1 );
					zmax = QMIN( zmin + dz + 1, target.z+targetheight+1 );	//to prevent floor-mistakes
				}
				else
				{
					zmin = QMIN( (SI08)floor( zmin + dz/2 ), target.z+1 );
					zmax = QMAX( zmin - dz + 1, z );	//to prevent floor-mistakes
				}*/
	
				// Texture mapping  
				map1 = Map->seekMap( *pit );
				map2 = Map->seekMap( Coord_cl( (*pit).x + sgn_x, (*pit).y + sgn_y, (*pit).z, map ) );
			
				//Console::instance()->send( QString( "maphoehe:%1\n" ).arg( map1.z ) );
				StaticsIterator msi = Map->staticsIterator( *pit );
				if( (map1.id != 2) && (map2.id != 2) ) 
				{
					if( ( map1.z >= zmin ) && ( map1.z <= zmax ) )
					{
						//its just in our way
						//Console::instance()->send( QString( "Map gescheitert\n" ) );
						blocked = true;
						break;
					}
	
					//now we have to test, if this tile is under our line and the next above or
					//vice verse
					//in this case, both dont cut our line, but the mapconnection between them
					//should do
					if( ( ( map1.z < map2.z ) && ( map1.z < zmin ) && ( map2.z > zmax+dz ) ) ||						// 1) lineofsight collides with a map "wall"
						( ( map1.z > map2.z ) && ( map1.z > zmin ) && ( map2.z < zmax-dz ) ) ||
						( ( ( map1.id >= 431  && map1.id <= 432  ) ||	// 3) lineofsight cuts a mountain
						( map1.id >= 467  && map1.id <= 475  ) ||
						( map1.id >= 543  && map1.id <= 560  ) ||
						( map1.id >= 1754 && map1.id <= 1757 ) ||
						( map1.id >= 1787 && map1.id <= 1789 ) ||
						( map1.id >= 1821 && map1.id <= 1824 ) ||
						( map1.id >= 1851 && map1.id <= 1854 ) ||
						( map1.id >= 1881 && map1.id <= 1884 ) ) &&
						( posHigherThanMap ) && ( msi.atEnd() ) ) ) // mountains cut only if we are not beneath them
					{
						//Console::instance()->send( QString( "map1:%1,map2:%2\n" ).arg( map1.z ).arg( map2.z ) );
						blocked = true;
						break;
					}
				}	 
				
				//Console::instance()->send( QString( "after map\n" ) );
	
				// Statics
				tile_st tile;
				while( !msi.atEnd() )
				{
					tile = TileCache::instance()->getTile( msi->itemid );
					//if it is in our way
					//Console::instance()->send( QString( "tilepos:%1,zmax:%2" ).arg( msi->zoff ).arg( zmax ) );
					//Console::instance()->send( QString( "zmin:%3\n" ).arg( zmin ) );
					if( ( zmax >= msi->zoff ) && ( zmin <= ( msi->zoff + tile.height ) ) )
					{
						//Console::instance()->send( QString( "statictile, id: %1\n" ).arg( msi->itemid ) );
						if( tile.isNoShoot() )
						{
							blocked = true;
							break;
						}
					}
		
					++msi;
				}
				if( blocked )
				{
					break;
				}
				
				//Console::instance()->send( QString( "after statics\n" ) );
				// Items
				RegionIterator4Items rj( (*pit), 0 );
				for( rj.Begin(); !rj.atEnd(); rj++ )
				{
					P_ITEM pi = rj.GetData();
					if( pi && pi->id() < 0x4000 )
					{
						tile = TileCache::instance()->getTile( pi->id() );
						if(	( zmax >= pi->pos().z ) && ( zmin <= ( pi->pos().z + tile.height ) ) && ( pi->visible() == 0 ) )
						{
							//Console::instance()->send( QString( "item, id: %1" ).arg( pi->id() ) );
							if( tile.isNoShoot() )
							{
								blocked = true;
								break;
							}
						}	
					}
				}
				if( blocked )
				{
					break;
				}
	
				//Console::instance()->send( QString( "after items\n" ) );
				// Multis
				QPtrListIterator< cItem > mit( multis );
				P_ITEM pi;
				while( ( pi = mit.current() ) )
				{
					MultiDefinition* def = MultiCache::instance()->getMulti( pi->id() - 0x4000 );
					if ( !def )
						continue;
					QValueVector<multiItem_st> multi = def->getEntries();
					for( j = 0; j < multi.size(); ++j )
					{
						if( ( multi[j].visible ) && ( pi->pos().x + multi[j].x == (*pit).x ) &&
							( pi->pos().y + multi[j].y == (*pit).y ) )			
						{
							tile = TileCache::instance()->getTile( multi[j].tile );
							if( ( zmax >= pi->pos().z + multi[j].z ) &&
								( zmin <= pi->pos().z + multi[j].z + tile.height ) )
							{
								if( tile.isNoShoot() )
								{
									blocked = true;
									break;
								}
							}	
						}
					}
					++mit;
				}
				//Console::instance()->send( QString( "after multis\n" ) );
				++pit;
			}
	
			if( !blocked )
				return true;
		}
		//there was no line to see through
		return false;
	}
	//now touch=true
	else
	{
		//Console::instance()->send( "touch\n" );
		double dz_up, dz_down;
		double gradient;
		double offset;
		if( (sgn_x == 0) && (sgn_y == 0) )
		{
			dz_up = ( (double)targetheight + (double)target.z ) - (double)z;
			dz_down = (double)target.z - ( (double)z - (double)15 );
			gradient = 1; //only to prevent problems, isnt used
			offset = 1;
		}
		else
		{
			//Console::instance()->send( QString( "xdiff:%1,ydiff:%2\n" ).arg( (double)target.x - (double)x ).arg( (double)target.y - (double)y ) );
			dz_up = ( ( (double)targetheight + (double)target.z ) - (double)z ) / sqrt( ((double)target.x - (double)x)*((double)target.x - (double)x) + ((double)target.y - (double)y)*((double)target.y - (double)y) );
			dz_down = ( (double)target.z - ( (double)z - (double)15 ) ) / sqrt( ((double)target.x - (double)x)*((double)target.x - (double)x) + ((double)target.y - (double)y)*((double)target.y - (double)y) );
			gradient = (double)m / (double)n;
			offset = 0.5*( (double)y + (double)target.y - gradient*( x + target.x ) );
		}
	
		map_st map1, map2;
		SI32 j;
	
		bool posHigherThanMap;
		map1 = Map->seekMap( (*this) );
		if( map1.z > z )
		{
			posHigherThanMap = false;
		}
		else
		{
			posHigherThanMap = true;
		}
		
		//Console::instance()->send( QString( "after first things\n" ) );
		QValueList< Coord_cl >::iterator pit = collisions.begin();
		while( pit != collisions.end() )
		{
			//Console::instance()->send( QString( "coordinate:%1,%2\n" ).arg( (*pit).x ).arg( (*pit).y ) );				
			//lets see what z-coordinates we have to test
			//anmerkung: touch kommt nur für chars vor, grösse von chars ist 15
			//SI08 zmin = (SI08)floor(  (double)z - (double)sourceheight + dz_down*sqrt( ((double)target.x - (double)(*pit).x)*((double)target.x - (double)(*pit).x) + ((double)target.y - (double)(*pit).y)*((double)target.y - (double)(*pit).y) ) );
			//SI08 zmax = (SI08)floor(  (double)z + dz_up*sqrt( ((double)target.x - (double)(*pit).x)*((double)target.x - (double)(*pit).x) + ((double)target.y - (double)(*pit).y)*((double)target.y - (double)(*pit).y) ) );
			SI08 zmin, zmax;
			double z1_up = -300;
			double z2_up = -300;
			double z1_down = -300;
			double z2_down = -300;
			bool targetpos = false;
			//Console::instance()->send( QString( "dz_down:%3,dz_up:%4\n" ).arg( dz_down ).arg( dz_up ) );

			if( (sgn_x == 0) && (sgn_y == 0) )
			{
				if( dz_up > 0 )
				{
					zmin = z+1;
					zmax = target.z;
				}
				else
				{
					zmin = target.z+1;
					zmax = z;
				}
				targetpos = true;
				if( (dz_up >= 0) && (dz_down >= 0) )
				{
					if( zmin < target.z )
					{
						zmax = target.z -1;
					}
					else
					{
						//we ignore this coordinate
						++pit;
						continue;
					}
				}
				else if( (dz_up <= 0) && (dz_down <= 0) )
				{
					if( zmax > target.z + targetheight+1 )
					{
						zmin = target.z + targetheight + 2;
					}
					else
					{
						++pit;
						continue;
					}
				}
				else 
				{
					//we may have to split the test into two if we would do it exactly
					//but i think we can throw away the test from down in this case
					if( zmax > target.z + targetheight+1 )
					{
						zmin = target.z + targetheight + 2;
					}
					else if( zmin < target.z )
					{
						zmax = target.z -1;
					}
					else
					{
						++pit;
						continue;
					}
				}
			}
			else if( sgn_x == 0 )
			{
				z1_up = target.z + targetheight - dz_up*( fabs( (double)target.y - (double)((*pit).y) ) -0.5 ); 
				z2_up = target.z + targetheight - dz_up*( fabs( (double)target.y - (double)((*pit).y) ) +0.5 );
			
				z1_down = target.z - dz_down*( fabs( (double)target.y - (double)((*pit).y) ) -0.5 );
				z2_down = target.z - dz_down*( fabs( (double)target.y - (double)((*pit).y) ) +0.5 );

				//Console::instance()->send( QString( "ty:%2,cy:%3\n" ).arg( target.y ).arg( (*pit).y ) );
				//Console::instance()->send( QString( "z1_up:%1,z2_up:%2,z1_down:%3,z2_down:%4\n" ).arg( z1_up ).arg( z2_up ).arg( z1_down ).arg( z2_down ) );

				zmax = QMAX( ceil( z1_up ), ceil( z2_up ) );
				zmin = QMIN( floor( z1_down ), floor( z2_down ) );

				//Console::instance()->send( QString( "y:zmin:%1,zmax:%2\n" ).arg( zmin ).arg( zmax ) );

				if( dz_up > 0 )
				{
					zmax = QMIN( zmax, target.z+targetheight );
				}
				else
				{
					zmax = QMIN( zmax, z );
				}

				//Console::instance()->send( QString( "y2:zmin:%1,zmax:%2\n" ).arg( zmin ).arg( zmax ) );

				if( dz_down > 0 )
				{
					zmin = QMAX( zmin, z-14 );
				}
				else
				{
					zmin = QMAX( zmin, target.z+1 );
				}

				//Console::instance()->send( QString( "y3:zmin:%1,zmax:%2\n" ).arg( zmin ).arg( zmax ) );

				if( (*pit).y == target.y )
				{
					targetpos = true;
					if( (dz_up >= 0) && (dz_down >= 0) )
					{
						if( zmin < target.z )
						{
							zmax = target.z -1;
						}
						else
						{
							//we ignore this coordinate
							++pit;
							continue;
						}
					}
					else if( (dz_up <= 0) && (dz_down <= 0) )
					{
						if( zmax > target.z + targetheight+1 )
						{
							zmin = target.z + targetheight + 2;
						}
						else
						{
							++pit;
							continue;
						}
					}
					else 
					{
						//we may have to split the test into two if we would do it exactly
						//but i think we can throw away the test from down in this case
						if( zmax > target.z + targetheight+1 )
						{
							zmin = target.z + targetheight + 2;
						}
						else if( zmin < target.z )
						{
							zmax = target.z -1;
						}
						else
						{
							++pit;
							continue;
						}
					}
					//Console::instance()->send( QString( "y4:zmin:%1,zmax:%2\n" ).arg( zmin ).arg( zmax ) );
				}
			}
			else if( sgn_y == 0 )
			{
				z1_up = target.z + targetheight - dz_up*( fabs( (double)target.x - (double)((*pit).x) ) -0.5 ); 
				z2_up = target.z + targetheight - dz_up*( fabs( (double)target.x - (double)((*pit).x) ) +0.5 );
			
				z1_down = target.z - dz_down*( fabs( (double)target.x - (double)((*pit).x) ) -0.5 );
				z2_down = target.z - dz_down*( fabs( (double)target.x - (double)((*pit).x) ) +0.5 );

				//Console::instance()->send( QString( "tx:%2,cx:%3\n" ).arg( target.x ).arg( (*pit).x ) );
				//Console::instance()->send( QString( "z1_up:%1,z2_up:%2,z1_down:%3,z2_down:%4\n" ).arg( z1_up ).arg( z2_up ).arg( z1_down ).arg( z2_down ) );

				zmax = QMAX( ceil( z1_up ), ceil( z2_up ) );
				zmin = QMIN( floor( z1_down ), floor( z2_down ) );

				if( dz_up > 0 )
				{
					zmax = QMIN( zmax, target.z+targetheight );
				}
				else
				{
					zmax = QMIN( zmax, z );
				}

				if( dz_down > 0 )
				{
					zmin = QMAX( zmin, z-14 );
				}
				else
				{
					zmin = QMAX( zmin, target.z+1 );
				}

				if( (*pit).x == target.x )
				{
					targetpos = true;
					if( (dz_up >= 0) && (dz_down >= 0) )
					{
						if( zmin < target.z )
						{
							zmax = target.z -1;
						}
						else
						{
							//we ignore this coordinate
							++pit;
							continue;
						}
					}
					else if( (dz_up <= 0) && (dz_down <= 0) )
					{
						if( zmax > target.z + targetheight+1 )
						{
							zmin = target.z + targetheight + 2;
						}
						else
						{
							++pit;
							continue;
						}
					}
					else 
					{
						//we may have to split the test into two if we would do it exactly
						//but i think we can throw away the test from down in this case
						if( zmax > target.z + targetheight+1 )
						{
							zmin = target.z + targetheight + 2;
						}
						else if( zmin < target.z )
						{
							zmax = target.z -1;
						}
						else
						{
							++pit;
							continue;
						}
					}
				}
			}
			else
			{
				double gradx_up = ( (double)target.y*(double)z - (double)y*(double)(target.z + targetheight) ) / ( (double)target.y*(double)x - (double)y*(double)target.x );
				double grady_up = ( (double)target.x*(double)z - (double)x*(double)(target.z + targetheight) ) / ( (double)y*(double)target.x - (double)target.y*(double)x );
				double gradx_down = ( (double)target.y*(double)(z-15) - (double)y*(double)(target.z) ) / ( (double)target.y*(double)x - (double)y*(double)target.x );
				double grady_down = ( (double)target.x*(double)(z-15) - (double)x*(double)(target.z) ) / ( (double)y*(double)target.x - (double)target.y*(double)x );

				//Console::instance()->send( QString( "gradx_up:%1,grady_up:%2,gradx_down:%3,grady_down:%4\n" ).arg( gradx_up ).arg( grady_up ).arg( gradx_down ).arg( grady_down ) );
				//Console::instance()->send( QString( "Gradient:%1,Offset:%2\n" ).arg( gradient ).arg( offset ) );

				double temp = specialFloor( gradient*( (double)((*pit).x) - 0.5 ) + offset );
				//Console::instance()->send( QString( "temp1:%1\n" ).arg( temp ) );
				if( ( temp >= ((double)((*pit).y)-0.5) ) && ( temp <= ((double)((*pit).y)+0.5) ) )
				{
					if( z1_up > -300 )
					{
						z2_up = gradx_up * ( (double)((*pit).x)-0.5 ) + grady_up * temp;
						z2_down = gradx_down * ( (double)((*pit).x)-0.5 ) + grady_down * temp;
					}
					else
					{
						z1_up = gradx_up * ( (double)((*pit).x)-0.5 ) + grady_up * temp;
						z1_down = gradx_down * ( (double)((*pit).x)-0.5 ) + grady_down * temp;
					}
					//Console::instance()->send( QString( "tx:%1,ty:%2,cy:%3,cy:%4\n" ).arg( target.x ).arg( target.y ).arg( (*pit).x ).arg( (*pit).y ) );
					//Console::instance()->send( QString( "z1_up:%1,z1_down:%2,z2_up:%3,z2_down:%4\n" ).arg( z1_up ).arg( z1_down ).arg( z2_up ).arg( z2_down ) );
				}
				temp = specialFloor( gradient*( (double)((*pit).x) + 0.5 ) + offset );
				//Console::instance()->send( QString( "temp2:%1\n" ).arg( temp ) );
				if( ( temp >= ((double)((*pit).y)-0.5) ) && ( temp <= ((double)((*pit).y)+0.5) ) )
				{
					if( z1_up > -300 )
					{
						z2_up = gradx_up * ( (double)((*pit).x)+0.5 ) + grady_up * temp;
						z2_down = gradx_down * ( (double)((*pit).x)+0.5 ) + grady_down * temp;
					}
					else
					{	
						z1_up = gradx_up * ( (double)((*pit).x)+0.5 ) + grady_up * temp;
						z1_down = gradx_down * ( (double)((*pit).x)+0.5 ) + grady_down * temp;
					}
					//Console::instance()->send( QString( "tx:%1,ty:%2,cy:%3,cy:%4\n" ).arg( target.x ).arg( target.y ).arg( (*pit).x ).arg( (*pit).y ) );
					//Console::instance()->send( QString( "z1_up:%1,z1_down:%2,z2_up:%3,z2_down:%4\n" ).arg( z1_up ).arg( z1_down ).arg( z2_up ).arg( z2_down ) );
				}
				temp = specialFloor( (double)((*pit).y) - 0.5 - offset ) / gradient;
				//Console::instance()->send( QString( "temp3:%1\n" ).arg( temp ) );
				if( ( temp > ((double)((*pit).x)-0.5) ) && ( temp < ((double)((*pit).x)+0.5) ) )
				{
					if( z1_up > -300 )
					{
						z2_up = gradx_up * temp + grady_up * ((double)((*pit).y)-0.5);
						z2_down = gradx_down * temp + grady_down * ((double)((*pit).y)-0.5);
					}
					else
					{
						z1_up = gradx_up * temp + grady_up * ((double)((*pit).y)-0.5);
						z1_down = gradx_down * temp + grady_down * ((double)((*pit).y)-0.5);
					}
					//Console::instance()->send( QString( "tx:%1,ty:%2,cy:%3,cy:%4\n" ).arg( target.x ).arg( target.y ).arg( (*pit).x ).arg( (*pit).y ) );
					//Console::instance()->send( QString( "z1_up:%1,z1_down:%2,z2_up:%3,z2_down:%4\n" ).arg( z1_up ).arg( z1_down ).arg( z2_up ).arg( z2_down ) );
				}
				temp = specialFloor( (double)((*pit).y) + 0.5 - offset ) / gradient;
				//Console::instance()->send( QString( "temp4:%1\n" ).arg( temp ) );
				if( ( temp > ((double)((*pit).x)-0.5) ) && ( temp < ((double)((*pit).x)+0.5) ) )
				{
					if( z1_up > -300 )
					{
						z2_up = gradx_up * temp + grady_up * ((double)((*pit).y)+0.5);
						z2_down = gradx_down * temp + grady_down * ((double)((*pit).y)+0.5);
					}
					else
					{
						z1_up = gradx_up * temp + grady_up * ((double)((*pit).y)+0.5);
						z1_down = gradx_down * temp + grady_down * ((double)((*pit).y)+0.5);
					}
					//Console::instance()->send( QString( "tx:%1,ty:%2,cy:%3,cy:%4\n" ).arg( target.x ).arg( target.y ).arg( (*pit).x ).arg( (*pit).y ) );
					//Console::instance()->send( QString( "z1_up:%1,z1_down:%2,z2_up:%3,z2_down:%4\n" ).arg( z1_up ).arg( z1_down ).arg( z2_up ).arg( z2_down ) );
				}

				//Console::instance()->send( QString( "ergebnis: z1_up:%1,z1_down:%2,z2_up:%3,z2_down:%4\n" ).arg( z1_up ).arg( z1_down ).arg( z2_up ).arg( z2_down ) );

				if( z2_up == -300 )
				{
					zmin = floor( z1_down );
					zmax = ceil( z1_up );
				}
				else
				{
					zmin = QMIN( floor( z1_down ), floor( z2_down ) );
					zmax = QMAX( ceil( z1_up ), ceil( z2_up ) );
				}

				//Console::instance()->send( QString( "zmin:%1,zmax:%2\n" ).arg( zmin ).arg( zmax ) );

				if( dz_up > 0 )
				{
					zmax = QMIN( zmax, target.z+targetheight );
				}
				else
				{
					zmax = QMIN( zmax, z );
				}

				if( dz_down > 0 )
				{
					zmin = QMAX( zmin, z-14 );
				}
				else
				{
					zmin = QMAX( zmin, target.z+1 );
				}

				//Console::instance()->send( QString( "zmin:%1,zmax:%2\n" ).arg( zmin ).arg( zmax ) );

				if( ((*pit).x == target.x) && ((*pit).y == target.y) )
				{
					targetpos = true;
					if( (dz_up >= 0) && (dz_down >= 0) )
					{
						if( zmin < target.z )
						{
							zmax = target.z -1;
						}
						else
						{
							//we ignore this coordinate
							++pit;
							continue;
						}
					}
					else if( (dz_up <= 0) && (dz_down <= 0) )
					{
						if( zmax > target.z + targetheight+1)
						{
							zmin = target.z + targetheight + 2;
						}
						else
						{
							++pit;
							continue;
						}
					}
					else 
					{
						//we may have to split the test into two if we would do it exactly
						//but i think we can throw away the test from down in this case
						if( zmax > target.z + targetheight+1 )
						{
							zmin = target.z + targetheight + 2;
						}
						else if( zmin < target.z )
						{
							zmax = target.z -1;
						}
						else
						{
							++pit;
							continue;
						}
					}
				}				
			}
			//Console::instance()->send( QString( "zmin:%1,zmax:%2\n" ).arg( zmin ).arg( zmax ) );

			// Texture mapping  
			map1 = Map->seekMap( *pit );
			map2 = Map->seekMap( Coord_cl( (*pit).x + sgn_x, (*pit).y + sgn_y, (*pit).z, map ) );
			
			//Console::instance()->send( QString( "try2" ) );
			StaticsIterator msi = Map->staticsIterator( *pit );
			RegionIterator4Items rj( (*pit), 0 );
			if( (map1.id != 2) && (map2.id != 2) ) 
			{
				if( ( map1.z > zmin ) && ( map1.z < zmax ) )
				{
					//its just in our way
					//Console::instance()->send( QString( "map cut 1\n" ) );
					return false;
				}
				
				//now we have to test, if this tile is under our line and the next above or
				//vice verse
				//in this case, both dont cut our line, but the mapconnection between them
				//should do
				land_st tile = TileCache::instance()->getLand( map1.id );
				if( ( ( map1.z < map2.z ) && ( map1.z < zmin ) && ( map2.z > zmax+dz_down ) && !targetpos ) ||						// 1) lineofsight collides with a map "wall"
					( ( map1.z > map2.z ) && ( map1.z > zmin ) && ( map2.z < zmin+dz_down ) && !targetpos ) ||
					( tile.isBlocking() && posHigherThanMap && msi.atEnd() && rj.atEnd() ) )
				{
					//Console::instance()->send( QString( "map1:%1,map2:%2,map1id:%3\n" ).arg( map1.z ).arg( map2.z ).arg( map1.id ) );
					if( ( map1.z < map2.z ) && ( map1.z < zmin ) && ( map2.z > zmax+dz_down ) )
					{
						//Console::instance()->send( QString( "mapcut1\n" ) ); 
					}
					else if( ( map1.z > map2.z ) && ( map1.z > zmin ) && ( map2.z < zmin+dz_down ) )
					{
						//Console::instance()->send( QString( "mapcut2\n" ) );
					}
					else if( tile.isBlocking() && posHigherThanMap && msi.atEnd() && rj.atEnd() )
					{
						//Console::instance()->send( QString( "mapcut3\n" ) );
					}
					else
					{
						//Console::instance()->send( QString( "mapcut: this isnt possible\n" ) );
					}
					return false;
				}
			}	 
			
			//Console::instance()->send( QString( "after map\n" ) );
		
			// Statics
			tile_st tile;
			while( !msi.atEnd() )
			{
				tile = TileCache::instance()->getTile( msi->itemid );
				//Console::instance()->send( QString( "statictilepos:%1,zmax:%2,zmin:%3\n" ).arg( msi->zoff ).arg( zmax ).arg( zmin ) );
				//if it is in our way
				if(	( zmax >= msi->zoff ) && ( zmin <= ( msi->zoff + tile.height ) ) )
				{
					if( tile.isBlocking() || tile.isRoofOrFloorTile() )
					{
						return false;
					}
				}
			
				++msi;
			}
			
			//Console::instance()->send( QString( "after statics\n" ) );
			// Items
			//Console::instance()->send( QString( "Items at: %1,%2,%3,%4\n" ).arg( (*pit).x ).arg( (*pit).y ).arg( (*pit).z ).arg( (*pit).map ) );
			for( rj.Begin(); !rj.atEnd(); rj++ )
			{
				//Console::instance()->send( QString( "foritem\n" ) );
				P_ITEM pi = rj.GetData();
				if( pi && pi->id() < 0x4000 )
				{
					tile = TileCache::instance()->getTile( pi->id() );
					//Console::instance()->send( QString( "itemtilepos:%1,zmax:%2,zmin:%3\n" ).arg( pi->pos().z ).arg( zmax ).arg( zmin ) );
					if(	( zmax >= pi->pos().z ) && ( zmin <= ( pi->pos().z + tile.height ) ) && ( pi->visible() == 0 ) )
					{
						if( tile.isBlocking() || tile.isRoofOrFloorTile() )
						{
							//Console::instance()->send( QString( "Item:%1,Z:%2,Height:%3\n" ).arg( pi->id() ).arg( pi->pos().z ).arg( tile.height ) );
							return false;
						}
					}	
				}
			}
			
			//Console::instance()->send( QString( "after items\n" ) );
			// Multis
			QPtrListIterator< cItem > mit( multis );
			P_ITEM pi;
			while( ( pi = mit.current() ) )
			{
				MultiDefinition* def = MultiCache::instance()->getMulti( pi->id() - 0x4000 );
				if ( !def )
				{
					++mit;
					continue;
				}
				QValueVector<multiItem_st> multi = def->getEntries();
				for( j = 0; j < multi.size(); ++j )
				{
					if( ( multi[j].visible ) && ( pi->pos().x + multi[j].x == (*pit).x ) &&
						( pi->pos().y + multi[j].y == (*pit).y ) )			
					{
						tile = TileCache::instance()->getTile( multi[j].tile );
						if( ( zmax >= pi->pos().z + multi[j].z ) &&
							( zmin <= pi->pos().z + multi[j].z + tile.height ) )
						{
							if( tile.isBlocking() || tile.isRoofOrFloorTile() )
							{
								return false;							
							}
						}	
					}
				}
				++mit;
			}
			//Console::instance()->send( QString( "after multis\n" ) );
			++pit;
		}
		return true;
	}
}
bool cNewMagic::checkTarget( P_CHAR pCaster, stNewSpell *sInfo, cUORxTarget *target )
{
	cUOSocket *socket;
	if( pCaster->objectType() == enPlayer )
		socket = dynamic_cast<P_PLAYER>(pCaster)->socket();

	P_CHAR pChar = FindCharBySerial( target->serial() );
	P_ITEM pItem = FindItemBySerial( target->serial() );

	if( sInfo->targets & (TARGET_CHAR|TARGET_ITEM) && !pItem && !pChar )
	{
		socket->sysMessage( tr( "Please target an object" ) );
		return false;
	}

	if( sInfo->targets & TARGET_CHAR && !pChar )
	{
		socket->sysMessage( tr( "Please target a living creature." ) );
		return false;
	}

	if( sInfo->targets & TARGET_ITEM && !pItem )
	{
		socket->sysMessage( tr( "Please target an item." ) );
		return false;
	}

	// Several checks (Get the correct position of the target and do NOT trust the position transmitted by the client)
	Coord_cl pos = socket->player()->pos();
	if( pChar )
		pos = pChar->pos();
	else if( pItem )
		pos = pItem->pos();
	else
	{
		pos.x = target->x();
		pos.y = target->y();
		pos.z = target->z();
	}


	if( pItem && !pItem->isInWorld() )
	{
		P_CHAR pOut = pItem->getOutmostChar();
		
		if( pOut == socket->player() )
			return true;
		else
		{
			socket->clilocMessage( 0x7A258, "", 0x3b2 ); // You cannot reach that
			return false;
		}
	}
	else
	{
		// Distance check (VisRange + 5 for macros)
		if( pos.distance( socket->player()->pos() ) > ( socket->player()->visualRange() + 5 ) )
		{
			socket->sysMessage( tr( "You can't see the target." ) );
			return false;
		}
	
		// Line of Sight check
		if( !lineOfSight( pos, socket->player()->pos()+Coord_cl( 0, 0, 13 ), WALLS_CHIMNEYS|TREES_BUSHES|ROOFING_SLANTED|LAVA_WATER|DOORS ) )
		{
			socket->sysMessage( tr( "You can't see the target." ) );
			return false;
		}
	}
	
	// Visibility check (Chars)
	if( pChar && pChar->isHidden() && !socket->player()->isGM() )
	{
		socket->sysMessage( tr( "You can't see this character." ) );
		return false;
	}

	// Visibility check (Items)
	if( pItem && ( ( pItem->visible() == 1 && !socket->player()->Owns( pItem ) ) || pItem->visible() == 2 ) && !socket->player()->isGM() )
	{
		socket->sysMessage( tr( "You can't see this item." ) );
		return false;
	}

	return true;
}
Example #17
0
// Get blocking tiles at the given x,y,map coordinate
// Get floor tiles, that are not blocking, too
// we need these floor tiles to know, if the 'black map' tile is the only floor here
void getBlockingTiles( const Coord& pos, QList<stBlockingItem>& items )
{
	stBlockingItem item;

	// Maptiles first
	Maps::instance()->mapTileSpan( pos, item.id, item.bottom, item.top );
	item.maptile = true;
	item.noblock = false;

	// Only include this maptile if it's relevant for our line of sight
	if ( item.id != 2 && item.id != 0x1DB && ( item.id <0x1AE || item.id> 0x1B5 ) )
	{
		items.append( item );
	}

	item.maptile = false;

	// Search for statics at the same position
	StaticsIterator statics = Maps::instance()->staticsIterator( pos, true );

	// Find blocking statics
	for ( ; !statics.atEnd(); ++statics )
	{
		const staticrecord &sitem = *statics;

		tile_st tile = TileCache::instance()->getTile( sitem.itemid );

		if ( tile.flag2 & 0x30 )
		{
			item.bottom = sitem.zoff;
			// Bridges are only half as high
			item.top = item.bottom + ( ( tile.flag2 & 0x04 ) ? ( tile.height / 2 ) : tile.height );
			item.id = sitem.itemid;
			item.noblock = false;
			items.append( item );
		}
		// floor tiles that aren't blocking
		else if ( tile.flag2 & 0x2 )
		{
			item.bottom = sitem.zoff;
			// Bridges are only half as high
			item.top = item.bottom + ( ( tile.flag2 & 0x04 ) ? ( tile.height / 2 ) : tile.height );
			item.id = sitem.itemid;
			item.noblock = true;
			items.append( item );
		}
	}

	// Search for items at the given location
	MapItemsIterator itemIter = MapObjects::instance()->listItemsAtCoord( pos );
	for ( P_ITEM pItem = itemIter.first(); pItem; pItem = itemIter.next() )
	{
		// If the item is invisible or a multi, skip past it.
		if ( pItem->isMulti() )
			continue;

		tile_st tile = TileCache::instance()->getTile( pItem->id() );

		// Window and noshoot tiles block
		if ( tile.flag2 & 0x30 )
		{
			item.id = pItem->id();
			item.bottom = pItem->pos().z;
			// Bridges are only half as high
			item.top = item.bottom + ( ( tile.flag2 & 0x04 ) ? ( tile.height / 2 ) : tile.height );
			item.noblock = false;
			items.append( item );
		}
		// floor tiles that aren't blocking
		else if ( tile.flag2 & 0x2 )
		{
			item.id = pItem->id();
			item.bottom = pItem->pos().z;
			// Bridges are only half as high
			item.top = item.bottom + ( ( tile.flag2 & 0x04 ) ? ( tile.height / 2 ) : tile.height );
			item.noblock = true;
			items.append( item );
		}
	}

	// Check for multis around the area
	MapMultisIterator multis = MapObjects::instance()->listMultisInCircle( pos, BUILDRANGE );

	// Check if there is an intersecting item for this multi
	for ( P_MULTI pMulti = multis.first(); pMulti; pMulti = multis.next() )
	{
		// Get all items for this multi
		MultiDefinition *data = MultiCache::instance()->getMulti( pMulti->id() - 0x4000 );
		if ( data )
		{
			QList<multiItem_st> mitems = data->getEntries();
			QList<multiItem_st>::iterator it;

			for ( it = mitems.begin(); it != mitems.end(); ++it )
			{
				multiItem_st mitem = *it;

				// Skip this multi tile if it's not at the position we need it to be
				if ( !mitem.visible || pMulti->pos().x + mitem.x != pos.x || pMulti->pos().y + mitem.y != pos.y )
				{
					continue;
				}

				tile_st tile = TileCache::instance()->getTile( mitem.tile );

				// Has to be blocking
				if ( tile.flag2 & 0x30 )
				{
					item.bottom = mitem.z + pMulti->pos().z;
					item.top = item.bottom + ( ( tile.flag2 & 0x04 ) ? ( tile.height / 2 ) : tile.height );
					item.id = mitem.tile;
					item.noblock = false;
					items.append( item );
				}
				// floor tiles that aren't blocking
				else if ( tile.flag2 & 0x2 )
				{
					item.bottom = mitem.z + pMulti->pos().z;
					item.top = item.bottom + ( ( tile.flag2 & 0x04 ) ? ( tile.height / 2 ) : tile.height );
					item.id = mitem.tile;
					item.noblock = true;
					items.append( item );
				}
			}
		}
	}
}
Example #18
0
bool cMulti::canPlace( const Coord& pos, unsigned short multiid, QPtrList<cUObject>& moveOut, unsigned short yard )
{
	MultiDefinition *multi = MultiCache::instance()->getMulti( multiid );

	if ( !multi )
	{
		return false;
	}

	moveOut.setAutoDelete( false );

	// Get the boundaries and build a list sorted by x,y
	int left = multi->getLeft();
	int right = multi->getRight();
	int bottom = multi->getBottom();
	int top = multi->getTop();
	int height = multi->getHeight();
	int width = multi->getWidth();

	Q_UNUSED( bottom );

	QValueList<Coord> borderList; // a list of points around the foundation that need to be clear of impassables
	QValueList<Coord> yardList; // a list of points in the yard (front/back of the house that needs to be clear)

	for ( int x = 0; x < width; ++x )
	{
		for ( int y = 0; y < height; ++y )
		{
			Coord point = pos + Coord( x + left, y + top );
			bool hasBase = false; // Has this multi tile a base below the floor?

			// See if there are any tiles at that position
			const QValueVector<multiItem_st> &multiItems = multi->itemsAt( x + left, y + top );

			if ( multiItems.size() == 0 )
			{
				continue; // Skip this tile since there are no items here
			}

			cTerritory *region = Territories::instance()->region( point );
			if ( region && region->isNoHousing() )
			{
				return false; // No housing is allowed in this region
			}

			// Collect data for the intersect checks
			StaticsIterator statics = Maps::instance()->staticsIterator( point );
			MapItemsIterator items = MapObjects::instance()->listItemsAtCoord( point );
			int top, bottom;
			unsigned short landId;
			Maps::instance()->mapTileSpan( point, landId, bottom, top );

			// Check every tile of the multi at the current position
			// The following algorithm is more or less a ripoff of RunUOs idea.
			for ( unsigned int i = 0; i < multiItems.size(); ++i )
			{
				multiItem_st multiItem = multiItems[i];
				tile_st tile = TileCache::instance()->getTile( multiItem.tile );

				// Calculcate the spawn of the tile
				int itemBottom = point.z + multiItem.z;
				// if the tile is a surface, someone has to be able to stand on it.
				int itemTop = itemBottom + tile.height + ( ( ( tile.flag2 & 0x02 ) != 0 ) ? 16 : 0 );

				// There is special handling for floor tiles
				bool baseTile = multiItem.z == 0 && ( tile.flag1 & 0x10 ) != 0;
				bool isHovering = true; // This tile has not yet something to "stand" on

				Q_UNUSED( isHovering );
				if ( baseTile )
					hasBase = true;

				// Does the multi item intersect a land-tile?
				if ( ( itemTop < top && itemTop >= bottom ) || ( itemBottom < top && itemBottom >= bottom ) )
				{
					return false;
				}

				// Since houses can only be built on land and not on items, it's enough to check for
				// a solid foundation here
				if ( pos.z != bottom || pos.z != top )
				{
					return false;
				}

				// Check if the multi item is interfering with a static tile at the same position
				statics.reset();
				while ( !statics.atEnd() )
				{
					const staticrecord &staticTile = ( statics++ ).data();
					tile_st staticInfo = TileCache::instance()->getTile( staticTile.itemid );

					int staticBottom = staticTile.zoff;
					int staticTop = staticBottom + staticInfo.height;

					// The tile intersects a static tile
					if ( ( itemTop < staticTop && itemTop >= staticBottom ) || ( itemBottom < staticTop && itemBottom >= staticBottom ) )
					{
						bool impassable = ( staticInfo.flag1 & 0x40 ) != 0;
						bool background = ( staticInfo.flag1 & 0x01 ) != 0;
						bool surface = ( staticInfo.flag2 & 0x02 ) != 0;

						// A normally blocking tile is intersecting our multi
						if ( impassable || ( !background && surface ) )
						{
							return false;
						}
					}
				}

				// Do the same check (as above) with movable items, but make sure that movable items
				// are moved out of the house
				for ( P_ITEM pItem = items.first(); pItem; pItem = items.next() )
				{
					tile_st itemInfo = TileCache::instance()->getTile( pItem->id() );

					int dynamicBottom = pItem->pos().z;
					int dynamicTop = dynamicBottom + itemInfo.height;

					// Only handle the tile if it is intersecting the multi
					if ( ( itemTop < dynamicTop && itemTop >= dynamicBottom ) || ( itemBottom < dynamicTop && itemBottom >= dynamicBottom ) )
					{
						// Move the item out of the multi space if possible
						if ( ( pItem->movable() == 0 && itemInfo.weight != 255 ) || pItem->movable() == 1 )
						{
							moveOut.append( pItem );
						}
						else
						{
							bool impassable = ( itemInfo.flag1 & 0x40 ) != 0;
							bool background = ( itemInfo.flag1 & 0x01 ) != 0;
							bool surface = ( itemInfo.flag2 & 0x02 ) != 0;

							// A normally blocking tile is intersecting our multi
							if ( impassable || ( !background && surface ) )
							{
								return false;
							}
						}
					}
				}

				// Moves mobiles inside the multi out to the ban location
				MapCharsIterator chars = MapObjects::instance()->listCharsAtCoord( point );
				for ( P_CHAR pChar = chars.first(); pChar; pChar = chars.next() )
				{
					// Move them ALWAYS out, they could be trapped by the castle
					// otherwise (or other strange multi forms)
					moveOut.append( pChar );
				}

				// To keep roads house free, here's a specialized check for roads
				if ( ( landId >= 0x71 && landId <= 0x8c ) || ( landId >= 0x14c && landId <= 0x14f ) || ( landId >= 0x161 && landId <= 0x174 ) || ( landId >= 0x1f0 && landId <= 0x1f3 ) || ( landId >= 0x26e && landId <= 0x279 ) || ( landId >= 0x27e && landId <= 0x281 ) || ( landId >= 0x324 && landId <= 0x3ac ) || ( landId >= 0x597 && landId <= 0x5a6 ) || ( landId >= 0x637 && landId <= 0x63a ) || ( landId >= 0x67d && landId <= 0x6a0 ) || ( landId >= 0x7ae && landId <= 0x7b1 ) || ( landId >= 0x442 && landId <= 0x479 ) || ( landId >= 0x501 && landId <= 0x510 ) || ( landId >= 0x009 && landId <= 0x015 ) || ( landId >= 0x150 && landId <= 0x15c ) )
				{
					return false; // Road Blocked
				}

				// For houses (they have a base you know...)
				// we collect another list of points around the house that need to be checked
				if ( hasBase )
				{
					int xOffset, yOffset;

					// We have to do two loops since the yard size does play a role here
					// but not for the border
					for ( xOffset = -1; xOffset <= 1; ++xOffset )
					{
						for ( yOffset = -yard; yOffset <= yard; ++yOffset )
						{
							Coord pos = point + Coord( xOffset, yOffset );

							if ( !yardList.contains( pos ) )
							{
								yardList.push_back( pos ); // Put this point into the yard checklist if it's not there
							}
						}
					}

					for ( xOffset = -1; xOffset <= 1; ++xOffset )
					{
						for ( yOffset = -1; yOffset <= 1; ++yOffset )
						{
							Coord pos = point + Coord( xOffset, yOffset );

							// Only do the following if the current tiles position differs from the
							// check position.
							if ( xOffset != 0 || yOffset != 0 )
							{
								// The border list should not contain tiles that are actually below the multis
								// floor and hence not visible and covered by a walkable multi tile.
								// So what we do here is check if within 8 z units of the multis floor there is a
								// walkable tile above the border tile
								int multiX = x + xOffset; // Offset to the upper left corner of the multi
								int multiY = y + yOffset; // Offset to the upper left corner of the multi
								bool found = false; // Assume there is no such tile

								// Only do this check if the to-be-checked tile is really within the multi
								// boundaries
								if ( multiX >= 0 && multiY >= 0 && multiX < width && multiY < height )
								{
									// Get the multi tiles at the to-check position
									const QValueVector<multiItem_st> &tiles = multi->itemsAt( multiX + left, multiY + right );
									QValueVector<multiItem_st>::const_iterator it;
									for ( it = tiles.begin(); it != tiles.end(); ++it )
									{
										if ( it->z > 8 )
										{
											continue; // Skip the tile if its above the base (2nd floor etc.)
										}

										// Get the tiledata info for this tile if it's below z8
										tile_st tileInfo = TileCache::instance()->getTile( it->tile );
										bool surface = ( tileInfo.flag2 & 0x02 ) != 0;

										if ( tileInfo.height == 0 && surface )
										{
											found = true;
											break;
										}
									}

									// We found a tile that we could stand on. So it's not neccesary to check
									// that the house stands on something walkable here.
									if ( found )
									{
										continue;
									}
								}

								// Add the tile to the list of border tiles.
								if ( !borderList.contains( pos ) )
								{
									borderList.append( pos );
								}
							}
						}
					}
				}
			}
		}
	}

	QValueList<Coord>::const_iterator it;

	// Now check all the accumulated border tiles
	for ( it = borderList.begin(); it != borderList.end(); ++it )
	{
		map_st mapTile = Maps::instance()->seekMap( *it );
		land_st mapTileInfo = TileCache::instance()->getLand( mapTile.id );

		// Impassable map tiles are not allowed nearby
		bool impassable = ( mapTileInfo.flag1 & 0x40 ) != 0;
		if ( impassable )
		{
			return false;
		}

		// Get Static Tiles
		StaticsIterator statics = Maps::instance()->staticsIterator( *it );
		while ( !statics.atEnd() )
		{
			const staticrecord &staticItem = statics.data();
			tile_st staticInfo = TileCache::instance()->getTile( staticItem.itemid );

			bool impassable = ( staticInfo.flag1 & 0x40 ) != 0;
			bool background = ( staticInfo.flag1 & 0x01 ) != 0;
			bool surface = ( staticInfo.flag2 & 0x02 ) != 0;

			// The tile is only of importance if it's not below the multi
			if ( ( staticItem.zoff > ( ( *it ).z + 2 ) ) && ( impassable || ( !background && surface ) ) )
			{
				return false; // A normally blocking tile is intersecting our multi border
			}

			statics++;
		}

		// Do the same check (as above) with dynamic items
		MapItemsIterator items = MapObjects::instance()->listItemsAtCoord( *it );
		for ( P_ITEM pItem = items.first(); pItem; pItem = items.next() )
		{
			tile_st itemInfo = TileCache::instance()->getTile( pItem->id() );

			// Move the item out of the multi space if possible
			if ( ( pItem->movable() == 0 && itemInfo.weight != 255 ) || pItem->movable() == 1 )
				continue;

			if ( pItem->pos().z <= ( ( *it ).z + 2 ) )
				continue; // Does not interfere with the border

			bool impassable = ( itemInfo.flag1 & 0x40 ) != 0;
			bool background = ( itemInfo.flag1 & 0x01 ) != 0;
			bool surface = ( itemInfo.flag2 & 0x02 ) != 0;

			// A normally blocking tile is intersecting our multi
			if ( impassable || ( !background && surface ) )
				return false;
		}
	}

	// The yard has to be free of any multis at that position
	for ( it = yardList.begin(); it != yardList.end(); ++it )
	{
		// Search for multis in the region
		MapMultisIterator multis = MapObjects::instance()->listMultisInCircle( *it, 18 );
		for ( cMulti*multi = multis.first(); multi; multi = multis.next() )
		{
			if ( multi->inMulti( *it ) )
			{
				// This is a simplified check but it should be sufficient.
				return false;
			}
		}
	}

	return true;
}