[+] Added new MissileStraightFly, which flies straightly, until it reaches terrain tile field flags mask specified in MissileType::MissileStopFlags. You can use KillFirstUnit property, so missile will explode on first unit.

[*] More correct missile handling in MissileHandleBlocking
[*] Missile with KillFirstUnit can't kill dead or non-solid units
This commit is contained in:
cybermind 2013-09-01 11:44:48 +06:00
parent c64a14b05c
commit 210369b752
5 changed files with 73 additions and 36 deletions

View file

@ -216,6 +216,7 @@ set(missile_SRCS
src/missile/missile_pointtopointbounce.cpp
src/missile/missile_pointtopointcycleonce.cpp
src/missile/missile_stay.cpp
src/missile/missile_straightfly.cpp
src/missile/missile_tracer.cpp
src/missile/missile_whirlwind.cpp
src/missile/missileconfig.cpp

View file

@ -318,23 +318,24 @@ class LuaCallback;
** Missile-class this defines how a missile-type reacts.
*/
enum {
MissileClassNone, /// Missile does nothing
MissileClassPointToPoint, /// Missile flies from x,y to x1,y1
MissileClassPointToPointWithHit, /// Missile flies from x,y to x1,y1 than shows hit animation.
MissileClassPointToPointCycleOnce, /// Missile flies from x,y to x1,y1 and animates ONCE from start to finish and back
MissileClassPointToPointBounce, /// Missile flies from x,y to x1,y1 than bounces three times.
MissileClassStay, /// Missile appears at x,y, does it's anim and vanishes.
MissileClassCycleOnce, /// Missile appears at x,y, then cycle through the frames once.
MissileClassFire, /// Missile doesn't move, than checks the source unit for HP.
MissileClassHit, /// Missile shows the hit points.
MissileClassParabolic, /// Missile flies from x,y to x1,y1 using a parabolic path
MissileClassLandMine, /// Missile wait on x,y until a non-air unit comes by, the explodes.
MissileClassWhirlwind, /// Missile appears at x,y, is whirlwind
MissileClassFlameShield, /// Missile surround x,y
MissileClassDeathCoil, /// Missile is death coil.
MissileClassTracer, /// Missile seeks towards to target unit
MissileClassClipToTarget, /// Missile remains clipped to target's current goal and plays his animation once
MissileClassContinious /// Missile stays and plays it's animation several times
MissileClassNone, /// Missile does nothing
MissileClassPointToPoint, /// Missile flies from x,y to x1,y1
MissileClassPointToPointWithHit, /// Missile flies from x,y to x1,y1 than shows hit animation.
MissileClassPointToPointCycleOnce, /// Missile flies from x,y to x1,y1 and animates ONCE from start to finish and back
MissileClassPointToPointBounce, /// Missile flies from x,y to x1,y1 than bounces three times.
MissileClassStay, /// Missile appears at x,y, does it's anim and vanishes.
MissileClassCycleOnce, /// Missile appears at x,y, then cycle through the frames once.
MissileClassFire, /// Missile doesn't move, than checks the source unit for HP.
MissileClassHit, /// Missile shows the hit points.
MissileClassParabolic, /// Missile flies from x,y to x1,y1 using a parabolic path
MissileClassLandMine, /// Missile wait on x,y until a non-air unit comes by, the explodes.
MissileClassWhirlwind, /// Missile appears at x,y, is whirlwind
MissileClassFlameShield, /// Missile surround x,y
MissileClassDeathCoil, /// Missile is death coil.
MissileClassTracer, /// Missile seeks towards to target unit
MissileClassClipToTarget, /// Missile remains clipped to target's current goal and plays his animation once
MissileClassContinious, /// Missile stays and plays it's animation several times
MissileClassStraightFly /// Missile flies from x,y to x1,y1 then continues to fly, until incompatible terrain is detected
};
/// Base structure of missile-types
@ -389,6 +390,7 @@ public:
int Damage; /// missile damage (used for non-direct missiles, e.g. impacts)
int ReduceFactor; /// Multiplier for reduce or increase damage dealt to the next unit
int SmokePrecision; /// How frequently the smoke missile will generate itself
int MissileStopFlags; /// On which terrain types missile won't fly
int Range; /// missile damage range
int SplashFactor; /// missile splash divisor
@ -445,8 +447,9 @@ public:
int Damage; /// direct damage that missile applies
int TTL; /// time to live (ticks) used for spells
int Hidden; /// If this is 1 then the missile is invisible
int TTL; /// time to live (ticks) used for spells
int Hidden; /// If this is 1 then the missile is invisible
int DestroyMissile; /// this tells missile-class-straight-fly, that it's time to die
// Internal use:
int CurrentStep; /// Current step (0 <= x < TotalStep).
@ -552,6 +555,13 @@ public:
virtual void Action();
};
class MissileStraightFly : public Missile
{
public:
virtual void Action();
};
class BurningBuildingFrame
{
public:

View file

@ -146,7 +146,7 @@ MissileType *NewMissileTypeSlot(const std::string &ident)
Missile::Missile() :
Type(NULL), SpriteFrame(0), State(0), AnimWait(0), Wait(0),
Delay(0), SourceUnit(), TargetUnit(), Damage(0),
TTL(-1), Hidden(0),
TTL(-1), Hidden(0), DestroyMissile(0),
CurrentStep(0), TotalStep(0),
Local(0)
{
@ -224,6 +224,9 @@ Missile::Missile() :
case MissileClassContinious :
missile = new MissileContinious;
break;
case MissileClassStraightFly :
missile = new MissileStraightFly;
break;
}
const PixelPos halfSize = mtype.size / 2;
missile->position = startPos - halfSize;
@ -666,8 +669,16 @@ void MissileHandlePierce(Missile &missile, const Vec2i &pos)
bool MissileHandleBlocking(Missile &missile, const PixelPos &position)
{
if (missile.SourceUnit && (missile.SourceUnit->CurrentAction() == UnitActionAttackGround
|| (missile.TargetUnit && missile.SourceUnit->Type->UnitType == missile.TargetUnit->Type->UnitType))) {
const MissileType &mtype = *missile.Type;
if (missile.SourceUnit) {
bool shouldHit = false;
if (missile.TargetUnit && missile.SourceUnit->Type->UnitType == missile.TargetUnit->Type->UnitType) {
shouldHit = true;
}
if (mtype.Range && mtype.CorrectSphashDamage) {
shouldHit = true;
}
if (shouldHit) {
// search for blocking units
std::vector<CUnit *> blockingUnits;
const Vec2i missilePos = Map.MapPixelPosToTilePos(position);
@ -686,20 +697,27 @@ bool MissileHandleBlocking(Missile &missile, const PixelPos &position)
} else {
missile.position = position;
}
missile.DestroyMissile = 1;
return true;
}
}
// missile can kill any unit on it's way
if (missile.Type->KillFirstUnit && &unit != missile.SourceUnit) {
// can't kill non-solid or dead units
if (unit.IsAliveOnMap() == false || unit.Type->BoolFlag[NONSOLID_INDEX].value) {
continue;
}
if (missile.Type->FriendlyFire == false || unit.IsEnemy(*missile.SourceUnit->Player)) {
missile.TargetUnit = &unit;
if (unit.Type->TileWidth == 1 || unit.Type->TileHeight == 1) {
missile.position = Map.TilePosToMapPixelPos_TopLeft(unit.tilePos);
}
missile.DestroyMissile = 1;
return true;
}
}
}
}
}
return false;
}
@ -725,7 +743,6 @@ bool PointToPointMissile(Missile &missile)
const PixelPrecise oldPos((double)missile.position.x, (double)missile.position.y); // Remember old position
PixelPrecise pos(oldPos);
missile.position = missile.source + diff * missile.CurrentStep / missile.TotalStep;
Vec2i mapPos = Map.MapPixelPosToTilePos(missile.position);
for (; pos.x * sign.x <= missile.position.x * sign.x
&& pos.y * sign.y <= missile.position.y * sign.y;
@ -755,18 +772,29 @@ bool PointToPointMissile(Missile &missile)
}
// Handle wall blocking and kill first enemy
mapPos = Map.MapPixelPosToTilePos(missile.position);
for (pos = oldPos; pos.x * sign.x <= missile.position.x * sign.x
&& pos.y * sign.y <= missile.position.y * sign.y;
pos.x += (double)diff.x / missile.TotalStep,
pos.y += (double)diff.y / missile.TotalStep) {
const PixelPos position((int)pos.x + missile.Type->size.x / 2,
(int)pos.y + missile.Type->size.y / 2);
if (Map.MapPixelPosToTilePos(position) != mapPos) {
if (MissileHandleBlocking(missile, position)) {
return true;
const Vec2i tilePos(Map.MapPixelPosToTilePos(position));
if (MissileHandleBlocking(missile, position)) {
return true;
}
if (missile.Type->MissileStopFlags) {
if (!Map.Info.IsPointOnMap(tilePos)) { // gone outside
missile.TTL = 0;
return false;
}
const CMapField &mf = *Map.Field(tilePos);
if (missile.Type->MissileStopFlags & mf.Flags) { // incompatible terrain
missile.position = position;
missile.MissileHit();
missile.TTL = 0;
return false;
}
mapPos = Map.MapPixelPosToTilePos(position);
}
}
@ -1290,7 +1318,7 @@ MissileType::MissileType(const std::string &ident) :
AlwaysFire(false), Pierce(false), PierceOnce(false), IgnoreWalls(true), KillFirstUnit(false),
Class(), NumBounces(0), ParabolCoefficient(2048), StartDelay(0),
Sleep(0), Speed(0), TTL(-1), Damage(0), ReduceFactor(100), SmokePrecision(0),
Range(0), SplashFactor(0),
MissileStopFlags(0), Range(0), SplashFactor(0),
ImpactParticle(NULL), SmokeParticle(NULL), OnImpact(NULL),
G(NULL)
{

View file

@ -69,7 +69,6 @@ static bool TracerMissile(Missile &missile)
const PixelPrecise oldPos((double)missile.position.x, (double)missile.position.y); // Remember old position
PixelPrecise pos(oldPos);
missile.position = missile.source + diff * missile.CurrentStep / missile.TotalStep;
Vec2i mapPos = Map.MapPixelPosToTilePos(missile.position);
for (; pos.x * sign.x <= missile.position.x * sign.x
&& pos.y * sign.y <= missile.position.y * sign.y;
@ -98,18 +97,14 @@ static bool TracerMissile(Missile &missile)
}
// Handle wall blocking
mapPos = Map.MapPixelPosToTilePos(missile.position);
for (pos = oldPos; pos.x * sign.x <= missile.position.x * sign.x
&& pos.y * sign.y <= missile.position.y * sign.y;
pos.x += (double)diff.x / missile.TotalStep,
pos.y += (double)diff.y / missile.TotalStep) {
const PixelPos position((int)pos.x + missile.Type->size.x / 2,
(int)pos.y + missile.Type->size.y / 2);
if (Map.MapPixelPosToTilePos(position) != mapPos) {
if (MissileHandleBlocking(missile, position)) {
return true;
}
mapPos = Map.MapPixelPosToTilePos(position);
if (MissileHandleBlocking(missile, position)) {
return true;
}
}

View file

@ -69,6 +69,7 @@ static const char *MissileClassNames[] = {
"missile-class-tracer",
"missile-class-clip-to-target",
"missile-class-continious",
"missile-class-straight-fly",
NULL
};
@ -146,6 +147,8 @@ void MissileType::Load(lua_State *l)
this->ReduceFactor = LuaToNumber(l, -1);
} else if (!strcmp(value, "SmokePrecision")) {
this->SmokePrecision = LuaToNumber(l, -1);
} else if (!strcmp(value, "MissileStopFlags")) {
this->MissileStopFlags = LuaToNumber(l, -1);
} else if (!strcmp(value, "DrawLevel")) {
this->DrawLevel = LuaToNumber(l, -1);
} else if (!strcmp(value, "Range")) {