CObject_pointer<CFeature>* FeatureLoaderLoadFeature( string name, float3 pos, int team )
	{
		FeatureDef *def = featureHandler->GetFeatureDef(name);
		CFeature* feature = new CFeature();
		feature->Initialize( pos,def,0, 0, team,"" );
		return new CObject_pointer<CFeature>(feature);
	}
CFeature* CFeatureHandler::CreateWreckage(const float3& pos, const std::string& name,
	float rot, int facing, int iter, int team, int allyteam, bool emitSmoke, std::string fromUnit,
	const float3& speed)
{
	const FeatureDef* fd;
	const std::string* defname = &name;

	int i = iter;
	do {
		if (defname->empty()) return NULL;
		fd = GetFeatureDef(*defname);
		if (!fd) return NULL;
		defname = &(fd->deathFeature);
	}while (--i > 0);

	if (luaRules && !luaRules->AllowFeatureCreation(fd, team, pos))
		return NULL;

	if (!fd->modelname.empty()) {
		if (fd->resurrectable==0 || (iter>1 && fd->resurrectable<0))
			fromUnit = "";

		CFeature* f = new CFeature;
		f->Initialize(pos, fd, (short int) rot, facing, team, allyteam, fromUnit, speed, fd->smokeTime);

		return f;
	}
	return NULL;
}
void CFeatureHandler::TerrainChanged(int x1, int y1, int x2, int y2)
{
	ASSERT_SYNCED_MODE;
	vector<int> quads=qf->GetQuadsRectangle(float3(x1*SQUARE_SIZE,0,y1*SQUARE_SIZE),
	                                        float3(x2*SQUARE_SIZE,0,y2*SQUARE_SIZE));
//	logOutput.Print("Checking feature pos %i",quads.size());

	for(vector<int>::iterator qi=quads.begin();qi!=quads.end();++qi){
		list<CFeature*>::iterator fi;
		list<CFeature*>& features = qf->baseQuads[*qi].features;
		for(fi = features.begin(); fi != features.end(); ++fi) {
			CFeature* feature = *fi;
			float3& fpos = feature->pos;
			if (fpos.y > ground->GetHeight(fpos.x, fpos.z)) {
				SetFeatureUpdateable(feature);

				if (feature->def->floating){
					feature->finalHeight = ground->GetHeight(fpos.x, fpos.z);
				} else {
					feature->finalHeight = ground->GetHeight2(fpos.x, fpos.z);
				}

				feature->CalculateTransform ();
			}
		}
	}
}
Example #4
0
void CFeatureHandler::TerrainChanged(int x1, int y1, int x2, int y2)
{
    std::vector<int> quads = qf->GetQuadsRectangle(float3(x1 * SQUARE_SIZE, 0, y1 * SQUARE_SIZE),
                             float3(x2 * SQUARE_SIZE, 0, y2 * SQUARE_SIZE));

    for (std::vector<int>::iterator qi = quads.begin(); qi != quads.end(); ++qi) {
        std::list<CFeature*>::const_iterator fi;
        const list<CFeature*>& features = qf->GetQuad(*qi).features;

        for (fi = features.begin(); fi != features.end(); ++fi) {
            CFeature* feature = *fi;
            float3& fpos = feature->pos;
            float gh = ground->GetHeight2(fpos.x, fpos.z);
            float wh = gh;
            if(feature->def->floating)
                wh = ground->GetHeight(fpos.x, fpos.z);
            if (fpos.y > wh || fpos.y < gh) {
                SetFeatureUpdateable(feature);

                feature->finalHeight = wh;

                feature->CalculateTransform ();
            }
        }
    }
}
CFeature* CFeatureHandler::CreateWreckage(const float3& pos, const std::string& name,
	float rot, int facing, int iter, int team, int allyteam, bool emitSmoke, std::string fromUnit,
	const float3& speed)
{
	ASSERT_SYNCED_MODE;
	if (name.empty()) {
		return NULL;
	}
	const FeatureDef* fd = GetFeatureDef(name);

	if (!fd) {
		return NULL;
	}

	if (iter > 1) {
		return CreateWreckage(pos, fd->deathFeature, rot, facing, iter - 1, team, allyteam, emitSmoke, "", speed);
	}
	else {
		if (luaRules && !luaRules->AllowFeatureCreation(fd, team, pos)) {
			return NULL;
		}
		if (!fd->modelname.empty()) {
			CFeature* f = SAFE_NEW CFeature;
			f->Initialize(pos, fd, (short int) rot, facing, team, fromUnit, speed);
			// allow area-reclaiming wrecks of all units, including your own (they set allyteam = -1)
			f->allyteam = allyteam;
			if (emitSmoke && f->blocking)
				f->emitSmokeTime = 300;
			return f;
		}
	}
	return NULL;
}
void CFeatureHandler::Update()
{
	SCOPED_TIMER("Feature::Update");

	if ((gs->frameNum & 31) == 0) {
		// let all areareclaimers choose a target with a different id
		bool dontClear = false;
		for (std::list<int>::iterator it = toBeFreedIDs.begin(); it != toBeFreedIDs.end(); ++it) {
			if (CBuilderCAI::IsFeatureBeingReclaimed(*it)) {
				// postpone recycling
				dontClear = true;
				break;
			}
		}
		if (!dontClear)
			freeIDs.splice(freeIDs.end(), toBeFreedIDs, toBeFreedIDs.begin(), toBeFreedIDs.end());
	}

	if(!toBeRemoved.empty()) {

		GML_RECMUTEX_LOCK(feat); // Update
		GML_RECMUTEX_LOCK(quad); // Update

		while (!toBeRemoved.empty()) {
			CFeatureSet::iterator it = activeFeatures.find(toBeRemoved.back());
			toBeRemoved.pop_back();
			if (it != activeFeatures.end()) {
				CFeature* feature = *it;
				toBeFreedIDs.push_back(feature->id);
				activeFeatures.erase(feature);

				if (feature->drawQuad >= 0) {
					DrawQuad* dq = &drawQuads[feature->drawQuad];
					dq->features.erase(feature);
				}

				if (feature->inUpdateQue) {
					updateFeatures.erase(feature);
				}
				fadeFeatures.erase(feature);
				fadeFeaturesS3O.erase(feature);

				delete feature;
			}
		}
	}

	CFeatureSet::iterator fi = updateFeatures.begin();
	while (fi != updateFeatures.end()) {
		CFeature* feature = *fi;
		++fi;

		if (!feature->Update()) {
			// remove it
			feature->inUpdateQue = false;
			updateFeatures.erase(feature);
		}
	}
}
Example #7
0
int CUnitHandler::TestBuildSquare(const float3& pos, const UnitDef* unitdef, CFeature*& feature, int allyteam)
{
	if (pos.x < 0 || pos.x >= gs->mapx * SQUARE_SIZE || pos.z < 0 || pos.z >= gs->mapy * SQUARE_SIZE) {
		return 0;
	}

	int ret = 2;
	int yardxpos = int(pos.x + 4) / SQUARE_SIZE;
	int yardypos = int(pos.z + 4) / SQUARE_SIZE;
	CSolidObject* s;

	if ((s = groundBlockingObjectMap->GroundBlocked(yardypos * gs->mapx + yardxpos))) {
		CFeature* f;
		if ((f = dynamic_cast<CFeature*>(s))) {
			if ((allyteam < 0) || f->IsInLosForAllyTeam(allyteam)) {
				if (!f->def->reclaimable) {
					return 0;
				}
				feature = f;
			}
		} else if (!dynamic_cast<CUnit*>(s) || (allyteam < 0) ||
			(((CUnit*) s)->losStatus[allyteam] & LOS_INLOS)) {
			if (s->immobile) {
				return 0;
			} else {
				ret = 1;
			}
		}
	}

	const float groundheight = ground->GetHeight2(pos.x, pos.z);

	if (!unitdef->floater || groundheight > 0.0f) {
		// if we are capable of floating, only test local
		// height difference if terrain is above sea-level
		const float* heightmap = readmap->GetHeightmap();
		int x = (int) (pos.x / SQUARE_SIZE);
		int z = (int) (pos.z / SQUARE_SIZE);
		float orgh = readmap->orgheightmap[z * (gs->mapx + 1) + x];
		float h = heightmap[z * (gs->mapx + 1) + x];
		float hdif = unitdef->maxHeightDif;

		if (pos.y > orgh + hdif && pos.y > h + hdif) { return 0; }
		if (pos.y < orgh - hdif && pos.y < h - hdif) { return 0; }
	}

	if (!unitdef->floater && groundheight < -unitdef->maxWaterDepth) {
		// ground is deeper than our maxWaterDepth, cannot build here
		return 0;
	}
	if (groundheight > -unitdef->minWaterDepth) {
		// ground is shallower than our minWaterDepth, cannot build here
		return 0;
	}

	return ret;
}
Example #8
0
void CFeatureHandler::LoadFeaturesFromMap(bool onlyCreateDefs)
{
	// add default tree and geo FeatureDefs defined by the map
	const int numFeatureTypes = readmap->GetNumFeatureTypes();

	for (int a = 0; a < numFeatureTypes; ++a) {
		const string& name = StringToLower(readmap->GetFeatureTypeName(a));

		if (GetFeatureDef(name, false) == NULL) {
			if (name.find("treetype") != string::npos) {
				AddFeatureDef(name, CreateDefaultTreeFeatureDef(name));
			}
			else if (name.find("geovent") != string::npos) {
				AddFeatureDef(name, CreateDefaultGeoFeatureDef(name));
			}
			else {
				LOG_L(L_ERROR, "[%s] unknown map feature type \"%s\"",
						__FUNCTION__, name.c_str());
			}
		}
	}

	// add a default geovent FeatureDef if the map did not
	if (GetFeatureDef("geovent", false) == NULL) {
		AddFeatureDef("geovent", CreateDefaultGeoFeatureDef("geovent"));
	}

	if (!onlyCreateDefs) {
		// create map-specified feature instances
		const int numFeatures = readmap->GetNumFeatures();
		MapFeatureInfo* mfi = new MapFeatureInfo[numFeatures];
		readmap->GetFeatureInfo(mfi);

		for (int a = 0; a < numFeatures; ++a) {
			const string& name = StringToLower(readmap->GetFeatureTypeName(mfi[a].featureType));
			map<string, const FeatureDef*>::iterator def = featureDefs.find(name);

			if (def == featureDefs.end()) {
				LOG_L(L_ERROR, "Unknown feature named '%s'", name.c_str());
				continue;
			}

			const float ypos = ground->GetHeightReal(mfi[a].pos.x, mfi[a].pos.z);
			const float3 fpos = float3(mfi[a].pos.x, ypos, mfi[a].pos.z);
			const FeatureDef* fdef = def->second;

			CFeature* f = new CFeature();
			f->Initialize(fpos, fdef, (short int) mfi[a].rotation, 0, -1, -1, NULL);
		}

		delete[] mfi;
	}
}
Example #9
0
std::vector<CSolidObject*> CQuadField::GetSolidsExact(
    const float3& pos,
    const float radius,
    const unsigned int physicalStateBits,
    const unsigned int collisionStateBits
) {
    GML_RECMUTEX_LOCK(qnum); // GetSolidsExact

    const std::vector<int>& quads = GetQuads(pos, radius);
    const int tempNum = gs->tempNum++;

    std::vector<CSolidObject*> solids;
    std::vector<int>::const_iterator qi;

    std::list<CUnit*>::iterator ui;
    std::list<CFeature*>::iterator fi;

    for (qi = quads.begin(); qi != quads.end(); ++qi) {
        for (ui = baseQuads[*qi].units.begin(); ui != baseQuads[*qi].units.end(); ++ui) {
            CUnit* u = *ui;

            if (u->tempNum == tempNum)
                continue;
            if (!u->HasPhysicalStateBit(physicalStateBits))
                continue;
            if (!u->HasCollidableStateBit(collisionStateBits))
                continue;
            if ((pos - u->midPos).SqLength() >= Square(radius + u->radius))
                continue;

            u->tempNum = tempNum;
            solids.push_back(u);
        }

        for (fi = baseQuads[*qi].features.begin(); fi != baseQuads[*qi].features.end(); ++fi) {
            CFeature* f = *fi;

            if (f->tempNum == tempNum)
                continue;
            if (!f->HasPhysicalStateBit(physicalStateBits))
                continue;
            if (!f->HasCollidableStateBit(collisionStateBits))
                continue;
            if ((pos - f->midPos).SqLength() >= Square(radius + f->radius))
                continue;

            f->tempNum = tempNum;
            solids.push_back(f);
        }
    }

    return solids;
}
Example #10
0
float CGameHelper::GuiTraceRayFeature(const float3& start, const float3& dir, float length, CFeature*& feature)
{
    float nearHit = length;

    GML_RECMUTEX_LOCK(quad); // GuiTraceRayFeature

    std::vector<int> quads = qf->GetQuadsOnRay(start, dir, length);
    std::vector<int>::iterator qi;

    for (qi = quads.begin(); qi != quads.end(); ++qi) {
        const CQuadField::Quad& quad = qf->GetQuad(*qi);
        std::list<CFeature*>::const_iterator ui;

        // NOTE: switch this to custom volumes fully?
        // (not used for any LOF checks, maybe wasteful)
        for (ui = quad.features.begin(); ui != quad.features.end(); ++ui) {
            CFeature* f = *ui;

            if (!gu->spectatingFullView && !f->IsInLosForAllyTeam(gu->myAllyTeam)) {
                continue;
            }
            if (f->noSelect) {
                continue;
            }

            CollisionVolume* cv = f->collisionVolume;
            const float3& midPosOffset = cv? cv->GetOffsets(): ZeroVector;
            const float3 dif = (f->midPos + midPosOffset) - start;
            float closeLength = dif.dot(dir);

            if (closeLength < 0)
                continue;
            if (closeLength > nearHit)
                continue;

            float3 closeVect = dif - dir * closeLength;
            if (closeVect.SqLength() < f->sqRadius) {
                nearHit = closeLength;
                feature = f;
            }
        }
    }

    return nearHit;
}
void CFeatureHandler::LoadSaveFeatures(CLoadSaveInterface* file, bool loading)
{
	if(loading)
		freeIDs.clear();
	for(int a=0;a<MAX_FEATURES;++a){
		bool exists=!!features[a];
		file->lsBool(exists);
		if(exists){
			if(loading){
				overrideId=a;
				float3 pos;
				file->lsFloat3(pos);

				string def;
				file->lsString(def);
				if(featureDefs.find(def)==featureDefs.end())
					GetFeatureDef(def);

				short rotation;
				file->lsShort(rotation);
				string fromUnit;
				file->lsString(fromUnit);
				CFeature* f = SAFE_NEW CFeature;
				f->Initialize (pos,featureDefs[def],rotation,0,-1,fromUnit);
			} else {
				file->lsFloat3(features[a]->pos);
				file->lsString(features[a]->def->myName);
				file->lsShort(features[a]->heading);
				file->lsString(features[a]->createdFromUnit);
			}
			CFeature* f=features[a];
			file->lsFloat(f->health);
			file->lsFloat(f->reclaimLeft);
			file->lsInt(f->allyteam);
		} else {
			if(loading)
				freeIDs.push_back(a);
		}
	}
	overrideId=-1;
}
void CFeatureHandler::Update()
{
	ASSERT_SYNCED_MODE;
START_TIME_PROFILE
	while (!toBeRemoved.empty()) {
		CFeature* feature = features[toBeRemoved.back()];
		toBeRemoved.pop_back();
		if (feature) {
			freeIDs.push_back(feature->id);
			features[feature->id] = 0;

			if (feature->drawQuad >= 0) {
				DrawQuad* dq = &drawQuads[feature->drawQuad];
				dq->features.erase(feature);
			}

			if (feature->inUpdateQue) {
				updateFeatures.erase(feature->id);
			}

			delete feature;
		}
	}

	SPRING_HASH_SET<int>::iterator fi=updateFeatures.begin();
	while(fi!= updateFeatures.end()){
		CFeature* feature = features[*fi];

		const bool remove = !feature->Update();
		if (remove) {
			feature->inUpdateQue = false;
			updateFeatures.erase(fi++);
		} else {
			++fi;
		}
	}
END_TIME_PROFILE("Feature::Update");
}
Example #13
0
// called by {CRifle, CBeamLaser, CLightningCannon}::Fire(), CWeapon::HaveFreeLineOfFire(), and Skirmish AIs
float TraceRay(
	const float3& start,
	const float3& dir,
	float length,
	int avoidFlags,
	const CUnit* owner,
	CUnit*& hitUnit,
	CFeature*& hitFeature,
	CollisionQuery* hitColQuery
) {
	const bool ignoreEnemies  = ((avoidFlags & Collision::NOENEMIES   ) != 0);
	const bool ignoreAllies   = ((avoidFlags & Collision::NOFRIENDLIES) != 0);
	const bool ignoreFeatures = ((avoidFlags & Collision::NOFEATURES  ) != 0);
	const bool ignoreNeutrals = ((avoidFlags & Collision::NONEUTRALS  ) != 0);
	const bool ignoreGround   = ((avoidFlags & Collision::NOGROUND    ) != 0);

	const bool ignoreUnits = ignoreEnemies && ignoreAllies && ignoreNeutrals;

	hitFeature = NULL;
	hitUnit = NULL;

	if (dir == ZeroVector)
		return -1.0f;

	if (!ignoreFeatures || !ignoreUnits) {
		GML_RECMUTEX_LOCK(quad); // TraceRay

		CollisionQuery cq;

		int* begQuad = NULL;
		int* endQuad = NULL;

		quadField->GetQuadsOnRay(start, dir, length, begQuad, endQuad);

		// locally point somewhere non-NULL; we cannot pass hitColQuery
		// to DetectHit directly because each call resets it internally
		if (hitColQuery == NULL)
			hitColQuery = &cq;

		// feature intersection
		if (!ignoreFeatures) {
			for (int* quadPtr = begQuad; quadPtr != endQuad; ++quadPtr) {
				const CQuadField::Quad& quad = quadField->GetQuad(*quadPtr);

				for (std::list<CFeature*>::const_iterator ui = quad.features.begin(); ui != quad.features.end(); ++ui) {
					CFeature* f = *ui;

					// NOTE:
					//     if f is non-blocking, ProjectileHandler will not test
					//     for collisions with projectiles so we can skip it here
					if (!f->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS))
						continue;

					if (CCollisionHandler::DetectHit(f, start, start + dir * length, &cq, true)) {
						const float len = cq.GetHitPosDist(start, dir);

						// we want the closest feature (intersection point) on the ray
						if (len < length) {
							length = len;
							hitFeature = f;
							*hitColQuery = cq;
						}
					}
				}
			}
		}

		// unit intersection
		if (!ignoreUnits) {
			for (int* quadPtr = begQuad; quadPtr != endQuad; ++quadPtr) {
				const CQuadField::Quad& quad = quadField->GetQuad(*quadPtr);

				for (std::list<CUnit*>::const_iterator ui = quad.units.begin(); ui != quad.units.end(); ++ui) {
					CUnit* u = *ui;

					if (u == owner)
						continue;
					if (!u->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS))
						continue;
					if (ignoreAllies && u->allyteam == owner->allyteam)
						continue;
					if (ignoreNeutrals && u->IsNeutral())
						continue;
					if (ignoreEnemies && u->allyteam != owner->allyteam)
						continue;

					if (CCollisionHandler::DetectHit(u, start, start + dir * length, &cq, true)) {
						const float len = cq.GetHitPosDist(start, dir);

						// we want the closest unit (intersection point) on the ray
						if (len < length) {
							length = len;
							hitUnit = u;
							*hitColQuery = cq;
						}
					}
				}
			}
			if (hitUnit)
				hitFeature = NULL;
		}
	}

	if (!ignoreGround) {
		// ground intersection
		const float groundLength = ground->LineGroundCol(start, start + dir * length);

		if (length > groundLength && groundLength > 0.0f) {
			length = groundLength;
			hitUnit = NULL;
			hitFeature = NULL;
		}
	}

	return length;
}
Example #14
0
int CAICallback::HandleCommand(int commandId, void* data)
{
	switch (commandId) {
		case AIHCQuerySubVersionId: {
			return 1; // current version of Handle Command interface
		} break;
		case AIHCAddMapPointId: {
			const AIHCAddMapPoint* cmdData = static_cast<AIHCAddMapPoint*>(data);
			/*
			   TODO: gu->myPlayerNum makes the command to look like as it comes from the local player,
			   "team" should be used (but needs some major changes in other engine parts)
			*/
			clientNet->Send(CBaseNetProtocol::Get().SendMapDrawPoint(gu->myPlayerNum, (short)cmdData->pos.x, (short)cmdData->pos.z, std::string(cmdData->label), false));
			return 1;
		} break;
		case AIHCAddMapLineId: {
			const AIHCAddMapLine* cmdData = static_cast<AIHCAddMapLine*>(data);
			// see TODO above
			clientNet->Send(CBaseNetProtocol::Get().SendMapDrawLine(gu->myPlayerNum, (short)cmdData->posfrom.x, (short)cmdData->posfrom.z, (short)cmdData->posto.x, (short)cmdData->posto.z, false));
			return 1;
		} break;
		case AIHCRemoveMapPointId: {
			const AIHCRemoveMapPoint* cmdData = static_cast<AIHCRemoveMapPoint*>(data);
			// see TODO above
			clientNet->Send(CBaseNetProtocol::Get().SendMapErase(gu->myPlayerNum, (short)cmdData->pos.x, (short)cmdData->pos.z));
			return 1;
		} break;
		case AIHCSendStartPosId:
		case AIHCGetUnitDefByIdId:
		case AIHCGetWeaponDefByIdId:
		case AIHCGetFeatureDefByIdId:
		case AIHCGetDataDirId:
		{
			// NOTE: these commands should never arrive, handled in SSkirmishAICallbackImpl
			assert(false);
			return 0;
		} break;

		case AIHCTraceRayId: {
			AIHCTraceRay* cmdData = static_cast<AIHCTraceRay*>(data);

			if (CHECK_UNITID(cmdData->srcUID)) {
				const CUnit* srcUnit = unitHandler->GetUnit(cmdData->srcUID);

				if (srcUnit != nullptr) {
					CUnit* hitUnit = nullptr;
					CFeature* hitFeature = nullptr;

					//FIXME add COLLISION_NOFEATURE?
					const float realLen = TraceRay::TraceRay(cmdData->rayPos, cmdData->rayDir, cmdData->rayLen, cmdData->flags, srcUnit, hitUnit, hitFeature);

					if (hitUnit != nullptr) {
						myAllyTeamId = teamHandler->AllyTeam(team);

						if (unit_IsInLos(hitUnit)) {
							cmdData->rayLen = realLen;
							cmdData->hitUID = hitUnit->id;
						}
					}
				}
			}

			return 1;
		} break;

		case AIHCFeatureTraceRayId: {
			AIHCFeatureTraceRay* cmdData = static_cast<AIHCFeatureTraceRay*>(data);

			if (CHECK_UNITID(cmdData->srcUID)) {
				const CUnit* srcUnit = unitHandler->GetUnit(cmdData->srcUID);

				if (srcUnit != nullptr) {
					CUnit* hitUnit = nullptr;
					CFeature* hitFeature = nullptr;

					//FIXME add COLLISION_NOENEMIES || COLLISION_NOFRIENDLIES || COLLISION_NONEUTRALS?
					const float realLen = TraceRay::TraceRay(cmdData->rayPos, cmdData->rayDir, cmdData->rayLen, cmdData->flags, srcUnit, hitUnit, hitFeature);

					if (hitFeature != nullptr) {
						if (hitFeature->IsInLosForAllyTeam(teamHandler->AllyTeam(team))) {
							cmdData->rayLen = realLen;
							cmdData->hitFID = hitFeature->id;
						}
					}
				}
			}

			return 1;
		} break;

		case AIHCPauseId: {
			AIHCPause* cmdData = static_cast<AIHCPause*>(data);

			clientNet->Send(CBaseNetProtocol::Get().SendPause(gu->myPlayerNum, cmdData->enable));
			LOG("Skirmish AI controlling team %i paused the game, reason: %s",
					team,
					cmdData->reason != NULL ? cmdData->reason : "UNSPECIFIED");

			return 1;
		} break;

		case AIHCDebugDrawId: {
			AIHCDebugDraw* cmdData = static_cast<AIHCDebugDraw*>(data);

			switch (cmdData->cmdMode) {
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_ADD_GRAPH_POINT: {
					debugDrawerAI->AddGraphPoint(this->team, cmdData->lineId, cmdData->x, cmdData->y);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_DEL_GRAPH_POINTS: {
					debugDrawerAI->DelGraphPoints(this->team, cmdData->lineId, cmdData->numPoints);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_POS: {
					debugDrawerAI->SetGraphPos(this->team, cmdData->x, cmdData->y);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_SIZE: {
					debugDrawerAI->SetGraphSize(this->team, cmdData->w, cmdData->h);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_LINE_COLOR: {
					debugDrawerAI->SetGraphLineColor(this->team, cmdData->lineId, cmdData->color);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_LINE_LABEL: {
					debugDrawerAI->SetGraphLineLabel(this->team, cmdData->lineId, cmdData->label);
				} break;

				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_ADD_OVERLAY_TEXTURE: {
					cmdData->texHandle = debugDrawerAI->AddOverlayTexture(
						this->team,
						cmdData->texData,
						int(cmdData->w),   // interpret as absolute width
						int(cmdData->h)    // interpret as absolute height
					);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_UPDATE_OVERLAY_TEXTURE: {
					debugDrawerAI->UpdateOverlayTexture(
						this->team,
						cmdData->texHandle,
						cmdData->texData,
						int(cmdData->x),    // interpret as absolute pixel col
						int(cmdData->y),    // interpret as absolute pixel row
						int(cmdData->w),    // interpret as absolute width
						int(cmdData->h)     // interpret as absolute height
					);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_DEL_OVERLAY_TEXTURE: {
					debugDrawerAI->DelOverlayTexture(this->team, cmdData->texHandle);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_OVERLAY_TEXTURE_POS: {
					debugDrawerAI->SetOverlayTexturePos(this->team, cmdData->texHandle, cmdData->x, cmdData->y);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_OVERLAY_TEXTURE_SIZE: {
					debugDrawerAI->SetOverlayTextureSize(this->team, cmdData->texHandle, cmdData->w, cmdData->h);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_OVERLAY_TEXTURE_LABEL: {
					debugDrawerAI->SetOverlayTextureLabel(this->team, cmdData->texHandle, cmdData->label);
				} break;

				default: {
				} break;
			}

			return 1;
		} break;

		default: {
			return 0;
		}
	}
}
Example #15
0
void CFeatureHandler::Update()
{
	SCOPED_TIMER("FeatureHandler::Update");

	if ((gs->frameNum & 31) == 0) {
		// let all areareclaimers choose a target with a different id
		bool dontClear = false;
		for (list<int>::iterator it = toBeFreedIDs.begin(); it != toBeFreedIDs.end(); ++it) {
			if (CBuilderCAI::IsFeatureBeingReclaimed(*it)) {
				// postpone recycling
				dontClear = true;
				break;
			}
		}
		if (!dontClear)
			freeIDs.splice(freeIDs.end(), toBeFreedIDs, toBeFreedIDs.begin(), toBeFreedIDs.end());
	}

	{
		GML_STDMUTEX_LOCK(rfeat); // Update

		if(!toBeRemoved.empty()) {

			GML_RECMUTEX_LOCK(obj); // Update

			eventHandler.DeleteSyncedObjects();

			GML_RECMUTEX_LOCK(feat); // Update

			eventHandler.DeleteSyncedFeatures();

			GML_RECMUTEX_LOCK(quad); // Update

			while (!toBeRemoved.empty()) {
				CFeature* feature = GetFeature(toBeRemoved.back());
				toBeRemoved.pop_back();
				if (feature) {
					int delID = feature->id;
					toBeFreedIDs.push_back(delID);
					activeFeatures.erase(feature);
					features[delID] = 0;

					if (feature->inUpdateQue) {
						updateFeatures.erase(feature);
					}
					CSolidObject::SetDeletingRefID(delID + uh->MaxUnits());
					delete feature;
					CSolidObject::SetDeletingRefID(-1);
				}
			}
		}

		eventHandler.UpdateFeatures();
	}

	CFeatureSet::iterator fi = updateFeatures.begin();
	while (fi != updateFeatures.end()) {
		CFeature* feature = *fi;
		++fi;

		if (!feature->Update()) {
			// remove it
			feature->inUpdateQue = false;
			updateFeatures.erase(feature);
		}
	}
}
Example #16
0
void CFeatureQuadDrawer::DrawQuad(int x, int y)
{
	CFeatureDrawer::DrawQuad* dq = &(*drawQuads)[y * drawQuadsX + x];

	for (set<CFeature*>::iterator fi = dq->features.begin(); fi != dq->features.end(); ++fi) {
		CFeature* f = (*fi);
		const FeatureDef* def = f->def;

		if (def->drawType == DRAWTYPE_MODEL
				&& (gu->spectatingFullView || f->IsInLosForAllyTeam(gu->myAllyTeam))) {
			if (drawReflection) {
				float3 zeroPos;
				if (f->midPos.y < 0) {
					zeroPos = f->midPos;
				} else {
					float dif = f->midPos.y - camera->pos.y;
					zeroPos = camera->pos * (f->midPos.y / dif) + f->midPos * (-camera->pos.y / dif);
				}
				if (ground->GetApproximateHeight(zeroPos.x, zeroPos.z) > f->radius) {
					continue;
				}
			}
			if (drawRefraction) {
				if (f->pos.y > 0)
					continue;
			}

			float sqDist = (f->pos - camera->pos).SqLength2D();
			float farLength = f->sqRadius * unitDrawDist * unitDrawDist;

			if(statFeatures && (f->reclaimLeft < 1.0f || f->resurrectProgress > 0.0f))
				statFeatures->push_back(f);

			if (sqDist < farLength) {
				float sqFadeDistE;
				float sqFadeDistB;
				if(farLength < sqFadeDistEnd) {
					sqFadeDistE = farLength;
					sqFadeDistB = farLength * 0.75f * 0.75f;
				} else {
					sqFadeDistE = sqFadeDistEnd;
					sqFadeDistB = sqFadeDistBegin;
				}
				if(sqDist < sqFadeDistB) {
					f->tempalpha = 0.99f;
					if (f->model->type == MODELTYPE_3DO) {
						unitDrawer->DrawFeatureStatic(f);
					} else {
						unitDrawer->QueS3ODraw(f, f->model->textureType);
					}
				} else if(sqDist < sqFadeDistE) {
					f->tempalpha = 1.0f - (sqDist - sqFadeDistB) / (sqFadeDistE - sqFadeDistB);
					if (f->model->type == MODELTYPE_3DO) {
						featureDrawer->fadeFeatures.insert(f);
					} else {
						featureDrawer->fadeFeaturesS3O.insert(f);
					}
				}
			} else {
				if (farFeatures)
					farFeatures->push_back(f);
			}
		}
	}
}
Example #17
0
CGameHelper::BuildSquareStatus CGameHelper::TestBuildSquare(const float3& pos, const float buildHeight, const UnitDef* unitdef, const MoveDef* moveDef, CFeature*& feature, int allyteam, bool synced)
{
	if (!pos.IsInMap()) {
		return BUILDSQUARE_BLOCKED;
	}

	BuildSquareStatus ret = BUILDSQUARE_OPEN;
	const int yardxpos = int(pos.x + 4) / SQUARE_SIZE;
	const int yardypos = int(pos.z + 4) / SQUARE_SIZE;
	CSolidObject* s = groundBlockingObjectMap->GroundBlocked(yardxpos, yardypos);

	if (s != NULL) {
		CFeature* f = dynamic_cast<CFeature*>(s);
		if (f != NULL) {
			if ((allyteam < 0) || f->IsInLosForAllyTeam(allyteam)) {
				if (!f->def->reclaimable) {
					ret = BUILDSQUARE_BLOCKED;
				} else {
					ret = BUILDSQUARE_RECLAIMABLE;
					feature = f;
				}
			}
		} else if (!dynamic_cast<CUnit*>(s) || (allyteam < 0) ||
				(static_cast<CUnit*>(s)->losStatus[allyteam] & LOS_INLOS)) {
			if (s->immobile) {
				ret = BUILDSQUARE_BLOCKED;
			} else {
				ret = BUILDSQUARE_OCCUPIED;
			}
		}

		if ((ret == BUILDSQUARE_BLOCKED) || (ret == BUILDSQUARE_OCCUPIED)) {
			if (CMoveMath::IsNonBlocking(s, moveDef, pos, buildHeight)) {
				ret = BUILDSQUARE_OPEN;
			}
		}

		if (ret == BUILDSQUARE_BLOCKED) {
			return ret;
		}
	}

	const float groundHeight = ground->GetHeightReal(pos.x, pos.z, synced);

	if (!unitdef->floatOnWater || groundHeight > 0.0f) {
		// if we are capable of floating, only test local
		// height difference IF terrain is above sea-level
		const float* orgHeightMap = readmap->GetOriginalHeightMapSynced();
		const float* curHeightMap = readmap->GetCornerHeightMapSynced();

		#ifdef USE_UNSYNCED_HEIGHTMAP
		if (!synced) {
			orgHeightMap = readmap->GetCornerHeightMapUnsynced();
			curHeightMap = readmap->GetCornerHeightMapUnsynced();
		}
		#endif

		const int sqx = pos.x / SQUARE_SIZE;
		const int sqz = pos.z / SQUARE_SIZE;
		const float orgHgt = orgHeightMap[sqz * gs->mapxp1 + sqx];
		const float curHgt = curHeightMap[sqz * gs->mapxp1 + sqx];
		const float difHgt = unitdef->maxHeightDif;

		if (pos.y > std::max(orgHgt + difHgt, curHgt + difHgt)) { return BUILDSQUARE_BLOCKED; }
		if (pos.y < std::min(orgHgt - difHgt, curHgt - difHgt)) { return BUILDSQUARE_BLOCKED; }
	}

	if (!unitdef->IsAllowedTerrainHeight(moveDef, groundHeight))
		ret = BUILDSQUARE_BLOCKED;

	return ret;
}
Example #18
0
float GuiTraceRay(
	const float3& start,
	const float3& dir,
	float length,
	bool useRadar,
	const CUnit* exclude,
	CUnit*& hitUnit,
	CFeature*& hitFeature,
	bool groundOnly
) {
	hitUnit = NULL;
	hitFeature = NULL;

	if (dir == ZeroVector)
		return -1.0f;

	// ground intersection
	const float origlength = length;
	const float groundLength = ground->LineGroundCol(start, start + dir * origlength, false);
	float length2 = length;

	if (groundOnly)
		return groundLength;

	GML_RECMUTEX_LOCK(quad); // GuiTraceRay

	int* begQuad = NULL;
	int* endQuad = NULL;
	bool hitFactory = false;
	CollisionQuery cq;

	qf->GetQuadsOnRay(start, dir, length, begQuad, endQuad);

	std::list<CUnit*>::const_iterator ui;
	std::list<CFeature*>::const_iterator fi;

	for (int* quadPtr = begQuad; quadPtr != endQuad; ++quadPtr) {
		const CQuadField::Quad& quad = qf->GetQuad(*quadPtr);

		// Unit Intersection
		for (ui = quad.units.begin(); ui != quad.units.end(); ++ui) {
			CUnit* unit = *ui;

			if (unit == exclude)
				continue;

			if ((unit->allyteam == gu->myAllyTeam) || gu->spectatingFullView ||
				(unit->losStatus[gu->myAllyTeam] & (LOS_INLOS | LOS_CONTRADAR)) ||
				(useRadar && radarhandler->InRadar(unit, gu->myAllyTeam)))
			{

				CollisionVolume cv(unit->collisionVolume);

				if (unit->isIcon) {
					// for iconified units, just pretend the collision
					// volume is a sphere of radius <unit->IconRadius>
					// randomize it (by scaling up to a factor of 4) so
					// unit size cannot be determined by hovering mouse
					// over radar blips
					cv.Init(unit->iconRadius * (1.0f + (gu->usRandFloat() * 3.0f)));
				}

				if (CCollisionHandler::MouseHit(unit, start, start + dir * origlength, &cv, &cq)) {
					// get the distance to the ray-volume ingress point
					const float3& ingressPos = (cq.b0)? cq.p0 : cq.p1;
					const float3&  egressPos = (cq.b1)? cq.p1 : cq.p0;
					const float ingressDist  = (ingressPos - start).dot(dir); // same as (intPos  - start).Length()
					const float  egressDist  = ( egressPos - start).dot(dir); // same as (intPos2 - start).Length()
					const bool isFactory = unit->unitDef->IsFactoryUnit();

					// give units in a factory higher priority than the factory itself
					if (!hitUnit ||
						(isFactory && ((hitFactory && ingressDist < length) || (!hitFactory && egressDist < length))) ||
						(!isFactory && ((hitFactory && ingressDist < length2) || (!hitFactory && ingressDist < length)))) {
							hitFactory = isFactory;
							length = ingressDist;
							length2 = egressDist;
							hitUnit = unit;
							hitFeature = NULL;
					}
				}
			}
		}

		// Feature Intersection
		// NOTE: switch this to custom volumes fully?
		// (not used for any LOF checks, maybe wasteful)
		for (fi = quad.features.begin(); fi != quad.features.end(); ++fi) {
			CFeature* f = *fi;

			// FIXME add useradar?
			if (!gu->spectatingFullView && !f->IsInLosForAllyTeam(gu->myAllyTeam))
				continue;
			if (f->noSelect)
				continue;

			if (CCollisionHandler::DetectHit(f, start, start + dir * origlength, &cq, true)) {
				const float3& ingressPos = (cq.b0)? cq.p0 : cq.p1;
				const float ingressDist = (ingressPos - start).dot(dir); // same as (intPos - start).Length()

				// we want the closest feature (intersection point) on the ray
				// give features in a factory (?) higher priority than the factory itself
				if (!hitUnit ||
					((hitFactory && ingressDist < length2) || (!hitFactory && ingressDist < length))) {
					hitFactory = false;
					length = ingressDist;
					hitFeature = f;
					hitUnit = NULL;
				}
			}
		}
	}

	if ((groundLength > 0.0f) && ((groundLength + 200.0f) < length)) {
		length     = groundLength;
		hitUnit    = NULL;
		hitFeature = NULL;
	}

	return length;
}
void CFeatureHandler::LoadFeaturesFromMap(CFileHandler* file,bool onlyCreateDefs)
{
	file->Seek(readmap->header.featurePtr);
	MapFeatureHeader fh;
	READ_MAPFEATUREHEADER(fh, file);
	if(file->Eof()){
		info->AddLine("No features in map file?");
		return;
	}
	string* mapids=new string[fh.numFeatureType];

	for(int a=0;a<fh.numFeatureType;++a){
		char c;
		file->Read(&c,1);
		while(c){
			mapids[a]+=c;
			file->Read(&c,1);
		}
		string name=mapids[a];
		if(name.find("TreeType")!=string::npos){
			FeatureDef* fd=new FeatureDef;
			fd->blocking=1;
			fd->burnable=true;
			fd->destructable=1;
			fd->drawType=DRAWTYPE_TREE;
			fd->modelType=atoi(name.substr(8).c_str());
			fd->energy=250;
			fd->metal=0;
			fd->maxHealth=5;
			fd->radius=20;
			fd->xsize=2;
			fd->ysize=2;
			fd->myName=name;
			fd->mass=20;
			featureDefs[name]=fd;
		} else if(name.find("GeoVent")!=string::npos){
			FeatureDef* fd=new FeatureDef;
			fd->blocking=0;
			fd->burnable=0;
			fd->destructable=0;
			fd->geoThermal=1;
			fd->drawType=DRAWTYPE_NONE;	//geos are drawn into the ground texture and emit smoke to be visible
			fd->modelType=0;
			fd->energy=0;
			fd->metal=0;
			fd->maxHealth=0;
			fd->radius=0;
			fd->xsize=0;
			fd->ysize=0;
			fd->myName=name;
			fd->mass=100000;
			featureDefs[name]=fd;
		} else if(wreckParser.SectionExist(name)){
			GetFeatureDef(name);
		} else {
			info->AddLine("Unknown feature type %s",name.c_str());
		}
	}

	if(!onlyCreateDefs){
		for(int a=0;a<fh.numFeatures;++a){
			MapFeatureStruct ffs;
			READ_MAPFEATURESTRUCT(ffs, file);

			string name=mapids[ffs.featureType];

			ffs.ypos=ground->GetHeight2(ffs.xpos,ffs.zpos);
			CFeature *f = new CFeature;
			f->Initialize (float3(ffs.xpos,ffs.ypos,ffs.zpos),featureDefs[name],(short int)ffs.rotation,-1,"");
		}
	}

	delete[] mapids;
}
Example #20
0
float GuiTraceRay(const float3 &start, const float3 &dir, float length, bool useRadar, const CUnit* exclude, CUnit*& hitUnit, CFeature*& hitFeature)
{
	hitUnit = NULL;
	hitFeature = NULL;

	if (dir == ZeroVector) {
		return -1.0f;
	}

	bool hover_factory = false;
	CollisionQuery cq;

	{
		GML_RECMUTEX_LOCK(quad); //! GuiTraceRay

		const vector<int> &quads = qf->GetQuadsOnRay(start, dir, length);
		std::list<CUnit*>::const_iterator ui;
		std::list<CFeature*>::const_iterator fi;

		for (vector<int>::const_iterator qi = quads.begin(); qi != quads.end(); ++qi) {
			const CQuadField::Quad& quad = qf->GetQuad(*qi);

			//! Unit Intersection
			for (ui = quad.units.begin(); ui != quad.units.end(); ++ui) {
				CUnit* unit = *ui;
				if (unit == exclude) {
					continue;
				}

				if ((unit->allyteam == gu->myAllyTeam) || gu->spectatingFullView ||
					(unit->losStatus[gu->myAllyTeam] & (LOS_INLOS | LOS_CONTRADAR)) ||
					(useRadar && radarhandler->InRadar(unit, gu->myAllyTeam)))
				{

					CollisionVolume cv(unit->collisionVolume);

					if (unit->isIcon) {
						//! for iconified units, just pretend the collision
						//! volume is a sphere of radius <unit->IconRadius>
						cv.Init(unit->iconRadius);
					}

					if (CCollisionHandler::MouseHit(unit, start, start + dir * length, &cv, &cq)) {
						//! get the distance to the ray-volume ingress point
						const float3& intPos = (cq.b0)? cq.p0 : cq.p1;
						const float len = (intPos - start).dot(dir); //! same as (intPos - start).Length()
						const bool isfactory = dynamic_cast<CFactory*>(unit);

						if (len < length) {
							if (!isfactory || !hitUnit || hover_factory) {
								hover_factory = isfactory;
								length = len;
								hitUnit = unit;
								hitFeature = NULL;
							}
						} else if (!isfactory && hover_factory) { //FIXME still check if the unit is BEHIND (and not IN) the factory!
							//! give an unit in a factory a higher priority than the factory itself
							hover_factory = isfactory;
							length = len;
							hitUnit = unit;
							hitFeature = NULL;
						}
					}
				}
			}

			//! Feature Intersection
			// NOTE: switch this to custom volumes fully?
			// (not used for any LOF checks, maybe wasteful)
			for (fi = quad.features.begin(); fi != quad.features.end(); ++fi) {
				CFeature* f = *fi;

				if (!f->collisionVolume) {
					continue;
				}
				//FIXME add useradar?
				if (!gu->spectatingFullView && !f->IsInLosForAllyTeam(gu->myAllyTeam)) {
					continue;
				}
				if (f->noSelect) {
					continue;
				}

				if (CCollisionHandler::Intersect(f, start, start + dir * length, &cq)) {
					const float3& intPos = (cq.b0)? cq.p0 : cq.p1;
					const float len = (intPos - start).dot(dir); //! same as (intPos - start).Length()

					//! we want the closest feature (intersection point) on the ray
					if (len < length) {
						hover_factory = false;
						length = len;
						hitFeature = f;
						hitUnit = NULL;
					} else if (hover_factory) { //FIXME still check if the unit is BEHIND (and not IN) the factory!
						//! give features in a factory a higher priority than the factory itself
						hover_factory = false;
						length = len;
						hitFeature = f;
						hitUnit = NULL;
					}
				}
			}
		}
	}

	//! ground intersection
	float groundLen = ground->LineGroundCol(start, start + dir * length);
	if (groundLen > 0.0f) {
		if (groundLen+200.0f < length) {
			length     = groundLen;
			hitUnit    = NULL;
			hitFeature = NULL;
		}
	}

	return length;
}
Geodatabase::CFeature* CShapefileFeatureClass::ShpReadFeature(long index)
{

	Geometry* pGeometry = NULL;
	pGeometry = ReadShape(index);


	CFeature *pFeature =new CFeature(this,index+1);
	pFeature->SetShape(pGeometry);

	for( int iField = 0; iField < m_allFields.size();iField++ )
	{
		// Skip null fields.
		if( DBFIsAttributeNULL( m_dbfHandle, index, iField ) )
			continue;

		switch( m_allFields[iField]->GetType())
		{
		case Geodatabase::FTYPE_DATE:
			{
				
				const char* pszDateValue = 
					DBFReadStringAttribute(m_dbfHandle,index,iField); 

				pFeature->m_values[iField].vtype =Geodatabase::FieldValue::VT_DATE;

				if( pszDateValue[2] == '/' && pszDateValue[5] == '/' 
					&& strlen(pszDateValue) >= 10 )
				{
					pFeature->m_values[iField].m_Var.date.Month =atoi(pszDateValue+0);
						
					pFeature->m_values[iField].m_Var.date.Day   = atoi(pszDateValue+3);
					pFeature->m_values[iField].m_Var.date.Year  = atoi(pszDateValue+6);
				}
				else
				{
					int nFullDate = atoi(pszDateValue);
					pFeature->m_values[iField].m_Var.date.Year = (int)(nFullDate / 10000);
					pFeature->m_values[iField].m_Var.date.Month = (unsigned char)((nFullDate / 100) % 100);
					pFeature->m_values[iField].m_Var.date.Day = (unsigned char)(nFullDate % 100);
				}
				break;
			}
		case Geodatabase::FTYPE_STRING:

			pFeature->m_values[iField].SetString(DBFReadStringAttribute(m_dbfHandle, index,iField ));

			break;

		case Geodatabase::FTYPE_LONG:
			pFeature->m_values[iField].vtype =FieldValue::VT_INT;
			pFeature->m_values[iField].m_Var.iVal =DBFReadIntegerAttribute(m_dbfHandle, index,iField );

			break;

		case Geodatabase::FTYPE_FLOAT:
		case Geodatabase::FTYPE_DOUBLE:
			pFeature->m_values[iField].vtype =FieldValue::VT_DOUBLE;
			pFeature->m_values[iField].m_Var.dVal =DBFReadDoubleAttribute(m_dbfHandle, index,iField );

			break;

		default:
			break;
		}
	}


	return  pFeature;
}
Example #22
0
	void DrawQuad(int x, int y)
	{
		std::vector<IWorldObjectModelRenderer*>& opaqueModelRenderers = featureDrawer->opaqueModelRenderers;
		std::vector<IWorldObjectModelRenderer*>& cloakedModelRenderers = featureDrawer->cloakedModelRenderers;

		const CFeatureDrawer::DrawQuad* dq = &(*drawQuads)[y * drawQuadsX + x];

		for (std::set<CFeature*>::const_iterator fi = dq->features.begin(); fi != dq->features.end(); ++fi) {
			CFeature* f = (*fi);

			if (f->IsInVoid())
				continue;

			assert(f->def->drawType == DRAWTYPE_MODEL);

			if (gu->spectatingFullView || f->IsInLosForAllyTeam(gu->myAllyTeam)) {
				if (drawReflection) {
					float3 zeroPos;

					if (f->midPos.y < 0.0f) {
						zeroPos = f->midPos;
					} else {
						const float dif = f->midPos.y - camera->GetPos().y;
						zeroPos =
							camera->GetPos() * (f->midPos.y / dif) +
							f->midPos * (-camera->GetPos().y / dif);
					}
					if (ground->GetApproximateHeight(zeroPos.x, zeroPos.z, false) > f->drawRadius) {
						continue;
					}
				}
				if (drawRefraction) {
					if (f->pos.y > 0.0f)
						continue;
				}

				const float sqDist = (f->pos - camera->GetPos()).SqLength();
				const float farLength = f->sqRadius * unitDrawer->unitDrawDistSqr;
#ifdef USE_GML
				if (statFeatures && (f->reclaimLeft < 1.0f || f->resurrectProgress > 0.0f))
					statFeatures->push_back(f);
#endif

				if (sqDist < farLength) {
					float sqFadeDistE;
					float sqFadeDistB;

					if (farLength < sqFadeDistEnd) {
						sqFadeDistE = farLength;
						sqFadeDistB = farLength * sqFadeDistBegin / sqFadeDistEnd;
					} else {
						sqFadeDistE = sqFadeDistEnd;
						sqFadeDistB = sqFadeDistBegin;
					}

					if (sqDist < sqFadeDistB) {
						cloakedModelRenderers[MDL_TYPE(f)]->DelFeature(f);
						if (camera->InView(f->drawMidPos, f->drawRadius))
							opaqueModelRenderers[MDL_TYPE(f)]->AddFeature(f);
					} else if (sqDist < sqFadeDistE) {
						const float falpha = 1.0f - (sqDist - sqFadeDistB) / (sqFadeDistE - sqFadeDistB);
						opaqueModelRenderers[MDL_TYPE(f)]->DelFeature(f);
						if (camera->InView(f->drawMidPos, f->drawRadius))
							cloakedModelRenderers[MDL_TYPE(f)]->AddFeature(f, falpha);
					}
				} else {
					if (farFeatures) {
						farTextureHandler->Queue(f);
					}
				}
			}
		}
	}
Example #23
0
// FOR SYNCED MESSAGES
void CGame::ActionReceived(const Action& action, int playernum)
{
	if (action.command == "cheat") {
		SetBoolArg(gs->cheatEnabled, action.extra);
		if (gs->cheatEnabled)
			logOutput.Print("Cheating!");
		else
			logOutput.Print("No more cheating");
	}
	else if (action.command == "nohelp") {
		SetBoolArg(gs->noHelperAIs, action.extra);
		selectedUnits.PossibleCommandChange(NULL);
		logOutput.Print("LuaUI control is %s", gs->noHelperAIs ? "disabled" : "enabled");
	}
	else if (action.command == "nospecdraw") {
		bool buf;
		SetBoolArg(buf, action.extra);
		inMapDrawer->SetSpecMapDrawingAllowed(buf);
	}
	else if (action.command == "godmode") {
		if (!gs->cheatEnabled)
			logOutput.Print("godmode requires /cheat");
		else {
			SetBoolArg(gs->godMode, action.extra);
			CLuaUI::UpdateTeams();
			if (gs->godMode) {
				logOutput.Print("God Mode Enabled");
			} else {
				logOutput.Print("God Mode Disabled");
			}
			CPlayer::UpdateControlledTeams();
		}
	}
	else if (action.command == "globallos") {
		if (!gs->cheatEnabled) {
			logOutput.Print("globallos requires /cheat");
		} else {
			SetBoolArg(gs->globalLOS, action.extra);
			if (gs->globalLOS) {
				logOutput.Print("Global LOS Enabled");
			} else {
				logOutput.Print("Global LOS Disabled");
			}
		}
	}
	else if (action.command == "nocost" && gs->cheatEnabled) {
		if (unitDefHandler->ToggleNoCost()) {
			logOutput.Print("Everything is for free!");
		} else {
			logOutput.Print("Everything costs resources again!");
		}
	}
	else if (action.command == "give" && gs->cheatEnabled) {
		std::string s = "give "; //FIXME lazyness
		s += action.extra;

		// .give [amount] <unitName> [team] <@x,y,z>
		const vector<string> &args = CSimpleParser::Tokenize(s, 0);

		if (args.size() < 3) {
			logOutput.Print("Someone is spoofing invalid .give messages!");
			return;
		}

		float3 pos;
		if (sscanf(args[args.size() - 1].c_str(), "@%f,%f,%f", &pos.x, &pos.y, &pos.z) != 3) {
			logOutput.Print("Someone is spoofing invalid .give messages!");
			return;
		}

		int amount = 1;
		int team = playerHandler->Player(playernum)->team;

		int amountArgIdx = -1;
		int teamArgIdx = -1;

		if (args.size() == 5) {
			amountArgIdx = 1;
			teamArgIdx = 3;
		}
		else if (args.size() == 4) {
			if (args[1].find_first_not_of("0123456789") == string::npos) {
				amountArgIdx = 1;
			} else {
				teamArgIdx = 2;
			}
		}

		if (amountArgIdx >= 0) {
			const string& amountStr = args[amountArgIdx];
			amount = atoi(amountStr.c_str());
			if ((amount < 0) || (amountStr.find_first_not_of("0123456789") != string::npos)) {
				logOutput.Print("Bad give amount: %s", amountStr.c_str());
				return;
			}
		}

		if (teamArgIdx >= 0) {
			const string& teamStr = args[teamArgIdx];
			team = atoi(teamStr.c_str());
			if ((!teamHandler->IsValidTeam(team)) || (teamStr.find_first_not_of("0123456789") != string::npos)) {
				logOutput.Print("Bad give team: %s", teamStr.c_str());
				return;
			}
		}

		const string unitName = (amountArgIdx >= 0) ? args[2] : args[1];

		if (unitName == "all") {
			// player entered ".give all"
			int numRequestedUnits = unitDefHandler->unitDefs.size() - 1; /// defid=0 is not valid
			int currentNumUnits = teamHandler->Team(team)->units.size();
			int sqSize = (int) streflop::ceil(streflop::sqrt((float) numRequestedUnits));

			// make sure team unit-limit not exceeded
			if ((currentNumUnits + numRequestedUnits) > uh->MaxUnitsPerTeam()) {
				numRequestedUnits = uh->MaxUnitsPerTeam() - currentNumUnits;
			}

			// make sure square is entirely on the map
			float sqHalfMapSize = sqSize / 2 * 10 * SQUARE_SIZE;
			pos.x = std::max(sqHalfMapSize, std::min(pos.x, float3::maxxpos - sqHalfMapSize - 1));
			pos.z = std::max(sqHalfMapSize, std::min(pos.z, float3::maxzpos - sqHalfMapSize - 1));

			for (int a = 1; a <= numRequestedUnits; ++a) {
				float posx = pos.x + (a % sqSize - sqSize / 2) * 10 * SQUARE_SIZE;
				float posz = pos.z + (a / sqSize - sqSize / 2) * 10 * SQUARE_SIZE;
				float3 pos2 = float3(posx, pos.y, posz);
				const UnitDef* ud = unitDefHandler->GetUnitDefByID(a);
				if (ud) {
					const CUnit* unit = unitLoader->LoadUnit(ud, pos2, team, false, 0, NULL);
					if (unit) {
						unitLoader->FlattenGround(unit);
					}
				}
			}
		}
		else if (!unitName.empty()) {
			int numRequestedUnits = amount;
			int currentNumUnits = teamHandler->Team(team)->units.size();

			if (currentNumUnits >= uh->MaxUnitsPerTeam()) {
				LogObject() << "Unable to give any more units to team " << team << "(current: " << currentNumUnits << ", max: " << uh->MaxUnits() << ")";
				return;
			}

			// make sure team unit-limit is not exceeded
			if ((currentNumUnits + numRequestedUnits) > uh->MaxUnitsPerTeam()) {
				numRequestedUnits = uh->MaxUnitsPerTeam() - currentNumUnits;
			}

			const UnitDef* unitDef = unitDefHandler->GetUnitDefByName(unitName);

			if (unitDef != NULL) {
				int xsize = unitDef->xsize;
				int zsize = unitDef->zsize;
				int squareSize = (int) streflop::ceil(streflop::sqrt((float) numRequestedUnits));
				int total = numRequestedUnits;

				float3 minpos = pos;
				minpos.x -= ((squareSize - 1) * xsize * SQUARE_SIZE) / 2;
				minpos.z -= ((squareSize - 1) * zsize * SQUARE_SIZE) / 2;

				for (int z = 0; z < squareSize; ++z) {
					for (int x = 0; x < squareSize && total > 0; ++x) {
						float minposx = minpos.x + x * xsize * SQUARE_SIZE;
						float minposz = minpos.z + z * zsize * SQUARE_SIZE;
						const float3 upos(minposx, minpos.y, minposz);
						const CUnit* unit = unitLoader->LoadUnit(unitDef, upos, team, false, 0, NULL);

						if (unit) {
							unitLoader->FlattenGround(unit);
						}
						--total;
					}
				}

				logOutput.Print("Giving %i %s to team %i", numRequestedUnits, unitName.c_str(), team);
			} else {
				int allyteam = -1;
				if (teamArgIdx < 0) {
					team = -1; // default to world features
					allyteam = -1;
				} else {
					allyteam = teamHandler->AllyTeam(team);
				}

				const FeatureDef* featureDef = featureHandler->GetFeatureDef(unitName);
				if (featureDef) {
					int xsize = featureDef->xsize;
					int zsize = featureDef->zsize;
					int squareSize = (int) streflop::ceil(streflop::sqrt((float) numRequestedUnits));
					int total = amount; // FIXME -- feature count limit?

					float3 minpos = pos;
					minpos.x -= ((squareSize - 1) * xsize * SQUARE_SIZE) / 2;
					minpos.z -= ((squareSize - 1) * zsize * SQUARE_SIZE) / 2;

					for (int z = 0; z < squareSize; ++z) {
						for (int x = 0; x < squareSize && total > 0; ++x) {
							float minposx = minpos.x + x * xsize * SQUARE_SIZE;
							float minposz = minpos.z + z * zsize * SQUARE_SIZE;
							float minposy = ground->GetHeightReal(minposx, minposz);
							const float3 upos(minposx, minposy, minposz);

							CFeature* feature = new CFeature();
							// Initialize() adds the feature to the FeatureHandler -> no memory-leak
							feature->Initialize(upos, featureDef, 0, 0, team, allyteam, "");
							--total;
						}
					}

					logOutput.Print("Giving %i %s (feature) to team %i",
									numRequestedUnits, unitName.c_str(), team);
				}
				else {
					logOutput.Print(unitName + " is not a valid unitname");
				}
			}
		}
	}
	else if (action.command == "destroy" && gs->cheatEnabled) {
		std::stringstream ss(action.extra);
		logOutput.Print("Killing units: %s", action.extra.c_str());
		do {
			unsigned id;
			ss >> id;
			if (!ss)
				break;
			if (id >= uh->units.size())
				continue;
			if (uh->units[id] == NULL)
				continue;
			uh->units[id]->KillUnit(false, false, 0);
		} while (true);
	}
Example #24
0
int CAICallback::HandleCommand(int commandId, void* data)
{
	switch (commandId) {
		case AIHCQuerySubVersionId: {
			return 1; // current version of Handle Command interface
		} break;
		case AIHCAddMapPointId: {
			const AIHCAddMapPoint* cmdData = static_cast<AIHCAddMapPoint*>(data);
			net->Send(CBaseNetProtocol::Get().SendMapDrawPoint(team, (short)cmdData->pos.x, (short)cmdData->pos.z, std::string(cmdData->label), false));
			return 1;
		} break;
		case AIHCAddMapLineId: {
			const AIHCAddMapLine* cmdData = static_cast<AIHCAddMapLine*>(data);
			net->Send(CBaseNetProtocol::Get().SendMapDrawLine(team, (short)cmdData->posfrom.x, (short)cmdData->posfrom.z, (short)cmdData->posto.x, (short)cmdData->posto.z, false));
			return 1;
		} break;
		case AIHCRemoveMapPointId: {
			const AIHCRemoveMapPoint* cmdData = static_cast<AIHCRemoveMapPoint*>(data);
			net->Send(CBaseNetProtocol::Get().SendMapErase(team, (short)cmdData->pos.x, (short)cmdData->pos.z));
			return 1;
		} break;
		case AIHCSendStartPosId: {
			const AIHCSendStartPos* cmdData = static_cast<AIHCSendStartPos*>(data);
			SendStartPos(cmdData->ready, cmdData->pos);
			return 1;
		} break;
		case AIHCGetUnitDefByIdId: {
			// NOTE: this command should never arrive, handled in SSkirmishAICallbackImpl
			return 0;
		} break;
		case AIHCGetWeaponDefByIdId: {
			// NOTE: this command should never arrive, handled in SSkirmishAICallbackImpl
			return 0;
		} break;
		case AIHCGetFeatureDefByIdId: {
			// NOTE: this command should never arrive, handled in SSkirmishAICallbackImpl
			return 0;
		} break;

		case AIHCTraceRayId: {
			AIHCTraceRay* cmdData = static_cast<AIHCTraceRay*>(data);

			if (CHECK_UNITID(cmdData->srcUID)) {
				const CUnit* srcUnit = unitHandler->units[cmdData->srcUID];

				if (srcUnit != NULL) {
					CUnit* hitUnit = NULL;
					CFeature* hitFeature = NULL;
					//FIXME add COLLISION_NOFEATURE?
					const float realLen = TraceRay::TraceRay(cmdData->rayPos, cmdData->rayDir, cmdData->rayLen, cmdData->flags, srcUnit, hitUnit, hitFeature);

					if (hitUnit != NULL) {
						myAllyTeamId = teamHandler->AllyTeam(team);
						const bool isUnitVisible = unit_IsInLos(hitUnit);
						if (isUnitVisible) {
							cmdData->rayLen = realLen;
							cmdData->hitUID = hitUnit->id;
						}
					}
				}
			}

			return 1;
		} break;

		case AIHCFeatureTraceRayId: {
			AIHCFeatureTraceRay* cmdData = static_cast<AIHCFeatureTraceRay*>(data);

			if (CHECK_UNITID(cmdData->srcUID)) {
				const CUnit* srcUnit = unitHandler->units[cmdData->srcUID];

				if (srcUnit != NULL) {
					CUnit* hitUnit = NULL;
					CFeature* hitFeature = NULL;
					//FIXME add COLLISION_NOENEMIES || COLLISION_NOFRIENDLIES || COLLISION_NONEUTRALS?
					const float realLen = TraceRay::TraceRay(cmdData->rayPos, cmdData->rayDir, cmdData->rayLen, cmdData->flags, srcUnit, hitUnit, hitFeature);

					if (hitFeature != NULL) {
						const bool isFeatureVisible = hitFeature->IsInLosForAllyTeam(teamHandler->AllyTeam(team));
						if (isFeatureVisible) {
							cmdData->rayLen = realLen;
							cmdData->hitFID = hitFeature->id;
						}
					}
				}
			}

			return 1;
		} break;

		case AIHCPauseId: {
			AIHCPause* cmdData = static_cast<AIHCPause*>(data);

			net->Send(CBaseNetProtocol::Get().SendPause(gu->myPlayerNum, cmdData->enable));
			LOG("Skirmish AI controlling team %i paused the game, reason: %s",
					team,
					cmdData->reason != NULL ? cmdData->reason : "UNSPECIFIED");

			return 1;
		} break;

		case AIHCGetDataDirId: {
			// do nothing
			// this event will never end up here, as
			// it is handled in the C layer directly
			// see Clb_DataDirs_allocatePath in rts/ExternalAI/Interface/SSkirmishAICallback.h

			return 0;
		} break;

		case AIHCDebugDrawId: {
			AIHCDebugDraw* cmdData = static_cast<AIHCDebugDraw*>(data);

			switch (cmdData->cmdMode) {
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_ADD_GRAPH_POINT: {
					debugDrawerAI->AddGraphPoint(this->team, cmdData->lineId, cmdData->x, cmdData->y);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_DEL_GRAPH_POINTS: {
					debugDrawerAI->DelGraphPoints(this->team, cmdData->lineId, cmdData->numPoints);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_POS: {
					debugDrawerAI->SetGraphPos(this->team, cmdData->x, cmdData->y);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_SIZE: {
					debugDrawerAI->SetGraphSize(this->team, cmdData->w, cmdData->h);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_LINE_COLOR: {
					debugDrawerAI->SetGraphLineColor(this->team, cmdData->lineId, cmdData->color);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_GRAPH_LINE_LABEL: {
					debugDrawerAI->SetGraphLineLabel(this->team, cmdData->lineId, cmdData->label);
				} break;

				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_ADD_OVERLAY_TEXTURE: {
					cmdData->texHandle = debugDrawerAI->AddOverlayTexture(
						this->team,
						cmdData->texData,
						int(cmdData->w),   // interpret as absolute width
						int(cmdData->h)    // interpret as absolute height
					);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_UPDATE_OVERLAY_TEXTURE: {
					debugDrawerAI->UpdateOverlayTexture(
						this->team,
						cmdData->texHandle,
						cmdData->texData,
						int(cmdData->x),    // interpret as absolute pixel col
						int(cmdData->y),    // interpret as absolute pixel row
						int(cmdData->w),    // interpret as absolute width
						int(cmdData->h)     // interpret as absolute height
					);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_DEL_OVERLAY_TEXTURE: {
					debugDrawerAI->DelOverlayTexture(this->team, cmdData->texHandle);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_OVERLAY_TEXTURE_POS: {
					debugDrawerAI->SetOverlayTexturePos(this->team, cmdData->texHandle, cmdData->x, cmdData->y);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_OVERLAY_TEXTURE_SIZE: {
					debugDrawerAI->SetOverlayTextureSize(this->team, cmdData->texHandle, cmdData->w, cmdData->h);
				} break;
				case AIHCDebugDraw::AIHC_DEBUGDRAWER_MODE_SET_OVERLAY_TEXTURE_LABEL: {
					debugDrawerAI->SetOverlayTextureLabel(this->team, cmdData->texHandle, cmdData->label);
				} break;

				default: {
				} break;
			}

			return 1;
		} break;

		default: {
			return 0;
		}
	}
}
Example #25
0
float GuiTraceRay(
	const float3& start,
	const float3& dir,
	const float length,
	const CUnit* exclude,
	CUnit*& hitUnit,
	CFeature*& hitFeature,
	bool useRadar,
	bool groundOnly,
	bool ignoreWater
) {
	hitUnit = NULL;
	hitFeature = NULL;

	if (dir == ZeroVector)
		return -1.0f;

	// ground intersection
	const float guiRayLength = length;
	const float groundRayLength = ground->LineGroundCol(start, start + dir * guiRayLength, false);
	const float waterRayLength = math::floor(math::fabs(start.y / std::min(dir.y, -0.00001f)));

	float minRayLength = groundRayLength;
	float minIngressDist = length;
	float minEgressDist = length;

	if (!ignoreWater)
		minRayLength = std::min(groundRayLength, waterRayLength);
	if (groundOnly)
		return minRayLength;

	GML_RECMUTEX_LOCK(quad); // GuiTraceRay

	int* begQuad = NULL;
	int* endQuad = NULL;
	bool hitFactory = false;
	CollisionQuery cq;

	quadField->GetQuadsOnRay(start, dir, length, begQuad, endQuad);

	std::list<CUnit*>::const_iterator ui;
	std::list<CFeature*>::const_iterator fi;

	for (int* quadPtr = begQuad; quadPtr != endQuad; ++quadPtr) {
		const CQuadField::Quad& quad = quadField->GetQuad(*quadPtr);

		// Unit Intersection
		for (ui = quad.units.begin(); ui != quad.units.end(); ++ui) {
			CUnit* unit = *ui;

			const bool unitIsEnemy = !teamHandler->Ally(unit->allyteam, gu->myAllyTeam);
			const bool unitOnRadar = (useRadar && radarhandler->InRadar(unit, gu->myAllyTeam));
			const bool unitInSight = (unit->losStatus[gu->myAllyTeam] & (LOS_INLOS | LOS_CONTRADAR));
			const bool unitVisible = !unitIsEnemy || unitOnRadar || unitInSight || gu->spectatingFullView;

			if (unit == exclude)
				continue;
			if (!unitVisible)
				continue;

			CollisionVolume cv(unit->collisionVolume);

			if (unit->isIcon || (!unitInSight && unitOnRadar && unitIsEnemy)) {
				// for iconified units, just pretend the collision
				// volume is a sphere of radius <unit->IconRadius>
				// (count radar blips as such too)
				cv.InitSphere(unit->iconRadius);
			}

			if (CCollisionHandler::MouseHit(unit, start, start + dir * guiRayLength, &cv, &cq)) {
				// get the distance to the ray-volume ingress point
				// (not likely to generate inside-hit special cases)
				const float ingressDist = cq.GetIngressPosDist(start, dir);
				const float  egressDist = cq.GetEgressPosDist(start, dir);

				const bool isFactory = unit->unitDef->IsFactoryUnit();
				const bool factoryHitBeforeUnit = ((hitFactory && ingressDist < minIngressDist) || (!hitFactory &&  egressDist < minIngressDist));
				const bool unitHitInsideFactory = ((hitFactory && ingressDist <  minEgressDist) || (!hitFactory && ingressDist < minIngressDist));

				// give units in a factory higher priority than the factory itself
				if (hitUnit == NULL || (isFactory && factoryHitBeforeUnit) || (!isFactory && unitHitInsideFactory)) {
					hitFactory = isFactory;
					minIngressDist = ingressDist;
					minEgressDist = egressDist;

					hitUnit = unit;
					hitFeature = NULL;
				}
			}
		}

		// Feature Intersection
		// NOTE: switch this to custom volumes fully?
		// (not used for any LOF checks, maybe wasteful)
		for (fi = quad.features.begin(); fi != quad.features.end(); ++fi) {
			CFeature* f = *fi;

			// FIXME add useradar?
			if (!gu->spectatingFullView && !f->IsInLosForAllyTeam(gu->myAllyTeam))
				continue;
			if (f->noSelect)
				continue;

			if (CCollisionHandler::DetectHit(f, start, start + dir * guiRayLength, &cq, true)) {
				const float hitDist = cq.GetHitPosDist(start, dir);

				const bool factoryHitBeforeUnit = ( hitFactory && hitDist <  minEgressDist);
				const bool unitHitInsideFactory = (!hitFactory && hitDist < minIngressDist);

				// we want the closest feature (intersection point) on the ray
				// give features in a factory (?) higher priority than the factory itself
				if (hitUnit == NULL || factoryHitBeforeUnit || unitHitInsideFactory) {
					hitFactory = false;
					minIngressDist = hitDist;

					hitFeature = f;
					hitUnit = NULL;
				}
			}
		}
	}

	if ((minRayLength > 0.0f) && ((minRayLength + 200.0f) < minIngressDist)) {
		minIngressDist = minRayLength;

		hitUnit    = NULL;
		hitFeature = NULL;
	}

	return minIngressDist;
}
Example #26
0
void CUnitLoader::GiveUnits(const std::string& objectName, float3 pos, int amount, int team, int featureAllyTeam)
{
    const CTeam* receivingTeam = teamHandler->Team(team);

    if (objectName == "all") {
        unsigned int numRequestedUnits = unitDefHandler->unitDefs.size() - 1; /// defid=0 is not valid
        unsigned int currentNumUnits = receivingTeam->units.size();

        // make sure team unit-limit is not exceeded
        if ((currentNumUnits + numRequestedUnits) > receivingTeam->maxUnits) {
            numRequestedUnits = receivingTeam->maxUnits - currentNumUnits;
        }

        // make sure square is entirely on the map
        const int sqSize = math::ceil(math::sqrt((float) numRequestedUnits));
        const float sqHalfMapSize = sqSize / 2 * 10 * SQUARE_SIZE;

        pos.x = std::max(sqHalfMapSize, std::min(pos.x, float3::maxxpos - sqHalfMapSize - 1));
        pos.z = std::max(sqHalfMapSize, std::min(pos.z, float3::maxzpos - sqHalfMapSize - 1));

        for (int a = 1; a <= numRequestedUnits; ++a) {
            Watchdog::ClearPrimaryTimers(); // the other thread may be waiting for a mutex held by this one, triggering hang detection
            const float px = pos.x + (a % sqSize - sqSize / 2) * 10 * SQUARE_SIZE;
            const float pz = pos.z + (a / sqSize - sqSize / 2) * 10 * SQUARE_SIZE;
            const float3 unitPos = float3(px, ground->GetHeightReal(px, pz), pz);
            const UnitDef* unitDef = unitDefHandler->GetUnitDefByID(a);

            if (unitDef != NULL) {
                const CUnit* unit = LoadUnit(unitDef, unitPos, team, false, 0, NULL);

                if (unit != NULL) {
                    FlattenGround(unit);
                }
            }
        }
    } else {
        unsigned int numRequestedUnits = amount;
        unsigned int currentNumUnits = receivingTeam->units.size();

        if (receivingTeam->AtUnitLimit()) {
            LOG_L(L_WARNING,
                  "[%s] unable to give more units to team %d (current: %u, team limit: %u, global limit: %u)",
                  __FUNCTION__, team, currentNumUnits, receivingTeam->maxUnits, uh->MaxUnits()
                 );
            return;
        }

        // make sure team unit-limit is not exceeded
        if ((currentNumUnits + numRequestedUnits) > receivingTeam->maxUnits) {
            numRequestedUnits = receivingTeam->maxUnits - currentNumUnits;
        }

        const UnitDef* unitDef = unitDefHandler->GetUnitDefByName(objectName);
        const FeatureDef* featureDef = featureHandler->GetFeatureDef(objectName, false);

        if (unitDef == NULL && featureDef == NULL) {
            LOG_L(L_WARNING, "[%s] %s is not a valid object-name", __FUNCTION__, objectName.c_str());
            return;
        }

        if (unitDef != NULL) {
            const int xsize = unitDef->xsize;
            const int zsize = unitDef->zsize;
            const int squareSize = math::ceil(math::sqrt((float) numRequestedUnits));
            const float3 squarePos = float3(
                                         pos.x - (((squareSize - 1) * xsize * SQUARE_SIZE) / 2),
                                         pos.y,
                                         pos.z - (((squareSize - 1) * zsize * SQUARE_SIZE) / 2)
                                     );

            int total = numRequestedUnits;

            for (int z = 0; z < squareSize; ++z) {
                for (int x = 0; x < squareSize && total > 0; ++x) {
                    const float px = squarePos.x + x * xsize * SQUARE_SIZE;
                    const float pz = squarePos.z + z * zsize * SQUARE_SIZE;

                    const float3 unitPos = float3(px, ground->GetHeightReal(px, pz), pz);
                    Watchdog::ClearPrimaryTimers();
                    const CUnit* unit = LoadUnit(unitDef, unitPos, team, false, 0, NULL);

                    if (unit != NULL) {
                        FlattenGround(unit);
                    }

                    --total;
                }
            }

            LOG("[%s] spawned %i %s unit(s) for team %i",
                __FUNCTION__, numRequestedUnits, objectName.c_str(), team);
        }

        if (featureDef != NULL) {
            if (featureAllyTeam < 0) {
                team = -1; // default to world features
            }

            const int xsize = featureDef->xsize;
            const int zsize = featureDef->zsize;
            const int squareSize = math::ceil(math::sqrt((float) numRequestedUnits));
            const float3 squarePos = float3(
                                         pos.x - (((squareSize - 1) * xsize * SQUARE_SIZE) / 2),
                                         pos.y,
                                         pos.z - (((squareSize - 1) * zsize * SQUARE_SIZE) / 2)
                                     );

            int total = amount; // FIXME -- feature count limit?

            for (int z = 0; z < squareSize; ++z) {
                for (int x = 0; x < squareSize && total > 0; ++x) {
                    const float px = squarePos.x + x * xsize * SQUARE_SIZE;
                    const float pz = squarePos.z + z * zsize * SQUARE_SIZE;
                    const float3 featurePos = float3(px, ground->GetHeightReal(px, pz), pz);

                    Watchdog::ClearPrimaryTimers();
                    CFeature* feature = new CFeature();
                    // Initialize() adds the feature to the FeatureHandler -> no memory-leak
                    feature->Initialize(featurePos, featureDef, 0, 0, team, featureAllyTeam, NULL);

                    --total;
                }
            }

            LOG("[%s] spawned %i %s feature(s) for team %i",
                __FUNCTION__, numRequestedUnits, objectName.c_str(), team);
        }
    }
}