Пример #1
0
static void CancelBuilt(COrder_Built &order, CUnit &unit)
{
	Assert(unit.CurrentOrder() == &order);
	CUnit *worker = order.GetWorkerPtr();

	// Drop out unit
	if (worker != NULL) {
		worker->ClearAction();

		DropOutOnSide(*worker, LookingW, &unit);
	}
	// Player gets back 75% of the original cost for a building.
	unit.Player->AddCostsFactor(unit.Stats->Costs, CancelBuildingCostsFactor);
	// Cancel building
	LetUnitDie(unit);
}
Пример #2
0
/**
**  Unit upgrades unit!
**
**  @param unit  Pointer to unit.
*/
void HandleActionUpgradeTo(CUnit &unit)
{
    if (!unit.SubAction) { // first entry
        unit.Data.UpgradeTo.Ticks = 0;
        unit.SubAction = 1;
    }
    unit.Type->Animations->Upgrade ?
    UnitShowAnimation(unit, unit.Type->Animations->Upgrade) :
    UnitShowAnimation(unit, unit.Type->Animations->Still);
    if (unit.Wait) {
        unit.Wait--;
        return;
    }
    CPlayer *player = unit.Player;
    CUnitType &newtype = *unit.CurrentOrder()->Arg1.Type;
    const CUnitStats *newstats = &newtype.Stats[player->Index];

    // FIXME: Should count down here
    unit.Data.UpgradeTo.Ticks += SpeedUpgrade;
    if (unit.Data.UpgradeTo.Ticks < newstats->Costs[TimeCost]) {
        unit.Wait = CYCLES_PER_SECOND / 6;
        return;
    }

    unit.ClearAction();
    unit.State = 0;

    if (TransformUnitIntoType(unit, newtype) == 0) {
        player->Notify(NotifyGreen, unit.tilePos.x, unit.tilePos.y,
                       _("Upgrade to %s canceled"), newtype.Name.c_str());
        return ;
    }
    //  Warn AI.
    if (player->AiEnabled) {
        AiUpgradeToComplete(unit, newtype);
    }
    player->Notify(NotifyGreen, unit.tilePos.x, unit.tilePos.y,
                   _("Upgrade to %s complete"), unit.Type->Name.c_str());
}
Пример #3
0
/**
**  Repair a unit.
**
**  @param unit  unit repairing
**  @param goal  unit being repaired
*/
static void RepairUnit(CUnit &unit, CUnit &goal)
{
	CPlayer *player;
	int animlength;
	int hp;
	char buf[100];

	player = unit.Player;

	if (goal.CurrentAction() != UnitActionBuilt) {
		//
		// Calculate the repair costs.
		//
		Assert(goal.Stats->Variables[HP_INDEX].Max);

		//
		// Check if enough resources are available
		//
		for (int i = 1; i < MaxCosts; ++i) {
			if (player->Resources[i] < goal.Type->RepairCosts[i]) {
				snprintf(buf, 100, _("We need more %s for repair!"),
					DefaultResourceNames[i].c_str());
				player->Notify(NotifyYellow, unit.tilePos.x, unit.tilePos.y, buf);
				if (player->AiEnabled) {
					// FIXME: call back to AI?
					unit.CurrentOrder()->ClearGoal();
					if (!unit.RestoreOrder()) {
						unit.ClearAction();
						unit.State = 0;
					}
				}
				// FIXME: We shouldn't animate if no resources are available.
				return;
			}
		}
		//
		// Subtract the resources
		//
		player->SubCosts(goal.Type->RepairCosts);

		goal.Variable[HP_INDEX].Value += goal.Type->RepairHP;
		if (goal.Variable[HP_INDEX].Value > goal.Variable[HP_INDEX].Max) {
			goal.Variable[HP_INDEX].Value = goal.Variable[HP_INDEX].Max;
		}
	} else {
		int costs = goal.Stats->Costs[TimeCost] * 600;
		// hp is the current damage taken by the unit.
		hp = (goal.Data.Built.Progress * goal.Variable[HP_INDEX].Max) /
			costs - goal.Variable[HP_INDEX].Value;
		//
		// Calculate the length of the attack (repair) anim.
		//
		animlength = unit.Data.Repair.Cycles;
		unit.Data.Repair.Cycles = 0;

		// FIXME: implement this below:
		//unit.Data.Built.Worker->Type->BuilderSpeedFactor;
		goal.Data.Built.Progress += 100 * animlength * SpeedBuild;
		// Keep the same level of damage while increasing HP.
		goal.Variable[HP_INDEX].Value =
			(goal.Data.Built.Progress * goal.Stats->Variables[HP_INDEX].Max) /
			costs - hp;
		if (goal.Variable[HP_INDEX].Value > goal.Variable[HP_INDEX].Max) {
			goal.Variable[HP_INDEX].Value = goal.Variable[HP_INDEX].Max;
		}
	}
}
Пример #4
0
/**
**  Unit repairs
**
**  @param unit  Unit, for that the attack is handled.
*/
void HandleActionRepair(CUnit &unit)
{
	CUnit *goal;
	int err;

	switch (unit.SubAction) {
		case 0:
			NewResetPath(unit);
			unit.SubAction = 1;
			// FALL THROUGH
		//
		// Move near to target.
		//
		case 1:
			// FIXME: RESET FIRST!! Why? We move first and than check if
			// something is in sight.
			err = DoActionMove(unit);
			if (!unit.Anim.Unbreakable) {
				//
				// No goal: if meeting damaged building repair it.
				//
				goal = unit.CurrentOrder()->GetGoal();

				//
				// Target is dead, choose new one.
				//
				// Check if goal is correct unit.
				if (goal) {
					if (!goal->IsVisibleAsGoal(unit.Player)) {
						DebugPrint("repair target gone.\n");
						unit.CurrentOrder()->goalPos = goal->tilePos;
						// FIXME: should I clear this here?
						unit.CurrentOrder()->ClearGoal();
						goal = NULL;
						NewResetPath(unit);
					}
				} else if (unit.Player->AiEnabled) {
					// Ai players workers should stop if target is killed
					err = -1;
				}

				//
				// Have reached target? FIXME: could use return value
				//
				if (goal && unit.MapDistanceTo(*goal) <= unit.Type->RepairRange &&
						goal->Variable[HP_INDEX].Value < goal->Variable[HP_INDEX].Max) {
					unit.State = 0;
					unit.SubAction = 2;
					unit.Data.Repair.Cycles = 0;
					const Vec2i dir = goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos;
					UnitHeadingFromDeltaXY(unit, dir);
				} else if (err < 0) {
					unit.CurrentOrder()->ClearGoal();
					if (!unit.RestoreOrder()) {
						unit.ClearAction();
						unit.State = 0;
					}
					return;
				}

				// FIXME: Should be it already?
				Assert(unit.CurrentAction() == UnitActionRepair);
			}
			break;

		//
		// Repair the target.
		//
		case 2:
			AnimateActionRepair(unit);
			unit.Data.Repair.Cycles++;
			if (!unit.Anim.Unbreakable) {
				goal = unit.CurrentOrder()->GetGoal();

				//
				// Target is dead, choose new one.
				//
				// Check if goal is correct unit.
				// FIXME: should I do a function for this?
				if (goal) {
					if (!goal->IsVisibleAsGoal(unit.Player)) {
						DebugPrint("repair goal is gone\n");
						unit.CurrentOrder()->goalPos = goal->tilePos;
						// FIXME: should I clear this here?
						unit.CurrentOrder()->ClearGoal();
						goal = NULL;
						NewResetPath(unit);
					} else {
						int dist = unit.MapDistanceTo(*goal);
						if (dist <= unit.Type->RepairRange) {
							RepairUnit(unit, *goal);
							goal = unit.CurrentOrder()->GetGoal();
						} else if (dist > unit.Type->RepairRange) {
							// If goal has move, chase after it
							unit.State = 0;
							unit.SubAction = 0;
						}
					}
				}


				//
				// Target is fine, choose new one.
				//
				if (!goal || goal->Variable[HP_INDEX].Value >=
					 goal->Variable[HP_INDEX].Max) {
					unit.CurrentOrder()->ClearGoal();
					if (!unit.RestoreOrder()) {
						unit.ClearAction();
						unit.State = 0;
					}
					return;
				}

				// FIXME: automatic repair
			}
			break;
	}
}
Пример #5
0
static void Finish(COrder_Built &order, CUnit &unit)
{
	const CUnitType &type = *unit.Type;
	CPlayer &player = *unit.Player;

	DebugPrint("%d: Building %s(%s) ready.\n" _C_ player.Index _C_ type.Ident.c_str() _C_ type.Name.c_str());

	// HACK: the building is ready now
	player.UnitTypesCount[type.Slot]++;
	if (unit.Active) {
		player.UnitTypesAiActiveCount[type.Slot]++;
	}
	unit.Constructed = 0;
	if (unit.Frame < 0) {
		unit.Frame = -1;
	} else {
		unit.Frame = 0;
	}
	CUnit *worker = order.GetWorkerPtr();

	if (worker != NULL) {
		if (type.BoolFlag[BUILDERLOST_INDEX].value) {
			// Bye bye worker.
			LetUnitDie(*worker);
			worker = NULL;
		} else { // Drop out the worker.
			worker->ClearAction();

			DropOutOnSide(*worker, LookingW, &unit);

			// If we can harvest from the new building, do it.
			if (worker->Type->ResInfo[type.GivesResource]) {
				CommandResource(*worker, unit, 0);
			}
			// If we can reurn goods to a new depot, do it.
			if (worker->CurrentResource && worker->ResourcesHeld > 0 && type.CanStore[worker->CurrentResource]) {
				CommandReturnGoods(*worker, &unit, 0);
			}
		}
	}

	if (type.GivesResource && type.StartingResources != 0) {
		// Has StartingResources, Use those
		unit.ResourcesHeld = type.StartingResources;
	}

	player.Notify(NotifyGreen, unit.tilePos, _("New %s done"), type.Name.c_str());
	if (&player == ThisPlayer) {
		if (type.MapSound.Ready.Sound) {
			PlayUnitSound(unit, VoiceReady);
		} else if (worker) {
			PlayUnitSound(*worker, VoiceWorkCompleted);
		} else {
			PlayUnitSound(unit, VoiceBuilding);
		}
	}

	if (player.AiEnabled) {
		/* Worker can be NULL */
		AiWorkComplete(worker, unit);
	}

	// FIXME: Vladi: this is just a hack to test wall fixing,
	// FIXME:  also not sure if the right place...
	// FIXME: Johns: hardcoded unit-type wall / more races!
	if (&type == UnitTypeOrcWall || &type == UnitTypeHumanWall) {
		Map.SetWall(unit.tilePos, &type == UnitTypeHumanWall);
		unit.Remove(NULL);
		UnitLost(unit);
		UnitClearOrders(unit);
		unit.Release();
		return ;
	}

	UpdateForNewUnit(unit, 0);

	// Set the direction of the building if it supports them
	if (type.NumDirections > 1 && type.BoolFlag[NORANDOMPLACING_INDEX].value == false) {
		if (type.BoolFlag[WALL_INDEX].value) { // Special logic for walls
			CorrectWallDirections(unit);
			CorrectWallNeighBours(unit);
		} else {
			unit.Direction = (MyRand() >> 8) & 0xFF; // random heading
		}
		UnitUpdateHeading(unit);
	}

	if (IsOnlySelected(unit) || &player == ThisPlayer) {
		SelectedUnitChanged();
	}
	MapUnmarkUnitSight(unit);
	unit.CurrentSightRange = unit.Stats->Variables[SIGHTRANGE_INDEX].Max;
	MapMarkUnitSight(unit);
	order.Finished = true;
}