Esempio n. 1
0
/**
**  Get a player's units in rectangle box specified with 2 coordinates
**
**  @param l  Lua state.
**
**  @return   Array of units.
*/
static int CclGetUnitsAroundUnit(lua_State *l)
{
	const int nargs = lua_gettop(l);
	if (nargs != 2 && nargs != 3) {
		LuaError(l, "incorrect argument\n");
	}

	const int slot = LuaToNumber(l, 1);
	const CUnit &unit = UnitManager.GetSlotUnit(slot);
	const int range = LuaToNumber(l, 2);
	bool allUnits = false;
	if (nargs == 3) {
		allUnits = LuaToBoolean(l, 3);
	}
	lua_newtable(l);
	std::vector<CUnit *> table;
	if (allUnits) {
		SelectAroundUnit(unit, range, table, HasNotSamePlayerAs(Players[PlayerNumNeutral]));
	} else {
		SelectAroundUnit(unit, range, table, HasSamePlayerAs(*unit.Player));
	}
	size_t n = 0;
	for (size_t i = 0; i < table.size(); ++i) {
		if (table[i]->IsAliveOnMap()) {
			lua_pushnumber(l, UnitNumber(*table[i]));
			lua_rawseti(l, -2, ++n);
		}
	}
	return 1;
}
Esempio n. 2
0
/**
**  Check if the unit's container has an adjacent unit owned by another non-neutral player
**
**  @return  true if the unit is now sheltered (or if exited a shelter), false otherwise
*/
static bool LeaveShelter(CUnit &unit)
{
	if (
		!unit.Container
		|| (unit.Container->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value && unit.Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value)
		|| (!unit.Player->AiEnabled && !(unit.Type->BoolFlag[FAUNA_INDEX].value && unit.Player->Type == PlayerNeutral))
		|| unit.Container->CanMove() //is a transporter, not a shelter
		|| !unit.Container->Type->CanTransport() //is not a garrisonable building
		|| (unit.Container->Type->BoolFlag[RECRUITHEROES_INDEX].value && unit.Character && unit.Player->Type == PlayerNeutral) //if is a hireable hero in a hero recruitment building, don't leave it
	) {
		return false;
	}
	
	std::vector<CUnit *> table;
	if (unit.Type->BoolFlag[FAUNA_INDEX].value) {
		SelectAroundUnit(*unit.Container, 1, table, HasNotSamePlayerAs(*unit.Player));
	} else {
		SelectAroundUnit(*unit.Container, unit.CurrentSightRange, table, MakeAndPredicate(IsEnemyWith(*unit.Player), HasNotSamePlayerAs(Players[PlayerNumNeutral])));
	}

	if (table.size() > 0) {
		CommandUnload(*unit.Container, unit.Container->tilePos, &unit, FlushCommands, unit.Container->MapLayer->ID);
		return true;
	}

	return false;
}
Esempio n. 3
0
/**
**  Attack units in distance.
**
**  If the unit can attack must be handled by caller.
**  Choose the best target, that can be attacked.
**
**  @param unit           Find in distance for this unit.
**  @param range          Distance range to look.
**  @param onlyBuildings  Search only buildings (useful when attacking with AI force)
**
**  @return       Unit to be attacked.
*/
CUnit *AttackUnitsInDistance(const CUnit &unit, int range, CUnitFilter pred)
{
	// if necessary, take possible damage on allied units into account...
	//Wyrmgus start
//	if (unit.Type->Missile.Missile->Range > 1
//		&& (range + unit.Type->Missile.Missile->Range < 15)) {
	if (unit.GetMissile().Missile->Range > 1
		&& (range + unit.GetMissile().Missile->Range < 15)) {
	//Wyrmgus end
		//  If catapult, count units near the target...
		//   FIXME : make it configurable

		//Wyrmgus start
//		int missile_range = unit.Type->Missile.Missile->Range + range - 1;
		int missile_range = unit.GetMissile().Missile->Range + range - 1;
		//Wyrmgus end

		Assert(2 * missile_range + 1 < 32);

		// If unit is removed, use containers x and y
		const CUnit *firstContainer = unit.Container ? unit.Container : &unit;
		std::vector<CUnit *> table;
		SelectAroundUnit(*firstContainer, missile_range, table,
			//Wyrmgus start
//			MakeAndPredicate(HasNotSamePlayerAs(Players[PlayerNumNeutral]), pred));
			pred);
			//Wyrmgus end

		if (table.empty() == false) {
			return BestRangeTargetFinder(unit, range).Find(table);
		}
		return NULL;
	} else {
		// If unit is removed, use containers x and y
		const CUnit *firstContainer = unit.Container ? unit.Container : &unit;
		std::vector<CUnit *> table;

		SelectAroundUnit(*firstContainer, range, table,
			//Wyrmgus start
//			MakeAndPredicate(HasNotSamePlayerAs(Players[PlayerNumNeutral]), pred));
			pred);
			//Wyrmgus end

		const int n = static_cast<int>(table.size());
		if (range > 25 && table.size() > 9) {
			std::sort(table.begin(), table.begin() + n, CompareUnitDistance(unit));
		}

		// Find the best unit to attack
		return BestTargetFinder(unit).Find(table);
	}
}
Esempio n. 4
0
/**
**  PickUpItem
**
**  @return  true if the unit picks up an item, false otherwise
*/
static bool PickUpItem(CUnit &unit)
{
	if (
		!unit.Type->BoolFlag[ORGANIC_INDEX].value
		|| !unit.Player->AiEnabled
	) {
		return false;
	}
	
	if (unit.Variable[HP_INDEX].Value == unit.GetModifiedVariable(HP_INDEX, VariableMax) && !unit.HasInventory()) { //only look for items to pick up if the unit is damaged or has an inventory
		return false;
	}

	// look for nearby items to pick up
	std::vector<CUnit *> table;
	SelectAroundUnit(unit, unit.GetReactionRange(), table);

	for (size_t i = 0; i != table.size(); ++i) {
		if (!table[i]->Removed) {
			if (CanPickUp(unit, *table[i])) {
				if (table[i]->Variable[HITPOINTHEALING_INDEX].Value > 0 && (unit.GetModifiedVariable(HP_INDEX, VariableMax) - unit.Variable[HP_INDEX].Value) > 0) {
					if (UnitReachable(unit, *table[i], 1, unit.GetReactionRange() * 8)) {
						CommandPickUp(unit, *table[i], FlushCommands);
						return true;
					}
				}
			}
		}
	}
	return false;
}
Esempio n. 5
0
/**
**  Player has the quantity of rescued unit-type near to unit-type.
*/
static int CclIfRescuedNearUnit(lua_State *l)
{
	LuaCheckArgs(l, 5);

	lua_pushvalue(l, 1);
	const int plynr = TriggerGetPlayer(l);
	lua_pop(l, 1);
	const char *op = LuaToString(l, 2);
	const int q = LuaToNumber(l, 3);
	lua_pushvalue(l, 4);
	const CUnitType *unittype = TriggerGetUnitType(l);
	lua_pop(l, 1);
	const CUnitType *ut2 = CclGetUnitType(l);
	if (!unittype || !ut2) {
		LuaError(l, "CclIfRescuedNearUnit: not a unit-type valid");
	}

	CompareFunction compare = GetCompareFunction(op);
	if (!compare) {
		LuaError(l, "Illegal comparison operation in if-rescued-near-unit: %s" _C_ op);
	}

	// Get all unit types 'near'.
	std::vector<CUnit *> table;
	FindUnitsByType(*ut2, table);
	for (size_t i = 0; i != table.size(); ++i) {
		CUnit &centerUnit = *table[i];
		std::vector<CUnit *> around;

		SelectAroundUnit(centerUnit, 1, around);
		// Count the requested units
		int s = 0;
		for (size_t j = 0; j != around.size(); ++j) {
			CUnit &unit = *around[j];

			if (unit.RescuedFrom) { // only rescued units
				// Check unit type

				if (unittype == ANY_UNIT
					|| (unittype == ALL_FOODUNITS && !unit.Type->Building)
					|| (unittype == ALL_BUILDINGS && unit.Type->Building)
					|| (unittype == unit.Type)) {

					// Check the player
					if (plynr == -1 || plynr == unit.Player->Index) {
						++s;
					}
				}
			}
		}
		if (compare(s, q)) {
			lua_pushboolean(l, 1);
			return 1;
		}
	}
	lua_pushboolean(l, 0);
	return 1;
}
Esempio n. 6
0
void SelectAroundUnit(const CUnit &unit, int range, std::vector<CUnit *> &around)
{
	SelectAroundUnit(unit, range, around, NoFilter());
}
Esempio n. 7
0
/**
**  Select the target for the autocast.
**
**  @param caster    Unit who would cast the spell.
**  @param spell     Spell-type pointer.
**
**  @return          Target* chosen target or Null if spell can't be cast.
**  @todo FIXME: should be global (for AI) ???
**  @todo FIXME: write for position target.
*/
static Target *SelectTargetUnitsOfAutoCast(CUnit &caster, const SpellType &spell)
{
	AutoCastInfo *autocast;

	// Ai cast should be a lot better. Use autocast if not found.
	if (caster.Player->AiEnabled && spell.AICast) {
		autocast = spell.AICast;
	} else {
		autocast = spell.AutoCast;
	}
	Assert(autocast);
	const Vec2i &pos = caster.tilePos;
	int range = autocast->Range;

	// Select all units aroung the caster
	std::vector<CUnit *> table;
	SelectAroundUnit(caster, range, table);

	// Check generic conditions. FIXME: a better way to do this?
	if (autocast->Combat != CONDITION_TRUE) {
		// Check each unit if it is hostile.
		bool inCombat = false;
		for (size_t i = 0; i < table.size(); ++i) {
			const CUnit &target = *table[i];

			// Note that CanTarget doesn't take into account (offensive) spells...
			if (target.IsVisibleAsGoal(*caster.Player) && caster.IsEnemy(target)
				&& (CanTarget(*caster.Type, *target.Type) || CanTarget(*target.Type, *caster.Type))) {
				inCombat = true;
				break;
			}
		}
		if ((autocast->Combat == CONDITION_ONLY) ^ (inCombat)) {
			return NULL;
		}
	}

	switch (spell.Target) {
		case TargetSelf :
			if (PassCondition(caster, spell, &caster, pos, spell.Condition)
				&& PassCondition(caster, spell, &caster, pos, autocast->Condition)) {
				return NewTargetUnit(caster);
			}
			return NULL;
		case TargetPosition:
			return 0;
			//  Autocast with a position? That's hard
			//  Possibilities: cast reveal-map on a dark region
			//  Cast raise dead on a bunch of corpses. That would rule.
			//  Cast summon until out of mana in the heat of battle. Trivial?
			//  Find a tight group of units and cast area-damage spells. HARD,
			//  but it is a must-have for AI. What about area-heal?
		case TargetUnit: {
			// The units are already selected.
			//  Check every unit if it is a possible target

			int n = 0;
			for (size_t i = 0; i != table.size(); ++i) {
				// Check if unit in battle
				if (autocast->Attacker == CONDITION_ONLY) {
					if (table[i]->CurrentAction() != UnitActionAttack
						&& table[i]->CurrentAction() != UnitActionAttackGround
						&& table[i]->CurrentAction() != UnitActionSpellCast) {
						continue;
					}
				}
				if (PassCondition(caster, spell, table[i], pos, spell.Condition)
					&& PassCondition(caster, spell, table[i], pos, autocast->Condition)) {
					table[n++] = table[i];
				}
			}
			// Now select the best unit to target.
			if (n != 0) {
				// For the best target???
				if (autocast->PriorytyVar != ACP_NOVALUE) {
					std::sort(table.begin(), table.begin() + n,
							  AutoCastPrioritySort(caster, autocast->PriorytyVar, autocast->ReverseSort));
					return NewTargetUnit(*table[0]);
				} else { // Use the old behavior
					return NewTargetUnit(*table[SyncRand() % n]);
				}
			}
			break;
		}
		default:
			// Something is wrong
			DebugPrint("Spell is screwed up, unknown target type\n");
			Assert(0);
			return NULL;
			break;
	}
	return NULL; // Can't spell the auto-cast.
}
Esempio n. 8
0
/**
**  Move in a random direction
**
**  @return  true if the unit moves, false otherwise
*/
static bool MoveRandomly(CUnit &unit)
{
	if (!unit.Type->RandomMovementProbability || SyncRand(100) > unit.Type->RandomMovementProbability) {
		return false;
	}
	// pick random location
	Vec2i pos = unit.tilePos;

	pos.x += SyncRand(unit.Type->RandomMovementDistance * 2 + 1) - unit.Type->RandomMovementDistance;
	pos.y += SyncRand(unit.Type->RandomMovementDistance * 2 + 1) - unit.Type->RandomMovementDistance;

	// restrict to map
	Map.Clamp(pos, unit.MapLayer->ID);

	// move if possible
	if (pos != unit.tilePos) {
		UnmarkUnitFieldFlags(unit);
		if (UnitCanBeAt(unit, pos, unit.MapLayer->ID)) {
			MarkUnitFieldFlags(unit);
			//Wyrmgus start
			//prefer terrains which this unit's species is native to; only go to other ones if is already in a non-native terrain type
			if (unit.Type->Species && std::find(unit.Type->Species->Terrains.begin(), unit.Type->Species->Terrains.end(), Map.GetTileTopTerrain(unit.tilePos, false, unit.MapLayer->ID)) != unit.Type->Species->Terrains.end()) {
				if (std::find(unit.Type->Species->Terrains.begin(), unit.Type->Species->Terrains.end(), Map.GetTileTopTerrain(pos, false, unit.MapLayer->ID)) == unit.Type->Species->Terrains.end()) {
					return false;
				}
			}
			
			if (unit.Type->BoolFlag[PEOPLEAVERSION_INDEX].value) {
				std::vector<CUnit *> table;
				SelectAroundUnit(unit, std::max(6, unit.Type->RandomMovementDistance), table, HasNotSamePlayerAs(*unit.Player));
				if (!table.size()) { //only avoid going near a settled area if isn't already surrounded by civilizations' units
					//don't go near settled areas
					Vec2i minpos = pos;
					Vec2i maxpos = pos;
					minpos.x = pos.x - std::max(6, unit.Type->RandomMovementDistance);
					minpos.y = pos.y - std::max(6, unit.Type->RandomMovementDistance);
					maxpos.x = pos.x + std::max(6, unit.Type->RandomMovementDistance);
					maxpos.y = pos.y + std::max(6, unit.Type->RandomMovementDistance);
					std::vector<CUnit *> second_table;
					Select(minpos, maxpos, second_table, unit.MapLayer->ID, HasNotSamePlayerAs(*unit.Player));

					if (second_table.size() > 0) {
						return false;
					}
				} else { //even if is already in a settled area, don't go to places adjacent to units owned by players other than the neutral player
					Vec2i minpos = pos;
					Vec2i maxpos = pos;
					minpos.x = pos.x - 1;
					minpos.y = pos.y - 1;
					maxpos.x = pos.x + 1;
					maxpos.y = pos.y + 1;
					std::vector<CUnit *> second_table;
					Select(minpos, maxpos, second_table, unit.MapLayer->ID, HasNotSamePlayerAs(*unit.Player));

					if (second_table.size() > 0) {
						return false;
					}
				}
			}
			CommandMove(unit, pos, FlushCommands, unit.MapLayer->ID);
			return true;
		}
		MarkUnitFieldFlags(unit);
	}
	return false;
}
Esempio n. 9
0
/**
**  Cast capture.
**
**  @param caster       Unit that casts the spell
**  @param spell        Spell-type pointer
**  @param target       Target unit that spell is addressed to
**  @param goalPos      coord of target spot when/if target does not exist
**
**  @return             =!0 if spell should be repeated, 0 if not
*/
/* virtual */ int Spell_Capture::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &/*goalPos*/)
{
	if (!target || caster.Player == target->Player) {
		return 0;
	}

	if (this->DamagePercent) {
		if ((100 * target->Variable[HP_INDEX].Value) /
			target->Variable[HP_INDEX].Max > this->DamagePercent &&
			target->Variable[HP_INDEX].Value > this->Damage) {
			HitUnit(&caster, *target, this->Damage);
			if (this->SacrificeEnable) {
				// No corpse.
				caster.Remove(NULL);
				caster.Release();
			}
			return 1;
		}
	}
	caster.Player->Score += target->Variable[POINTS_INDEX].Value;
	if (caster.IsEnemy(*target)) {
		if (target->Type->Building) {
			caster.Player->TotalRazings++;
		} else {
			caster.Player->TotalKills++;
		}
		//Wyrmgus start
		caster.Player->UnitTypeKills[target->Type->Slot]++;
		/*
		if (UseHPForXp) {
			caster.Variable[XP_INDEX].Max += target->Variable[HP_INDEX].Value;
		} else {
			caster.Variable[XP_INDEX].Max += target->Variable[POINTS_INDEX].Value;
		}
		caster.Variable[XP_INDEX].Value = caster.Variable[XP_INDEX].Max;
		*/
		
		//distribute experience between nearby units belonging to the same player
		if (!target->Type->BoolFlag[BUILDING_INDEX].value) {
			std::vector<CUnit *> table;
			SelectAroundUnit(caster, 6, table, MakeAndPredicate(HasSamePlayerAs(*caster.Player), IsNotBuildingType()));

			if (UseHPForXp) {
				caster.Variable[XP_INDEX].Max += target->Variable[HP_INDEX].Value / (table.size() + 1);
			} else {
				caster.Variable[XP_INDEX].Max += target->Variable[POINTS_INDEX].Value / (table.size() + 1);
			}
			caster.Variable[XP_INDEX].Value = caster.Variable[XP_INDEX].Max;
			caster.XPChanged();

			for (size_t i = 0; i != table.size(); ++i) {
				if (UseHPForXp) {
					table[i]->Variable[XP_INDEX].Max += target->Variable[HP_INDEX].Value / (table.size() + 1);
				} else {
					table[i]->Variable[XP_INDEX].Max += target->Variable[POINTS_INDEX].Value / (table.size() + 1);
				}
				table[i]->Variable[XP_INDEX].Value = table[i]->Variable[XP_INDEX].Max;
				table[i]->XPChanged();
			}
		}
		//Wyrmgus end
		caster.Variable[KILL_INDEX].Value++;
		caster.Variable[KILL_INDEX].Max++;
		caster.Variable[KILL_INDEX].Enable = 1;
	}
	target->ChangeOwner(*caster.Player);
	UnitClearOrders(*target);
	if (this->JoinToAIForce && caster.Player->AiEnabled) {
		int force = caster.Player->Ai->Force.GetForce(caster);
		if (force != -1) {
			caster.Player->Ai->Force[force].Insert(*target);
			target->GroupId = caster.GroupId;
			CommandDefend(*target, caster, FlushCommands);
		}
	}
	if (this->SacrificeEnable) {
		// No corpse.
		caster.Remove(NULL);
		caster.Release();
	} else {
		caster.Variable[MANA_INDEX].Value -= spell.ManaCost;
	}
	return 0;
}