Ejemplo n.º 1
0
/**
**  Auto attack nearby units if possible
*/
bool AutoAttack(CUnit &unit)
{
	if (unit.Type->CanAttack == false) {
		return false;
	}
	// Normal units react in reaction range.
	CUnit *goal = AttackUnitsInReactRange(unit);

	if (goal == NULL) {
		return false;
	}
	COrder *savedOrder = NULL;

	if (unit.CurrentAction() == UnitActionStill) {
		savedOrder = COrder::NewActionAttack(unit, unit.tilePos);
	} else if (unit.CanStoreOrder(unit.CurrentOrder())) {
		savedOrder = unit.CurrentOrder()->Clone();
	}
	// Weak goal, can choose other unit, come back after attack
	CommandAttack(unit, goal->tilePos, NULL, FlushCommands);

	if (savedOrder != NULL) {
		unit.SavedOrder = savedOrder;
	}
	return true;
}
Ejemplo n.º 2
0
/**
**	@brief	Auto attack nearby units if possible
*/
bool AutoAttack(CUnit &unit)
{
	//Wyrmgus start
//	if (unit.Type->CanAttack == false) {
	if (unit.CanAttack() == false) {
	//Wyrmgus end
		return false;
	}
	// Normal units react in reaction range.
	CUnit *goal = AttackUnitsInReactRange(unit);

	if (goal == nullptr) {
		return false;
	}
	COrder *savedOrder = nullptr;

	if (unit.CurrentAction() == UnitActionStill) {
		//Wyrmgus start
//		savedOrder = COrder::NewActionAttack(unit, unit.tilePos);
		savedOrder = COrder::NewActionAttack(unit, unit.tilePos, unit.MapLayer->ID);
		//Wyrmgus end
	} else if (unit.CanStoreOrder(unit.CurrentOrder())) {
		savedOrder = unit.CurrentOrder()->Clone();
	}
	// Weak goal, can choose other unit, come back after attack
	CommandAttack(unit, goal->tilePos, nullptr, FlushCommands, goal->MapLayer->ID);

	if (savedOrder != nullptr) {
		unit.SavedOrder = savedOrder;
	}
	return true;
}
Ejemplo n.º 3
0
/**
**  Auto repair a unit if possible
**
**  @return  true if the unit is repairing, false otherwise
*/
bool AutoRepair(CUnit &unit)
{
	const int repairRange = unit.Type->DefaultStat.Variables[AUTOREPAIRRANGE_INDEX].Value;

	if (unit.AutoRepair == false || repairRange == 0) {
		return false;
	}
	CUnit *repairedUnit = UnitToRepairInRange(unit, repairRange);

	if (repairedUnit == NULL) {
		return false;
	}
	const Vec2i invalidPos(-1, -1);
	COrder *savedOrder = NULL;
	if (unit.CanStoreOrder(unit.CurrentOrder())) {
		savedOrder = unit.CurrentOrder()->Clone();
	}

	//Command* will clear unit.SavedOrder
	CommandRepair(unit, invalidPos, repairedUnit, FlushCommands);
	if (savedOrder != NULL) {
		unit.SavedOrder = savedOrder;
	}
	return true;
}
Ejemplo n.º 4
0
/**
**  Auto repair a unit if possible
**
**  @return  true if the unit is repairing, false otherwise
*/
bool AutoRepair(CUnit &unit)
{
	//Wyrmgus start
//	const int repairRange = unit.Type->DefaultStat.Variables[AUTOREPAIRRANGE_INDEX].Value;
	const int repairRange = unit.Variable[AUTOREPAIRRANGE_INDEX].Value;
	//Wyrmgus end

	if (unit.AutoRepair == false || repairRange == 0) {
		return false;
	}
	CUnit *repairedUnit = UnitToRepairInRange(unit, repairRange);

	if (repairedUnit == nullptr) {
		return false;
	}
	const Vec2i invalidPos(-1, -1);
	COrder *savedOrder = nullptr;
	if (unit.CanStoreOrder(unit.CurrentOrder())) {
		savedOrder = unit.CurrentOrder()->Clone();
	}

	//Command* will clear unit.SavedOrder
	CommandRepair(unit, invalidPos, repairedUnit, FlushCommands, repairedUnit->MapLayer->ID);
	if (savedOrder != nullptr) {
		unit.SavedOrder = savedOrder;
	}
	return true;
}
Ejemplo n.º 5
0
/**
**  Check if the spell can be auto cast and cast it.
**
**  @param caster    Unit who can cast the spell.
**  @param spell     Spell-type pointer.
**
**  @return          1 if spell is casted, 0 if not.
*/
int AutoCastSpell(CUnit &caster, const SpellType &spell)
{
	//  Check for mana and cooldown time, trivial optimization.
	if (!SpellIsAvailable(*caster.Player, spell.Slot)
		|| caster.Variable[MANA_INDEX].Value < spell.ManaCost
		|| caster.SpellCoolDownTimers[spell.Slot]) {
		return 0;
	}
	Target *target = SelectTargetUnitsOfAutoCast(caster, spell);
	if (target == NULL) {
		return 0;
	} else {
		// Save previous order
		COrder *savedOrder = NULL;
		if (caster.CurrentAction() != UnitActionStill && caster.CanStoreOrder(caster.CurrentOrder())) {
			savedOrder = caster.CurrentOrder()->Clone();
		}
		// Must move before ?
		CommandSpellCast(caster, target->targetPos, target->Unit, spell, FlushCommands);
		delete target;
		if (savedOrder != NULL) {
			caster.SavedOrder = savedOrder;
		}
	}
	return 1;
}
Ejemplo n.º 6
0
/**
**  Repair a unit.
**
**  @param unit  unit repairing
**  @param goal  unit being repaired
**
**  @return true when action is finished/canceled.
*/
bool COrder_Repair::RepairUnit(const CUnit &unit, CUnit &goal)
{
	CPlayer &player = *unit.Player;

	if (goal.CurrentAction() == UnitActionBuilt) {
		COrder_Built &order = *static_cast<COrder_Built *>(goal.CurrentOrder());

		order.ProgressHp(goal, 100 * this->RepairCycle);
		this->RepairCycle = 0;
		if (ResourcesMultiBuildersMultiplier && SubRepairCosts(unit, player, goal)) {
			return true;
		}
		return false;
	}
	if (goal.Variable[HP_INDEX].Value >= goal.Variable[HP_INDEX].Max) {
		return true;
	}

	Assert(goal.Stats->Variables[HP_INDEX].Max);

	if (SubRepairCosts(unit, player, goal)) {
		return true;
	}

	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;
		return true;
	}
	return false;
}
Ejemplo n.º 7
0
/**
**  Show Map Location
**
**  @param l  Lua state.
*/
static int CclShowMapLocation(lua_State *l)
{
	// Put a unit on map, use its properties, except for
	// what is listed below

	LuaCheckArgs(l, 4);
	const char *unitname = LuaToString(l, 5);
	CUnitType* unitType = UnitTypeByIdent(unitname);
	if (!unitType) {
		DebugPrint("Unable to find UnitType '%s'" _C_ unitname);
		return 0;
	}
	CUnit *target = MakeUnit(*unitType, ThisPlayer);
	if (target != NoUnitP) {
		target->CurrentOrder()->Action = UnitActionStill;
		target->Variable[HP_INDEX].Value = 0;
		target->tilePos.x = LuaToNumber(l, 1);
		target->tilePos.y = LuaToNumber(l, 2);
		target->TTL = GameCycle + LuaToNumber(l, 4);
		target->CurrentSightRange = LuaToNumber(l, 3);
		MapMarkUnitSight(*target);
	} else {
		DebugPrint("Unable to allocate Unit");
	}
	return 0;
}
Ejemplo n.º 8
0
/* virtual */ void COrder_Train::UpdateUnitVariables(CUnit &unit) const
{
	Assert(unit.CurrentOrder() == this);

	unit.Variable[TRAINING_INDEX].Value = this->Ticks;
	unit.Variable[TRAINING_INDEX].Max = this->Type->Stats[unit.Player->Index].Costs[TimeCost];
}
Ejemplo n.º 9
0
/* virtual */ void CAnimation_Rotate::Action(CUnit &unit, int &/*move*/, int /*scale*/) const
{
	Assert(unit.Anim.Anim == this);

	if (!strcmp(this->rotateStr.c_str(), "target") && unit.CurrentOrder()->HasGoal()) {
		COrder &order = *unit.CurrentOrder();
		const CUnit &target = *order.GetGoal();
		if (target.Destroyed) {
			order.ClearGoal();
			return;
		}
		const Vec2i pos = target.tilePos + target.Type->GetHalfTileSize() - unit.tilePos;
		UnitHeadingFromDeltaXY(unit, pos);
	} else {
		UnitRotate(unit, ParseAnimInt(unit, this->rotateStr.c_str()));
	}
}
Ejemplo n.º 10
0
/**
**  Cancel the building construction, or kill a unit.
**
**  @param unit  pointer to unit.
*/
void CommandDismiss(CUnit &unit)
{
	// Check if building is still under construction? (NETWORK!)
	if (unit.CurrentAction() == UnitActionBuilt) {
		unit.CurrentOrder()->Cancel(unit);
	} else {
		DebugPrint("Suicide unit ... \n");
		LetUnitDie(unit, true);
	}
	ClearSavedAction(unit);
}
Ejemplo n.º 11
0
/* virtual */ void COrder_Built::UpdateUnitVariables(CUnit &unit) const
{
	Assert(unit.CurrentOrder() == this);

	unit.Variable[BUILD_INDEX].Value = this->ProgressCounter;
	unit.Variable[BUILD_INDEX].Max = unit.Type->Stats[unit.Player->Index].Costs[TimeCost] * 600;

	// This should happen when building unit with several peons
	// Maybe also with only one.
	// FIXME : Should be better to fix it in action_{build,repair}.c ?
	unit.Variable[BUILD_INDEX].Value = std::min(unit.Variable[BUILD_INDEX].Max, unit.Variable[BUILD_INDEX].Value);
}
Ejemplo n.º 12
0
/**
**  Cancel Building researching.
**
**  @param unit  Pointer to unit.
*/
void CommandCancelResearch(CUnit &unit)
{
	// Check if unit is still researching? (NETWORK!)
	if (unit.CurrentAction() == UnitActionResearch) {
		unit.CurrentOrder()->Cancel(unit);
		RemoveOrder(unit, 0);
		if (!Selected.empty()) {
			SelectedUnitChanged();
		}
	}
	ClearSavedAction(unit);
}
Ejemplo n.º 13
0
/**
**  Cancel building upgrading to.
**
**  @param unit  pointer to unit.
*/
void CommandCancelUpgradeTo(CUnit &unit)
{
	// Check if unit is still upgrading? (NETWORK!)
	if (unit.CurrentAction() == UnitActionUpgradeTo) {
		unit.CurrentOrder()->Cancel(unit);
		RemoveOrder(unit, 0);
		if (Selected) {
			SelectedUnitChanged();
		}
	}
	ClearSavedAction(unit);
}
Ejemplo n.º 14
0
static bool IsReadyToRepair(const CUnit &unit)
{
    if (unit.IsIdle()) {
        return true;
    } else if (unit.Orders.size() == 1 && unit.CurrentAction() == UnitActionResource) {
        COrder_Resource &order = *static_cast<COrder_Resource *>(unit.CurrentOrder());

        if (order.IsGatheringStarted() == false) {
            return true;
        }
    }
    return false;
}
Ejemplo n.º 15
0
static bool DrawUnitInfo_single_selection(const CUnit &unit)
{
	switch (unit.CurrentAction()) {
		case UnitActionTrain: { //  Building training units.
			DrawUnitInfo_Training(unit);
			return true;
		}
		case UnitActionUpgradeTo: { //  Building upgrading to better type.
			if (UI.UpgradingButton) {
				const COrder_UpgradeTo &order = *static_cast<COrder_UpgradeTo *>(unit.CurrentOrder());
				CIcon &icon = *order.GetUnitType().Icon.Icon;
				unsigned int flag = (ButtonAreaUnderCursor == ButtonAreaUpgrading
									 && ButtonUnderCursor == 0) ?
									(IconActive | (MouseButtons & LeftButton)) : 0;
				const PixelPos pos(UI.UpgradingButton->X, UI.UpgradingButton->Y);
				icon.DrawUnitIcon(*UI.UpgradingButton->Style, flag, pos, "", unit.RescuedFrom 
					? GameSettings.Presets[unit.RescuedFrom->Index].PlayerColor 
					: GameSettings.Presets[unit.Player->Index].PlayerColor);
			}
			return true;
		}
		case UnitActionResearch: { //  Building research new technology.
			if (UI.ResearchingButton) {
				COrder_Research &order = *static_cast<COrder_Research *>(unit.CurrentOrder());
				CIcon &icon = *order.GetUpgrade().Icon;
				int flag = (ButtonAreaUnderCursor == ButtonAreaResearching
							&& ButtonUnderCursor == 0) ?
						   (IconActive | (MouseButtons & LeftButton)) : 0;
				PixelPos pos(UI.ResearchingButton->X, UI.ResearchingButton->Y);
				icon.DrawUnitIcon(*UI.ResearchingButton->Style, flag, pos, "", unit.RescuedFrom 
					? GameSettings.Presets[unit.RescuedFrom->Index].PlayerColor 
					: GameSettings.Presets[unit.Player->Index].PlayerColor);
			}
			return true;
		}
		default:
			return false;
	}
}
Ejemplo n.º 16
0
static void DrawUnitInfo_Training(const CUnit &unit)
{
	if (unit.Orders.size() == 1 || unit.Orders[1]->Action != UnitActionTrain) {
		if (!UI.SingleTrainingText.empty()) {
			CLabel label(*UI.SingleTrainingFont);
			label.Draw(UI.SingleTrainingTextX, UI.SingleTrainingTextY, UI.SingleTrainingText);
		}
		if (UI.SingleTrainingButton) {
			const COrder_Train &order = *static_cast<COrder_Train *>(unit.CurrentOrder());
			CIcon &icon = *order.GetUnitType().Icon.Icon;
			//Wyrmgus start
//			const unsigned int flags = (ButtonAreaUnderCursor == ButtonAreaTraining && ButtonUnderCursor == 0) ?
			unsigned int flags = (ButtonAreaUnderCursor == ButtonAreaTraining && ButtonUnderCursor == 0) ?
			//Wyrmgus end
									   (IconActive | (MouseButtons & LeftButton)) : 0;
			//Wyrmgus start
			flags |= IconCommandButton;
			//Wyrmgus end
			const PixelPos pos(UI.SingleTrainingButton->X, UI.SingleTrainingButton->Y);
			icon.DrawUnitIcon(*UI.SingleTrainingButton->Style, flags, pos, "", unit.RescuedFrom ? unit.RescuedFrom->Index : unit.Player->Index);
		}
	} else {
		if (!UI.TrainingText.empty()) {
			CLabel label(*UI.TrainingFont);
			label.Draw(UI.TrainingTextX, UI.TrainingTextY, UI.TrainingText);
		}
		if (!UI.TrainingButtons.empty()) {
			for (size_t i = 0; i < unit.Orders.size()
				 && i < UI.TrainingButtons.size(); ++i) {
				if (unit.Orders[i]->Action == UnitActionTrain) {
					const COrder_Train &order = *static_cast<COrder_Train *>(unit.Orders[i]);
					CIcon &icon = *order.GetUnitType().Icon.Icon;
					//Wyrmgus start
//					const int flag = (ButtonAreaUnderCursor == ButtonAreaTraining
					int flag = (ButtonAreaUnderCursor == ButtonAreaTraining
					//Wyrmgus end
									  && static_cast<size_t>(ButtonUnderCursor) == i) ?
									 (IconActive | (MouseButtons & LeftButton)) : 0;
					const PixelPos pos(UI.TrainingButtons[i].X, UI.TrainingButtons[i].Y);
					//Wyrmgus start
					flag |= IconCommandButton;
					//Wyrmgus end
					icon.DrawUnitIcon(*UI.TrainingButtons[i].Style, flag, pos, "", unit.RescuedFrom ? unit.RescuedFrom->Index : unit.Player->Index);
				}
			}
		}
	}
}
Ejemplo n.º 17
0
int GetNumWaitingWorkers(const CUnit &mine)
{
	int ret = 0;
	CUnit *worker = mine.Resource.Workers;

	for (int i = 0; NULL != worker; worker = worker->NextWorker, ++i) {
		Assert(worker->CurrentAction() == UnitActionResource);
		COrder_Resource &order = *static_cast<COrder_Resource *>(worker->CurrentOrder());

		if (order.IsGatheringWaiting()) {
			ret++;
		}
		Assert(i <= mine.Resource.Assigned);
	}
	return ret;
}
Ejemplo n.º 18
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);
}
Ejemplo n.º 19
0
/**
**  Make one or more unit leave the transporter.
**
**  @return false if action should continue
*/
bool COrder_Unload::LeaveTransporter(CUnit &transporter)
{
	int stillonboard = 0;

	// Goal is the specific unit unit that you want to unload.
	// This can be NULL, in case you want to unload everything.
	if (this->HasGoal()) {
		CUnit &goal = *this->GetGoal();

		if (goal.Destroyed) {
			DebugPrint("destroyed unit unloading?\n");
			this->ClearGoal();
			return true;
		}
		transporter.CurrentOrder()->ClearGoal();
		// Try to unload the unit. If it doesn't work there is no problem.
		if (UnloadUnit(transporter, goal)) {
			this->ClearGoal();
		} else {
			++stillonboard;
		}
	} else {
		// Unload all units.
		CUnit *goal = transporter.UnitInside;
		for (int i = transporter.InsideCount; i; --i, goal = goal->NextContained) {
			if (goal->Boarded) {
				if (!UnloadUnit(transporter, *goal)) {
					++stillonboard;
				}
			}
		}
	}
	if (IsOnlySelected(transporter)) {
		SelectedUnitChanged();
	}

	// We still have some units to unload, find a piece of free coast.
	if (stillonboard) {
		// We tell it to unload at it's current position. This can't be done,
		// so it will search for a piece of free coast nearby.
		this->State = 0;
		return false;
	} else {
		return true;
	}
}
Ejemplo n.º 20
0
void COrder_Built::Boost(CUnit &building, int amount, int varIndex) const
{
	Assert(building.CurrentOrder() == this);

	const int costs = building.Stats->Costs[TimeCost] * 600;
	const int progress = this->ProgressCounter;
	const int newProgress = progress + std::max(1, amount * building.Player->SpeedBuild / SPEEDUP_FACTOR);
	const int maxValue = building.Variable[varIndex].Max;

	int &currentValue = building.Variable[varIndex].Value;

	// damageValue is the current damage taken by the unit.
	const int damageValue = (progress * maxValue) / costs - currentValue;

	// Keep the same level of damage while increasing Value.
	currentValue = (newProgress * maxValue) / costs - damageValue;
	currentValue = std::min(currentValue, maxValue);
}
Ejemplo n.º 21
0
/**
**  Cancel the training of an unit.
**
**  @param unit  pointer to unit.
**  @param slot  slot number to cancel.
**  @param type  Unit-type to cancel.
*/
void CommandCancelTraining(CUnit &unit, int slot, const CUnitType *type)
{
	DebugPrint("Cancel %d type: %s\n" _C_ slot _C_
			   type ? type->Ident.c_str() : "-any-");

	ClearSavedAction(unit);

	// Check if unit is still training 'slot'? (NETWORK!)
	if (slot == -1) {
		// Cancel All training
		while (unit.CurrentAction() == UnitActionTrain) {
			unit.CurrentOrder()->Cancel(unit);
			RemoveOrder(unit, 0);
		}
		if (unit.Player == ThisPlayer && unit.Selected) {
			SelectedUnitChanged();
		}
	} else if (unit.Orders.size() <= static_cast<size_t>(slot)) {
		// Order has moved
		return;
	} else if (unit.Orders[slot]->Action != UnitActionTrain) {
		// Order has moved, we are not training
		return;
	} else if (unit.Orders[slot]->Action == UnitActionTrain) {
		COrder_Train &order = *static_cast<COrder_Train *>(unit.Orders[slot]);
		// Still training this order, same unit?
		if (type && &order.GetUnitType() != type) {
			// Different unit being trained
			return;
		}
		order.Cancel(unit);
		RemoveOrder(unit, slot);

		// Update interface.
		if (unit.Player == ThisPlayer && unit.Selected) {
			SelectedUnitChanged();
		}
	}
}
Ejemplo n.º 22
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());
}
Ejemplo n.º 23
0
/**
**  Called if a unit is killed.
**
**  @param unit  Pointer to unit.
*/
void AiUnitKilled(CUnit &unit)
{
	DebugPrint("%d: %d(%s) killed\n" _C_
			   unit.Player->Index _C_ UnitNumber(unit) _C_ unit.Type->Ident.c_str());

	Assert(unit.Player->Type != PlayerPerson);

	if (unit.GroupId) {
		AiForce &force = unit.Player->Ai->Force[unit.GroupId - 1];

		force.Remove(unit);
		if (force.Size() == 0) {
			force.Attacking = false;
			if (!force.Defending && force.State > 0) {
				DebugPrint("%d: Attack force #%lu was destroyed, giving up\n"
						   _C_ unit.Player->Index _C_(long unsigned int)(&force  - & (unit.Player->Ai->Force[0])));
				force.Reset(true);
			}
		}
	}

	unit.CurrentOrder()->AiUnitKilled(unit);
}
Ejemplo n.º 24
0
/* virtual */ bool COrder_Attack::OnAiHitUnit(CUnit &unit, CUnit *attacker, int /*damage*/)
{
	CUnit *goal = this->GetGoal();

	if (goal) {
		if (goal->IsAlive() == false) {
			this->ClearGoal();
			this->goalPos = goal->tilePos;
			return false;
		}
		if (goal == attacker) {
			return true;
		}
		if (goal->CurrentAction() == UnitActionAttack) {
			const COrder_Attack &order = *static_cast<COrder_Attack *>(goal->CurrentOrder());
			if (order.GetGoal() == &unit) {
				//we already fight with one of attackers;
				return true;
			}
		}
	}
	return false;
}
Ejemplo n.º 25
0
bool COrder_Build::StartBuilding(CUnit &unit, CUnit &ontop)
{
	const CUnitType &type = this->GetUnitType();

	unit.Player->SubUnitType(type);

	CUnit *build = MakeUnit(const_cast<CUnitType &>(type), unit.Player);

	// If unable to make unit, stop, and report message
	if (build == NULL) {
		// FIXME: Should we retry this?
		unit.Player->Notify(NotifyYellow, unit.tilePos,
							_("Unable to create building %s"), type.Name.c_str());
		if (unit.Player->AiEnabled) {
			AiCanNotBuild(unit, type);
		}
		return false;
	}
	build->Constructed = 1;
	build->CurrentSightRange = 0;

	// Building on top of something, may remove what is beneath it
	if (&ontop != &unit) {
		CBuildRestrictionOnTop *b;

		b = static_cast<CBuildRestrictionOnTop *>(OnTopDetails(*build, ontop.Type));
		Assert(b);
		if (b->ReplaceOnBuild) {
			build->ResourcesHeld = ontop.ResourcesHeld; // We capture the value of what is beneath.
			build->Variable[GIVERESOURCE_INDEX].Value = ontop.Variable[GIVERESOURCE_INDEX].Value;
			build->Variable[GIVERESOURCE_INDEX].Max = ontop.Variable[GIVERESOURCE_INDEX].Max;
			build->Variable[GIVERESOURCE_INDEX].Enable = ontop.Variable[GIVERESOURCE_INDEX].Enable;
			ontop.Remove(NULL); // Destroy building beneath
			UnitLost(ontop);
			UnitClearOrders(ontop);
			ontop.Release();
		}
	}

	// Must set action before placing, otherwise it will incorrectly mark radar
	delete build->CurrentOrder();
	build->Orders[0] = COrder::NewActionBuilt(unit, *build);

	UpdateUnitSightRange(*build);
	// Must place after previous for map flags
	build->Place(this->goalPos);

	// HACK: the building is not ready yet
	build->Player->UnitTypesCount[type.Slot]--;
	if (build->Active) {
		build->Player->UnitTypesAiActiveCount[type.Slot]--;
	}

	// We need somebody to work on it.
	if (!type.BoolFlag[BUILDEROUTSIDE_INDEX].value) {
		UnitShowAnimation(unit, unit.Type->Animations->Still);
		unit.Remove(build);
		this->State = State_BuildFromInside;
		if (unit.Selected) {
			SelectedUnitChanged();
		}
	} else {
		this->State = State_BuildFromOutside;
		this->BuildingUnit = build;
		unit.Direction = DirectionToHeading(build->tilePos - unit.tilePos);
		UnitUpdateHeading(unit);
	}
	return true;
}
Ejemplo n.º 26
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;
		}
	}
}
Ejemplo n.º 27
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;
	}
}
Ejemplo n.º 28
0
void AiNewDepotRequest(CUnit &worker) {
/*
	DebugPrint("%d: Worker %d report: Resource [%d] too far from depot, returning time [%d].\n"
				_C_ worker->Player->Index _C_ worker->Slot
				_C_ worker->CurrentResource
				_C_ worker->Data.Move.Cycles
				);
	*/

	Vec2i pos = {-1, -1};
	ResourceInfo *resinfo = worker.Type->ResInfo[worker.CurrentResource];

	if (resinfo->TerrainHarvester) {
		pos = worker.CurrentOrder()->Arg1.Resource.Pos;
	} else {
		CUnit *mine = worker.CurrentOrder()->Arg1.Resource.Mine;
		if (mine) {
			pos = mine->tilePos;
		}
	}

	if (pos.x != -1 && NULL != FindDepositNearLoc(worker.Player,
				pos.x, pos.y, 10, worker.CurrentResource)) {
		/*
		 * New Depot has just be finished and worker just return to old depot
		 * (far away) from new Deopt.
		 */
		return;
	}
	CUnitType *best_type = NULL;
	int best_cost = 0;
	//int best_mask = 0;
	// Count the already made build requests.
	int counter[UnitTypeMax];

	AiGetBuildRequestsCount(worker.Player->Ai, counter);

	const int n = AiHelpers.Depots[worker.CurrentResource - 1].size();

	for (int i = 0; i < n; ++i) {
		CUnitType &type = *AiHelpers.Depots[worker.CurrentResource - 1][i];

		if (counter[type.Slot]) { // Already ordered.
			return;
		}
		if (!AiRequestedTypeAllowed(worker.Player, type)) {
			continue;
		}

		// Check if resources available.
		//int needmask = AiCheckUnitTypeCosts(type);
		int cost = 0;
		for (int c = 1; c < MaxCosts; ++c) {
			cost += type.Stats[worker.Player->Index].Costs[c];
		}

		if (best_type == NULL || (cost < best_cost)) {
			best_type = &type;
			best_cost = cost;
			//best_mask = needmask;
		}

	}

	if (best_type) {
		//if(!best_mask) {
			AiBuildQueue queue;

			queue.Type = best_type;
			queue.Want = 1;
			queue.Made = 0;
			queue.X = pos.x;
			queue.Y = pos.y;

			worker.Player->Ai->UnitTypeBuilt.push_back(queue);

			DebugPrint("%d: Worker %d report: Requesting new depot near [%d,%d].\n"
				_C_ worker.Player->Index _C_ worker.Slot
				_C_ queue.X _C_ queue.Y
				);
			/*
		} else {
			AiPlayer->NeededMask |= best_mask;
		}
		*/
	}
}
Ejemplo n.º 29
0
/**
**  Try to move a unit that's in the way
*/
static void AiMoveUnitInTheWay(CUnit &unit)
{
	static Vec2i dirs[8] = {Vec2i(-1, -1), Vec2i(-1, 0), Vec2i(-1, 1), Vec2i(0, 1), Vec2i(1, 1), Vec2i(1, 0), Vec2i(1, -1), Vec2i(0, -1)};
	CUnit *movableunits[16];
	Vec2i movablepos[16];
	int movablenb;

	AiPlayer = unit.Player->Ai;

	// No more than 1 move per 10 cycle ( avoid stressing the pathfinder )
	if (GameCycle <= AiPlayer->LastCanNotMoveGameCycle + 10) {
		return;
	}

	const CUnitType &unittype = *unit.Type;
	const Vec2i u0 = unit.tilePos;
	const Vec2i u1(u0.x + unittype.TileWidth - 1, u0.y + unittype.TileHeight - 1);

	movablenb = 0;

	// Try to make some unit moves around it
	for (CUnitManager::Iterator it = UnitManager.begin(); it != UnitManager.end(); ++it) {
		CUnit &blocker = **it;

		if (blocker.IsUnusable()) {
			continue;
		}
		if (!blocker.CanMove() || blocker.Moving) {
			continue;
		}
		if (blocker.Player != unit.Player && blocker.Player->IsAllied(*unit.Player) == false) {
			continue;
		}
		const CUnitType &blockertype = *blocker.Type;

		if (blockertype.UnitType != unittype.UnitType) {
			continue;
		}

		const Vec2i b0 = blocker.tilePos;
		const Vec2i b1(b0.x + blockertype.TileWidth - 1, b0.y + blockertype.TileHeight - 1);

		if (&unit == &blocker) {
			continue;
		}
		// Check for collision
		if (unit.MapDistanceTo(blocker) >= unit.Type->TileWidth + 1) {
			continue;
		}

		// Move blocker in a rand dir
		int r = SyncRand() & 7;
		int trycount = 8;
		while (trycount > 0) {
			r = (r + 1) & 7;
			--trycount;

			const Vec2i pos = blocker.tilePos + blocker.Type->TileWidth * dirs[r];

			// Out of the map => no !
			if (!Map.Info.IsPointOnMap(pos)) {
				continue;
			}
			// move to blocker ? => no !
			if (pos == u0) {
				continue;
			}
			if (Map.Field(pos)->UnitCache.size() > 0) {
				continue;
			}

			movableunits[movablenb] = &blocker;
			movablepos[movablenb] = pos;

			++movablenb;
			trycount = 0;
		}
		if (movablenb >= 16) {
			break;
		}
	}

	// Don't move more than 1 unit.
	if (movablenb) {
		const int index = SyncRand() % movablenb;
		COrder *savedOrder = NULL;
		if (movableunits[index]->IsIdle() == false) {
			if (unit.CanStoreOrder(unit.CurrentOrder())) {
				savedOrder = unit.CurrentOrder()->Clone();
			}
		}
		CommandMove(*movableunits[index], movablepos[index], FlushCommands);
		if (savedOrder != NULL) {
			unit.SavedOrder = savedOrder;
		}
		AiPlayer->LastCanNotMoveGameCycle = GameCycle;
	}
}
Ejemplo n.º 30
0
int ParseAnimInt(const CUnit &unit, const char *parseint)
{
	char s[100];
	const CUnit *goal = &unit;

	if (!strlen(parseint)) {
		return 0;
	}

	strcpy(s, parseint);
	char *cur = &s[2];
	if (s[0] == 'v' || s[0] == 't') { //unit variable detected
		if (s[0] == 't') {
			if (unit.CurrentOrder()->HasGoal()) {
				goal = unit.CurrentOrder()->GetGoal();
			} else {
				return 0;
			}
		}
		char *next = strchr(cur, '.');
		if (next == NULL) {
			fprintf(stderr, "Need also specify the variable '%s' tag \n", cur);
			ExitFatal(1);
		} else {
			*next = '\0';
		}
		const int index = UnitTypeVar.VariableNameLookup[cur];// User variables
		if (index == -1) {
			if (!strcmp(cur, "ResourcesHeld")) {
				return goal->ResourcesHeld;
			} else if (!strcmp(cur, "ResourceActive")) {
				return goal->Resource.Active;
			} else if (!strcmp(cur, "_Distance")) {
				return unit.MapDistanceTo(*goal);
			}
			fprintf(stderr, "Bad variable name '%s'\n", cur);
			ExitFatal(1);
		}
		if (!strcmp(next + 1, "Value")) {
			return goal->Variable[index].Value;
		} else if (!strcmp(next + 1, "Max")) {
			return goal->Variable[index].Max;
		} else if (!strcmp(next + 1, "Increase")) {
			return goal->Variable[index].Increase;
		} else if (!strcmp(next + 1, "Enable")) {
			return goal->Variable[index].Enable;
		} else if (!strcmp(next + 1, "Percent")) {
			return goal->Variable[index].Value * 100 / goal->Variable[index].Max;
		}
		return 0;
	} else if (s[0] == 'b' || s[0] == 'g') { //unit bool flag detected
		if (s[0] == 'g') {
			if (unit.CurrentOrder()->HasGoal()) {
				goal = unit.CurrentOrder()->GetGoal();
			} else {
				return 0;
			}
		}
		const int index = UnitTypeVar.BoolFlagNameLookup[cur];// User bool flags
		if (index == -1) {
			fprintf(stderr, "Bad bool-flag name '%s'\n", cur);
			ExitFatal(1);
		}
		return goal->Type->BoolFlag[index].value;
	} else if (s[0] == 's') { //spell type detected
		Assert(goal->CurrentAction() == UnitActionSpellCast);
		const COrder_SpellCast &order = *static_cast<COrder_SpellCast *>(goal->CurrentOrder());
		const SpellType &spell = order.GetSpell();
		if (!strcmp(spell.Ident.c_str(), cur)) {
			return 1;
		}
		return 0;
	} else if (s[0] == 'S') { // check if autocast for this spell available
		const SpellType *spell = SpellTypeByIdent(cur);
		if (!spell) {
			fprintf(stderr, "Invalid spell: '%s'\n", cur);
			ExitFatal(1);
		}
		if (unit.AutoCastSpell[spell->Slot]) {
			return 1;
		}
		return 0;
	} else if (s[0] == 'p') { //player variable detected
		char *next;
		if (*cur == '(') {
			++cur;
			char *end = strchr(cur, ')');
			if (end == NULL) {
				fprintf(stderr, "ParseAnimInt: expected ')'\n");
				ExitFatal(1);
			}
			*end = '\0';
			next = end + 1;
		} else {
			next = strchr(cur, '.');
		}
		if (next == NULL) {
			fprintf(stderr, "Need also specify the %s player's property\n", cur);
			ExitFatal(1);
		} else {
			*next = '\0';
		}
		char *arg = strchr(next + 1, '.');
		if (arg != NULL) {
			*arg = '\0';
		}
		return GetPlayerData(ParseAnimPlayer(unit, cur), next + 1, arg + 1);
	} else if (s[0] == 'r') { //random value
		char *next = strchr(cur, '.');
		if (next == NULL) {
			return SyncRand(atoi(cur) + 1);
		} else {
			*next = '\0';
			const int min = atoi(cur);
			return min + SyncRand(atoi(next + 1) - min + 1);
		}
	} else if (s[0] == 'l') { //player number
		return ParseAnimPlayer(unit, cur);

	}
	// Check if we trying to parse a number
	Assert(isdigit(s[0]) || s[0] == '-');
	return atoi(parseint);
}