void MissileHandlePierce(Missile &missile, const Vec2i &pos) { CUnit *unit = UnitOnMapTile(pos, -1); if (unit && unit->IsAliveOnMap() && (missile.Type->FriendlyFire || unit->IsEnemy(*missile.SourceUnit->Player))) { missile.MissileHit(unit); } }
/** ** Cast polymorph. ** ** @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_Polymorph::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &goalPos) { if (!target) { return 0; } CUnitType &type = *this->NewForm; const Vec2i pos(goalPos - type.GetHalfTileSize()); caster.Player->Score += target->Variable[POINTS_INDEX].Value; if (caster.IsEnemy(*target)) { if (target->Type->Building) { caster.Player->TotalRazings++; } else { caster.Player->TotalKills++; } 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; caster.Variable[KILL_INDEX].Value++; caster.Variable[KILL_INDEX].Max++; caster.Variable[KILL_INDEX].Enable = 1; } // as said somewhere else -- no corpses :) target->Remove(NULL); Vec2i offset; for (offset.x = 0; offset.x < type.TileWidth; ++offset.x) { for (offset.y = 0; offset.y < type.TileHeight; ++offset.y) { if (!UnitTypeCanBeAt(type, pos + offset)) { target->Place(target->tilePos); return 0; } } } caster.Variable[MANA_INDEX].Value -= spell.ManaCost; if (this->PlayerNeutral == 1) { MakeUnitAndPlace(pos, type, Players + PlayerNumNeutral); } else if (this->PlayerNeutral == 2) { MakeUnitAndPlace(pos, type, caster.Player); } else { MakeUnitAndPlace(pos, type, target->Player); } UnitLost(*target); UnitClearOrders(*target); target->Release(); return 1; }
/** ** Check the condition. ** ** @param caster Pointer to caster unit. ** @param spell Pointer to the spell to cast. ** @param target Pointer to target unit, or 0 if it is a position spell. ** @param goalPos position, or {-1, -1} if it is a unit spell. ** @param condition Pointer to condition info. ** ** @return true if passed, false otherwise. */ static bool PassCondition(const CUnit &caster, const SpellType &spell, const CUnit *target, const Vec2i &/*goalPos*/, const ConditionInfo *condition) { if (caster.Variable[MANA_INDEX].Value < spell.ManaCost) { // Check caster mana. return false; } // check countdown timer if (caster.SpellCoolDownTimers[spell.Slot]) { // Check caster mana. return false; } // Check caster's resources if (caster.Player->CheckCosts(spell.Costs, false)) { return false; } if (spell.Target == TargetUnit) { // Casting a unit spell without a target. if ((!target) || target->IsAlive() == false) { return false; } } if (!condition) { // no condition, pass. return true; } for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); i++) { // for custom variables const CUnit *unit; if (!condition->Variable[i].Check) { continue; } unit = (condition->Variable[i].ConditionApplyOnCaster) ? &caster : target; // Spell should target location and have unit condition. if (unit == NULL) { continue; } if (condition->Variable[i].Enable != CONDITION_TRUE) { if ((condition->Variable[i].Enable == CONDITION_ONLY) ^ (unit->Variable[i].Enable)) { return false; } } // Value and Max if (condition->Variable[i].ExactValue != -1 && condition->Variable[i].ExactValue != unit->Variable[i].Value) { return false; } if (condition->Variable[i].ExceptValue != -1 && condition->Variable[i].ExceptValue == unit->Variable[i].Value) { return false; } if (condition->Variable[i].MinValue >= unit->Variable[i].Value) { return false; } if (condition->Variable[i].MaxValue != -1 && condition->Variable[i].MaxValue <= unit->Variable[i].Value) { return false; } if (condition->Variable[i].MinMax >= unit->Variable[i].Max) { return false; } if (!unit->Variable[i].Max) { continue; } // Percent if (condition->Variable[i].MinValuePercent * unit->Variable[i].Max >= 100 * unit->Variable[i].Value) { return false; } if (condition->Variable[i].MaxValuePercent * unit->Variable[i].Max <= 100 * unit->Variable[i].Value) { return false; } } if (!target) { return true; } if (!target->Type->CheckUserBoolFlags(condition->BoolFlag)) { return false; } if (condition->Alliance != CONDITION_TRUE) { if ((condition->Alliance == CONDITION_ONLY) ^ // own units could be not allied ? (caster.IsAllied(*target) || target->Player == caster.Player)) { return false; } } if (condition->Opponent != CONDITION_TRUE) { if ((condition->Opponent == CONDITION_ONLY) ^ (caster.IsEnemy(*target) && 1)) { return false; } } if (condition->TargetSelf != CONDITION_TRUE) { if ((condition->TargetSelf == CONDITION_ONLY) ^ (&caster == target)) { return false; } } return true; }
/** ** Cast demolish ** @param caster Unit that casts the spell ** @param spell Spell-type pointer ** @param target Target unit that spell is addressed to ** @param goalPos tilePos of target spot when/if target does not exist ** ** @return =!0 if spell should be repeated, 0 if not */ /* virtual */ int Spell_Demolish::Cast(CUnit &caster, const SpellType &, CUnit *, const Vec2i &goalPos) { // Allow error margins. (Lame, I know) const Vec2i offset(this->Range + 2, this->Range + 2); //Wyrmgus start // Vec2i minpos = goalPos - offset; // Vec2i maxpos = goalPos + offset; Vec2i minpos = caster.tilePos - offset; Vec2i maxpos = caster.tilePos + Vec2i(caster.Type->TileWidth - 1, caster.Type->TileHeight - 1) + offset; //Wyrmgus end Map.FixSelectionArea(minpos, maxpos); // // Terrain effect of the explosion // //Wyrmgus start /* Vec2i ipos; for (ipos.x = minpos.x; ipos.x <= maxpos.x; ++ipos.x) { for (ipos.y = minpos.y; ipos.y <= maxpos.y; ++ipos.y) { const CMapField &mf = *Map.Field(ipos); if (SquareDistance(ipos, goalPos) > square(this->Range)) { // Not in circle range continue; } else if (mf.isAWall()) { Map.RemoveWall(ipos); } else if (mf.RockOnMap()) { Map.ClearRockTile(ipos); } else if (mf.ForestOnMap()) { Map.ClearWoodTile(ipos); } } } */ if (this->DamageTerrain) { Vec2i ipos; for (ipos.x = minpos.x; ipos.x <= maxpos.x; ++ipos.x) { for (ipos.y = minpos.y; ipos.y <= maxpos.y; ++ipos.y) { const CMapField &mf = *Map.Field(ipos); //Wyrmgus start // if (SquareDistance(ipos, caster.tilePos) > square(this->Range)) { if (caster.MapDistanceTo(ipos) > this->Range) { //Wyrmgus end // Not in circle range continue; } else if (mf.isAWall()) { Map.RemoveWall(ipos); } else if (mf.RockOnMap()) { Map.ClearRockTile(ipos); } else if (mf.ForestOnMap()) { Map.ClearWoodTile(ipos); } } } } //Wyrmgus end // // Effect of the explosion on units. Don't bother if damage is 0 // //Wyrmgus start //if (this->Damage) { if (this->Damage || this->BasicDamage || this->PiercingDamage || this->FireDamage || this->ColdDamage || this->ArcaneDamage || this->LightningDamage || this->AirDamage || this->EarthDamage || this->WaterDamage) { //Wyrmgus end std::vector<CUnit *> table; SelectFixed(minpos, maxpos, table); for (size_t i = 0; i != table.size(); ++i) { CUnit &unit = *table[i]; if (unit.Type->UnitType != UnitTypeFly && unit.IsAlive() //Wyrmgus start // && unit.MapDistanceTo(goalPos) <= this->Range) { // Don't hit flying units! // HitUnit(&caster, unit, this->Damage); && unit.MapDistanceTo(caster) <= this->Range && (UnitNumber(unit) != UnitNumber(caster) || this->DamageSelf) && (caster.IsEnemy(unit) || this->DamageFriendly)) { int damage = 0; if (this->BasicDamage || this->PiercingDamage || this->FireDamage || this->ColdDamage || this->ArcaneDamage || this->LightningDamage || this->AirDamage || this->EarthDamage || this->WaterDamage) { damage = std::max<int>(this->BasicDamage - unit.Variable[ARMOR_INDEX].Value, 1); damage += this->PiercingDamage; //apply resistances if (this->HackDamage) { damage *= 100 - unit.Variable[HACKRESISTANCE_INDEX].Value; damage /= 100; } else if (this->PierceDamage) { damage *= 100 - unit.Variable[PIERCERESISTANCE_INDEX].Value; damage /= 100; } else if (this->BluntDamage) { damage *= 100 - unit.Variable[BLUNTRESISTANCE_INDEX].Value; damage /= 100; } //apply fire and cold damage damage += this->FireDamage * (100 - unit.Variable[FIRERESISTANCE_INDEX].Value) / 100; damage += this->ColdDamage * (100 - unit.Variable[COLDRESISTANCE_INDEX].Value) / 100; damage += this->ArcaneDamage * (100 - unit.Variable[ARCANERESISTANCE_INDEX].Value) / 100; damage += this->LightningDamage * (100 - unit.Variable[LIGHTNINGRESISTANCE_INDEX].Value) / 100; damage += this->AirDamage * (100 - unit.Variable[AIRRESISTANCE_INDEX].Value) / 100; damage += this->EarthDamage * (100 - unit.Variable[EARTHRESISTANCE_INDEX].Value) / 100; damage += this->WaterDamage * (100 - unit.Variable[WATERRESISTANCE_INDEX].Value) / 100; damage -= SyncRand() % ((damage + 2) / 2); } HitUnit(&caster, unit, this->Damage + damage); //Wyrmgus end } } } return 1; }
/** ** 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; }