示例#1
0
/**
**  Missile hits the goal.
**
**  @param missile  Missile hitting the goal.
**  @param goal     Goal of the missile.
**  @param splash   Splash damage divisor.
*/
static void MissileHitsGoal(const Missile &missile, CUnit &goal, int splash)
{
	if (!missile.Type->CanHitOwner && missile.SourceUnit == &goal) {
		return;
	}

	if (goal.CurrentAction() != UnitActionDie) {
		int damage;

		if (missile.Type->Damage) {   // custom formula
			Assert(missile.SourceUnit != NULL);
			damage = CalculateDamage(*missile.SourceUnit, goal, missile.Type->Damage) / splash;
		} else if (missile.Damage) {  // direct damage, spells mostly
			damage = missile.Damage / splash;
		} else {
			Assert(missile.SourceUnit != NULL);
			damage = CalculateDamage(*missile.SourceUnit, goal, Damage) / splash;
		}
		if (missile.Type->Pierce) {  // Handle pierce factor
			for (size_t i = 0; i < (missile.PiercedUnits.size() - 1); ++i) {
				damage *= (double)missile.Type->ReduceFactor / 100;
			}
		}

		HitUnit(missile.SourceUnit, goal, damage, &missile);
	}
}
示例#2
0
/**
**  Missile hits the goal.
**
**  @param missile  Missile hitting the goal.
**  @param goal     Goal of the missile.
**  @param splash   Splash damage divisor.
*/
static void MissileHitsGoal(const Missile &missile, CUnit &goal, int splash)
{
	if (!missile.Type->CanHitOwner && missile.SourceUnit == &goal) {
		return;
	}
	
	//Wyrmgus start
	if (CalculateHit(*missile.SourceUnit, *goal.Stats, &goal) == false) {
		if (splash == 1 && missile.Type->SplashFactor == 0) {
			return;
		} else if (splash == 1 && missile.Type->SplashFactor > 0) {
			splash = missile.Type->SplashFactor; // if missile has splash factor but missed, apply splash damage
		}
	}
	//Wyrmgus end

	if (goal.CurrentAction() != UnitActionDie) {
		int damage;

		if (missile.Type->Damage) {   // custom formula
			Assert(missile.SourceUnit != NULL);
			damage = CalculateDamage(*missile.SourceUnit, goal, missile.Type->Damage) / splash;
		} else if (missile.Damage) {  // direct damage, spells mostly
			damage = missile.Damage / splash;
		} else {
			Assert(missile.SourceUnit != NULL);
			damage = CalculateDamage(*missile.SourceUnit, goal, Damage) / splash;
		}
		if (missile.Type->Pierce) {  // Handle pierce factor
			for (size_t i = 0; i < (missile.PiercedUnits.size() - 1); ++i) {
				damage *= (double)missile.Type->ReduceFactor / 100;
			}
		}

		HitUnit(missile.SourceUnit, goal, damage, &missile);
		
		//Wyrmgus start
		//apply Thorns damage if attacker is at melee range
		if (&goal && goal.Variable[THORNSDAMAGE_INDEX].Value && missile.SourceUnit->MapDistanceTo(goal) <= 1) {
			int thorns_damage = std::max<int>(goal.Variable[THORNSDAMAGE_INDEX].Value - missile.SourceUnit->Variable[ARMOR_INDEX].Value, 1);
			if (GameSettings.NoRandomness) {
				thorns_damage -= ((thorns_damage + 2) / 2) / 2; //if no randomness setting is used, then the damage will always return what would have been the average damage with randomness
			} else {
				thorns_damage -= SyncRand() % ((thorns_damage + 2) / 2);
			}
			HitUnit(&goal, *missile.SourceUnit, thorns_damage);
		}
		//Wyrmgus end
	}
}
示例#3
0
文件: unit.cpp 项目: Fredi/Cpp
void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* damageInfo)
{
    damageInfo->attacker    = this;
    damageInfo->target      = victim;
    damageInfo->damage      = 0;
    damageInfo->hitType  = HIT_MISS;

    if (!victim)
        return;

    if (!IsAlive() || !victim->IsAlive())
        return;

    damage += CalculateDamage();
    damageInfo->damage = damage;

    damageInfo->hitType = RollHitType(damageInfo->target);

    switch (damageInfo->hitType)
    {
        case HIT_MISS:
            damageInfo->damage = 0;
            break;
        case HIT_CRITICAL:
            damageInfo->damage += damageInfo->damage;
            break;
        case HIT_NORMAL:
        default:
            break;
    }
}
示例#4
0
/**
**	Fire missile.
*/
global void FireMissile(Unit* unit)
{
    int x;
    int y;
    int dx;
    int dy;
    Unit* goal;
    Missile* missile;

    DebugLevel3("%s:\n",__FUNCTION__);

    if( unit->Type->MissileWeapon==MissileNone ) {
        // FIXME: must hit now!!!
        if( !(goal=unit->Command.Data.Move.Goal) ) {
            DebugLevel1("Missile-none hits no unit, shouldn't happen!\n");
            return;
        }

        // FIXME: make sure thats the correct unit.
        if( !goal->HP || goal->Command.Action==UnitActionDie ) {
            return;
        }

        HitUnit(goal,CalculateDamage(unit->Stats,goal));

        return;
    }

    x=unit->X*TileSizeX+TileSizeX/2;
    y=unit->Y*TileSizeY+TileSizeY/2;
    if( (goal=unit->Command.Data.Move.Goal) ) {
        // Fire to nearest point of unit!
        if( goal->Type ) {
            NearestOfUnit(goal,unit->X,unit->Y,&dx,&dy);
        } else {
            // FIXME: unit killed?
            dx=goal->X;
            dy=goal->Y;
        }
        DebugLevel3("Fire to unit at %d,%d\n",dx,dy);
        dx=dx*TileSizeX+TileSizeX/2;
        dy=dy*TileSizeY+TileSizeY/2;
    } else {
        dx=unit->Command.Data.Move.DX*TileSizeX+TileSizeX/2;
        dy=unit->Command.Data.Move.DY*TileSizeY+TileSizeY/2;
    }

    missile=MakeMissile(unit->Type->MissileWeapon,x,y,dx,dy);
    //
    //	Damage of missile
    //
    // missile->Damage=CalculateDamage(unit,goal);
    missile->SourceUnit=unit;
    missile->SourceType=unit->Type;
    missile->SourceStats=unit->Stats;
    missile->SourcePlayer=unit->Player;
}
示例#5
0
/**
**	Work for missile hit.
*/
global void MissileHit(int missile)
{
    Unit* goal;

    // FIXME: should I move the PlayMissileSound to here?
    // FIXME: And should the the connected missile be defined in the missile
    // FIXME: structure

    switch( Missiles[missile].Type->Type ) {
    case MissileArrow:
    case MissileAxe:
        PlayMissileSound(Missiles+missile,
                         Missiles[missile].Type->ImpactSound.Sound);
        break;
    case MissileBallistaBolt:
    case MissileBigCannon:
        PlayMissileSound(Missiles+missile,
                         Missiles[missile].Type->ImpactSound.Sound);
        MakeMissile(MissileImpact
                    ,Missiles[missile].X
                    ,Missiles[missile].Y
                    ,0,0);
        break;

    case MissileSubmarineMissile:
    case MissileTurtleMissile:
        PlayMissileSound(Missiles+missile,
                         Missiles[missile].Type->ImpactSound.Sound);
        MakeMissile(MissileImpact
                    ,Missiles[missile].X
                    ,Missiles[missile].Y
                    ,0,0);
        break;

    case MissileGreenCross:
        break;
    }

    if( !Missiles[missile].SourceType ) {
        return;
    }

    // FIXME: must choose better goal!
    // FIXME: what can the missile hit?
    goal=UnitOnMapTile(Missiles[missile].X/TileSizeX
                       ,Missiles[missile].Y/TileSizeY);
    if( !goal || !goal->HP ) {
        return;
    }

    HitUnit(goal,CalculateDamage(Missiles[missile].SourceStats,goal));
}
示例#6
0
/**
**  Missile hits the goal.
**
**  @param missile  Missile hitting the goal.
**  @param goal     Goal of the missile.
**  @param splash   Splash damage divisor.
*/
static void MissileHitsGoal(const Missile *missile, CUnit *goal, int splash)
{
	if (!missile->Type->CanHitOwner && goal == missile->SourceUnit) {
		return;
	}

	if (goal->Orders[0]->Action != UnitActionDie) {
		if (missile->Damage) {  // direct damage, spells mostly
			HitUnit(missile->SourceUnit, goal, missile->Damage / splash);
		} else {
			Assert(missile->SourceUnit != NULL);
			HitUnit(missile->SourceUnit, goal,
				CalculateDamage(missile->SourceUnit, goal) / splash);
		}
	}
}
示例#7
0
文件: spell.cpp 项目: Fredi/Cpp
void Spell::HandleEffects(Unit* unitTarget, uint32 i)
{
    // if the effect has been handled we skip it
    if (m_effectMask & (1 << i))
        return;

    uint8 eff = m_spellInfo->Effect[i];

    printf("Spell: %u Effect: %u\n", m_spellInfo->Id, eff);

    damage = CalculateDamage(i);

    if (eff < TOTAL_SPELL_EFFECTS)
    {
        (this->*SpellEffects[eff])((SpellEffIndex)i);
    }
}
示例#8
0
/**
**  Fire missile.
**
**  @param unit  Unit that fires the missile.
*/
void FireMissile(CUnit &unit, CUnit *goal, const Vec2i &goalPos)
{
	Vec2i newgoalPos = goalPos;
	// Goal dead?
	if (goal) {
		Assert(!unit.Type->Missile.Missile->AlwaysFire || unit.Type->Missile.Missile->Range);
		if (goal->Destroyed) {
			DebugPrint("destroyed unit\n");
			return;
		}
		if (goal->Removed) {
			return;
		}
		if (goal->CurrentAction() == UnitActionDie) {
			if (unit.Type->Missile.Missile->AlwaysFire) {
				newgoalPos = goal->tilePos;
				goal = NULL;
			} else {
				return;
			}
		}
	}

	// No missile hits immediately!
	if (unit.Type->Missile.Missile->Class == MissileClassNone) {
		// No goal, take target coordinates
		if (!goal) {
			if (Map.WallOnMap(goalPos)) {
				if (Map.HumanWallOnMap(goalPos)) {
					Map.HitWall(goalPos,
								CalculateDamageStats(*unit.Stats,
													 *UnitTypeHumanWall->Stats, unit.Variable[BLOODLUST_INDEX].Value));
				} else {
					Map.HitWall(goalPos,
								CalculateDamageStats(*unit.Stats,
													 *UnitTypeOrcWall->Stats, unit.Variable[BLOODLUST_INDEX].Value));
				}
				return;
			}
			DebugPrint("Missile-none hits no unit, shouldn't happen!\n");
			return;
		}
		HitUnit(&unit, *goal, CalculateDamage(unit, *goal));
		return;
	}

	// If Firing from inside a Bunker
	CUnit *from = GetFirstContainer(unit);
	const int dir = ((unit.Direction + NextDirection / 2) & 0xFF) / NextDirection;
	const PixelPos startPixelPos = Map.TilePosToMapPixelPos_Center(from->tilePos)
								   + unit.Type->MissileOffsets[dir][0];

	Vec2i dpos;
	if (goal) {
		Assert(goal->Type);  // Target invalid?
		// Moved out of attack range?

		if (unit.MapDistanceTo(*goal) < unit.Type->MinAttackRange) {
			DebugPrint("Missile target too near %d,%d\n" _C_
					   unit.MapDistanceTo(*goal) _C_ unit.Type->MinAttackRange);
			// FIXME: do something other?
			return;
		}
		// Fire to nearest point of the unit!
		// If Firing from inside a Bunker
		if (unit.Container) {
			NearestOfUnit(*goal, GetFirstContainer(unit)->tilePos, &dpos);
		} else {
			dpos = goal->tilePos + goal->Type->GetHalfTileSize();
		}
	} else {
		dpos = newgoalPos;
		// FIXME: Can this be too near??
	}

	PixelPos destPixelPos = Map.TilePosToMapPixelPos_Center(dpos);
	Missile *missile = MakeMissile(*unit.Type->Missile.Missile, startPixelPos, destPixelPos);
	//
	// Damage of missile
	//
	if (goal) {
		missile->TargetUnit = goal;
	}
	missile->SourceUnit = &unit;
}
示例#9
0
		void Compute(CUnit *const dest)
		{
			const CPlayer &player = *attacker->Player;

			if (!dest->IsVisibleAsGoal(player)) {
				dest->CacheLock = 1;
				return;
			}

			const CUnitType &type =  *attacker->Type;
			const CUnitType &dtype = *dest->Type;
			// won't be a target...
			if (!CanTarget(type, dtype)) { // can't be attacked.
				dest->CacheLock = 1;
				return;
			}
			// Don't attack invulnerable units
			if (dtype.BoolFlag[INDESTRUCTIBLE_INDEX].value || dest->Variable[UNHOLYARMOR_INDEX].Value) {
				dest->CacheLock = 1;
				return;
			}

			//  Calculate the costs to attack the unit.
			//  Unit with the smallest attack costs will be taken.

			int cost = 0;
			int hp_damage_evaluate;
			if (Damage) {
				hp_damage_evaluate = CalculateDamage(*attacker, *dest, Damage);
			} else {
				hp_damage_evaluate = attacker->Stats->Variables[BASICDAMAGE_INDEX].Value
									 + attacker->Stats->Variables[PIERCINGDAMAGE_INDEX].Value;
			}
			if (!player.IsEnemy(*dest)) { // a friend or neutral
				dest->CacheLock = 1;

				// Calc a negative cost
				// The gost is more important when the unit would be killed
				// by our fire.

				// It costs (is positive) if hp_damage_evaluate>dest->HP ...)
				// FIXME : assume that PRIORITY_FACTOR>HEALTH_FACTOR
				cost = HEALTH_FACTOR * (2 * hp_damage_evaluate -
										dest->Variable[HP_INDEX].Value) /
					   (dtype.TileWidth * dtype.TileWidth);
				cost = std::max(cost, 1);
				cost = -cost;
			} else {
				//  Priority 0-255
				cost += dtype.DefaultStat.Variables[PRIORITY_INDEX].Value * PRIORITY_FACTOR;

				for (unsigned int i = 0; i < UnitTypeVar.GetNumberBoolFlag(); i++) {
					if (type.BoolFlag[i].AiPriorityTarget != CONDITION_TRUE) {
						if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_ONLY) &
							(dtype.BoolFlag[i].value)) {
							cost -= AIPRIORITY_BONUS;
						} else if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_FALSE) &
								   (dtype.BoolFlag[i].value)) {
							cost += AIPRIORITY_BONUS;
						}
					}
				}

				//  Remaining HP (Health) 0-65535
				// Give a boost to unit we can kill in one shoot only

				// calculate HP which will remain in the enemy unit, after hit
				int effective_hp = (dest->Variable[HP_INDEX].Value - 2 * hp_damage_evaluate);

				// Unit we won't kill are evaluated the same
				// Unit we are sure to kill are all evaluated the same (except PRIORITY)
				clamp(&effective_hp, -hp_damage_evaluate, 0);

				// Here, effective_hp vary from -hp_damage_evaluate (unit will be killed) to 0 (unit can't be killed)
				// => we prefer killing rather than only hitting...
				cost += -effective_hp * HEALTH_FACTOR;

				//  Unit can attack back.
				if (CanTarget(dtype, type)) {
					cost += CANATTACK_BONUS;
				}

				// the cost may be divided across multiple cells
				cost = cost / (dtype.TileWidth * dtype.TileWidth);
				cost = std::max(cost, 1);

				// Removed Unit's are in bunkers
				int d;
				if (attacker->Removed) {
					d = attacker->Container->MapDistanceTo(*dest);
				} else {
					d = attacker->MapDistanceTo(*dest);
				}

				int attackrange = attacker->Stats->Variables[ATTACKRANGE_INDEX].Max;
				if (d <= attackrange ||
					(d <= range && UnitReachable(*attacker, *dest, attackrange))) {
					++enemy_count;
				} else {
					dest->CacheLock = 1;
				}
				// Attack walls only if we are stuck in them
				if (dtype.BoolFlag[WALL_INDEX].value && d > 1) {
					dest->CacheLock = 1;
				}
			}

			// cost map is relative to attacker position
			const int x = dest->tilePos.x - attacker->tilePos.x + (size / 2);
			const int y = dest->tilePos.y - attacker->tilePos.y + (size / 2);
			Assert(x >= 0 && y >= 0);

			// Mark the good/bad array...
			for (int yy = 0; yy < dtype.TileHeight; ++yy) {
				for (int xx = 0; xx < dtype.TileWidth; ++xx) {
					int pos = (y + yy) * (size / 2) + (x + xx);
					if (pos >= good->size()) {
						printf("BUG: RangeTargetFinder::FillBadGood.Compute out of range. "\
						       "size: %d, pos: %d, " \
						       "x: %d, xx: %d, y: %d, yy: %d",
						       size, pos, x, xx, y, yy);
						break;
					}
					if (cost < 0) {
						good->at(pos) -= cost;
					} else {
						bad->at(pos) += cost;
					}
				}
			}
		}
示例#10
0
int CastMagic::FlyingWeapon(MonsterData* caster, int x, int y)
{
    Monster * d = World.getDungeonManager().level(World.GetCurrentLevel()).getCell(x, y).GetMonster();
    MonsterData* defender = World.getMonsterManager().FindMonsterData(d);
    if (caster == defender)
    {
        if (caster->isPlayer())
            World.getTextManager().newLine("Not a good idea. ");
        return 0;
    }
    WorldBuilder & world = World;
    StandardMonsterActions::ShowTrajectory(caster->level, caster->pos.x, caster->pos.y, x, y, '*', Random::getInt(2,0) ? 255:128, 0, 255);

    if (defender == NULL || d == NULL) 
        return 0;

    int attackStrength  = caster->AttackStrength();
    int defenceStrength = defender->DefendStrength();

    attackStrength  = attackStrength  + (Random::getInt(7, 1) + Random::getInt(7, 1));
    defenceStrength = defenceStrength + (Random::getInt(7, 1) + Random::getInt(7, 1));

    if (attackStrength > defenceStrength) //caster hits defender
    {
        if (caster->isPlayer()) //player is caster
        {
            if (defender->AbsorbTest())
            {
                World.getTextManager().newLine("The %s's armour deflected the magic missile. ", defender->monster.name.c_str());
            }
            else
            {
                if (int effect = defender->monster.GetEffect(repelMissiles) > 0) //repel missiles
                {
                    if (Random::getInt(5, 0) <= effect)
                    {
                        World.getTextManager().newLine("The %s repels your magic missile. ", defender->monster.name.c_str());
                        return 1;
                    }
                }

                World.getTextManager().newLine("Your magic missile hits the %s. ", defender->monster.name.c_str());
                defender->monster.stamina -= CalculateDamage(attackStrength, defenceStrength);
                if (defender->monster.stamina <= 0)
                {
                    caster->experience += defender->monster.experience;
                    caster->XP();
                }
            }
        }
        else if (defender->isPlayer())
        {
            if (defender->AbsorbTest())
            {
                World.getTextManager().newLine("The %s's magic missile was deflected by your armour. ", caster->monster.name.c_str());
            }
            else
            {
                if (int effect = defender->monster.GetEffect(repelMissiles) > 0) //repel missiles
                {
                    if (Random::getInt(5, 0) <= effect)
                    {
                        World.getTextManager().newLine("The repel the %s's magic missile. ", caster->monster.name.c_str());
                        return 1;
                    }
                }
                World.getTextManager().newLine("The %s's magic missile hits you. ", caster->monster.name.c_str());
                defender->monster.stamina -= CalculateDamage(attackStrength, defenceStrength);
                World.getDeathMessage().SetDeathMessage("was killed by a %s's magic missile. ", caster->monster.name.c_str());

            }
        }
    }

    else if (attackStrength < defenceStrength) //defender blocks caster
    {
        if (caster->isPlayer()) //player is caster
        {
            World.getTextManager().newLine("Your magic missile misses the %s. ", defender->monster.name.c_str());
        }

        else if (defender->isPlayer()) //player is defender
        {
            World.getTextManager().newLine("You dodge the %s's magic missile. ", caster->monster.name.c_str());
        }
    }
    else //no damage done
    {
        if (caster->isPlayer())
        {
            World.getTextManager().newLine("The %s dodges your magic missile. ", defender->monster.name.c_str());
        }
        if (defender->isPlayer())
        {
            //	if(defender->slots.weapon !=NULL)
            if (World.getMonsterManager().getMonsterItems().GetEquipment(defender, weapon))
            {
                World.getTextManager().newLine("The %s's magic missile misses you. ", caster->monster.name.c_str());
            }
            else
            {
                World.getTextManager().newLine("You dodge the %s's magic missile. ", caster->monster.name.c_str());
            }
        }
    }

    return 1;
}
示例#11
0
/**
**  Fire missile.
**
**  @param unit  Unit that fires the missile.
*/
void FireMissile(CUnit &unit, CUnit *goal, const Vec2i &goalPos)
{
	Vec2i newgoalPos = goalPos;
	// Goal dead?
	if (goal) {
		Assert(!unit.Type->Missile.Missile->AlwaysFire || unit.Type->Missile.Missile->Range);
		if (goal->Destroyed) {
			DebugPrint("destroyed unit\n");
			return;
		}
		if (goal->Removed) {
			return;
		}
		if (goal->CurrentAction() == UnitActionDie) {
			if (unit.Type->Missile.Missile->AlwaysFire) {
				newgoalPos = goal->tilePos;
				goal = NULL;
			} else {
				return;
			}
		}
	}

	// No missile hits immediately!
	//Wyrmgus start
//	if (unit.Type->Missile.Missile->Class == MissileClassNone) {
	if (unit.Type->Missile.Missile->Class == MissileClassNone || (unit.Type->Animations && unit.Type->Animations->Attack && unit.Type->Animations->RangedAttack && ((goal && unit.MapDistanceTo(*goal) <= 1) || (!goal && unit.MapDistanceTo(goalPos) <= 1)) && !unit.Container)) { // treat melee attacks from units that have both attack and ranged attack animations as having missile class none
	//Wyrmgus end
		//Wyrmgus start
		int damage = 0;
		//Wyrmgus end
		// No goal, take target coordinates
		if (!goal) {
			if (Map.WallOnMap(goalPos)) {
				//Wyrmgus start
//				if (Map.HumanWallOnMap(goalPos)) {
				if (Map.HumanWallOnMap(goalPos) && CalculateHit(unit, *UnitTypeHumanWall->Stats, NULL) == true) {
				//Wyrmgus end
					//Wyrmgus start
					PlayUnitSound(unit, VoiceHit);
					damage = CalculateDamageStats(unit, *UnitTypeHumanWall->Stats, NULL);
					//Wyrmgus end
					Map.HitWall(goalPos,
								//Wyrmgus start
//								CalculateDamageStats(*unit.Stats,
//													 *UnitTypeHumanWall->Stats, unit.Variable[BLOODLUST_INDEX].Value));
								damage);
								//Wyrmgus end
				//Wyrmgus start
//				} else {
				} else if (Map.OrcWallOnMap(goalPos) && CalculateHit(unit, *UnitTypeOrcWall->Stats, NULL) == true) {
				//Wyrmgus end
					//Wyrmgus start
					PlayUnitSound(unit, VoiceHit);
					damage = CalculateDamageStats(unit, *UnitTypeOrcWall->Stats, NULL);
					//Wyrmgus end
					Map.HitWall(goalPos,
								//Wyrmgus start
//								CalculateDamageStats(*unit.Stats,
//													 *UnitTypeOrcWall->Stats, unit.Variable[BLOODLUST_INDEX].Value));
								damage);
								//Wyrmgus end
				}
				return;
			}
			DebugPrint("Missile-none hits no unit, shouldn't happen!\n");
			return;
		}
		//Wyrmgus start
//		HitUnit(&unit, *goal, CalculateDamage(unit, *goal, Damage));
		if (CalculateHit(unit, *goal->Stats, goal) == true) {
			damage = CalculateDamage(unit, *goal, Damage);
			HitUnit(&unit, *goal, damage);
			PlayUnitSound(unit, VoiceHit);
			
			//apply Thorns damage if attacker is at melee range
			if (goal && goal->Variable[THORNSDAMAGE_INDEX].Value && unit.MapDistanceTo(*goal) <= 1) {
				int thorns_damage = std::max<int>(goal->Variable[THORNSDAMAGE_INDEX].Value - unit.Variable[ARMOR_INDEX].Value, 1);
				if (GameSettings.NoRandomness) {
					thorns_damage -= ((thorns_damage + 2) / 2) / 2; //if no randomness setting is used, then the damage will always return what would have been the average damage with randomness
				} else {
					thorns_damage -= SyncRand() % ((thorns_damage + 2) / 2);
				}
				HitUnit(goal, unit, thorns_damage);
			}
		} else {
			PlayUnitSound(unit, VoiceMiss);
		}
		//Wyrmgus end
		return;
	}

	// If Firing from inside a Bunker
	CUnit *from = GetFirstContainer(unit);
	const int dir = ((unit.Direction + NextDirection / 2) & 0xFF) / NextDirection;
	const PixelPos startPixelPos = Map.TilePosToMapPixelPos_Center(from->tilePos)
								   + unit.Type->MissileOffsets[dir][0];

	Vec2i dpos;
	if (goal) {
		Assert(goal->Type);  // Target invalid?
		// Moved out of attack range?

		if (unit.MapDistanceTo(*goal) < unit.Type->MinAttackRange) {
			DebugPrint("Missile target too near %d,%d\n" _C_
					   unit.MapDistanceTo(*goal) _C_ unit.Type->MinAttackRange);
			// FIXME: do something other?
			return;
		}
		// Fire to nearest point of the unit!
		// If Firing from inside a Bunker
		if (unit.Container) {
			NearestOfUnit(*goal, GetFirstContainer(unit)->tilePos, &dpos);
		} else {
			dpos = goal->tilePos + goal->Type->GetHalfTileSize();
		}
	} else {
		dpos = newgoalPos;
		// FIXME: Can this be too near??
	}

	PixelPos destPixelPos = Map.TilePosToMapPixelPos_Center(dpos);
	Missile *missile = MakeMissile(*unit.Type->Missile.Missile, startPixelPos, destPixelPos);
	//
	// Damage of missile
	//
	if (goal) {
		missile->TargetUnit = goal;
	}
	missile->SourceUnit = &unit;
}
示例#12
0
/**
**  Fire missile.
**
**  @param unit  Unit that fires the missile.
*/
void FireMissile(CUnit *unit)
{
	int x;
	int y;
	int dx;
	int dy;
	CUnit *goal;
	Missile *missile;

	//
	// Goal dead?
	//
	goal = unit->Orders[0]->Goal;
	if (goal) {

		// Better let the caller/action handle this.

		if (goal->Destroyed) {
			DebugPrint("destroyed unit\n");
			return;
		}
		if (goal->Removed || goal->Orders[0]->Action == UnitActionDie) {
			return;
		}

		// FIXME: Some missile hit the field of the target and all units on it.
		// FIXME: goal is already dead, but missile could hit others?
	}

	//
	// No missile hits immediately!
	//
	if (unit->Type->Missile.Missile->Class == MissileClassNone) {
		// No goal, take target coordinates
		if (!goal) {
			DebugPrint("Missile-none hits no unit, shouldn't happen!\n");
			return;
		}

		HitUnit(unit, goal, CalculateDamage(unit, goal));

		return;
	}

	// If Firing from inside a Bunker
	if (unit->Container) {
		x = unit->Container->X * TileSizeX + TileSizeX / 2;  // missile starts in tile middle
		y = unit->Container->Y * TileSizeY + TileSizeY / 2;
	} else {
		x = unit->X * TileSizeX + TileSizeX / 2;  // missile starts in tile middle
		y = unit->Y * TileSizeY + TileSizeY / 2;
	}

	if (goal) {
		Assert(goal->Type);  // Target invalid?
		//
		// Moved out of attack range?
		//
		if (MapDistanceBetweenUnits(unit, goal) < unit->Type->MinAttackRange) {
			DebugPrint("Missile target too near %d,%d\n" _C_
				MapDistanceBetweenUnits(unit, goal) _C_ unit->Type->MinAttackRange);
			// FIXME: do something other?
			return;
		}
		// Fire to nearest point of the unit!
		// If Firing from inside a Bunker
		if (unit->Container) {
			NearestOfUnit(goal, unit->Container->X, unit->Container->Y, &dx, &dy);
		} else {
			NearestOfUnit(goal, unit->X, unit->Y, &dx, &dy);
		}
	} else {
		dx = unit->Orders[0]->X;
		dy = unit->Orders[0]->Y;
		// FIXME: Can this be too near??
	}

	// Fire to the tile center of the destination.
	dx = dx * TileSizeX + TileSizeX / 2;
	dy = dy * TileSizeY + TileSizeY / 2;
	missile = MakeMissile(unit->Type->Missile.Missile, x, y, dx, dy);
	//
	// Damage of missile
	//
	if (goal) {
		missile->TargetUnit = goal;
		goal->RefsIncrease();
	}
	missile->SourceUnit = unit;
	unit->RefsIncrease();
}