/** ** Pass to the next frame for animation. ** ** @param missile missile to animate. ** @param sign 1 for next frame, -1 for previous frame. ** @param longAnimation 1 if Frame is conditionned by covered distance, 0 else. ** ** @return 1 if animation is finished, 0 else. */ static int NextMissileFrame(Missile *missile, char sign, char longAnimation) { int neg; // True for mirroring sprite. int animationIsFinished; // returned value. int numDirections; // Number of direction of the missile. // // Animate missile, cycle through frames // neg = 0; animationIsFinished = 0; numDirections = missile->Type->NumDirections / 2 + 1; if (missile->SpriteFrame < 0) { neg = 1; missile->SpriteFrame = -missile->SpriteFrame - 1; } if (longAnimation) { int totalf; // Total number of frame (for one direction). int df; // Current frame (for one direction). int totalx; // Total distance to cover. int dx; // Covered distance. totalx = MapDistance(missile->DX, missile->DY, missile->SourceX, missile->SourceY); dx = MapDistance(missile->X, missile->Y, missile->SourceX, missile->SourceY); totalf = missile->Type->SpriteFrames / numDirections; df = missile->SpriteFrame / numDirections; if ((sign == 1 && dx * totalf <= df * totalx) || (sign == -1 && dx * totalf > df * totalx)) { return animationIsFinished; } } missile->SpriteFrame += sign * numDirections; if (sign > 0) { if (missile->SpriteFrame >= missile->Type->SpriteFrames) { missile->SpriteFrame -= missile->Type->SpriteFrames; animationIsFinished = 1; } } else { if (missile->SpriteFrame < 0) { missile->SpriteFrame += missile->Type->SpriteFrames; animationIsFinished = 1; } } if (neg) { missile->SpriteFrame = -missile->SpriteFrame - 1; } return animationIsFinished; }
/** ** Init the move. ** ** @param missile missile to initialise for movement. ** ** @return 1 if goal is reached, 0 else. */ static int MissileInitMove(Missile *missile) { int dx; int dy; dx = missile->DX - missile->X; dy = missile->DY - missile->Y; MissileNewHeadingFromXY(missile, dx, dy); if (!(missile->State & 1)) { missile->CurrentStep = 0; missile->TotalStep = 0; if (dx == 0 && dy == 0) { return 1; } // initialize missile->TotalStep = MapDistance(missile->SourceX, missile->SourceY, missile->DX, missile->DY); missile->State++; return 0; } Assert(missile->TotalStep != 0); missile->CurrentStep += missile->Type->Speed; if (missile->CurrentStep >= missile->TotalStep) { missile->X = missile->DX; missile->Y = missile->DY; return 1; } return 0; }
/** ** Unit Demolishs ** ** @param unit Unit, for that the demolish is handled. */ global void HandleActionDemolish(Unit* unit) { Unit* table[MAX_UNITS]; int i; int n; int x, y, ix, iy; Unit* goal; int err; DebugLevel3("Demolish %d\n",unit-Units); switch( unit->SubAction ) { // // Move near to target. // case 0: // FIXME: RESET FIRST!! err=HandleActionMove(unit); if( unit->Reset ) { goal=unit->Command.Data.Move.Goal; // // Target is dead, stop demolish // if( goal && (!goal->Type || !goal->HP || goal->Command.Action==UnitActionDie) ) { // FIXME: this can't happen, HandleActionMove resets goal! unit->Command.Data.Move.Goal=NoUnitP; unit->Command.Action=UnitActionStill; return; } // // Have reached target? // if( goal ) { if( MapDistanceToUnit(unit->X,unit->Y,goal)<=1 ) { unit->State=0; unit->SubAction=1; } } else if( MapDistance(unit->X,unit->Y ,unit->Command.Data.Move.DX ,unit->Command.Data.Move.DY)<=1 ) { unit->State=0; unit->SubAction=1; } else if( err ) { return; } unit->Command.Action=UnitActionDemolish; } break; // // Demolish the target. // case 1: x=unit->X; y=unit->Y; DestroyUnit(unit); // FIXME: Must play explosion sound n=SelectUnits(x-2,y-2, x+2, y+2,table); // FIXME: Don't hit flying units! for( i=0; i<n; ++i ) { HitUnit(table[i],DEMOLISH_DAMAGE); } for( ix=x-2; ix<=x+2; ix++ ) { for( iy=y-2; iy<=y+2; iy++ ) { n=TheMap.Fields[ix+iy*TheMap.Width].Flags; if( n&MapFieldWall ) { MapRemoveWall(ix,iy); } else if( n&MapFieldRocks ) { MapRemoveRock(ix,iy); } else if( n&MapFieldForest ) { MapRemoveWood(ix,iy); } } } break; } }
/** ** Unit Demolishs ** ** @param unit Unit, for that the demolish is handled. */ global void HandleActionDemolish(Unit* unit) { Unit* table[UnitMax]; int i; int n; int xmin, ymin, xmax, ymax; int ix, iy; Unit* goal; int err; DebugLevel3Fn("Demolish %d\n" _C_ UnitNumber(unit)); switch( unit->SubAction ) { // // Move near to target. // case 0: // first entry. NewResetPath(unit); unit->SubAction=1; // FALL THROUGH case 1: // FIXME: reset first!! why? (johns) err=DoActionMove(unit); if( unit->Reset ) { goal=unit->Orders[0].Goal; // // Target is dead, stop demolish. // FIXME: what should I do, go back or explode on place? // if( goal ) { if( goal->Destroyed ) { DebugLevel0Fn("Destroyed unit\n"); RefsDebugCheck( !goal->Refs ); if( !--goal->Refs ) { ReleaseUnit(goal); } // FIXME: perhaps I should choose an alternative unit->Orders[0].Goal=NoUnitP; unit->Orders[0].Action=UnitActionStill; unit->SubAction=0; return; } else if( goal->Removed || !goal->HP || goal->Orders[0].Action==UnitActionDie ) { RefsDebugCheck( !goal->Refs ); --goal->Refs; RefsDebugCheck( !goal->Refs ); unit->Orders[0].Goal=NoUnitP; // FIXME: perhaps I should choose an alternative unit->Orders[0].Action=UnitActionStill; unit->SubAction=0; return; } } // // Have reached target? FIXME: could use pathfinder result? // if( goal ) { if( MapDistanceToUnit(unit->X,unit->Y,goal)<=1 ) { unit->State=0; unit->SubAction=2; } } else if( MapDistance(unit->X,unit->Y ,unit->Orders[0].X,unit->Orders[0].Y)<=1 ) { unit->State=0; unit->SubAction=2; } else if( err==PF_UNREACHABLE ) { unit->Orders[0].Action=UnitActionStill; return; } DebugCheck( unit->Orders[0].Action!=UnitActionDemolish ); } break; // // Demolish the target. // case 2: goal=unit->Orders[0].Goal; if( goal ) { RefsDebugCheck( !goal->Refs ); --goal->Refs; RefsDebugCheck( !goal->Refs ); unit->Orders[0].Goal=NoUnitP; } xmin = unit->X - 2; ymin = unit->Y - 2; xmax = unit->X + 2; ymax = unit->Y + 2; if (xmin<0) xmin=0; if (xmax > TheMap.Width-1) xmax = TheMap.Width-1; if (ymin<0) ymin=0; if (ymax > TheMap.Height-1) ymax = TheMap.Height-1; // FIXME: Must play explosion sound // FIXME: Currently we take the X fields, the original only the O // XXXXX ..O.. // XXXXX .OOO. // XX.XX OO.OO // XXXXX .OOO. // XXXXX ..O.. // // // Effect of the explosion on units. // n=SelectUnits(xmin,ymin, xmax, ymax,table); for( i=0; i<n; ++i ) { if( table[i]->Type->UnitType!=UnitTypeFly && table[i]->HP && table[i] != unit ) { // Don't hit flying units! HitUnit(unit,table[i],DEMOLISH_DAMAGE); } } // // Terrain effect of the explosion // for( ix=xmin; ix<=xmax; ix++ ) { for( iy=ymin; iy<=ymax; iy++ ) { n=TheMap.Fields[ix+iy*TheMap.Width].Flags; if( n&MapFieldWall ) { MapRemoveWall(ix,iy); } else if( n&MapFieldRocks ) { MapRemoveRock(ix,iy); } else if( n&MapFieldForest ) { MapRemoveWood(ix,iy); } } } LetUnitDie(unit); #ifdef HIERARCHIC_PATHFINDER PfHierMapChangedCallback (xmin, ymin, xmax, ymax); #endif break; } }