Exemplo n.º 1
0
/**
**  Handle moving to the target.
**
**  @param unit  Unit, for that the spell cast is handled.
*/
bool COrder_SpellCast::SpellMoveToTarget(CUnit &unit)
{
	// Unit can't move
	int err = 1;
	if (unit.CanMove()) {
		err = DoActionMove(unit);
		if (unit.Anim.Unbreakable) {
			return false;
		}
	}

	// when reached DoActionMove changes unit action
	// FIXME: use return codes from pathfinder
	CUnit *goal = this->GetGoal();

	if (goal && unit.MapDistanceTo(*goal) <= this->Range) {
		// there is goal and it is in range
		UnitHeadingFromDeltaXY(unit, goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos);
		this->State++; // cast the spell
		return false;
	} else if (!goal && unit.MapDistanceTo(this->goalPos) <= this->Range) {
		// there is no goal and target spot is in range
		UnitHeadingFromDeltaXY(unit, this->goalPos - unit.tilePos);
		this->State++; // cast the spell
		return false;
	} else if (err == PF_UNREACHABLE || !unit.CanMove()) {
		// goal/spot unreachable and out of range -- give up
		return true;
	}
	return false;
}
Exemplo n.º 2
0
/**
**  Unit attacks!
**
**  if (SubAction & WEAK_TARGET) is true the goal is a weak goal.
**  This means the unit AI (little AI) could choose a new better goal.
**
**  @todo  Lets do some tries to reach the target.
**         If target place is not reachable, choose better goal to reduce
**         the pathfinder load.
**
**  @param unit  Unit, for that the attack is handled.
*/
/* virtual */ void COrder_Attack::Execute(CUnit &unit)
{
	Assert(this->HasGoal() || Map.Info.IsPointOnMap(this->goalPos));

	if (unit.Wait) {
		unit.Wait--;
		return;
	}

	switch (this->State) {
		case 0: { // First entry
			// did Order change ?
			if (CheckForTargetInRange(unit)) {
				return;
			}
			// Can we already attack ?
			if (this->HasGoal()) {
				CUnit &goal = *this->GetGoal();
				const int dist = goal.MapDistanceTo(unit);

				if (unit.Type->MinAttackRange < dist &&
					dist <= unit.Stats->Variables[ATTACKRANGE_INDEX].Max) {
					const Vec2i dir = goal.tilePos + goal.Type->GetHalfTileSize() - unit.tilePos;

					UnitHeadingFromDeltaXY(unit, dir);
					this->State = ATTACK_TARGET;
					AttackTarget(unit);
					return;
				}
			}
			this->State = MOVE_TO_TARGET;
			// FIXME: should use a reachable place to reduce pathfinder time.
		}
		// FALL THROUGH
		case MOVE_TO_TARGET:
		case MOVE_TO_TARGET + WEAK_TARGET:
			if (!unit.CanMove()) {
				this->Finished = true;
				return;
			}
			MoveToTarget(unit);
			break;

		case ATTACK_TARGET:
		case ATTACK_TARGET + WEAK_TARGET:
			AttackTarget(unit);
			break;

		case WEAK_TARGET:
			DebugPrint("FIXME: wrong entry.\n");
			break;
	}
}
Exemplo n.º 3
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()));
	}
}
Exemplo n.º 4
0
bool COrder_Still::AutoAttackStand(CUnit &unit)
{
	//Wyrmgus start
//	if (unit.Type->CanAttack == false) {
	if (unit.CanAttack() == false) {
	//Wyrmgus end
		return false;
	}
	// Removed units can only attack in AttackRange, from bunker
	//Wyrmgus start
	//if unit is in a container which is attacking, and the container has a goal, use that goal (if possible) instead
//	CUnit *autoAttackUnit = AttackUnitsInRange(unit);
	CUnit *autoAttackUnit = unit.Container && unit.Container->CurrentAction() == UnitActionAttack && unit.Container->CurrentOrder()->HasGoal() ? unit.Container->CurrentOrder()->GetGoal() : AttackUnitsInRange(unit);
	//Wyrmgus end

	if (autoAttackUnit == nullptr) {
		return false;
	}
	// If unit is removed, use container's x and y
	const CUnit *firstContainer = unit.GetFirstContainer();
	if (firstContainer->MapDistanceTo(*autoAttackUnit) > unit.GetModifiedVariable(ATTACKRANGE_INDEX)) {
		return false;
	}
	//Wyrmgus start
//	if (GameSettings.Inside && CheckObstaclesBetweenTiles(unit.tilePos, autoAttackUnit->tilePos, MapFieldRocks | MapFieldForest) == false) {
	if (Map.IsLayerUnderground(autoAttackUnit->MapLayer->ID) && unit.GetModifiedVariable(ATTACKRANGE_INDEX) > 1 && CheckObstaclesBetweenTiles(unit.tilePos, autoAttackUnit->tilePos, MapFieldAirUnpassable, autoAttackUnit->MapLayer->ID) == false) {
	//Wyrmgus end
		return false;
	}
	this->State = SUB_STILL_ATTACK; // Mark attacking.
	this->SetGoal(autoAttackUnit);
	//Wyrmgus start
//	UnitHeadingFromDeltaXY(unit, autoAttackUnit->tilePos + autoAttackUnit->Type->GetHalfTileSize() - unit.tilePos);
	UnitHeadingFromDeltaXY(unit, PixelSize(PixelSize(autoAttackUnit->tilePos) * Map.GetMapLayerPixelTileSize(autoAttackUnit->MapLayer->ID)) + autoAttackUnit->GetHalfTilePixelSize() - PixelSize(PixelSize(unit.tilePos) * Map.GetMapLayerPixelTileSize(autoAttackUnit->MapLayer->ID)) - unit.GetHalfTilePixelSize());
	//Wyrmgus end
	return true;
}
Exemplo n.º 5
0
bool COrder_Still::AutoAttackStand(CUnit &unit)
{
	if (unit.Type->CanAttack == false) {
		return false;
	}
	// Removed units can only attack in AttackRange, from bunker
	CUnit *autoAttackUnit = AttackUnitsInRange(unit);

	if (autoAttackUnit == NULL) {
		return false;
	}
	// If unit is removed, use containers x and y
	const CUnit *firstContainer = unit.Container ? unit.Container : &unit;
	if (firstContainer->MapDistanceTo(*autoAttackUnit) > unit.Stats->Variables[ATTACKRANGE_INDEX].Max) {
		return false;
	}
	if (GameSettings.Inside && CheckObstaclesBetweenTiles(unit.tilePos, autoAttackUnit->tilePos, MapFieldRocks | MapFieldForest) == false) {
		return false;
	}
	this->State = SUB_STILL_ATTACK; // Mark attacking.
	this->SetGoal(autoAttackUnit);
	UnitHeadingFromDeltaXY(unit, autoAttackUnit->tilePos + autoAttackUnit->Type->GetHalfTileSize() - unit.tilePos);
	return true;
}
Exemplo n.º 6
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;
	}
}
Exemplo n.º 7
0
/**
**  Unit moves! Generic function called from other actions.
**
**  @param unit  Pointer to unit.
**
**  @return      >0 remaining path length, 0 wait for path, -1
**               reached goal, -2 can't reach the goal.
*/
int DoActionMove(CUnit &unit)
{
	Vec2i posd; // movement in tile.
	int d;

	Assert(unit.CanMove());

	if (!unit.Moving && (unit.Type->Animations->Move != unit.Anim.CurrAnim || !unit.Anim.Wait)) {
		Assert(!unit.Anim.Unbreakable);

		// FIXME: So units flying up and down are not affected.
		unit.IX = 0;
		unit.IY = 0;

		UnmarkUnitFieldFlags(unit);
		d = NextPathElement(unit, &posd.x, &posd.y);
		MarkUnitFieldFlags(unit);
		switch (d) {
			case PF_UNREACHABLE: // Can't reach, stop
				if (unit.Player->AiEnabled) {
					AiCanNotMove(unit);
				}
				unit.Moving = 0;
				return d;
			case PF_REACHED: // Reached goal, stop
				unit.Moving = 0;
				return d;
			case PF_WAIT: // No path, wait
				// Reset frame to still frame while we wait
				// FIXME: Unit doesn't animate.
				unit.Frame = unit.Type->StillFrame;
				UnitUpdateHeading(unit);
				unit.Wait = 10;

				unit.Moving = 0;
				return d;
			default: // On the way moving
				unit.Moving = 1;
				break;
		}

		if (unit.Type->UnitType == UnitTypeNaval) { // Boat (un)docking?
			const CMapField &mf_cur = *Map.Field(unit.Offset);
			const CMapField &mf_next = *Map.Field(unit.tilePos + posd);

			if (mf_cur.WaterOnMap() && mf_next.CoastOnMap()) {
				PlayUnitSound(unit, VoiceDocking);
			} else if (mf_cur.CoastOnMap() && mf_next.WaterOnMap()) {
				PlayUnitSound(unit, VoiceDocking); // undocking
			}
		}
		Vec2i pos = unit.tilePos + posd;
		unit.MoveToXY(pos);

		// Remove unit from the current selection
		if (unit.Selected && !Map.Field(pos)->playerInfo.IsTeamVisible(*ThisPlayer)) {
			if (NumSelected == 1) { //  Remove building cursor
				CancelBuildingMode();
			}
			if (!ReplayRevealMap) {
				UnSelectUnit(unit);
				SelectionChanged();
			}
		}

		unit.IX = -posd.x * PixelTileSize.x;
		unit.IY = -posd.y * PixelTileSize.y;
		unit.Frame = unit.Type->StillFrame;
		UnitHeadingFromDeltaXY(unit, posd);
	} else {
		posd.x = Heading2X[unit.Direction / NextDirection];
		posd.y = Heading2Y[unit.Direction / NextDirection];
		d = unit.pathFinderData->output.Length + 1;
	}

	unit.pathFinderData->output.Cycles++;//reset have to be manualy controled by caller.
	int move = UnitShowAnimationScaled(unit, unit.Type->Animations->Move, Map.Field(unit.Offset)->Cost);

	unit.IX += posd.x * move;
	unit.IY += posd.y * move;

	// Finished move animation, set Moving to 0 so we recalculate the path
	// next frame
	// FIXME: this is broken for subtile movement
	if (!unit.Anim.Unbreakable && !unit.IX && !unit.IY) {
		unit.Moving = 0;
	}
	return d;
}
Exemplo n.º 8
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->Orders[0]->Goal;

				//
				// 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->Orders[0]->X = goal->X;
						unit->Orders[0]->Y = goal->Y;
						goal->RefsDecrease();
						// FIXME: should I clear this here?
						unit->Orders[0]->Goal = 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 && MapDistanceBetweenUnits(unit, 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;
					UnitHeadingFromDeltaXY(unit,
						goal->X + (goal->Type->TileWidth - 1) / 2 - unit->X,
						goal->Y + (goal->Type->TileHeight - 1) / 2 - unit->Y);
				} else if (err < 0) {
					if (goal) { // release reference
						goal->RefsDecrease();
						unit->Orders[0]->Goal = NoUnitP;
					}
					unit->Orders[0]->Action = UnitActionStill;
					unit->State = unit->SubAction = 0;
					if (unit->Selected) { // update display for new action
						SelectedUnitChanged();
					}
					return;
				}

				// FIXME: Should be it already?
				Assert(unit->Orders[0]->Action == UnitActionRepair);
			}
			break;

		//
		// Repair the target.
		//
		case 2:
			AnimateActionRepair(unit);
			unit->Data.Repair.Cycles++;
			if (!unit->Anim.Unbreakable) {
				goal = unit->Orders[0]->Goal;

				//
				// 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->Orders[0]->X = goal->X;
						unit->Orders[0]->Y = goal->Y;
						goal->RefsDecrease();
						// FIXME: should I clear this here?
						unit->Orders[0]->Goal = goal = NULL;
						NewResetPath(unit);
					}
				}
				if (goal && MapDistanceBetweenUnits(unit, goal) <= unit->Type->RepairRange) {
					RepairUnit(unit, goal);
					goal = unit->Orders[0]->Goal;
				} else if (goal && MapDistanceBetweenUnits(unit, goal) > 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) {
					if (goal) { // release reference
						goal->RefsDecrease();
						unit->Orders[0]->Goal = NULL;
					}
					unit->Orders[0]->Action = UnitActionStill;
					unit->SubAction = unit->State = 0;
					if (unit->Selected) { // update display for new action
						SelectedUnitChanged();
					}
					return;
				}

				// FIXME: automatic repair
			}
			break;
	}
}
Exemplo n.º 9
0
/**
**  Handle attacking the target.
**
**  @param unit  Unit, for that the attack is handled.
*/
void COrder_Attack::AttackTarget(CUnit &unit)
{
	Assert(this->HasGoal() || Map.Info.IsPointOnMap(this->goalPos));

	AnimateActionAttack(unit, *this);
	if (unit.Anim.Unbreakable) {
		return;
	}

	if (!this->HasGoal() && (this->Action == UnitActionAttackGround || Map.WallOnMap(this->goalPos))) {
		return;
	}

	// Target is dead ? Change order ?
	if (CheckForDeadGoal(unit)) {
		return;
	}
	CUnit *goal = this->GetGoal();
	bool dead = !goal || goal->IsAlive() == false;

	// No target choose one.
	if (!goal) {
		goal = AttackUnitsInReactRange(unit);

		// No new goal, continue way to destination.
		if (!goal) {
			// Return to old task ?
			if (unit.RestoreOrder()) {
				return;
			}
			this->State = MOVE_TO_TARGET;
			return;
		}
		// Save current command to come back.
		COrder *savedOrder = COrder::NewActionAttack(unit, this->goalPos);

		if (unit.CanStoreOrder(savedOrder) == false) {
			delete savedOrder;
			savedOrder = NULL;
		} else {
			unit.SavedOrder = savedOrder;
		}
		this->SetGoal(goal);
		this->goalPos = goal->tilePos;
		this->MinRange = unit.Type->MinAttackRange;
		this->Range = unit.Stats->Variables[ATTACKRANGE_INDEX].Max;
		this->State |= WEAK_TARGET;

		// Have a weak target, try a better target.
		// FIXME: if out of range also try another target quick
	} else {
		if ((this->State & WEAK_TARGET)) {
			CUnit *newTarget = AttackUnitsInReactRange(unit);
			if (newTarget && ThreatCalculate(unit, *newTarget) < ThreatCalculate(unit, *goal)) {
				if (unit.CanStoreOrder(this)) {
					unit.SavedOrder = this->Clone();
				}
				goal = newTarget;
				this->SetGoal(newTarget);
				this->goalPos = newTarget->tilePos;
				this->MinRange = unit.Type->MinAttackRange;
				this->State = MOVE_TO_TARGET;
			}
		}
	}

	// Still near to target, if not goto target.
	const int dist = unit.MapDistanceTo(*goal);
	if (dist > unit.Stats->Variables[ATTACKRANGE_INDEX].Max) {
		// towers don't chase after goal
		if (unit.CanMove()) {
			if (unit.CanStoreOrder(this)) {
				if (dead) {
					unit.SavedOrder = COrder::NewActionAttack(unit, this->goalPos);
				} else {
					unit.SavedOrder = this->Clone();
				}
			}
		}
		unit.Frame = 0;
		this->State &= WEAK_TARGET;
		this->State |= MOVE_TO_TARGET;
	}
	if (dist < unit.Type->MinAttackRange) {
		this->State = MOVE_TO_TARGET;
	}

	// Turn always to target
	if (goal) {
		const Vec2i dir = goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos;
		UnitHeadingFromDeltaXY(unit, dir);
	}
}
Exemplo n.º 10
0
/**
**  Controls moving a unit to its target when attacking
**
**  @param unit  Unit that is attacking and moving
*/
void COrder_Attack::MoveToTarget(CUnit &unit)
{
	Assert(!unit.Type->Vanishes && !unit.Destroyed && !unit.Removed);
	Assert(unit.CurrentOrder() == this);
	Assert(unit.CanMove());
	Assert(this->HasGoal() || Map.Info.IsPointOnMap(this->goalPos));

	int err = DoActionMove(unit);

	if (unit.Anim.Unbreakable) {
		return;
	}

	// Look if we have reached the target.
	if (err == 0 && !this->HasGoal()) {
		// Check if we're in range when attacking a location and we are waiting
		if (unit.MapDistanceTo(this->goalPos) <= unit.Stats->Variables[ATTACKRANGE_INDEX].Max) {
			err = PF_REACHED;
		}
	}
	if (err >= 0) {
		if (CheckForTargetInRange(unit)) {
			return;
		}
		return;
	}
	if (err == PF_REACHED) {
		CUnit *goal = this->GetGoal();
		// Have reached target? FIXME: could use the new return code?
		if (goal
			&& unit.MapDistanceTo(*goal) <= unit.Stats->Variables[ATTACKRANGE_INDEX].Max) {
			// Reached another unit, now attacking it
			const Vec2i dir = goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos;
			UnitHeadingFromDeltaXY(unit, dir);
			this->State++;
			return;
		}
		// Attacking wall or ground.
		if (((goal && goal->Type && goal->Type->Wall)
			 || (!goal && (Map.WallOnMap(this->goalPos) || this->Action == UnitActionAttackGround)))
			&& unit.MapDistanceTo(this->goalPos) <= unit.Stats->Variables[ATTACKRANGE_INDEX].Max) {
			// Reached wall or ground, now attacking it
			UnitHeadingFromDeltaXY(unit, this->goalPos - unit.tilePos);
			this->State &= WEAK_TARGET;
			this->State |= ATTACK_TARGET;
			return;
		}
	}
	// Unreachable.

	if (err == PF_UNREACHABLE) {
		if (!this->HasGoal()) {
			// When attack-moving we have to allow a bigger range
			this->Range++;
			unit.Wait = 5;
			return;
		} else {
			this->ClearGoal();
		}
	}

	// Return to old task?
	if (!unit.RestoreOrder()) {
		this->Finished = true;
	}
}
Exemplo n.º 11
0
/**
**  Auto attack nearby units if possible
*/
static void AutoAttack(CUnit *unit, bool stand_ground)
{
	CUnit *temp;
	CUnit *goal;

	if (unit->Wait) {
		unit->Wait--;
		return;
	}

	// Cowards don't attack unless ordered.
	if (unit->Type->CanAttack && !unit->Type->Coward) {
		// Normal units react in reaction range.
		if (CanMove(unit) && !unit->Removed && !stand_ground) {
			if ((goal = AttackUnitsInReactRange(unit))) {
				// Weak goal, can choose other unit, come back after attack
				CommandAttack(unit, goal->X, goal->Y, NULL, FlushCommands);
				Assert(unit->SavedOrder.Action == UnitActionStill);
				Assert(!unit->SavedOrder.Goal);
				unit->SavedOrder.Action = UnitActionAttack;
				unit->SavedOrder.Range = 0;
				unit->SavedOrder.X = unit->X;
				unit->SavedOrder.Y = unit->Y;
				unit->SavedOrder.Goal = NoUnitP;
			} else {
				unit->Wait = 15;
			}
		// Removed units can only attack in AttackRange, from bunker
		} else if ((goal = AttackUnitsInRange(unit))) {
			temp = unit->Orders[0]->Goal;
			if (temp && temp->Orders[0]->Action == UnitActionDie) {
				temp->RefsDecrease();
				unit->Orders[0]->Goal = temp = NoUnitP;
			}
			if (!unit->SubAction || temp != goal) {
				// New target.
				if (temp) {
					temp->RefsDecrease();
				}
				unit->Orders[0]->Goal = goal;
				goal->RefsIncrease();
				unit->State = 0;
				unit->SubAction = 1; // Mark attacking.
				UnitHeadingFromDeltaXY(unit,
					goal->X + (goal->Type->TileWidth - 1) / 2 - unit->X,
					goal->Y + (goal->Type->TileHeight - 1) / 2 - unit->Y);
			}
			return;
		}
	} else {
		unit->Wait = 15;
	}

	if (unit->SubAction) { // was attacking.
		if ((temp = unit->Orders[0]->Goal)) {
			temp->RefsDecrease();
			unit->Orders[0]->Goal = NoUnitP;
		}
		unit->SubAction = unit->State = 0; // No attacking, restart
	}
	Assert(!unit->Orders[0]->Goal);
}
Exemplo n.º 12
0
/**
**  Start harvesting the resource.
**
**  @param unit  Pointer to unit.
**
**  @return      TRUE if ready, otherwise FALSE.
*/
int COrder_Resource::StartGathering(CUnit &unit)
{
	CUnit *goal;
	const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource];
	Assert(!unit.IX);
	Assert(!unit.IY);

	//Wyrmgus start
//	if (resinfo.TerrainHarvester) {
	if (Map.Info.IsPointOnMap(this->goalPos)) {
	//Wyrmgus end
		// This shouldn't happened?
#if 0
		if (!Map.IsTerrainResourceOnMap(unit.Orders->goalPos, this->CurrentResource)) {
			DebugPrint("Wood gone, just like that?\n");
			return 0;
		}
#endif
		UnitHeadingFromDeltaXY(unit, this->goalPos - unit.tilePos);
		if (resinfo.WaitAtResource) {
			this->TimeToHarvest = std::max<int>(1, resinfo.WaitAtResource * SPEEDUP_FACTOR / unit.Player->SpeedResourcesHarvest[resinfo.ResourceId]);
		} else {
			this->TimeToHarvest = 1;
		}
		this->DoneHarvesting = 0;
		if (this->CurrentResource != unit.CurrentResource) {
			DropResource(unit);
			unit.CurrentResource = this->CurrentResource;
		}
		return 1;
	}

	goal = this->GetGoal();

	// Target is dead, stop getting resources.
	if (!goal || goal->IsVisibleAsGoal(*unit.Player) == false) {
		// Find an alternative, but don't look too far.
		this->goalPos.x = -1;
		this->goalPos.y = -1;
		if ((goal = UnitFindResource(unit, unit, 15, this->CurrentResource, unit.Player->AiEnabled))) {
			this->State = SUB_START_RESOURCE;
			this->SetGoal(goal);
		} else {
			this->ClearGoal();
			this->Finished = true;
		}
		return 0;
	}

	// FIXME: 0 can happen, if to near placed by map designer.
	Assert(unit.MapDistanceTo(*goal) <= 1);

	// Update the heading of a harvesting unit to looks straight at the resource.
	//Wyrmgus start
//	UnitHeadingFromDeltaXY(unit, goal->tilePos - unit.tilePos + goal->Type->GetHalfTileSize());
	UnitHeadingFromDeltaXY(unit, Vec2i(goal->tilePos.x * PixelTileSize.x, goal->tilePos.y * PixelTileSize.y) - Vec2i(unit.tilePos.x * PixelTileSize.x, unit.tilePos.y * PixelTileSize.y) + goal->Type->GetHalfTilePixelSize() - unit.Type->GetHalfTilePixelSize());
	//Wyrmgus end

	// If resource is still under construction, wait!
	if ((goal->Type->MaxOnBoard && goal->Resource.Active >= goal->Type->MaxOnBoard)
		|| goal->CurrentAction() == UnitActionBuilt) {
		// FIXME: Determine somehow when the resource will be free to use
		// FIXME: Could we somehow find another resource? Think minerals
		// FIXME: We should add a flag for that, and a limited range.
		// However the CPU usage is really low (no pathfinding stuff).
		unit.Wait = 10;
		return 0;
	}

	// Place unit inside the resource
	//Wyrmgus start
//	if (!resinfo.HarvestFromOutside) {
	if (!goal->Type->BoolFlag[HARVESTFROMOUTSIDE_INDEX].value) {
	//Wyrmgus end
		if (goal->Variable[MAXHARVESTERS_INDEX].Value == 0 || goal->Variable[MAXHARVESTERS_INDEX].Value > goal->InsideCount) {
			this->ClearGoal();
			int selected = unit.Selected;
			unit.Remove(goal);
			if (selected && !Preference.DeselectInMine) {
				unit.Removed = 0;
				SelectUnit(unit);
				SelectionChanged();
				unit.Removed = 1;
			}
		} else if (goal->Variable[MAXHARVESTERS_INDEX].Value <= goal->InsideCount) {
			//Resource is full, wait
			unit.Wait = 10;
			return 0;
		}
	}

	if (this->CurrentResource != unit.CurrentResource) {
		DropResource(unit);
		unit.CurrentResource = this->CurrentResource;
	}

	// Activate the resource
	goal->Resource.Active++;

	if (resinfo.WaitAtResource) {
		//Wyrmgus start
//		this->TimeToHarvest = std::max<int>(1, resinfo.WaitAtResource * SPEEDUP_FACTOR / unit.Player->SpeedResourcesHarvest[resinfo.ResourceId]);
		int wait_at_resource = resinfo.WaitAtResource;
		if (!goal->Type->BoolFlag[HARVESTFROMOUTSIDE_INDEX].value) {
			wait_at_resource = resinfo.WaitAtResource * 100 / resinfo.ResourceStep;
		}
		this->TimeToHarvest = std::max<int>(1, wait_at_resource * SPEEDUP_FACTOR / (unit.Player->SpeedResourcesHarvest[resinfo.ResourceId] + goal->Variable[TIMEEFFICIENCYBONUS_INDEX].Value));
		//Wyrmgus end
	} else {
		this->TimeToHarvest = 1;
	}
	this->DoneHarvesting = 0;
	return 1;
}
Exemplo n.º 13
0
/* virtual */ void COrder_Repair::Execute(CUnit &unit)
{
	Assert(this->ReparableTarget == this->GetGoal());

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

				if (goal) {
					if (!goal->IsVisibleAsGoal(*unit.Player)) {
						DebugPrint("repair target gone.\n");
						this->goalPos = goal->tilePos + goal->Type->GetHalfTileSize();
						ReparableTarget = NULL;
						this->ClearGoal();
						goal = NULL;
					}
				} 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) {
					this->State = 2;
					this->RepairCycle = 0;
					const Vec2i dir = goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos;
					UnitHeadingFromDeltaXY(unit, dir);
				} else if (err < 0) {
					this->Finished = true;
					return ;
				}
			}
			break;
		}
		case 2: {// Repair the target.
			AnimateActionRepair(unit);
			this->RepairCycle++;
			if (unit.Anim.Unbreakable) {
				return ;
			}
			CUnit *goal = this->GetGoal();

			if (goal) {
				if (!goal->IsVisibleAsGoal(*unit.Player)) {
					DebugPrint("repair goal is gone\n");
					this->goalPos = goal->tilePos + goal->Type->GetHalfTileSize();
					// FIXME: should I clear this here?
					this->ClearGoal();
					ReparableTarget = NULL;
					goal = NULL;
				} else {
					const int dist = unit.MapDistanceTo(*goal);

					if (dist <= unit.Type->RepairRange) {
						if (RepairUnit(unit, *goal)) {
							this->Finished = true;
							return ;
						}
					} else if (dist > unit.Type->RepairRange) {
						// If goal has move, chase after it
						this->State = 0;
					}
				}
			}
			// Target is fine, choose new one.
			if (!goal || goal->Variable[HP_INDEX].Value >= goal->Variable[HP_INDEX].Max) {
				this->Finished = true;
				return ;
			}
			// FIXME: automatic repair
		}
		break;
	}
}