SharedModel(shared_ptr<IStorm3D_Model> model_, shared_ptr<IStorm3D_Model> fadeModel_, const std::string &bones_, const std::string &idleAnimation_)
		:	model(model_),
			fadeModel(fadeModel_),
			bones(bones_),
			idleAnimation(idleAnimation_),
			radius(0),
			radius2d(0)
		{
			boost::scoped_ptr<Iterator<IStorm3D_Model_Object *> > objectIterator(model->ITObject->Begin());
			for(; !objectIterator->IsEnd(); objectIterator->Next())
			{
				IStorm3D_Model_Object *object = objectIterator->GetCurrent();
				if(!object)
					continue;

				IStorm3D_Mesh *mesh = object->GetMesh();
				if(!mesh)
					continue;

				VC3 objectPosition = object->GetPosition();
				float objectDistance = objectPosition.GetLength();
				float distance = objectDistance + mesh->GetRadius();
				if(distance > radius)
					radius = distance;

				float meshRadius = mesh->GetRadius();
				if(meshRadius + objectDistance > radius2d)
					radius2d = meshRadius + objectDistance;
			}

			if(!bones.empty())
				model->LoadBones(bones.c_str());
		}
	void VisualEffect::moveBetween(const VC3 &position_, const VC3 &endPosition, const VC3 &rotation, float alpha, float scale)
	{
		if (visualObject == NULL)
			return;

		if (effectType->getType() == VisualEffectType::VISUALEFFECT_TYPE_RAY)
		{
			this->position = (position_ + endPosition) / 2;
			VC3 diffVec = endPosition - position_;
			float raylen = diffVec.GetLength();
			VC3 scalevec(1*scale,1*scale,raylen);
			visualObject->setScale(scalevec);
			visualObject->setVisibilityFactor(alpha);
		}
		visualObject->setPosition(this->position);
		visualObject->setRotation(rotation.x, rotation.y, rotation.z);
	}
	bool LineAreaChecker::isPointInsideLineArea(const VC3 &point, 
		const VC3 &lineStart, const VC3 &lineEnd, float lineWidth)
	{
		VC3 projPos = lineEnd - lineStart;
		float projPosLenSq = projPos.GetSquareLength();
		VC3 projPosNorm = VC3(0,0,0);
		if (projPosLenSq > 0.00001f)
		{
			projPosNorm = projPos.GetNormalized();
		}

		VC3 chkpos = point - lineStart;
		float chkposLen = chkpos.GetLength();

		VC3 hitAndUnitDiff = chkpos - projPos;
		float hitAndUnitDiffLenSq = hitAndUnitDiff.GetSquareLength();

		float lineRadiusSq = (float)(lineWidth * lineWidth);

		if (hitAndUnitDiffLenSq < lineRadiusSq)
		{
			return true;
		}

		if (chkposLen * chkposLen < projPosLenSq)
		{
			VC3 pipedPos = projPosNorm * chkposLen;
			VC3 posdiff = chkpos - pipedPos;

			if (posdiff.GetSquareLength() < lineRadiusSq)
			{
				return true;
			}
		}

		return false;
	}
	void PositionScripting::process(util::ScriptProcess *sp, 
		int command, int intData, char *stringData, ScriptLastValueType *lastValue, 
		GameScriptData *gsd, Game *game)
	{
		switch(command)
		{
		case GS_CMD_SETPOSITION:
			{
				VC3 tmp(0,0,0);
				if (gs_coordinate_param(game->gameMap, stringData, &tmp))
				{
					float x = tmp.x;
					float y = tmp.z;
					game->gameMap->keepWellInScaledBoundaries(&x, &y);
					gsd->position = VC3(
						x, game->gameMap->getScaledHeightAt(x,y), y);
				} else {
					sp->error("PositionScripting::process - Missing or bad setPosition parameter.");
				}
			}
			break;

		case GS_CMD_POSITIONRANDOMOFFSET:
			if (intData > 0)
			{
				int o1 = (game->gameRandom->nextInt() % (intData * 20 + 10)) - intData*10;
				int o2 = (game->gameRandom->nextInt() % (intData * 20 + 10)) - intData*10;
				int o3 = (game->gameRandom->nextInt() % (intData * 20 + 10)) - intData*10;
				gsd->position.x += (float)o1 * 0.1f;
				gsd->position.y += (float)o2 * 0.1f;
				gsd->position.z += (float)o3 * 0.1f;
			} else {
				sp->warning("PositionScripting::process - positionRandomOffset with zero parameter.");
				lastValue = 0;
			}
			break;

		case GS_CMD_MOVEPOSITIONZ:
			gsd->position.z += (float)intData / 100.0f;
			break;

		case GS_CMD_MOVEPOSITIONX:
			gsd->position.x += (float)intData / 100.0f;
			break;

		case GS_CMD_MOVEPOSITIONZFLOAT:
			{
				float floatData = *((float *)(&intData));
				gsd->position.z += floatData;
			}
			break;

		case GS_CMD_MOVEPOSITIONXFLOAT:
			{
				float floatData = *((float *)(&intData));
				gsd->position.x += floatData;
			}
			break;

		case GS_CMD_GETPOSITIONX:
			*lastValue = (int)gsd->position.x;
			break;

		case GS_CMD_GETPOSITIONZ:
			*lastValue = (int)gsd->position.z;
			break;
							 
		case GS_CMD_GETPOSITIONHEIGHT:
			*lastValue = (int)gsd->position.y;
			break;
							 
		case GS_CMD_GETACCURATEPOSITIONX:
			*lastValue = (int)(gsd->position.x * 1000.0f);
			break;

		case GS_CMD_GETACCURATEPOSITIONZ:
			*lastValue = (int)(gsd->position.z * 1000.0f);
			break;
							 
		case GS_CMD_SETPOSITIONHEIGHT:
			if (stringData != NULL)
			{
				gsd->position.y = (float)atof(stringData);
			} else {
				sp->error("PositionScripting::process - Missing setPositionHeight parameter.");
			}
			break;

		case GS_CMD_SETPOSITIONHEIGHTONGROUND:
			{
				float x = gsd->position.x;
				float y = gsd->position.z;
				game->gameMap->keepWellInScaledBoundaries(&x, &y);
				gsd->position = VC3(
					x, game->gameMap->getScaledHeightAt(x,y), y);
			}
			break;

		case GS_CMD_POSITIONACCURATERANDOMOFFSET:
			if (intData > 0)
			{
				// intData values in cm
				int o1 = (game->gameRandom->nextInt() % (intData * 20 + 10)) - intData*10;
				int o2 = (game->gameRandom->nextInt() % (intData * 20 + 10)) - intData*10;
				int o3 = (game->gameRandom->nextInt() % (intData * 20 + 10)) - intData*10;
				gsd->position.x += (float)o1 * 0.1f * 0.01f;
				gsd->position.y += (float)o2 * 0.1f * 0.01f;
				gsd->position.z += (float)o3 * 0.1f * 0.01f;
			} else {
				sp->warning("PositionScripting::process - positionRandomOffset with zero parameter.");
				lastValue = 0;
			}
			break;

		case GS_CMD_MOVEPOSITIONHEIGHT:
			gsd->position.y += (float)intData / 100.0f;
			break;

		case GS_CMD_SETSECONDARYPOSITION:
			gsd->secondaryPosition = gsd->position;
			break;

		case GS_CMD_GETSECONDARYPOSITION:
			gsd->position = gsd->secondaryPosition;
			break;

		case GS_CMD_SETPOSITIONX:
			gsd->position.x = (float)*lastValue;
			break;

		case GS_CMD_SETPOSITIONZ:
			gsd->position.z = (float)*lastValue;
			break;
							 
		case GS_CMD_SETACCURATEPOSITIONX:
			gsd->position.x = (float)*lastValue / 1000.0f;
			break;

		case GS_CMD_SETACCURATEPOSITIONZ:
			gsd->position.z = (float)*lastValue / 1000.0f;
			break;

		case GS_CMD_setAccuratePositionHeight:
			gsd->position.y = (float)*lastValue / 1000.0f;
			break;

		case GS_CMD_getAccuratePositionHeight:
			*lastValue = (int)(gsd->position.y * 1000.0f);
			break;

		case GS_CMD_PUSHGLOBALTEMPPOSITION:
			if (gs_global_temp_position_used)
			{
				sp->warning("PositionScripting::process - pushGlobalTempPosition, stack full.");
			}
			gs_global_temp_position_used = true;
			gs_global_temp_position = gsd->position;
			break;
							 
		case GS_CMD_POPGLOBALTEMPPOSITION:
			if (!gs_global_temp_position_used)
			{
				sp->warning("PositionScripting::process - popGlobalTempPosition, stack empty.");
			}
			gsd->position = gs_global_temp_position;
			gs_global_temp_position_used = false;
			gs_global_temp_position = VC3(0,0,0);
			break;
							 
		case GS_CMD_MOVEPOSITIONTOANGLEVALUE:
			{
				float angle = (float)(*lastValue);
				if (angle < 0) angle += 360;
				if (angle >= 360) angle -= 360;
				float amount= (float)intData / 100.0f;
				gsd->position.x += -amount * sinf(UNIT_ANGLE_TO_RAD(angle));
				gsd->position.z += -amount * cosf(UNIT_ANGLE_TO_RAD(angle));
			}
			break;

		case GS_CMD_ISPOSITIONINSIDEBUILDING:
			{
				VC3 pos = gsd->position;
				int x = game->gameMap->scaledToPathfindX(pos.x);
				int y = game->gameMap->scaledToPathfindY(pos.z);
				if (game->gameMap->isWellInScaledBoundaries(pos.x, pos.z))
				{
					if (game->gameMap->getAreaMap()->isAreaAnyValue(x, y, AREAMASK_INBUILDING))
						*lastValue = 1;
					else
						*lastValue = 0;
				} else {
					*lastValue = 0;
				}
			}
			break;

		case GS_CMD_isPositionBlockedByUnmoving:
			{
				VC3 pos = gsd->position;
				int x = game->gameMap->scaledToPathfindX(pos.x);
				int y = game->gameMap->scaledToPathfindY(pos.z);
				if (game->gameMap->isWellInScaledBoundaries(pos.x, pos.z))
				{
					if (game->gameMap->getObstacleHeight(x, y) > 0
						&& !game->gameMap->isMovingObstacle(x, y))
						*lastValue = 1;
					else
						*lastValue = 0;
				} else {
					*lastValue = 0;
				}
			}
			break;

		case GS_CMD_isPositionBlockedByUnmovingOrDoor:
			{
				VC3 pos = gsd->position;
				int x = game->gameMap->scaledToPathfindX(pos.x);
				int y = game->gameMap->scaledToPathfindY(pos.z);
				if (game->gameMap->isWellInScaledBoundaries(pos.x, pos.z))
				{
					// HACK: rounded moving obstacles are actually doors
					if (game->gameMap->getObstacleHeight(x, y) > 0
						&& (!game->gameMap->isMovingObstacle(x, y)
						|| game->gameMap->isRoundedObstacle(x, y)))
						*lastValue = 1;
					else
						*lastValue = 0;
				} else {
					*lastValue = 0;
				}
			}
			break;

		case GS_CMD_isPositionBlockedByMoving:
			{
				VC3 pos = gsd->position;
				int x = game->gameMap->scaledToPathfindX(pos.x);
				int y = game->gameMap->scaledToPathfindY(pos.z);
				if (game->gameMap->isWellInScaledBoundaries(pos.x, pos.z))
				{
					if (game->gameMap->getObstacleHeight(x, y) > 0
						&& game->gameMap->isMovingObstacle(x, y))
						*lastValue = 1;
					else
						*lastValue = 0;
				} else {
					*lastValue = 0;
				}
			}
			break;

		case GS_CMD_setPositionVariable:
			if (stringData != NULL)
			{
				bool success = sp->getScript()->setGlobalPositionVariableValue(stringData, gsd->position.x, gsd->position.y, gsd->position.z);
				if (!success)
				{
					sp->error("PositionScripting::process - setPositionVariable, failed to set position variable value (variable does not exist or type mismatch).");
					sp->debug(stringData);
				}
			} else {
				sp->error("PositionScripting::process - setPositionVariable parameter missing, position variable name expected.");
			}
			break;

		case GS_CMD_getPositionVariable:
			if (stringData != NULL)
			{				
				float tmpx, tmpy, tmpz;
				bool success = sp->getScript()->getGlobalPositionVariableValue(stringData, &tmpx, &tmpy, &tmpz);
				if (success)
				{
					gsd->position = VC3(tmpx, tmpy, tmpz);
				} else {
					sp->error("PositionScripting::process - getPositionVariable, failed to get position variable value (variable does not exist or type mismatch).");
					sp->debug(stringData);
				}
			} else {
				sp->error("PositionScripting::process - getPositionVariable parameter missing, position variable name expected.");
			}
			break;

		case GS_CMD_addPositionVariableToPosition:
			if (stringData != NULL)
			{				
				float tmpx, tmpy, tmpz;
				bool success = sp->getScript()->getGlobalPositionVariableValue(stringData, &tmpx, &tmpy, &tmpz);
				if (success)
				{
					gsd->position += VC3(tmpx, tmpy, tmpz);
				} else {
					sp->error("PositionScripting::process - addPositionVariableToPosition, failed to get position variable value (variable does not exist or type mismatch).");
					sp->debug(stringData);
				}
			} else {
				sp->error("PositionScripting::process - addPositionVariableToPosition parameter missing, position variable name expected.");
			}
			break;

		case GS_CMD_setPositionKeepingHeight:
			{
				VC3 tmp(0,0,0);
				if (gs_coordinate_param(game->gameMap, stringData, &tmp))
				{
					gsd->position.x = tmp.x;
					gsd->position.y = tmp.y;
				} else {
					sp->error("PositionScripting::process - Missing or bad setPositionKeepingHeight parameter.");
				}
			}
			break;

		case GS_CMD_setPositionXToFloat:
			{
				float floatData = *((float *)&intData);
				gsd->position.x = floatData;
			}
			break;

		case GS_CMD_setPositionZToFloat:
			{
				float floatData = *((float *)&intData);
				gsd->position.z = floatData;
			}
			break;

		case GS_CMD_rayTraceToSecondaryPosition:
			{
				VC3 middle_pos = (gsd->secondaryPosition + gsd->position) * 0.5f;
				VC3 dir = gsd->secondaryPosition - gsd->position;
				float length = dir.GetLength();
				dir *= 1.0f / length;

				bool no_units = stringData && strstr(stringData, "no_units");

				// disable collision for units
				if(no_units)
				{
					IUnitListIterator *iter = game->units->getNearbyAllUnits(middle_pos, length * length);
					while (iter->iterateAvailable())
					{
						Unit *u = iter->iterateNext();
						if(u && u->getVisualObject())
						{
							u->getVisualObject()->setCollidable(false);
						}
					}
					delete iter;
				}

				// raycast
				*lastValue = 0;
				if(game && game->getGameScene())
				{
					GameCollisionInfo cinfo;
					game->getGameScene()->rayTrace(gsd->position, dir, length, cinfo, true, false);
					if(cinfo.hit)
					{
						gsd->position = cinfo.position;
						*lastValue = 1;
					}
				}

				// re-enable collision for units
				if(no_units)
				{
					IUnitListIterator *iter = game->units->getNearbyAllUnits(middle_pos, length * length);
					while (iter->iterateAvailable())
					{
						Unit *u = iter->iterateNext();
						if(u && u->getVisualObject())
						{
							u->getVisualObject()->setCollidable(true);
						}
					}
					delete iter;
				}
			}
			break;

		default:
			sp->error("PositionScripting::process - Unknown command.");
			assert(0);
		}
	}
	void VisualEffect::init(IPointableObject *object, IPointableObject *origin,
		const VC3 &position, const VC3 &endPosition, const VC3 &rotation,
		int muzzleFlashBarrelNumber)
	{
		// make sure this is called only once.
		assert(visualObject == NULL);

		// set the object we want to follow...
		if (effectType->getFollow() == VisualEffectType::VISUALEFFECT_FOLLOW_NONE)
		{
			follow = NULL;
		} else {
			if (effectType->getFollow() == VisualEffectType::VISUALEFFECT_FOLLOW_OBJECT)
			{
				follow = object;
			} else {
				follow = origin;
			}
		}

		// visual object...
		visualObject = effectType->getNewVisualObject();

		if (effectType->getType() == VisualEffectType::VISUALEFFECT_TYPE_MUZZLEFLASH)
		{
			// special case: muzzleflash
			if (visualObject != NULL && follow != NULL)
			{
				Muzzleflasher::createMuzzleflash(follow, visualObject, muzzleFlashBarrelNumber);
				visualObject->setInScene(false);
			} else {
				//Logger::getInstance()->error("VisualEffect::init - Null visualObject or follow unit for muzzleflash.");
			}

			this->position = position;
			return;
		}

		if (effectType->getType() == VisualEffectType::VISUALEFFECT_TYPE_EJECT)
		{
			this->position = position;
			return;
		}

		// normal and ray visualeffects...

		visualObject->setCollidable(false);
		visualObject->setInScene(true);

		// set position, rotation and scale based on type and the 
		// given parameters...
		if (effectType->getType() == VisualEffectType::VISUALEFFECT_TYPE_NORMAL)
		{
			this->position = position;
		}
		if (effectType->getType() == VisualEffectType::VISUALEFFECT_TYPE_RAY)
		{
			this->position = (position + endPosition) / 2;
			VC3 diffVec = endPosition - position;
			float raylen = diffVec.GetLength();
			VC3 scale(1,1,raylen);
			visualObject->setScale(scale);
		}
		if (effectType->getType() == VisualEffectType::VISUALEFFECT_TYPE_NORMAL)
		{
			this->position = position;
		}

		visualObject->setPosition(this->position);
		visualObject->setRotation(rotation.x, rotation.y, rotation.z);
	
	}
Exemple #6
0
	bool ReconChecker::isReconAvailableAtPosition(Game *game, int player, const VC3 &position)
	{
		LinkedList *ulist = game->units->getAllUnits();
		LinkedListIterator iter = LinkedListIterator(ulist);

		while (iter.iterateAvailable())		
		{
			Unit *u = (Unit *)iter.iterateNext();
			
			// unit is friendly towards player?
			// (but player does not need to be friendly towards the unit ;)
			if (u->isActive() && !u->isDestroyed()				
				&& !game->isHostile(u->getOwner(), player)
				&& u->getReconValue() > 0
				&& u->getMoveState() != Unit::UNIT_MOVE_STATE_UNCONSCIOUS)
			{
				VC3 rayStartPosition = u->getPosition() + VC3(0, 2.0f, 0);
				VC3 target = position;
				float terrainHeight = game->gameMap->getScaledHeightAt(target.x, target.z);
				if (target.y < terrainHeight + 2.0f) target.y = terrainHeight + 2.0f;
				VC3 dir = target - rayStartPosition;
				float dirLen = dir.GetLength();
				dir.Normalize();

				if (dirLen > u->getUnitType()->getVisionRange() + 2.0f)
				{
					continue;
				}

				// TEMP!
				// ignore all small own units (1.5m)...
				// except the one we're trying to hit
				LinkedList *oul = game->units->getOwnedUnits(u->getOwner());
				LinkedListIterator iter = LinkedListIterator(oul);
				while (iter.iterateAvailable())
				{
					Unit *ou = (Unit *)iter.iterateNext();
					if (ou != u && ou->getUnitType()->getSize() <= 1.5f)
						ou->getVisualObject()->setCollidable(false);
				}

				// disable collision check for this unit
				u->getVisualObject()->setCollidable(false);

				GameCollisionInfo cinfo;
				game->getGameScene()->rayTrace(rayStartPosition, dir, (float)dirLen, cinfo, false, true);

				// collision check back
				u->getVisualObject()->setCollidable(true);				

				// TEMP!
				// restore them all...
				oul = game->units->getOwnedUnits(u->getOwner());
				iter = LinkedListIterator(oul);
				while (iter.iterateAvailable())
				{
					Unit *ou = (Unit *)iter.iterateNext();
					if (ou != u && ou->getUnitType()->getSize() <= 1.5f)
						ou->getVisualObject()->setCollidable(true);
				}

				if (cinfo.hit)
				{
					// 4 meters max dist.
					if (cinfo.range >= dirLen - 4.0f)
					{
						return true;
					}
				} else {
					return true;
				}
			}
		}
		return false;
	}
Exemple #7
0
void UnitFormation::addMovePoint(std::vector<Unit *> *units, 
	const VC3 &scaledMapPos, Unit::MoveType moveType)
{
	// If some units have waypoints set, they are overriden

	// If all units have same amount of waypoints, just add
	//	-- Check if too far away from each other??
	// Just override all move points (and target?)

	int i;
	VC3 avgPos = VC3(0,0,0);
	for (i = 0; i < (int)units->size(); i++)
	{
		Unit *u = (*units)[i];
		avgPos += u->getPosition();
	}
	avgPos /= (float)units->size();

	// positions already taken by a unit
	// (so that others won't try to go there too)
	std::vector<std::pair<int, int> > pointsTaken;

	bool clickedBlocked = data->gameScene->isBlockedAtScaled(
		scaledMapPos.x, scaledMapPos.z, 0);

	IStorm3D_Model *buildingModel = NULL;
	ConnectionChecker *connectionChecker = NULL;
	if (units->size() > 1 || clickedBlocked)
	{
		int px = data->gameMap->scaledToPathfindX(scaledMapPos.x);
		int py = data->gameMap->scaledToPathfindY(scaledMapPos.z);		
		buildingModel = data->gameScene->getBuildingModelAtPathfind(px, py);

		// HACK:
		// ok, did we click blocked and not inside building (building wall maybe)?
		// now check if there is a building nearby..
		if (clickedBlocked && buildingModel == NULL)
		{
			px+=2;
			buildingModel = data->gameScene->getBuildingModelAtPathfind(px, py);

			px-=4;
			if (buildingModel == NULL && data->gameMap->inPathfindBoundaries(px, py))
			{
				buildingModel = data->gameScene->getBuildingModelAtPathfind(px, py);
			}

			px+=2;
			py+=2;
			if (buildingModel == NULL && data->gameMap->inPathfindBoundaries(px, py))
			{
				buildingModel = data->gameScene->getBuildingModelAtPathfind(px, py);
			}

			py-=4;
			if (buildingModel == NULL && data->gameMap->inPathfindBoundaries(px, py))
			{
				buildingModel = data->gameScene->getBuildingModelAtPathfind(px, py);
			}
		}

		// smaller "rooms" inside buildings...
		int connDist = 14;
		if (buildingModel != NULL) connDist = 10;
		connectionChecker = new ConnectionChecker(data->gameScene, px, py, connDist);
	}

	for (i = 0; i < (int)units->size(); i++)
	{
		Unit *u = (*units)[i];

		UnitActor *ua = getUnitActorForUnit(u);
		
    VC3 offset = u->getPosition() - avgPos;
		offset.y = 0;

		float offsetLen = offset.GetLength();
		if (offsetLen > 0.01f)
		{
			offset.Normalize();
		}

		float destX, okDestX;
		float destZ, okDestZ;

		//okDestX = scaledMapPos.x + offset.x;
		//okDestZ = scaledMapPos.z + offset.z;
		okDestX = scaledMapPos.x;
		okDestZ = scaledMapPos.z;

		float minDist = 0.5f;
		float maxDist = 3.0f;

		if (units->size() == 1) 
		{
			minDist = 0;
			maxDist = 1.0f;
		} else {
			minDist += (float)units->size() * 0.2f;
			maxDist += (float)units->size() * 0.2f;
		}

		bool nonBlockedPassed = false;

		// first solve a position that would kinda match current position
		// in formation... (from formation center to some direction)
		for (float distfactor = minDist; distfactor <= maxDist; distfactor += 0.1f)
		{
			if (distfactor > offsetLen)
				break;

			destX = scaledMapPos.x + (offset.x * distfactor);
			destZ = scaledMapPos.z + (offset.z * distfactor);

			if (data->gameMap->isWellInScaledBoundaries(destX, destZ))
			{
				int ox = data->gameMap->scaledToObstacleX(destX);
				int oy = data->gameMap->scaledToObstacleY(destZ);
				//if (data->gameMap->getObstacleHeight(ox, oy) == 0)
				if (!data->gameScene->isBlocked(ox, oy, 0))
				{
					okDestX = destX;
					okDestZ = destZ;
					nonBlockedPassed = true;
				} else {
					if (nonBlockedPassed)
					{
						break;
					}
				}
			}
		}

		// then try to adjust position so that it would be nicely
		// covered from enemy...
		if (units->size() > 1 || clickedBlocked)
		{			
			int ox = data->gameMap->scaledToObstacleX(okDestX);
			int oy = data->gameMap->scaledToObstacleY(okDestZ);

			/*
			if (connectionChecker != NULL)
			{
				delete connectionChecker;
			}
			connectionChecker = new ConnectionChecker(data->gameScene, ox, oy, 8);
			*/

			VC3 pos = u->getPosition();
			int moveDirX = ox - data->gameMap->scaledToObstacleX(pos.x);
			int moveDirY = oy - data->gameMap->scaledToObstacleY(pos.z);
			int coverFromX = ox + moveDirX;
			int coverFromY = oy + moveDirY;

			if (u->targeting.hasTarget() && u->targeting.getTargetUnit() != NULL)
			{
				VC3 targPos = u->targeting.getTargetUnit()->getPosition();
				coverFromX = data->gameMap->scaledToObstacleX(targPos.x);
				coverFromY = data->gameMap->scaledToObstacleY(targPos.z);
			} else {
				if (u->getSeeUnit() != NULL)
				{
					VC3 seePos = u->getSeeUnit()->getPosition();
					coverFromX = data->gameMap->scaledToObstacleX(seePos.x);
					coverFromY = data->gameMap->scaledToObstacleY(seePos.z);
				}
			}

			int tx1 = ox - 8;
			int ty1 = oy - 8;
			int tx2 = ox + 8;
			int ty2 = oy + 8;
			if (tx1 < 0) tx1 = 0;
			if (ty1 < 0) ty1 = 0;
			if (tx2 >= data->gameMap->getObstacleSizeX()) tx2 = data->gameMap->getObstacleSizeX() - 1;
			if (ty2 >= data->gameMap->getObstacleSizeX()) ty2 = data->gameMap->getObstacleSizeY() - 1;
			int preferX = ox;
			int preferY = oy;
			int preferDistFactorSq = 9999*9999;
			for (int ty = ty1; ty <= ty2; ty++)
			{
				for (int tx = tx1; tx <= tx2; tx++)
				{
					int factSq;
					if (CoverFinder::isCoveredFrom(data->gameMap->getCoverMap(), tx, ty, coverFromX, coverFromY))
					{
						factSq = (tx - ox)*(tx - ox) + (ty - oy)*(ty - oy); 
/*
					}
					else if (data->gameMap->getHideMap()->getHiddenessAt(tx, ty) == HideMap::maxHiddeness)
					{
						// hiddeness not quite as important as covered.
						factSq = 2*(tx - ox)*(tx - ox) + 2*(ty - oy)*(ty - oy);
						factSq += 3*3;
*/
					} else {
					  // no hiddeness, no cover, not preferred very much...
						factSq = 4*(tx - ox)*(tx - ox) + 4*(ty - oy)*(ty - oy);
						factSq += 6*6;
					}

					if (factSq < preferDistFactorSq)
					{
						// penalty for points are already taken by another unit
						int pointAmount = pointsTaken.size();
						for (int i = 0; i < pointAmount; i++)
						{
							if (abs(pointsTaken[i].first - tx) <= 2
								&& abs(pointsTaken[i].second - ty) <= 2)
							{
								if (abs(pointsTaken[i].first - tx) <= 1
									&& abs(pointsTaken[i].second - ty) <= 1)
								{
									if (pointsTaken[i].first == tx
										&& pointsTaken[i].second == ty)
										factSq += 6*6;
									else
										factSq += 4*4;
								} else {
									factSq += 2*2;
								}
							}
						}
					}

					// penalty if not in same building area
					if (factSq < preferDistFactorSq)
					{
						if (data->gameScene->getBuildingModelAtPathfind(tx, ty) != buildingModel)
						{
							factSq += 40*40;
						}
					}

					// penalty if not in the same "room"
					if (factSq < preferDistFactorSq)
					{
						if (!connectionChecker->isCenterConnectedTo(tx - ox, ty - oy))
						{
							factSq += 30*30;
						}
					}

					// blocked really gets penalty :)
					if (data->gameScene->isBlockedAtScaled(
						data->gameMap->obstacleToScaledX(tx), 
						data->gameMap->obstacleToScaledY(ty), 0))
					{
						factSq += 100*100;
					}

					if (factSq < preferDistFactorSq)
					{
						preferDistFactorSq = factSq;
						preferX = tx;
						preferY = ty;
					}
				}
			}
			okDestX = data->gameMap->obstacleToScaledX(preferX);
			okDestZ = data->gameMap->obstacleToScaledY(preferY);

			pointsTaken.push_back(std::pair<int, int>(preferX, preferY));
		}

		// now move right next to any obstacle...
		for (int i = 1; i < 10; i++)
		{
			// right?
			{
				float adjustedX = okDestX + ((float)i / 10.0f);
				float adjustedZ = okDestZ;
				int ox = data->gameMap->scaledToObstacleX(adjustedX);
				int oy = data->gameMap->scaledToObstacleY(adjustedZ);
				//if (data->gameMap->getObstacleHeight(ox, oy) != 0)
				if (data->gameScene->isBlocked(ox, oy, 0))
				{
					okDestX = adjustedX - 0.10f;
					break;
				}
			}
			// left?
			{
				float adjustedX = okDestX - ((float)i / 10.0f);
				float adjustedZ = okDestZ;
				int ox = data->gameMap->scaledToObstacleX(adjustedX);
				int oy = data->gameMap->scaledToObstacleY(adjustedZ);
				//if (data->gameMap->getObstacleHeight(ox, oy) != 0)
				if (data->gameScene->isBlocked(ox, oy, 0))
				{
					okDestX = adjustedX + 0.10f;
					break;
				}
			}
			// up?
			{
				float adjustedX = okDestX;
				float adjustedZ = okDestZ - ((float)i / 10.0f);
				int ox = data->gameMap->scaledToObstacleX(adjustedX);
				int oy = data->gameMap->scaledToObstacleY(adjustedZ);
				//if (data->gameMap->getObstacleHeight(ox, oy) != 0)
				if (data->gameScene->isBlocked(ox, oy, 0))
				{
					okDestZ = adjustedZ + 0.10f;
					break;
				}
			}
			// down?
			{
				float adjustedX = okDestX;
				float adjustedZ = okDestZ + ((float)i / 10.0f);
				int ox = data->gameMap->scaledToObstacleX(adjustedX);
				int oy = data->gameMap->scaledToObstacleY(adjustedZ);
				//if (data->gameMap->getObstacleHeight(ox, oy) != 0)
				if (data->gameScene->isBlocked(ox, oy, 0))
				{
					okDestZ = adjustedZ - 0.10f;
					break;
				}
			}
		}

		// now go to destination...		
		ua->setPathTo(u, VC3(okDestX, 0, okDestZ));

		
		// and set unit behaviour modes...
		if (moveType == Unit::MoveTypeNormal)
		{
			u->setStealthing(false);
			u->setSpeed(Unit::UNIT_SPEED_FAST);
			u->setMode(Unit::UNIT_MODE_DEFENSIVE);
		}
		if (moveType == Unit::MoveTypeFast)
		{
			u->setStealthing(false);
			if (u->getRunningValue() > 0)
			{
				u->setSpeed(Unit::UNIT_SPEED_SPRINT);
				u->setMode(Unit::UNIT_MODE_HOLD_FIRE);
			} else {
				// unit incapable of sprinting, just move at normal running speed.
				u->setSpeed(Unit::UNIT_SPEED_FAST);
				u->setMode(Unit::UNIT_MODE_HOLD_FIRE);
			}
		}
		if (moveType == Unit::MoveTypeStealth)
		{
			// stealth armor goes to stealth mode...
			if (u->getStealthValue() > 0)
			{
				u->setSpeed(Unit::UNIT_SPEED_FAST);
				u->setStealthing(true);
				u->setMode(Unit::UNIT_MODE_HOLD_FIRE);
			} else {
				// other just normal movement (no more sneak)
				//u->setSpeed(Unit::UNIT_SPEED_SLOW);
				u->setSpeed(Unit::UNIT_SPEED_FAST);
				u->setStealthing(false);
				u->setMode(Unit::UNIT_MODE_DEFENSIVE);
			}
		}
	}

	if (connectionChecker != NULL)
	{
		delete connectionChecker;
	}
}
void ParticleArea::biasValues(const VC3 &position, VC3 &velocity) const
{
	float wx = position.x;
	float wy = position.z;

	if(gameMap->isWellInScaledBoundaries(wx, wy))
	{
		float height = position.y / gameMap->getScaleHeight();
		int x = gameMap->scaledToObstacleX(wx);
		int y = gameMap->scaledToObstacleY(wy);

		CoverMap *coverMap = gameMap->getCoverMap();
		CoverMap::COVER_DIRECTION dir = coverMap->getNearestCoverDirection(x, y);
		int distance = coverMap->getDistanceToNearestCover(x, y);

		{
			AreaMap *areaMap = gameMap->getAreaMap();
			int ax = x;
			int ay = y;

			if(dir == CoverMap::COVER_DIRECTION_N)
				ay -= distance;
			else if(dir == CoverMap::COVER_DIRECTION_NE)
			{
				int df = int(distance * 0.7071f);
				ax += df;
				ay -= df;
			}
			else if(dir == CoverMap::COVER_DIRECTION_E)
				ax += distance;
			else if(dir == CoverMap::COVER_DIRECTION_SE)
			{
				int df = int(distance * 0.7071f);
				ax += df;
				ay += df;
			}
			else if(dir == CoverMap::COVER_DIRECTION_S)
				ay += distance;
			else if(dir == CoverMap::COVER_DIRECTION_SW)
			{
				int df = int(distance * 0.7071f);
				ax -= df;
				ay += df;
			}
			else if(dir == CoverMap::COVER_DIRECTION_W)
				ax -= distance;
			else if(dir == CoverMap::COVER_DIRECTION_NW)
			{
				int df = int(distance * 0.7071f);
				ax -= df;
				ay -= df;
			}

			if(areaMap->isAreaAnyValue(ax, ay, AREAMASK_OBSTACLE_UNHITTABLE) || gameMap->getObstacleHeight(ax, ay) < 20)
				return;
		}

		if(distance > 0 && distance < 8)
		{
			float blendFactor = 1.f - (distance / 8.f);
			blendFactor *= 0.04f;
			float angle = ((dir - 1) * 45.f) * PI / 180.f;
			VC3 blendVector;
			blendVector.x = -sinf(angle);
			blendVector.z = cosf(angle);

			blendVector *= velocity.GetLength();
			blendVector *= blendFactor;
			velocity *= 1.f - blendFactor;
			velocity += blendVector;
		}
	}
}
	// return true if position ok and decal should be added
	//        false, if position NOT ok and decal should NOT be added
	bool DecalPositionCalculator::calculateDecalPosition(
		game::GameScene *gameScene,
		const VC3 &origin, const VC3 &velocity, 
		DECAL_POSITIONING positioning, int positionRandom,
		VC3 *resultPosition, QUAT *resultRotation)
	{
		assert(positioning != DecalPositionCalculator::DECAL_POSITIONING_INVALID);

		game::GameMap *gameMap = gameScene->getGameMap();

		bool hitWall = false;

		*resultPosition = origin;
		*resultRotation = QUAT((-3.1415926f / 2.0f),0,0);

		// if velocity positioning...
		if (positioning == DecalPositionCalculator::DECAL_POSITIONING_VELOCITY)
		{
			VC3 velocityRandomized;
			if (positionRandom > 0)
			{
				velocityRandomized = velocity * GAME_TICKS_PER_SECOND;

	// TEMP
	//char buf[64];
	//sprintf(buf, "%f,%f,%f", velocity.x, velocity.y, velocity.z);
	//Logger::getInstance()->error(buf);

				// TODO: add positionRandom to velocity _angle_...
				// (or maybe just do a quick hack and add it to xz-coordinates?)
				velocityRandomized.x += float((SystemRandom::getInstance()->nextInt() % (positionRandom * 2 + 1)) - positionRandom) / 100.0f;
				velocityRandomized.z += float((SystemRandom::getInstance()->nextInt() % (positionRandom * 2 + 1)) - positionRandom) / 100.0f;
				
				// add to y too? maybe should add downward only? 
				// NOTE: biased downward
				velocityRandomized.y += float((SystemRandom::getInstance()->nextInt() % (positionRandom * 2 + 1)) - positionRandom) / 100.0f;
				velocityRandomized.y -= float(positionRandom) / 100.0f * 0.5f;

			} else {
				velocityRandomized = velocity;
			}

			velocityRandomized *= 2.0f;

			IStorm3D_Scene *scene = gameScene->getStormScene();

			VC3 dir = velocityRandomized.GetNormalized();			
			VC3 rayOrigin = origin;
			float rayLen = velocityRandomized.GetLength();

			Storm3D_CollisionInfo sceneColl;
			sceneColl.includeTerrainObjects = false;
			scene->RayTrace(rayOrigin, dir, rayLen, sceneColl, true);

			/*
			if (sceneColl.hit)
			{
				VC3 hitNormal = sceneColl.plane_normal;
				// make a "wall" hit if normal-y is not nearly 1
				//if (fabs(hitNormal.y) < 0.8f)
				{
					hitWall = true;

					VC3 x(rand() % 1000 / 999.f, 0, rand() % 1000 / 999.f);
					x.Normalize();
					x -= hitNormal * x.GetDotWith(hitNormal);
					VC3 y = -x.GetCrossWith(hitNormal);

					assert(fabsf(x.GetDotWith(y)) < 0.0001f);
					assert(fabsf(x.GetDotWith(hitNormal)) < 0.0001f);

					MAT tm;
					tm.Set(0, x.x);
					tm.Set(1, x.y);
					tm.Set(2, x.z);
					tm.Set(4, y.x);
					tm.Set(5, y.y);
					tm.Set(6, y.z);
					tm.Set(8, hitNormal.x);
					tm.Set(9, hitNormal.y);
					tm.Set(10, hitNormal.z);

					*resultRotation = tm.GetRotation();
				}
				*resultPosition = sceneColl.position;
			} else {
				*resultPosition += velocityRandomized;
			}
			*/

			// New version
			{
				VC3 hitNormal(0, 1.f, 0);
				if(sceneColl.hit)
				{
					hitNormal = sceneColl.plane_normal;
					*resultPosition = sceneColl.position;
				}
				else
				{
					*resultPosition += velocityRandomized;
					VC2 p2(resultPosition->x, resultPosition->z);
					hitNormal = gameScene->getTerrain()->getFaceNormal(p2);
				}

				{
					if(sceneColl.hit)
						hitWall = true;

		/*
		VC3 y = dir;
		VC3 z = hitNormal;
		y -= hitNormal * y.GetDotWith(hitNormal);
		VC3 x = z.GetCrossWith(y);
		*/

		VC3 x = dir;
		x.y = 0.f;
		x.Normalize();
		x -= hitNormal * x.GetDotWith(hitNormal);
		VC3 y = -x.GetCrossWith(hitNormal);
		VC3 z = hitNormal;

		MAT tm;
		tm.Set(0, x.x);
		tm.Set(1, x.y);
		tm.Set(2, x.z);
		tm.Set(4, y.x);
		tm.Set(5, y.y);
		tm.Set(6, y.z);
		tm.Set(8, z.x);
		tm.Set(9, z.y);
		tm.Set(10, z.z);

		resultRotation->MakeFromAngles(0.f, 0.f, -PI*0.5f);
		*resultRotation = (*resultRotation) * tm.GetRotation();

					/*
					VC3 x(rand() % 1000 / 999.f, 0, rand() % 1000 / 999.f);
					x.Normalize();
					x -= hitNormal * x.GetDotWith(hitNormal);
					VC3 y = -x.GetCrossWith(hitNormal);

					assert(fabsf(x.GetDotWith(y)) < 0.0001f);
					assert(fabsf(x.GetDotWith(hitNormal)) < 0.0001f);

					MAT tm;
					tm.Set(0, x.x);
					tm.Set(1, x.y);
					tm.Set(2, x.z);
					tm.Set(4, y.x);
					tm.Set(5, y.y);
					tm.Set(6, y.z);
					tm.Set(8, hitNormal.x);
					tm.Set(9, hitNormal.y);
					tm.Set(10, hitNormal.z);

					*resultRotation = tm.GetRotation();
					*/
				}
			}


			// TODO: some kind of terrain raytrace maybe...?
			// should collide to walls, etc.
		}

		// if downward positioning...
		if (positioning == DecalPositionCalculator::DECAL_POSITIONING_DOWNWARD)
		{
			if (positionRandom > 0)
			{
				// TODO: add a random xz-offset to result position
				//*resultPosition += randomizedOffset;
				resultPosition->x += float((SystemRandom::getInstance()->nextInt() % (positionRandom * 2 + 1)) - positionRandom) / 100.0f;
				resultPosition->z += float((SystemRandom::getInstance()->nextInt() % (positionRandom * 2 + 1)) - positionRandom) / 100.0f;
			}			

			/*
			// psd
			{
				VC2 p2(resultPosition->x, resultPosition->z);
				VC3 hitNormal = gameScene->getTerrain()->getFaceNormal(p2);

				VC3 x(rand() % 1000 / 999.f, 0, rand() % 1000 / 999.f);
				x.Normalize();
				x -= hitNormal * x.GetDotWith(hitNormal);
				VC3 y = -x.GetCrossWith(hitNormal);

				assert(fabsf(x.GetDotWith(y)) < 0.0001f);
				assert(fabsf(x.GetDotWith(hitNormal)) < 0.0001f);

				MAT tm;
				tm.Set(0, x.x);
				tm.Set(1, x.y);
				tm.Set(2, x.z);
				tm.Set(4, y.x);
				tm.Set(5, y.y);
				tm.Set(6, y.z);
				tm.Set(8, hitNormal.x);
				tm.Set(9, hitNormal.y);
				tm.Set(10, hitNormal.z);

				*resultRotation = tm.GetRotation();
			}
			*/
			VC3 fooNormal;
			calculateDecalRotation(gameScene, *resultPosition, *resultRotation, 0.f, fooNormal);
		}

		// now check that we're still inside map boundaries
		if (!gameMap->isWellInScaledBoundaries(resultPosition->x, resultPosition->z))
		{
			// out of map.
			return false;
		}

		// then fix decal height to ground height
		if (positioning == DecalPositionCalculator::DECAL_POSITIONING_DOWNWARD
			|| positioning == DecalPositionCalculator::DECAL_POSITIONING_VELOCITY)
		{
			if (!hitWall)
			{
				resultPosition->y = gameMap->getScaledHeightAt(resultPosition->x, resultPosition->z);
			}
		}

		// check that not on top of metal grid area...
		if (game::MaterialManager::isMaterialUnderPosition(gameMap, 
			*resultPosition, MATERIAL_METAL_GRATE))
		{
			// on top of grid, no decal here.
			return false;
		}

		// TODO: check that not inside a wall, terrainobject, etc.
		// if so, return false

		return true;
	}