Пример #1
0
void CFactory::StartBuild(const UnitDef* buildeeDef) {
	const float3& buildPos = CalcBuildPos();
	const bool blocked = groundBlockingObjectMap->GroundBlocked(buildPos, this);

	// wait until buildPos is no longer blocked (eg. by a previous buildee)
	//
	// it might rarely be the case that a unit got stuck inside the factory
	// or died right after completion and left some wreckage, but that is up
	// to players to fix (we no longer broadcast BuggerOff directives, since
	// those are indiscriminate and ineffective)
	if (blocked)
		return;

	CUnit* b = unitLoader->LoadUnit(buildeeDef, buildPos, team, true, buildFacing, this);

	if (!unitDef->canBeAssisted) {
		b->soloBuilder = this;
		b->AddDeathDependence(this, DEPENDENCE_BUILDER);
	}

	AddDeathDependence(b, DEPENDENCE_BUILD);
	script->StartBuilding();

	// set curBuildDef to NULL to indicate construction
	// has started, otherwise we would keep being called
	curBuild = b;
	curBuildDef = NULL;

	#if (PLAY_SOUNDS == 1)
	if (losStatus[gu->myAllyTeam] & LOS_INLOS) {
		Channels::General.PlayRandomSample(unitDef->sounds.build, buildPos);
	}
	#endif
}
Пример #2
0
bool CBuilder::StartBuild(BuildInfo& buildInfo)
{
	StopBuild(false);
//	logOutput.Print("start build");

	buildInfo.pos=helper->Pos2BuildPos(buildInfo);

	CFeature* feature;
	// Pass -1 as allyteam to behave like we have maphack.
	// This is needed to prevent building on top of cloaked stuff.
	int canBuild=uh->TestUnitBuildSquare(buildInfo, feature, -1);
	if(canBuild<2){
		CUnit* u=helper->GetClosestFriendlyUnit(buildInfo.pos,5,allyteam);
		if(u && u->unitDef==buildInfo.def && unitDef->canAssist){
			curBuild=u;
			AddDeathDependence(u);
			SetBuildStanceToward(buildInfo.pos);
			return true;
		}
		return false;
	}
	if(feature)
		return false;

	const UnitDef* unitDef = buildInfo.def;
	SetBuildStanceToward(buildInfo.pos);

	nextBuildType=buildInfo.def->name;
	nextBuildPos=buildInfo.pos;

	CUnit* b = unitLoader.LoadUnit(nextBuildType, nextBuildPos, team,
	                               true, buildInfo.buildFacing, this);

	if (mapDamage->disabled || !unitDef->levelGround || unitDef->floater ||
	    (unitDef->canmove && (unitDef->speed > 0.0f))) {
		// skip the terraforming job.
		b->terraformLeft = 0;
		b->groundLevelled=true;
	}
	else {
		tx1 = (int)max((float)0,(b->pos.x - (b->unitDef->xsize*0.5f*SQUARE_SIZE))/SQUARE_SIZE);
		tx2 = min(gs->mapx,tx1+b->unitDef->xsize);
		tz1 = (int)max((float)0,(b->pos.z - (b->unitDef->ysize*0.5f*SQUARE_SIZE))/SQUARE_SIZE);
		tz2 = min(gs->mapy,tz1+b->unitDef->ysize);

		b->terraformLeft = CalculateBuildTerraformCost(buildInfo);
		b->groundLevelled=false;

		terraforming=true;
		terraformType=Terraform_Building;
		terraformRadius=(tx2-tx1)*SQUARE_SIZE;
		terraformCenter = b->pos;
	}

	if (!this->unitDef->canBeAssisted) {
		b->soloBuilder = this;
		b->AddDeathDependence(this);
	}
	b->lineage = this->lineage;
	AddDeathDependence(b);
	curBuild=b;
	if (mapDamage->disabled && !(curBuild->floatOnWater)) {
		/* The ground isn't going to be terraformed.
		 * When the building is completed, it'll 'pop'
		 * into the correct height for the (un-flattened)
		 * terrain it's on.
		 *
		 * To prevent this visual artifact, put the building
		 * at the 'right' height to begin with.
		 *
		 * Duplicated from CMoveType::SlowUpdate(), which
		 * is why we use the regular code for floating things.
		 */
		curBuild->pos.y=ground->GetHeight2(curBuild->pos.x,curBuild->pos.z);
		curBuild->midPos.y=curBuild->pos.y+curBuild->relMidPos.y;
	}
	else {
		float d=nextBuildPos.y-curBuild->pos.y;
		curBuild->pos.y+=d;
		curBuild->midPos.y+=d;
	}

	return true;
}
Пример #3
0
void CBuilder::Update()
{
	if (beingBuilt) {
		return;
	}

	if (!stunned) {
		if (terraforming && inBuildStance) {
			const float* heightmap = readmap->GetHeightmap();
			assert(!mapDamage->disabled); // The map should not be deformed in the first place.
			float terraformScale = 0.1;

			switch (terraformType) {
				case Terraform_Building:
					if (curBuild) {
						terraformScale = (terraformSpeed + terraformHelp) / curBuild->terraformLeft;
						curBuild->terraformLeft -= (terraformSpeed + terraformHelp);
						terraformHelp = 0;

						if (terraformScale > 1.0f) {
							terraformScale = 1.0f;
						}

						// prevent building from timing out while terraforming for it
						curBuild->AddBuildPower(0.0f, this);
						for (int z = tz1; z <= tz2; z++) {
							for (int x = tx1; x <= tx2; x++) {
								int idx = z * (gs->mapx + 1) + x;
								float ch = heightmap[idx];

								readmap->AddHeight(idx, (curBuild->pos.y - ch) * terraformScale);
							}
						}

						if(curBuild->terraformLeft<=0){
							terraforming=false;
							mapDamage->RecalcArea(tx1,tx2,tz1,tz2);
							curBuild->groundLevelled = true;
							if (luaRules && luaRules->TerraformComplete(this, curBuild)) {
								StopBuild();
							}
						}
					}
					break;
				case Terraform_Restore:
					terraformScale = (terraformSpeed + terraformHelp) / myTerraformLeft;
					myTerraformLeft -= (terraformSpeed + terraformHelp);
					terraformHelp = 0;

					if (terraformScale > 1.0f) {
						terraformScale = 1.0f;
					}

					for (int z = tz1; z <= tz2; z++) {
						for (int x = tx1; x <= tx2; x++) {
							int idx = z * (gs->mapx + 1) + x;
							float ch = heightmap[idx];
							float oh = readmap->orgheightmap[idx];

							readmap->AddHeight(idx, (oh - ch) * terraformScale);
						}
					}

					if (myTerraformLeft <= 0) {
						terraforming=false;
						mapDamage->RecalcArea(tx1,tx2,tz1,tz2);
						StopBuild();
					}
					break;
			}

			CreateNanoParticle(terraformCenter,terraformRadius*0.5f,false);

			for (int z = tz1; z <= tz2; z++) {
				// smooth the borders x
				for (int x = 1; x <= 3; x++) {
					if (tx1 - 3 >= 0) {
						float ch3 = heightmap[z * (gs->mapx + 1) + tx1    ];
						float ch  = heightmap[z * (gs->mapx + 1) + tx1 - x];
						float ch2 = heightmap[z * (gs->mapx + 1) + tx1 - 3];
						float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale;

						readmap->AddHeight(z * (gs->mapx + 1) + tx1 - x, amount);
					}
					if (tx2 + 3 < gs->mapx) {
						float ch3 = heightmap[z * (gs->mapx + 1) + tx2    ];
						float ch  = heightmap[z * (gs->mapx + 1) + tx2 + x];
						float ch2 = heightmap[z * (gs->mapx + 1) + tx2 + 3];
						float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale;

						readmap->AddHeight(z * (gs->mapx + 1) + tx2 + x, amount);
					}
				}
			}
			for (int z = 1; z <= 3; z++) {
				// smooth the borders z
				for (int x = tx1; x <= tx2; x++) {
					if (tz1 - 3 >= 0) {
						float ch3 = heightmap[(tz1    ) * (gs->mapx + 1) + x];
						float ch  = heightmap[(tz1 - z) * (gs->mapx + 1) + x];
						float ch2 = heightmap[(tz1 - 3) * (gs->mapx + 1) + x];
						float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale;

						readmap->AddHeight((tz1 - z) * (gs->mapx + 1) + x, adjust);
					}
					if (tz2 + 3 < gs->mapy) {
						float ch3 = heightmap[(tz2    ) * (gs->mapx + 1) + x];
						float ch  = heightmap[(tz2 + z) * (gs->mapx + 1) + x];
						float ch2 = heightmap[(tz2 + 3) * (gs->mapx + 1) + x];
						float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale;
	
						readmap->AddHeight((tz2 + z) * (gs->mapx + 1) + x, adjust);
					}
				}
			}
		}
		else if (helpTerraform && inBuildStance) {
			if (helpTerraform->terraforming) {
				helpTerraform->terraformHelp += terraformSpeed;
				CreateNanoParticle(helpTerraform->terraformCenter,helpTerraform->terraformRadius*0.5f,false);
			} else {
				DeleteDeathDependence(helpTerraform);
				helpTerraform=0;
				StopBuild(true);
			}
		}
		else if (curBuild && f3Dist(curBuild->pos, pos) < buildDistance + curBuild->radius) {
			if (curBuild->soloBuilder && (curBuild->soloBuilder != this)) {
				StopBuild();
			} else {
				if (!inBuildStance) {
					curBuild->AddBuildPower(0.0f, this); //prevent building timing out
				} else {
					if (scriptCloak <= 2) {
						if (isCloaked) {
							isCloaked = false;
							eventHandler.UnitDecloaked(this);
						}
						curCloakTimeout = gs->frameNum + cloakTimeout;
					}

					float adjBuildSpeed; // adjusted build speed
					if (curBuild->buildProgress < 1.0f) {
						adjBuildSpeed = buildSpeed;  // new build
					} else {
						adjBuildSpeed = min(unitDef->maxRepairSpeed / 2.0f - curBuild->repairAmount, repairSpeed); // repair
					}

					if(adjBuildSpeed > 0 && !commandAI->commandQue.empty()
							&& commandAI->commandQue.front().id == CMD_WAIT) {
						curBuild->AddBuildPower(0, this);
					} else if (adjBuildSpeed > 0 && curBuild->AddBuildPower(adjBuildSpeed, this)) {
						CreateNanoParticle(curBuild->midPos, curBuild->radius * 0.5f, false);
					} else {
						if(!curBuild->beingBuilt && curBuild->health >= curBuild->maxHealth) {
							StopBuild();
						}
					}
				}
			}
		}
		else if(curReclaim && f3Dist(curReclaim->pos, pos)<buildDistance+curReclaim->radius && inBuildStance){
			if (scriptCloak <= 2) {
				if (isCloaked) {
					isCloaked = false;
					eventHandler.UnitDecloaked(this);
				}
				curCloakTimeout = gs->frameNum + cloakTimeout;
			}
			if (curReclaim->AddBuildPower(-reclaimSpeed, this)) {
				CreateNanoParticle(curReclaim->midPos, curReclaim->radius * 0.7f, true);
			}
		}
		else if(curResurrect && f3Dist(curResurrect->pos, pos)<buildDistance+curResurrect->radius && inBuildStance){
			const UnitDef* ud=unitDefHandler->GetUnitByName(curResurrect->createdFromUnit);
			if(ud){
				if ((modInfo.reclaimMethod != 1) && (curResurrect->reclaimLeft < 1)) {
					// This corpse has been reclaimed a little, need to restore the resources
					// before we can let the player resurrect it.
					curResurrect->AddBuildPower(repairSpeed, this);
				}
				else {
					// Corpse has been restored, begin resurrection
					if (UseEnergy(ud->energyCost * resurrectSpeed / ud->buildTime * modInfo.resurrectEnergyCostFactor)) {
						curResurrect->resurrectProgress+=resurrectSpeed/ud->buildTime;
						CreateNanoParticle(curResurrect->midPos,curResurrect->radius*0.7f,gs->randInt()&1);
					}
					if(curResurrect->resurrectProgress>1){		//resurrect finished
						curResurrect->UnBlock();
						CUnit* u = unitLoader.LoadUnit(curResurrect->createdFromUnit, curResurrect->pos,
													team, false, curResurrect->buildFacing, this);
						if (!this->unitDef->canBeAssisted) {
							u->soloBuilder = this;
							u->AddDeathDependence(this);
						}
						u->health*=0.05f;
						u->lineage = this->lineage;
						lastResurrected=u->id;
						curResurrect->resurrectProgress=0;
						featureHandler->DeleteFeature(curResurrect);
						StopBuild(true);
					}
				}
			} else {
				StopBuild(true);
			}
		}
		else if(curCapture && f3Dist(curCapture->pos, pos)<buildDistance+curCapture->radius && inBuildStance){
			if(curCapture->team!=team){

				float captureProgressTemp = curCapture->captureProgress + 1.0f/(150+curCapture->buildTime/captureSpeed*(curCapture->health+curCapture->maxHealth)/curCapture->maxHealth*0.4f);
				if (captureProgressTemp >= 1.0f) {
					captureProgressTemp = 1.0f;
				}

				const float captureFraction = captureProgressTemp - curCapture->captureProgress;
				const float energyUseScaled = curCapture->energyCost * captureFraction * modInfo.captureEnergyCostFactor;

				if (!UseEnergy(energyUseScaled)) {
					gs->Team(team)->energyPull += energyUseScaled;
				} else {
					curCapture->captureProgress = captureProgressTemp;
					CreateNanoParticle(curCapture->midPos,curCapture->radius*0.7f,false);
					if(curCapture->captureProgress >= 1.0f){
						if (!curCapture->ChangeTeam(team, CUnit::ChangeCaptured)) {
							// capture failed
							ENTER_MIXED;
							if (team == gu->myTeam) {
								logOutput.Print("%s: Capture failed, unit type limit reached", unitDef->humanName.c_str());
								logOutput.SetLastMsgPos(pos);
							}
							ENTER_SYNCED;
						} else {
							// capture succesful
							int oldLineage = curCapture->lineage;
							curCapture->lineage = this->lineage;
							gs->Team(oldLineage)->LeftLineage(curCapture);
						}
						curCapture->captureProgress=0.0f;
						StopBuild(true);
					}
				}
			} else {
				StopBuild(true);
			}
		}
	}

	CUnit::Update();
}
Пример #4
0
bool CBuilder::StartBuild(BuildInfo& buildInfo, CFeature*& feature, bool& waitstance)
{
	StopBuild(false);

	buildInfo.pos = helper->Pos2BuildPos(buildInfo, true);

	// Pass -1 as allyteam to behave like we have maphack.
	// This is needed to prevent building on top of cloaked stuff.
	const int canBuild = uh->TestUnitBuildSquare(buildInfo, feature, -1, true);

	if (canBuild < 2) {
		// the ground is blocked at the position we want
		// to build at; check if the blocking object is
		// of the same type as our buildee (which means
		// another builder has already started it)
		// note: even if construction has already started,
		// the buildee is *not* guaranteed to be the unit
		// closest to us
		CSolidObject* o = groundBlockingObjectMap->GroundBlocked(buildInfo.pos);
		CUnit* u = NULL;

		if (o != NULL) {
			u = dynamic_cast<CUnit*>(o);
		} else {
			// <pos> might map to a non-blocking portion
			// of the buildee's yardmap, fallback check
			u = helper->GetClosestFriendlyUnit(buildInfo.pos, buildDistance, allyteam);
		}

		if (u != NULL && u->unitDef == buildInfo.def && unitDef->canAssist) {
			curBuild = u;
			AddDeathDependence(u);
			SetBuildStanceToward(buildInfo.pos);
			return true;
		}

		return false;
	}

	if (feature)
		return false;

	const UnitDef* unitDef = buildInfo.def;
	SetBuildStanceToward(buildInfo.pos);

	if (!inBuildStance) {
		waitstance = true;
		return false;
	}

	CUnit* b = unitLoader->LoadUnit(buildInfo.def, buildInfo.pos, team,
	                               true, buildInfo.buildFacing, this);

	// floating structures don't terraform the seabed
	const float groundheight = ground->GetHeightReal(b->pos.x, b->pos.z);
	const bool onWater = (unitDef->floater && groundheight <= 0.0f);

	if (mapDamage->disabled || !unitDef->levelGround || onWater ||
	    (unitDef->canmove && (unitDef->speed > 0.0f))) {
		// skip the terraforming job
		b->terraformLeft = 0;
		b->groundLevelled=true;
	}
	else {
		tx1 = (int)max((float)0,(b->pos.x - (b->unitDef->xsize*0.5f*SQUARE_SIZE))/SQUARE_SIZE);
		tx2 = min(gs->mapx,tx1+b->unitDef->xsize);
		tz1 = (int)max((float)0,(b->pos.z - (b->unitDef->zsize*0.5f*SQUARE_SIZE))/SQUARE_SIZE);
		tz2 = min(gs->mapy,tz1+b->unitDef->zsize);

		b->terraformLeft = CalculateBuildTerraformCost(buildInfo);
		b->groundLevelled= false;

		terraforming    = true;
		terraformType   = Terraform_Building;
		terraformRadius = (tx2-tx1)*SQUARE_SIZE;
		terraformCenter = b->pos;
	}

	if (!this->unitDef->canBeAssisted) {
		b->soloBuilder = this;
		b->AddDeathDependence(this);
	}
	AddDeathDependence(b);
	curBuild=b;

	/* The ground isn't going to be terraformed.
	 * When the building is completed, it'll 'pop'
	 * into the correct height for the (un-flattened)
	 * terrain it's on.
	 *
	 * To prevent this visual artifact, put the building
	 * at the 'right' height to begin with.
	 */
	curBuild->moveType->SlowUpdate();

	return true;
}
Пример #5
0
void CBuilder::Update()
{
	if (!beingBuilt && !stunned) {
		if (terraforming && inBuildStance) {
			const float* heightmap = readmap->GetCornerHeightMapSynced();
			assert(!mapDamage->disabled); // The map should not be deformed in the first place.
			float terraformScale = 0.1;

			switch (terraformType) {
				case Terraform_Building:
					if (curBuild) {
						if (curBuild->terraformLeft <= 0)
							terraformScale = 0.0f;
						else
							terraformScale = (terraformSpeed + terraformHelp) / curBuild->terraformLeft;
						curBuild->terraformLeft -= (terraformSpeed + terraformHelp);
						terraformHelp = 0;

						if (terraformScale > 1.0f) {
							terraformScale = 1.0f;
						}

						// prevent building from timing out while terraforming for it
						curBuild->AddBuildPower(0.0f, this);
						for (int z = tz1; z <= tz2; z++) {
							for (int x = tx1; x <= tx2; x++) {
								int idx = z * gs->mapxp1 + x;
								float ch = heightmap[idx];

								readmap->AddHeight(idx, (curBuild->pos.y - ch) * terraformScale);
							}
						}

						if(curBuild->terraformLeft<=0){
							terraforming=false;
							mapDamage->RecalcArea(tx1,tx2,tz1,tz2);
							curBuild->groundLevelled = true;
							if (luaRules && luaRules->TerraformComplete(this, curBuild)) {
								StopBuild();
							}
						}
					}
					break;
				case Terraform_Restore:
					if (myTerraformLeft <= 0)
						terraformScale = 0.0f;
					else
						terraformScale = (terraformSpeed + terraformHelp) / myTerraformLeft;
					myTerraformLeft -= (terraformSpeed + terraformHelp);
					terraformHelp = 0;

					if (terraformScale > 1.0f) {
						terraformScale = 1.0f;
					}

					for (int z = tz1; z <= tz2; z++) {
						for (int x = tx1; x <= tx2; x++) {
							int idx = z * gs->mapxp1 + x;
							float ch = heightmap[idx];
							float oh = readmap->GetOriginalHeightMapSynced()[idx];

							readmap->AddHeight(idx, (oh - ch) * terraformScale);
						}
					}

					if (myTerraformLeft <= 0) {
						terraforming=false;
						mapDamage->RecalcArea(tx1,tx2,tz1,tz2);
						StopBuild();
					}
					break;
			}

			ScriptDecloak(true);

			CreateNanoParticle(terraformCenter,terraformRadius*0.5f,false);

			for (int z = tz1; z <= tz2; z++) {
				// smooth the borders x
				for (int x = 1; x <= 3; x++) {
					if (tx1 - 3 >= 0) {
						const float ch3 = heightmap[z * gs->mapxp1 + tx1    ];
						const float ch  = heightmap[z * gs->mapxp1 + tx1 - x];
						const float ch2 = heightmap[z * gs->mapxp1 + tx1 - 3];
						const float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale;

						readmap->AddHeight(z * gs->mapxp1 + tx1 - x, amount);
					}
					if (tx2 + 3 < gs->mapx) {
						const float ch3 = heightmap[z * gs->mapxp1 + tx2    ];
						const float ch  = heightmap[z * gs->mapxp1 + tx2 + x];
						const float ch2 = heightmap[z * gs->mapxp1 + tx2 + 3];
						const float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale;

						readmap->AddHeight(z * gs->mapxp1 + tx2 + x, amount);
					}
				}
			}
			for (int z = 1; z <= 3; z++) {
				// smooth the borders z
				for (int x = tx1; x <= tx2; x++) {
					if (tz1 - 3 >= 0) {
						const float ch3 = heightmap[(tz1    ) * gs->mapxp1 + x];
						const float ch  = heightmap[(tz1 - z) * gs->mapxp1 + x];
						const float ch2 = heightmap[(tz1 - 3) * gs->mapxp1 + x];
						const float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale;

						readmap->AddHeight((tz1 - z) * gs->mapxp1 + x, adjust);
					}
					if (tz2 + 3 < gs->mapy) {
						const float ch3 = heightmap[(tz2    ) * gs->mapxp1 + x];
						const float ch  = heightmap[(tz2 + z) * gs->mapxp1 + x];
						const float ch2 = heightmap[(tz2 + 3) * gs->mapxp1 + x];
						const float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale;

						readmap->AddHeight((tz2 + z) * gs->mapxp1 + x, adjust);
					}
				}
			}
		}
		else if (helpTerraform && inBuildStance) {
			if (helpTerraform->terraforming) {
				ScriptDecloak(true);

				helpTerraform->terraformHelp += terraformSpeed;
				CreateNanoParticle(helpTerraform->terraformCenter,helpTerraform->terraformRadius*0.5f,false);
			} else {
				DeleteDeathDependence(helpTerraform, DEPENDENCE_TERRAFORM);
				helpTerraform=0;
				StopBuild(true);
			}
		}
		else if (curBuild && f3SqDist(curBuild->pos, pos) < Square(buildDistance + curBuild->radius)) {
			if (curBuild->soloBuilder && (curBuild->soloBuilder != this)) {
				StopBuild();
			} else {
				if (!inBuildStance) {
					curBuild->AddBuildPower(0.0f, this); //prevent building timing out
				} else {
					ScriptDecloak(true);

					float adjBuildSpeed; // adjusted build speed
					if (curBuild->buildProgress < 1.0f) {
						adjBuildSpeed = buildSpeed;  // new build
					} else {
						adjBuildSpeed = min(unitDef->maxRepairSpeed / 2.0f - curBuild->repairAmount, repairSpeed); // repair
					}

					if(adjBuildSpeed > 0 && !commandAI->commandQue.empty()
							&& commandAI->commandQue.front().GetID() == CMD_WAIT) {
						curBuild->AddBuildPower(0, this);
					} else if (adjBuildSpeed > 0 && curBuild->AddBuildPower(adjBuildSpeed, this)) {
						CreateNanoParticle(curBuild->midPos, curBuild->radius * 0.5f, false);
					} else {
						if(!curBuild->beingBuilt && curBuild->health >= curBuild->maxHealth) {
							StopBuild();
						}
					}
				}
			}
		}
		else if(curReclaim && f3SqDist(curReclaim->pos, pos)<Square(buildDistance+curReclaim->radius) && inBuildStance){
			ScriptDecloak(true);

			if (curReclaim->AddBuildPower(-reclaimSpeed, this)) {
				CreateNanoParticle(curReclaim->midPos, curReclaim->radius * 0.7f, true);
			}
		}
		else if(curResurrect && f3SqDist(curResurrect->pos, pos)<Square(buildDistance+curResurrect->radius) && inBuildStance){
			const UnitDef* ud = curResurrect->udef;

			if (ud) {
				if ((modInfo.reclaimMethod != 1) && (curResurrect->reclaimLeft < 1)) {
					// This corpse has been reclaimed a little, need to restore the resources
					// before we can let the player resurrect it.
					curResurrect->AddBuildPower(repairSpeed, this);
				}
				else {
					// Corpse has been restored, begin resurrection
					if (UseEnergy(ud->energyCost * resurrectSpeed / ud->buildTime * modInfo.resurrectEnergyCostFactor)) {
						curResurrect->resurrectProgress+=resurrectSpeed/ud->buildTime;
						CreateNanoParticle(curResurrect->midPos,curResurrect->radius*0.7f,gs->randInt()&1);
					}
					if (curResurrect->resurrectProgress > 1) {
						// resurrect finished
						curResurrect->UnBlock();
						CUnit* u = unitLoader->LoadUnit(ud, curResurrect->pos,
													team, false, curResurrect->buildFacing, this);

						if (!this->unitDef->canBeAssisted) {
							u->soloBuilder = this;
							u->AddDeathDependence(this);
						}
						u->health *= 0.05f;

						CBuilderCAI *cai = (CBuilderCAI *)commandAI;
						for (CUnitSet::iterator it = cai->resurrecters.begin(); it != cai->resurrecters.end(); ++it) {
							CBuilder *bld = (CBuilder *)*it;
							if (bld->commandAI->commandQue.empty())
								continue;
							const Command& c = bld->commandAI->commandQue.front();
							if (c.GetID() != CMD_RESURRECT || c.params.size() != 1)
								continue;
							const int cmdFeatureId = (int)c.params[0];
							if (cmdFeatureId - uh->MaxUnits() == curResurrect->id && teamHandler->Ally(allyteam, bld->allyteam))
								bld->lastResurrected = u->id; // all units that were rezzing shall assist the repair too
						}

						curResurrect->resurrectProgress=0;
						featureHandler->DeleteFeature(curResurrect);
						StopBuild(true);
					}
				}
			} else {
				StopBuild(true);
			}
		}
		else if(curCapture && f3SqDist(curCapture->pos, pos)<Square(buildDistance+curCapture->radius) && inBuildStance){
			if(curCapture->team!=team){

				float captureProgressTemp = curCapture->captureProgress + 1.0f/(150+curCapture->buildTime/captureSpeed*(curCapture->health+curCapture->maxHealth)/curCapture->maxHealth*0.4f);
				if (captureProgressTemp >= 1.0f) {
					captureProgressTemp = 1.0f;
				}

				const float captureFraction = captureProgressTemp - curCapture->captureProgress;
				const float energyUseScaled = curCapture->energyCost * captureFraction * modInfo.captureEnergyCostFactor;

				if (!UseEnergy(energyUseScaled)) {
					teamHandler->Team(team)->energyPull += energyUseScaled;
				} else {
					curCapture->captureProgress = captureProgressTemp;
					CreateNanoParticle(curCapture->midPos,curCapture->radius*0.7f,false);
					if(curCapture->captureProgress >= 1.0f){
						if (!curCapture->ChangeTeam(team, CUnit::ChangeCaptured)) {
							// capture failed
							if (team == gu->myTeam) {
								logOutput.Print("%s: Capture failed, unit type limit reached", unitDef->humanName.c_str());
								logOutput.SetLastMsgPos(pos);
							}
						}
						curCapture->captureProgress=0.0f;
						StopBuild(true);
					}
				}
			} else {
				StopBuild(true);
			}
		}
	}

	CUnit::Update();
}
Пример #6
0
void CFactory::Update()
{
	if (beingBuilt) {
		// factory under construction
		CUnit::Update();
		return;
	}

	if (quedBuild && !opening && !stunned) {
		script->Activate();
		groundBlockingObjectMap->OpenBlockingYard(this, curYardMap);
		opening = true;
	}

	if (quedBuild && inBuildStance && !stunned) {
		// start building a unit
		const float3        buildPos = CalcBuildPos();
		const CSolidObject* solidObj = groundBlockingObjectMap->GroundBlocked(buildPos);

		if (solidObj == NULL || (dynamic_cast<const CUnit*>(solidObj) == this)) {
			quedBuild = false;
			CUnit* b = unitLoader.LoadUnit(nextBuild, buildPos + float3(0.01f, 0.01f, 0.01f), team,
											true, buildFacing, this);

			if (!unitDef->canBeAssisted) {
				b->soloBuilder = this;
				b->AddDeathDependence(this);
			}

			AddDeathDependence(b);
			curBuild = b;

			script->StartBuilding();

			int soundIdx = unitDef->sounds.build.getRandomIdx();
			if (soundIdx >= 0) {
				Channels::UnitReply.PlaySample(
					unitDef->sounds.build.getID(soundIdx), pos,
					unitDef->sounds.build.getVolume(0));
			}
		} else {
			helper->BuggerOff(buildPos - float3(0.01f, 0, 0.02f), radius + 8, true, true, NULL);
		}
	}


	if (curBuild && !beingBuilt) {
		if (!stunned) {
			// factory not under construction and
			// nanolathing unit: continue building
			lastBuild = gs->frameNum;

			// buildPiece is the rotating platform
			const int buildPiece = GetBuildPiece();
			const CMatrix44f& mat = script->GetPieceMatrix(buildPiece);
			const int h = GetHeadingFromVector(mat[2], mat[10]); //! x.z, z.z

			// rotate unit nanoframe with platform
			curBuild->heading = (h + GetHeadingFromFacing(buildFacing)) & 65535;

			const float3 buildPos = CalcBuildPos(buildPiece);
			curBuild->pos = buildPos;

			if (curBuild->floatOnWater) {
				float waterline = ground->GetHeight(buildPos.x, buildPos.z) - curBuild->unitDef->waterline;
				if (waterline > curBuild->pos.y)
					curBuild->pos.y = waterline;
			}
			curBuild->midPos = curBuild->pos + (UpVector * curBuild->relMidPos.y);

			const CCommandQueue& queue = commandAI->commandQue;

			if(!queue.empty() && (queue.front().id == CMD_WAIT)) {
				curBuild->AddBuildPower(0, this);
			} else {
				if (curBuild->AddBuildPower(buildSpeed, this)) {
					CreateNanoParticle();
				}
			}
		}

		if (!curBuild->beingBuilt &&
				(!unitDef->fullHealthFactory ||
						(curBuild->health >= curBuild->maxHealth)))
		{
			if (group && curBuild->group == 0) {
				curBuild->SetGroup(group);
			}

			bool userOrders = true;
			if (curBuild->commandAI->commandQue.empty() ||
					(dynamic_cast<CMobileCAI*>(curBuild->commandAI) &&
					 ((CMobileCAI*)curBuild->commandAI)->unimportantMove)) {
				userOrders = false;

				AssignBuildeeOrders(curBuild);
				waitCommandsAI.AddLocalUnit(curBuild, this);
			}
			eventHandler.UnitFromFactory(curBuild, this, userOrders);

			StopBuild();
		}
	}

	if (((lastBuild + 200) < gs->frameNum) && !stunned &&
	    !quedBuild && opening && groundBlockingObjectMap->CanCloseYard(this)) {
		// close the factory after inactivity
		groundBlockingObjectMap->CloseBlockingYard(this, curYardMap);
		opening = false;
		script->Deactivate();
	}

	CBuilding::Update();
}
Пример #7
0
bool CBuilder::StartBuild(BuildInfo& buildInfo, CFeature*& feature, bool& waitStance)
{
	StopBuild(false);

	buildInfo.pos = CGameHelper::Pos2BuildPos(buildInfo, true);

	// Pass -1 as allyteam to behave like we have maphack.
	// This is needed to prevent building on top of cloaked stuff.
	const CGameHelper::BuildSquareStatus tbs = CGameHelper::TestUnitBuildSquare(buildInfo, feature, -1, true);

	switch (tbs) {
		case CGameHelper::BUILDSQUARE_OPEN:
			break;

		case CGameHelper::BUILDSQUARE_BLOCKED:
		case CGameHelper::BUILDSQUARE_OCCUPIED: {
			// the ground is blocked at the position we want
			// to build at; check if the blocking object is
			// of the same type as our buildee (which means
			// another builder has already started it)
			// note: even if construction has already started,
			// the buildee is *not* guaranteed to be the unit
			// closest to us
			CSolidObject* o = groundBlockingObjectMap->GroundBlocked(buildInfo.pos);
			CUnit* u = NULL;

			if (o != NULL) {
				u = dynamic_cast<CUnit*>(o);
			} else {
				// <pos> might map to a non-blocking portion
				// of the buildee's yardmap, fallback check
				u = CGameHelper::GetClosestFriendlyUnit(NULL, buildInfo.pos, buildDistance, allyteam);
			}

			if (u != NULL && CanAssistUnit(u, buildInfo.def)) {
				curBuild = u;
				AddDeathDependence(u, DEPENDENCE_BUILD);
				ScriptStartBuilding(u->pos, false);
				return true;
			}

			return false;
		}

		case CGameHelper::BUILDSQUARE_RECLAIMABLE:
			// caller should handle this
			return false;
	}

	if ((waitStance = !ScriptStartBuilding(buildInfo.pos, true))) {
		return false;
	}

	const UnitDef* buildeeDef = buildInfo.def;
	const UnitLoadParams buildeeParams = {buildeeDef, this, buildInfo.pos, ZeroVector, -1, team, buildInfo.buildFacing, true, false};

	CUnit* buildee = unitLoader->LoadUnit(buildeeParams);

	// floating structures don't terraform the seabed
	const float groundheight = ground->GetHeightReal(buildee->pos.x, buildee->pos.z);
	const bool onWater = (buildeeDef->floatOnWater && groundheight <= 0.0f);

	if (mapDamage->disabled || !buildeeDef->levelGround || onWater ||
	    (buildeeDef->canmove && (buildeeDef->speed > 0.0f))) {
		// skip the terraforming job
		buildee->terraformLeft = 0;
		buildee->groundLevelled = true;
	} else {
		tx1 = (int)max(    0.0f, (buildee->pos.x - (buildeeDef->xsize * 0.5f * SQUARE_SIZE)) / SQUARE_SIZE);
		tx2 =      min(gs->mapx,             tx1 +  buildeeDef->xsize                                     );
		tz1 = (int)max(    0.0f, (buildee->pos.z - (buildeeDef->zsize * 0.5f * SQUARE_SIZE)) / SQUARE_SIZE);
		tz2 =      min(gs->mapy,             tz1 +  buildeeDef->zsize                                     );

		buildee->terraformLeft = CalculateBuildTerraformCost(buildInfo);
		buildee->groundLevelled = false;

		terraforming    = true;
		terraformType   = Terraform_Building;
		terraformRadius = (tx2 - tx1) * SQUARE_SIZE;
		terraformCenter = buildee->pos;
	}

	if (!this->unitDef->canBeAssisted) {
		buildee->soloBuilder = this;
		buildee->AddDeathDependence(this, DEPENDENCE_BUILDER);
	}

	AddDeathDependence(buildee, DEPENDENCE_BUILD);
	curBuild = buildee;

	/* The ground isn't going to be terraformed.
	 * When the building is completed, it'll 'pop'
	 * into the correct height for the (un-flattened)
	 * terrain it's on.
	 *
	 * To prevent this visual artifact, put the building
	 * at the 'right' height to begin with.
	 */
	curBuild->moveType->SlowUpdate();
	return true;
}
Пример #8
0
void CBuilder::Update()
{
	CBuilderCAI* cai = static_cast<CBuilderCAI*>(commandAI);

	const CCommandQueue& cQueue = cai->commandQue;
	const Command& fCommand = (!cQueue.empty())? cQueue.front(): Command(CMD_STOP);

	nanoPieceCache.Update();

	if (!beingBuilt && !IsStunned()) {
		if (terraforming && inBuildStance) {
			assert(!mapDamage->disabled); // The map should not be deformed in the first place.

			const float* heightmap = readmap->GetCornerHeightMapSynced();
			float terraformScale = 0.1;

			switch (terraformType) {
				case Terraform_Building:
					if (curBuild != NULL) {
						if (curBuild->terraformLeft <= 0)
							terraformScale = 0.0f;
						else
							terraformScale = (terraformSpeed + terraformHelp) / curBuild->terraformLeft;

						curBuild->terraformLeft -= (terraformSpeed + terraformHelp);
						terraformHelp = 0;
						terraformScale = std::min(terraformScale, 1.0f);

						// prevent building from timing out while terraforming for it
						curBuild->AddBuildPower(0.0f, this);

						for (int z = tz1; z <= tz2; z++) {
							for (int x = tx1; x <= tx2; x++) {
								int idx = z * gs->mapxp1 + x;
								float ch = heightmap[idx];

								readmap->AddHeight(idx, (curBuild->pos.y - ch) * terraformScale);
							}
						}

						if (curBuild->terraformLeft <= 0.0f) {
							terraforming = false;
							mapDamage->RecalcArea(tx1, tx2, tz1, tz2);
							curBuild->groundLevelled = true;

							if (luaRules && luaRules->TerraformComplete(this, curBuild)) {
								StopBuild();
							}
						}
					}
					break;
				case Terraform_Restore:
					if (myTerraformLeft <= 0.0f)
						terraformScale = 0.0f;
					else
						terraformScale = (terraformSpeed + terraformHelp) / myTerraformLeft;

					myTerraformLeft -= (terraformSpeed + terraformHelp);
					terraformHelp = 0;
					terraformScale = std::min(terraformScale, 1.0f);

					for (int z = tz1; z <= tz2; z++) {
						for (int x = tx1; x <= tx2; x++) {
							int idx = z * gs->mapxp1 + x;
							float ch = heightmap[idx];
							float oh = readmap->GetOriginalHeightMapSynced()[idx];

							readmap->AddHeight(idx, (oh - ch) * terraformScale);
						}
					}

					if (myTerraformLeft <= 0.0f) {
						terraforming = false;
						mapDamage->RecalcArea(tx1, tx2, tz1, tz2);
						StopBuild();
					}
					break;
			}

			ScriptDecloak(true);
			CreateNanoParticle(terraformCenter, terraformRadius * 0.5f, false);

			for (int z = tz1; z <= tz2; z++) {
				// smooth the borders x
				for (int x = 1; x <= 3; x++) {
					if (tx1 - 3 >= 0) {
						const float ch3 = heightmap[z * gs->mapxp1 + tx1    ];
						const float ch  = heightmap[z * gs->mapxp1 + tx1 - x];
						const float ch2 = heightmap[z * gs->mapxp1 + tx1 - 3];
						const float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale;

						readmap->AddHeight(z * gs->mapxp1 + tx1 - x, amount);
					}
					if (tx2 + 3 < gs->mapx) {
						const float ch3 = heightmap[z * gs->mapxp1 + tx2    ];
						const float ch  = heightmap[z * gs->mapxp1 + tx2 + x];
						const float ch2 = heightmap[z * gs->mapxp1 + tx2 + 3];
						const float amount = ((ch3 * (3 - x) + ch2 * x) / 3 - ch) * terraformScale;

						readmap->AddHeight(z * gs->mapxp1 + tx2 + x, amount);
					}
				}
			}
			for (int z = 1; z <= 3; z++) {
				// smooth the borders z
				for (int x = tx1; x <= tx2; x++) {
					if (tz1 - 3 >= 0) {
						const float ch3 = heightmap[(tz1    ) * gs->mapxp1 + x];
						const float ch  = heightmap[(tz1 - z) * gs->mapxp1 + x];
						const float ch2 = heightmap[(tz1 - 3) * gs->mapxp1 + x];
						const float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale;

						readmap->AddHeight((tz1 - z) * gs->mapxp1 + x, adjust);
					}
					if (tz2 + 3 < gs->mapy) {
						const float ch3 = heightmap[(tz2    ) * gs->mapxp1 + x];
						const float ch  = heightmap[(tz2 + z) * gs->mapxp1 + x];
						const float ch2 = heightmap[(tz2 + 3) * gs->mapxp1 + x];
						const float adjust = ((ch3 * (3 - z) + ch2 * z) / 3 - ch) * terraformScale;

						readmap->AddHeight((tz2 + z) * gs->mapxp1 + x, adjust);
					}
				}
			}
		}
		else if (helpTerraform != NULL && inBuildStance) {
			if (helpTerraform->terraforming) {
				ScriptDecloak(true);

				helpTerraform->terraformHelp += terraformSpeed;
				CreateNanoParticle(helpTerraform->terraformCenter, helpTerraform->terraformRadius * 0.5f, false);
			} else {
				DeleteDeathDependence(helpTerraform, DEPENDENCE_TERRAFORM);
				helpTerraform = NULL;
				StopBuild(true);
			}
		}
		else if (curBuild != NULL && cai->IsInBuildRange(curBuild)) {
			if (fCommand.GetID() == CMD_WAIT) {
				if (curBuild->buildProgress < 1.0f) {
					// prevent buildee from decaying (we cannot call StopBuild here)
					curBuild->AddBuildPower(0.0f, this);
				} else {
					// stop repairing (FIXME: should be much cleaner to let BuilderCAI
					// call this instead when a wait command is given?)
					StopBuild();
				}
			} else {
				if (curBuild->soloBuilder != NULL && (curBuild->soloBuilder != this)) {
					StopBuild();
				} else {
					if (inBuildStance || true) {
						// NOTE:
						//     technically this block of code should be guarded by
						//     "if (inBuildStance)", but doing so can create zombie
						//     guarders because scripts might not set inBuildStance
						//     to true when guard or repair orders are executed and
						//     SetRepairTarget does not check for it
						//
						//     StartBuild *does* ensure construction will not start
						//     until inBuildStance is set to true by the builder's
						//     script, and there are no cases during construction
						//     when inBuildStance can become false yet the buildee
						//     should be kept from decaying, so this is free from
						//     serious side-effects (when repairing, a builder might
						//     start adding build-power before having fully finished
						//     its opening animation)
						//
						ScriptDecloak(true);

						// adjusted build-speed: use repair-speed on units with
						// progress >= 1 rather than raw build-speed on buildees
						// with progress < 1
						float adjBuildSpeed = 0.0f;

						if (curBuild->buildProgress >= 1.0f) {
							adjBuildSpeed = std::min(repairSpeed, unitDef->maxRepairSpeed * 0.5f - curBuild->repairAmount); // repair
						} else {
							adjBuildSpeed = buildSpeed; // build
						}

						if (adjBuildSpeed > 0.0f && curBuild->AddBuildPower(adjBuildSpeed, this)) {
							CreateNanoParticle(curBuild->midPos, curBuild->radius * 0.5f, false);
						} else {
							// check if buildee finished construction
							if (!curBuild->beingBuilt && curBuild->health >= curBuild->maxHealth) {
								StopBuild();
							}
						}
					}
				}
			}
		}
		else if (curReclaim != NULL && f3SqDist(curReclaim->pos, pos)<Square(buildDistance+curReclaim->radius) && inBuildStance) {
			if (fCommand.GetID() == CMD_WAIT) {
				StopBuild();
			} else {
				ScriptDecloak(true);

				if (curReclaim->AddBuildPower(-reclaimSpeed, this)) {
					CreateNanoParticle(curReclaim->midPos, curReclaim->radius * 0.7f, true, (reclaimingUnit && curReclaim->team != team));
				}
			}
		}
		else if (curResurrect != NULL && f3SqDist(curResurrect->pos, pos)<Square(buildDistance+curResurrect->radius) && inBuildStance) {
			const UnitDef* ud = curResurrect->udef;

			if (fCommand.GetID() == CMD_WAIT) {
				StopBuild();
			} else {
				if (ud != NULL) {
					if ((modInfo.reclaimMethod != 1) && (curResurrect->reclaimLeft < 1)) {
						// This corpse has been reclaimed a little, need to restore the resources
						// before we can let the player resurrect it.
						curResurrect->AddBuildPower(repairSpeed, this);
					} else {
						// Corpse has been restored, begin resurrection
						if (UseEnergy(ud->energy * resurrectSpeed / ud->buildTime * modInfo.resurrectEnergyCostFactor)) {
							curResurrect->resurrectProgress += (resurrectSpeed / ud->buildTime);
							CreateNanoParticle(curResurrect->midPos, curResurrect->radius * 0.7f, (gs->randInt() & 1));
						}
						if (curResurrect->resurrectProgress > 1) {
							// resurrect finished
							curResurrect->UnBlock();

							UnitLoadParams resurrecteeParams = {ud, this, curResurrect->pos, ZeroVector, -1, team, curResurrect->buildFacing, false, false};
							CUnit* resurrectee = unitLoader->LoadUnit(resurrecteeParams);

							if (!this->unitDef->canBeAssisted) {
								resurrectee->soloBuilder = this;
								resurrectee->AddDeathDependence(this, DEPENDENCE_BUILDER);
							}

							// TODO: make configurable if this should happen
							resurrectee->health *= 0.05f;

							for (CUnitSet::iterator it = cai->resurrecters.begin(); it != cai->resurrecters.end(); ++it) {
								CBuilder* bld = (CBuilder*) *it;
								CCommandAI* bldCAI = bld->commandAI;

								if (bldCAI->commandQue.empty())
									continue;

								Command& c = bldCAI->commandQue.front();

								if (c.GetID() != CMD_RESURRECT || c.params.size() != 1)
									continue;

								const int cmdFeatureId = c.params[0];

								if (cmdFeatureId - unitHandler->MaxUnits() == curResurrect->id && teamHandler->Ally(allyteam, bld->allyteam)) {
									bld->lastResurrected = resurrectee->id; // all units that were rezzing shall assist the repair too
									c.params[0] = INT_MAX / 2; // prevent FinishCommand from removing this command when the feature is deleted, since it is needed to start the repair
								}
							}

							curResurrect->resurrectProgress = 0;
							featureHandler->DeleteFeature(curResurrect);
							StopBuild(true);
						}
					}
				} else {
					StopBuild(true);
				}
			}
		}
		else if (curCapture != NULL && f3SqDist(curCapture->pos, pos)<Square(buildDistance+curCapture->radius) && inBuildStance) {
			if (fCommand.GetID() == CMD_WAIT) {
				StopBuild();
			} else {
				if (curCapture->team != team) {
					const float captureMagicNumber = (150 + curCapture->buildTime / captureSpeed * (curCapture->health + curCapture->maxHealth) / curCapture->maxHealth * 0.4f);
					const float captureProgressTemp = std::min(curCapture->captureProgress + 1.0f / captureMagicNumber, 1.0f);

					const float captureFraction = captureProgressTemp - curCapture->captureProgress;
					const float energyUseScaled = curCapture->energyCost * captureFraction * modInfo.captureEnergyCostFactor;

					if (!UseEnergy(energyUseScaled)) {
						teamHandler->Team(team)->energyPull += energyUseScaled;
					} else {
						curCapture->captureProgress = captureProgressTemp;
						CreateNanoParticle(curCapture->midPos, curCapture->radius * 0.7f, false, true);

						if (curCapture->captureProgress >= 1.0f) {
							if (!curCapture->ChangeTeam(team, CUnit::ChangeCaptured)) {
								// capture failed
								if (team == gu->myTeam) {
									LOG_L(L_WARNING, "%s: Capture failed, unit type limit reached", unitDef->humanName.c_str());
									eventHandler.LastMessagePosition(pos);
								}
							}
							curCapture->captureProgress = 0.0f;
							StopBuild(true);
						}
					}
				} else {
					StopBuild(true);
				}
			}
		}
	}

	CUnit::Update();
}
Пример #9
0
void CFactory::Update()
{
	if (beingBuilt) {
		// factory under construction
		CUnit::Update();
		return;
	}

	if (quedBuild && !opening && !stunned) {
		cob->Call(COBFN_Activate);
		groundBlockingObjectMap->OpenBlockingYard(this, yardMap);
		opening = true;
	}

	if (quedBuild && inBuildStance && !stunned) {
		// start building a unit
		float3 buildPos = CalcBuildPos();

		bool canBuild = true;
		std::vector<CUnit*> units = qf->GetUnitsExact(buildPos, 16);

		for (std::vector<CUnit*>::iterator ui = units.begin(); ui != units.end(); ++ui) {
			if ((*ui) != this)
				canBuild = false;
		}

		if (canBuild) {
			quedBuild = false;
			CUnit* b = unitLoader.LoadUnit(nextBuild, buildPos + float3(0.01f, 0.01f, 0.01f), team,
											true, buildFacing, this);
			b->lineage = this->lineage;

			if (!unitDef->canBeAssisted) {
				b->soloBuilder = this;
				b->AddDeathDependence(this);
			}

			AddDeathDependence(b);
			curBuild = b;

			cob->Call("StartBuilding");

			int soundIdx = unitDef->sounds.build.getRandomIdx();
			if (soundIdx >= 0) {
				sound->PlaySample(
					unitDef->sounds.build.getID(soundIdx), pos,
					unitDef->sounds.build.getVolume(0));
			}
		} else {
			helper->BuggerOff(buildPos - float3(0.01f, 0, 0.02f), radius + 8);
		}
	}


	if (curBuild && !beingBuilt) {
		if (!stunned) {
			// factory not under construction and
			// nanolathing unit: continue building
			lastBuild = gs->frameNum;
	
			// buildPiece is the rotating platform
			const int buildPiece = GetBuildPiece();
			CMatrix44f mat = localmodel->GetPieceMatrix(buildPiece);
			const int h = GetHeadingFromVector(mat[2], mat[10]);
	
			// rotate unit nanoframe with platform
			curBuild->heading = (h + GetHeadingFromFacing(buildFacing)) & 65535;
	
			const float3 buildPos = CalcBuildPos(buildPiece);
			curBuild->pos = buildPos;
	
			if (curBuild->floatOnWater) {
				curBuild->pos.y  = ground->GetHeight(buildPos.x, buildPos.z);
				curBuild->pos.y -= curBuild->unitDef->waterline;
			}
			curBuild->midPos = curBuild->pos + (UpVector * curBuild->relMidPos.y);
	
			const CCommandQueue& queue = commandAI->commandQue;
	
			if(!queue.empty() && (queue.front().id == CMD_WAIT)) {
				curBuild->AddBuildPower(0, this);
			} else {
				if (curBuild->AddBuildPower(buildSpeed, this)) {
					CreateNanoParticle();
				}
			}
		}

		if (!curBuild->beingBuilt &&
				(!unitDef->fullHealthFactory ||
						(curBuild->health >= curBuild->maxHealth)))
		{
			if (group && curBuild->group == 0) {
				curBuild->SetGroup(group);
			}

			bool userOrders = true;
			if (curBuild->commandAI->commandQue.empty() ||
					(dynamic_cast<CMobileCAI*>(curBuild->commandAI) &&
					 ((CMobileCAI*)curBuild->commandAI)->unimportantMove)) {
				userOrders = false;
				const CFactoryCAI* facAI = (CFactoryCAI*) commandAI;
				const CCommandQueue& newUnitCmds = facAI->newUnitCommands;

				if (newUnitCmds.empty()) {
					SendToEmptySpot(curBuild);
				} else {
					// XXX the pathfinder sometimes... makes mistakes, try to hack around it
					// XXX note this qualifies as HACK HACK HACK
					float3 testpos = curBuild->pos + frontdir * (this->radius - 1.0f);
					Command c;
					c.id = CMD_MOVE;
					c.params.push_back(testpos.x);
					c.params.push_back(testpos.y);
					c.params.push_back(testpos.z);
					curBuild->commandAI->GiveCommand(c);

					for (CCommandQueue::const_iterator ci = newUnitCmds.begin(); ci != newUnitCmds.end(); ++ci) {
						c = *ci;
						c.options |= SHIFT_KEY;
						curBuild->commandAI->GiveCommand(c);
					}
				}
				waitCommandsAI.AddLocalUnit(curBuild, this);
			}
			eventHandler.UnitFromFactory(curBuild, this, userOrders);

			StopBuild();
		}
	}

	if (((lastBuild + 200) < gs->frameNum) && !stunned &&
	    !quedBuild && opening && groundBlockingObjectMap->CanCloseYard(this)) {
		// close the factory after inactivity
		groundBlockingObjectMap->CloseBlockingYard(this, yardMap);
		opening = false;
		cob->Call(COBFN_Deactivate);
	}

	CBuilding::Update();
}