/** ** Missile flies from x,y to x1,y1 than bounces NumBounces times */ void MissilePointToPointBounce::Action() { this->Wait = this->Type->Sleep; if (PointToPointMissile(this)) { if (this->State < 2 * this->Type->NumBounces - 1 && this->TotalStep) { int xstep; int ystep; xstep = (this->DX - this->SourceX) * 1024 / this->TotalStep; ystep = (this->DY - this->SourceY) * 1024 / this->TotalStep; this->DX += xstep * (TileSizeX + TileSizeY) * 3 / 4 / 1024; this->DY += ystep * (TileSizeX + TileSizeY) * 3 / 4 / 1024; this->State++; // !(State & 1) to initialise this->SourceX = this->X; this->SourceY = this->Y; PointToPointMissile(this); //this->State++; MissileHit(this); // FIXME: hits to left and right // FIXME: reduce damage effects on later impacts } else { MissileHit(this); this->TTL = 0; } } else { NextMissileFrame(this, 1, 0); } }
/** ** Missile shows hit points? */ void MissileHit::Action() { this->Wait = this->Type->Sleep; if (PointToPointMissile(this)) { ::MissileHit(this); this->TTL = 0; } }
/** ** Missile flies from x,y to x1,y1 and stays there for a moment */ void MissilePointToPointCycleOnce::Action() { this->Wait = this->Type->Sleep; if (PointToPointMissile(this)) { MissileHit(this); this->TTL = 0; } else { NextMissileFrameCycle(this); } }
/** ** Missile flies from x,y to x1,y1 showing the first frame ** and then shows a hit animation. */ void MissilePointToPointWithHit::Action() { this->Wait = this->Type->Sleep; if (PointToPointMissile(this)) { if (NextMissileFrame(this, 1, 0)) { MissileHit(this); this->TTL = 0; } } }
/** ** Death-Coil class. Damages organic units and gives to the caster. ** ** @todo do it configurable. */ void MissileDeathCoil::Action() { this->Wait = this->Type->Sleep; if (PointToPointMissile(*this)) { Assert(this->SourceUnit != NULL); CUnit &source = *this->SourceUnit; if (source.Destroyed) { return; } // source unit still exists // // Target unit still exists and casted on a special target // if (this->TargetUnit && !this->TargetUnit->Destroyed && this->TargetUnit->CurrentAction() == UnitActionDie) { HitUnit(&source, *this->TargetUnit, this->Damage); if (source.CurrentAction() != UnitActionDie) { source.Variable[HP_INDEX].Value += this->Damage; if (source.Variable[HP_INDEX].Value > source.Variable[HP_INDEX].Max) { source.Variable[HP_INDEX].Value = source.Variable[HP_INDEX].Max; } } } else { // // No target unit -- try enemies in range 5x5 // Must be parametrable // std::vector<CUnit *> table; const Vec2i destPos = Map.MapPixelPosToTilePos(this->destination); const Vec2i range(2, 2); Select(destPos - range, destPos + range, table, IsEnemyWith(*source.Player)); if (table.empty()) { return; } const size_t n = table.size(); // enemy count const int damage = std::min<int>(1, this->Damage / n); // disperse damage between them for (size_t i = 0; i != n; ++i) { HitUnit(&source, *table[i], damage); } if (source.CurrentAction() != UnitActionDie) { source.Variable[HP_INDEX].Value += this->Damage; if (source.Variable[HP_INDEX].Value > source.Variable[HP_INDEX].Max) { source.Variable[HP_INDEX].Value = source.Variable[HP_INDEX].Max; } } } this->TTL = 0; } }
/** ** Whirlwind controller ** ** @todo do it more configurable. */ void MissileWhirlwind::Action() { // Animate, move. if (!this->AnimWait--) { if (this->NextMissileFrame(1, 0)) { this->SpriteFrame = 0; PointToPointMissile(*this); } this->AnimWait = this->Type->Sleep; } this->Wait = 1; // Center of the tornado const PixelPos pixelCenter = this->position + this->Type->size / 2; const PixelPos centerOffset(PixelTileSize.x / 2, PixelTileSize.y); const Vec2i center = Map.MapPixelPosToTilePos(pixelCenter + centerOffset); //Wyrmgus start Assert(this->Type->AttackSpeed); // if (!(this->TTL % CYCLES_PER_SECOND / 10)) { if (!(this->TTL % CYCLES_PER_SECOND / this->Type->AttackSpeed)) { //AttackSpeed is by default 10 //Wyrmgus end this->MissileHit(); } // Changes direction every 3 seconds (approx.) if (!(this->TTL % 100)) { // missile has reached target unit/spot Vec2i newPos; do { // find new destination in the map newPos.x = center.x + SyncRand() % 5 - 2; newPos.y = center.y + SyncRand() % 5 - 2; } while (!Map.Info.IsPointOnMap(newPos)); this->destination = Map.TilePosToMapPixelPos_Center(newPos); this->source = this->position; this->State = 0; DebugPrint("Whirlwind new direction: %d, %d, TTL: %d\n" _C_ this->destination.x _C_ this->destination.y _C_ this->TTL); } }
//FIXME: (Fabrice) I don't know if my update for missile visibility is fully //correct. global void MissileActions(void) { int missile; for( missile=0; missile<NumMissiles; ++missile ) { if( Missiles[missile].Type==MissileFree ) { continue; } if( Missiles[missile].Wait-- ) { continue; } if (MissileVisible(missile)) { // check before movement MustRedraw|=RedrawMap; } switch( Missiles[missile].Type->Class ) { case MissileClassPointToPoint: Missiles[missile].Wait=1; if( PointToPointMissile(missile) ) { MissileHit(missile); Missiles[missile].Type=MissileFree; } else { // // Animate missile, cycle through frames // Missiles[missile].Frame+=5; if( (Missiles[missile].Frame&127) >=Missiles[missile].Type->RleSprite->NumFrames ) { Missiles[missile].Frame= // (Missiles[missile].Frame&128)| (Missiles[missile].Frame -Missiles[missile].Type->RleSprite ->NumFrames); } DebugLevel3("Frame %d of %d\n" ,Missiles[missile].Frame ,Missiles[missile].Type->RleSprite->NumFrames); } break; case MissileClassPointToPointWithDelay: Missiles[missile].Wait=1; if( PointToPointMissile(missile) ) { switch( Missiles[missile].State++ ) { case 1: // FIXME: bounces up. PlayMissileSound(Missiles+missile, Missiles[missile].Type->ImpactSound.Sound); // FIXME: make this configurable!! switch( Missiles[missile].Type->Type ) { case MissileSmallCannon: MakeMissile(MissileCannonExplosion ,Missiles[missile].X ,Missiles[missile].Y ,0,0); break; case MissileBigCannon: MakeMissile(MissileCannonTowerExplosion ,Missiles[missile].X ,Missiles[missile].Y ,0,0); break; case MissileCatapultRock: case MissileBallistaBolt: MakeMissile(MissileImpact ,Missiles[missile].X ,Missiles[missile].Y ,0,0); break; } break; default: MissileHit(missile); Missiles[missile].Type=MissileFree; break; } } else { // // Animate missile, depends on the way. // // FIXME: how? } break; case MissileClassPointToPoint3Bounces: Missiles[missile].Wait=1; if( PointToPointMissile(missile) ) { // // 3 Bounces. // switch( Missiles[missile].State ) { case 1: case 3: case 5: Missiles[missile].State+=2; Missiles[missile].DX+= Missiles[missile].Xstep*TileSizeX*2; Missiles[missile].DY+= Missiles[missile].Ystep*TileSizeY*2; PlayMissileSound(Missiles+missile, Missiles[missile].Type->ImpactSound.Sound); MakeMissile(MissileExplosion ,Missiles[missile].X ,Missiles[missile].Y ,0,0); MissileHit(missile); // FIXME: hits to left and right // FIXME: reduce damage break; default: Missiles[missile].Type=MissileFree; break; } } else { // // Animate missile, cycle through frames // Missiles[missile].Frame+=5; if( (Missiles[missile].Frame&127) >=Missiles[missile].Type->RleSprite->NumFrames ) { Missiles[missile].Frame= // (Missiles[missile].Frame&128)| (Missiles[missile].Frame -Missiles[missile].Type->RleSprite ->NumFrames); } DebugLevel3("Frame %d of %d\n" ,Missiles[missile].Frame ,Missiles[missile].Type->RleSprite->NumFrames); } break; case MissileClassPointToPointWithHit: Missiles[missile].Wait=1; if( PointToPointMissile(missile) ) { // // Animate hit // Missiles[missile].Frame+=5; if( (Missiles[missile].Frame&127) >=Missiles[missile].Type->RleSprite->NumFrames ) { MissileHit(missile); Missiles[missile].Type=MissileFree; } } break; case MissileClassStayWithDelay: Missiles[missile].Wait=1; if( ++Missiles[missile].Frame ==Missiles[missile].Type->RleSprite ->NumFrames ) { MissileHit(missile); Missiles[missile].Type=MissileFree; } break; case MissileClassCycleOnce: Missiles[missile].Wait=Missiles[missile].Type->Speed; switch( Missiles[missile].State ) { case 0: case 2: ++Missiles[missile].State; break; case 1: if( ++Missiles[missile].Frame ==Missiles[missile].Type->RleSprite ->NumFrames ) { --Missiles[missile].Frame; ++Missiles[missile].State; } break; case 3: if( !Missiles[missile].Frame-- ) { MissileHit(missile); Missiles[missile].Type=MissileFree; } break; } break; } if (Missiles[missile].Type!=MissileFree && MissileVisible(missile)) { // check after movement MustRedraw|=RedrawMap; } } }