Ejemplo n.º 1
0
/**
**  Make one or more unit leave the transporter.
**
**  @param unit  Pointer to unit.
*/
static void LeaveTransporter(CUnit *unit)
{
	int i;
	int stillonboard;
	CUnit *goal;

	stillonboard = 0;
	goal = unit->Orders[0]->Goal;
	//
	// Goal is the specific unit unit that you want to unload.
	// This can be NULL, in case you want to unload everything.
	//
	if (goal) {
		unit->Orders[0]->Goal = NoUnitP;
		if (goal->Destroyed) {
			DebugPrint("destroyed unit unloading?\n");
			goal->RefsDecrease();
			return;
		}
		goal->RefsDecrease();
		goal->X = unit->X;
		goal->Y = unit->Y;
		// Try to unload the unit. If it doesn't work there is no problem.
		if (UnloadUnit(goal)) {
			unit->BoardCount--;
		}
	} else {
		// Unload all units.
		goal = unit->UnitInside;
		for (i = unit->InsideCount; i; --i, goal = goal->NextContained) {
			if (goal->Boarded) {
				goal->X = unit->X;
				goal->Y = unit->Y;
				if (!UnloadUnit(goal)) {
					++stillonboard;
				} else {
					unit->BoardCount--;
				}
			}
		}
	}
	if (IsOnlySelected(unit)) {
		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.
		unit->Orders[0]->Action = UnitActionUnload;
		unit->Orders[0]->Goal = NoUnitP;
		unit->Orders[0]->X = unit->X;
		unit->Orders[0]->Y = unit->Y;
		unit->SubAction = 0;
	} else {
		unit->ClearAction();
	}
}
Ejemplo n.º 2
0
/**
**  Handle AI of all players each game cycle.
*/
void PlayersEachCycle()
{
	for (int player = 0; player < NumPlayers; ++player) {
		CPlayer *p = &Players[player];
		if (p->AutoAttackTargets.size() > 0) {
			CUnitCache &autoatacktargets = p->AutoAttackTargets;
			/* both loops can not be connected !!!! */
			for (unsigned int i = 0; i < autoatacktargets.size();) {
				CUnit *aatarget = autoatacktargets[i];
				if (!aatarget->IsAliveOnMap() ||
					Map.Field(aatarget->Offset)->Guard[player] == 0) {
					autoatacktargets.Units.erase(autoatacktargets.Units.begin() + i);
					aatarget->RefsDecrease();
					continue;
				}
				++i;
			}
			if (autoatacktargets.size() > 0) {
				for (int j = 0; j < p->TotalNumUnits; ++j) {
					CUnit &guard = *p->Units[j];
					bool stand_ground = guard.CurrentAction() == UnitActionStandGround;
					if (guard.Type->CanAttack &&
								(stand_ground || guard.IsIdle()) &&
								 !guard.IsUnusable()) {
						AutoAttack(guard, autoatacktargets, stand_ground);
					}
				}
			}
		}
		if (p->AiEnabled) {
			AiEachCycle(p);
		}
	}
}
Ejemplo n.º 3
0
/**
**  Enter the transporter.
**
**  @param unit  Pointer to unit.
*/
static void EnterTransporter(CUnit *unit)
{
	CUnit *transporter;

	unit->ClearAction();

	transporter = unit->Orders[0]->Goal;
	if (!transporter->IsVisibleAsGoal(unit->Player)) {
		DebugPrint("Transporter gone\n");
		transporter->RefsDecrease();
		unit->Orders[0]->Goal = NoUnitP;
		return;
	}

	transporter->RefsDecrease();
	unit->Orders[0]->Goal = NoUnitP;

	//
	// Place the unit inside the transporter.
	//

	if (transporter->BoardCount < transporter->Type->MaxOnBoard) {
		unit->Remove(transporter);
		transporter->BoardCount++;
		unit->Boarded = 1;
		if (!unit->Player->AiEnabled) {
			// Don't make anything funny after going out of the transporter.
			// FIXME: This is probably wrong, but it works for me (n0b0dy)
			unit->OrderCount = 1;
			unit->ClearAction();
		}

		if (IsOnlySelected(transporter)) {
			SelectedUnitChanged();
		}
		return;
	}
	DebugPrint("No free slot in transporter\n");
}
Ejemplo n.º 4
0
/**
**  Free a missile.
**
**  @param missiles  Missile pointer.
**  @param i         Index in missiles of missile to free
*/
static void FreeMissile(std::vector<Missile *> &missiles, size_t i)
{
	Missile *missile;
	CUnit *unit;

	missile = missiles[i];
	//
	// Release all unit references.
	//
	if ((unit = missile->SourceUnit)) {
		unit->RefsDecrease();
	}
	if ((unit = missile->TargetUnit)) {
		unit->RefsDecrease();
	}
	for (std::vector<Missile*>::iterator j = missiles.begin(); j != missiles.end(); ++j) {
		if (*j == missile) {
			missiles.erase(j);
			break;
		}
	}
	delete missile;
}
Ejemplo n.º 5
0
/**
**  Wait for transporter.
**
**  @param unit  Pointer to unit.
**
**  @return      True if ship arrived/present, False otherwise.
*/
static int WaitForTransporter(CUnit *unit)
{
	CUnit *trans;

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

	trans = unit->Orders[0]->Goal;

	if (!trans || !CanTransport(trans, unit)) {
		// FIXME: destination destroyed??
		unit->Wait = 6;
		return 0;
	}

	if (!trans->IsVisibleAsGoal(unit->Player)) {
		DebugPrint("Transporter Gone\n");
		trans->RefsDecrease();
		unit->Orders[0]->Goal = NoUnitP;
		unit->Wait = 6;
		return 0;
	}

	if (MapDistanceBetweenUnits(unit, trans) == 1) {
		// enter transporter
		return 1;
	}

	//
	// FIXME: any enemies in range attack them, while waiting.
	//

	// n0b0dy: This means we have to search with a smaller range.
	// It happens only when you reach the shore,and the transporter
	// is not there. The unit searches with a big range, so it thinks
	// it's there. This is why we reset the search. The transporter
	// should be a lot closer now, so it's not as bad as it seems.
	unit->SubAction = 0;
	unit->Orders[0]->Range = 1;
	// Uhh wait a bit.
	unit->Wait = 10;

	return 0;
}
Ejemplo 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->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;
	}
}
Ejemplo n.º 7
0
/**
**  The unit boards a transporter.
**
**  @todo FIXME: While waiting for the transporter the units must defend themselves.
**
**  @param unit  Pointer to unit.
*/
void HandleActionBoard(CUnit *unit)
{
	int i;
	CUnit *goal;

	switch (unit->SubAction) {
		//
		// Wait for transporter
		//
		case 201:
			if (WaitForTransporter(unit)) {
				unit->SubAction = 202;
			} else {
				UnitShowAnimation(unit, unit->Type->Animations->Still);
			}
			break;
		//
		// Enter transporter
		//
		case 202:
			EnterTransporter(unit);
			break;
		//
		// Move to transporter
		//
		case 0:
			if (unit->Wait) {
				unit->Wait--;
				return;
			}
			NewResetPath(unit);
			unit->SubAction = 1;
			// FALL THROUGH
		default:
			if (unit->SubAction <= 200) {
				// FIXME: if near transporter wait for enter
				if ((i = MoveToTransporter(unit))) {
					if (i == PF_UNREACHABLE) {
						if (++unit->SubAction == 200) {
							unit->ClearAction();
							if ((goal = unit->Orders[0]->Goal)) {
								goal->RefsDecrease();
								unit->Orders[0]->Goal = NoUnitP;
							}
						} else {
							//
							// Try with a bigger range.
							//
							if (unit->Orders[0]->Range <= Map.Info.MapWidth ||
									unit->Orders[0]->Range <= Map.Info.MapHeight) {
								unit->Orders[0]->Range++;
								unit->SubAction--;
							}
						}
					} else if (i == PF_REACHED) {
						unit->SubAction = 201;
					}
				}
			}
			break;
	}
}
Ejemplo n.º 8
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);
}
Ejemplo n.º 9
0
/**
**  Work for missile hit.
**
**  @param missile  Missile reaching end-point.
*/
void MissileHit(Missile *missile)
{
	CUnit *goal;
	int x;
	int y;
	CUnit *table[UnitMax];
	int n;
	int i;
	int splash;

	if (missile->Type->ImpactSound.Sound) {
		PlayMissileSound(missile, missile->Type->ImpactSound.Sound);
	}

	x = missile->X + missile->Type->Width / 2;
	y = missile->Y + missile->Type->Height / 2;

	//
	// The impact generates a new missile.
	//
	if (missile->Type->ImpactMissile) {
		MakeMissile(missile->Type->ImpactMissile, x, y, x, y);
	}
	if (missile->Type->ImpactParticle) {
		missile->Type->ImpactParticle->pushPreamble();
		missile->Type->ImpactParticle->pushInteger(x);
		missile->Type->ImpactParticle->pushInteger(y);
		missile->Type->ImpactParticle->run();
	}

	if (!missile->SourceUnit) {  // no owner - green-cross ...
		return;
	}

	x /= TileSizeX;
	y /= TileSizeY;

	if (x < 0 || y < 0 || x >= Map.Info.MapWidth || y >= Map.Info.MapHeight) {
		// FIXME: this should handled by caller?
		DebugPrint("Missile gone outside of map!\n");
		return;  // outside the map.
	}

	//
	// Choose correct goal.
	//
	if (!missile->Type->Range) {
		if (missile->TargetUnit) {
			//
			// Missiles without range only hits the goal always.
			//
			goal = missile->TargetUnit;
			if (goal->Destroyed) {  // Destroyed
				goal->RefsDecrease();
				missile->TargetUnit = NoUnitP;
				return;
			}
			MissileHitsGoal(missile, goal, 1);
			return;
		}
		return;
	}

	//
	// Hits all units in range.
	//
	i = missile->Type->Range;
	n = UnitCache.Select(x - i + 1, y - i + 1, x + i, y + i, table, UnitMax);
	Assert(missile->SourceUnit != NULL);
	for (i = 0; i < n; ++i) {
		goal = table[i];
		//
		// Can the unit attack the this unit-type?
		// NOTE: perhaps this should be come a property of the missile.
		//
		if (CanTarget(missile->SourceUnit->Type, goal->Type)) {
			splash = MapDistanceToUnit(x, y, goal);
			if (splash) {
				splash *= missile->Type->SplashFactor;
			} else {
				splash = 1;
			}
			MissileHitsGoal(missile, goal, splash);
		}
	}
}