/** ** The transporter unloads a unit. ** ** @param unit Pointer to unit. */ void HandleActionUnload(CUnit *unit) { int i; int x; int y; if (!CanMove(unit)) { unit->SubAction = 2; } switch (unit->SubAction) { // // Move the transporter // case 0: if (!unit->Orders[0]->Goal) { if (!ClosestFreeDropZone(unit, unit->Orders[0]->X, unit->Orders[0]->Y, &x, &y)) { // Sorry... I give up. unit->ClearAction(); return; } unit->Orders[0]->X = x; unit->Orders[0]->Y = y; } NewResetPath(unit); unit->SubAction = 1; case 1: // The Goal is the unit that we have to unload. if (!unit->Orders[0]->Goal) { // We have to unload everything if ((i = MoveToDropZone(unit))) { if (i == PF_REACHED) { if (++unit->SubAction == 1) { unit->ClearAction(); } } else { unit->SubAction = 2; } } break; } // // Leave the transporter // case 2: // FIXME: show still animations ? LeaveTransporter(unit); if (CanMove(unit) && unit->Orders[0]->Action != UnitActionStill) { HandleActionUnload(unit); } break; } }
/** ** Swap the patrol points. */ static void SwapPatrolPoints(CUnit *unit) { int tmp; tmp = unit->Orders[0]->Arg1.Patrol.X; unit->Orders[0]->Arg1.Patrol.X = unit->Orders[0]->X; unit->Orders[0]->X = tmp; tmp = unit->Orders[0]->Arg1.Patrol.Y; unit->Orders[0]->Arg1.Patrol.Y = unit->Orders[0]->Y; unit->Orders[0]->Y = tmp; NewResetPath(unit); }
/** ** Move to transporter. ** ** @param unit Pointer to unit, moving to transporter. ** ** @return >0 remaining path length, 0 wait for path, -1 ** reached goal, -2 can't reach the goal. */ static int MoveToTransporter(CUnit *unit) { int i; int x; int y; x = unit->X; y = unit->Y; i = DoActionMove(unit); // We have to reset a lot, or else they will circle each other and stuff. if (x != unit->X || y != unit->Y) { unit->Orders[0]->Range = 1; NewResetPath(unit); } // New code has this as default. Assert(unit->Orders[0]->Action == UnitActionBoard); return i; }
/** ** Unit repairs ** ** @param unit Unit, for that the attack is handled. */ void HandleActionRepair(CUnit &unit) { CUnit *goal; int err; switch (unit.SubAction) { case 0: NewResetPath(unit); unit.SubAction = 1; // FALL THROUGH // // Move near to target. // case 1: // FIXME: RESET FIRST!! Why? We move first and than check if // something is in sight. err = DoActionMove(unit); if (!unit.Anim.Unbreakable) { // // No goal: if meeting damaged building repair it. // goal = unit.CurrentOrder()->GetGoal(); // // Target is dead, choose new one. // // Check if goal is correct unit. if (goal) { if (!goal->IsVisibleAsGoal(unit.Player)) { DebugPrint("repair target gone.\n"); unit.CurrentOrder()->goalPos = goal->tilePos; // FIXME: should I clear this here? unit.CurrentOrder()->ClearGoal(); goal = NULL; NewResetPath(unit); } } else if (unit.Player->AiEnabled) { // Ai players workers should stop if target is killed err = -1; } // // Have reached target? FIXME: could use return value // if (goal && unit.MapDistanceTo(*goal) <= unit.Type->RepairRange && goal->Variable[HP_INDEX].Value < goal->Variable[HP_INDEX].Max) { unit.State = 0; unit.SubAction = 2; unit.Data.Repair.Cycles = 0; const Vec2i dir = goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos; UnitHeadingFromDeltaXY(unit, dir); } else if (err < 0) { unit.CurrentOrder()->ClearGoal(); if (!unit.RestoreOrder()) { unit.ClearAction(); unit.State = 0; } return; } // FIXME: Should be it already? Assert(unit.CurrentAction() == UnitActionRepair); } break; // // Repair the target. // case 2: AnimateActionRepair(unit); unit.Data.Repair.Cycles++; if (!unit.Anim.Unbreakable) { goal = unit.CurrentOrder()->GetGoal(); // // Target is dead, choose new one. // // Check if goal is correct unit. // FIXME: should I do a function for this? if (goal) { if (!goal->IsVisibleAsGoal(unit.Player)) { DebugPrint("repair goal is gone\n"); unit.CurrentOrder()->goalPos = goal->tilePos; // FIXME: should I clear this here? unit.CurrentOrder()->ClearGoal(); goal = NULL; NewResetPath(unit); } else { int dist = unit.MapDistanceTo(*goal); if (dist <= unit.Type->RepairRange) { RepairUnit(unit, *goal); goal = unit.CurrentOrder()->GetGoal(); } else if (dist > unit.Type->RepairRange) { // If goal has move, chase after it unit.State = 0; unit.SubAction = 0; } } } // // Target is fine, choose new one. // if (!goal || goal->Variable[HP_INDEX].Value >= goal->Variable[HP_INDEX].Max) { unit.CurrentOrder()->ClearGoal(); if (!unit.RestoreOrder()) { unit.ClearAction(); unit.State = 0; } return; } // FIXME: automatic repair } break; } }
/** ** Unit repairs ** ** @param unit Unit, for that the attack is handled. */ void HandleActionRepair(CUnit *unit) { CUnit *goal; int err; switch (unit->SubAction) { case 0: NewResetPath(unit); unit->SubAction = 1; // FALL THROUGH // // Move near to target. // case 1: // FIXME: RESET FIRST!! Why? We move first and than check if // something is in sight. err = DoActionMove(unit); if (!unit->Anim.Unbreakable) { // // No goal: if meeting damaged building repair it. // goal = unit->Orders[0]->Goal; // // Target is dead, choose new one. // // Check if goal is correct unit. if (goal) { if (!goal->IsVisibleAsGoal(unit->Player)) { DebugPrint("repair target gone.\n"); unit->Orders[0]->X = goal->X; unit->Orders[0]->Y = goal->Y; goal->RefsDecrease(); // FIXME: should I clear this here? unit->Orders[0]->Goal = goal = NULL; NewResetPath(unit); } } else if (unit->Player->AiEnabled) { // Ai players workers should stop if target is killed err = -1; } // // Have reached target? FIXME: could use return value // if (goal && MapDistanceBetweenUnits(unit, goal) <= unit->Type->RepairRange && goal->Variable[HP_INDEX].Value < goal->Variable[HP_INDEX].Max) { unit->State = 0; unit->SubAction = 2; unit->Data.Repair.Cycles = 0; UnitHeadingFromDeltaXY(unit, goal->X + (goal->Type->TileWidth - 1) / 2 - unit->X, goal->Y + (goal->Type->TileHeight - 1) / 2 - unit->Y); } else if (err < 0) { if (goal) { // release reference goal->RefsDecrease(); unit->Orders[0]->Goal = NoUnitP; } unit->Orders[0]->Action = UnitActionStill; unit->State = unit->SubAction = 0; if (unit->Selected) { // update display for new action SelectedUnitChanged(); } return; } // FIXME: Should be it already? Assert(unit->Orders[0]->Action == UnitActionRepair); } break; // // Repair the target. // case 2: AnimateActionRepair(unit); unit->Data.Repair.Cycles++; if (!unit->Anim.Unbreakable) { goal = unit->Orders[0]->Goal; // // Target is dead, choose new one. // // Check if goal is correct unit. // FIXME: should I do a function for this? if (goal) { if (!goal->IsVisibleAsGoal(unit->Player)) { DebugPrint("repair goal is gone\n"); unit->Orders[0]->X = goal->X; unit->Orders[0]->Y = goal->Y; goal->RefsDecrease(); // FIXME: should I clear this here? unit->Orders[0]->Goal = goal = NULL; NewResetPath(unit); } } if (goal && MapDistanceBetweenUnits(unit, goal) <= unit->Type->RepairRange) { RepairUnit(unit, goal); goal = unit->Orders[0]->Goal; } else if (goal && MapDistanceBetweenUnits(unit, goal) > unit->Type->RepairRange) { // If goal has move, chase after it unit->State = 0; unit->SubAction = 0; } // // Target is fine, choose new one. // if (!goal || goal->Variable[HP_INDEX].Value >= goal->Variable[HP_INDEX].Max) { if (goal) { // release reference goal->RefsDecrease(); unit->Orders[0]->Goal = NULL; } unit->Orders[0]->Action = UnitActionStill; unit->SubAction = unit->State = 0; if (unit->Selected) { // update display for new action SelectedUnitChanged(); } return; } // FIXME: automatic repair } break; } }
/** ** The unit boards a transporter. ** ** @todo FIXME: the transporter must drive to the meating point. ** While waiting for the transporter the units must defend ** themselfs. ** ** @param unit Pointer to unit. */ global void HandleActionBoard(Unit* unit) { int i; Unit* goal; DebugLevel3Fn("%p(%d) SubAction %d\n" _C_ unit _C_ UnitNumber(unit) _C_ unit->SubAction); switch( unit->SubAction ) { // // Wait for transporter // case 201: // FIXME: show still animations DebugLevel3Fn("Waiting\n"); if( WaitForTransporter(unit) ) { unit->SubAction=202; } break; // // Enter transporter // case 202: EnterTransporter(unit); break; // // Move to transporter // case 0: NewResetPath(unit); unit->SubAction=1; // FALL THROUGH default: if( unit->SubAction<=200 ) { // FIXME: if near transporter wait for enter if( (i=MoveToTransporter(unit)) ) { if( i==PF_UNREACHABLE ) { if( ++unit->SubAction==200 ) { unit->Orders[0].Action=UnitActionStill; if( (goal=unit->Orders[0].Goal) ) { RefsDebugCheck(!goal->Refs); if( !--goal->Refs ) { RefsDebugCheck(!goal->Destroyed); if( goal->Destroyed ) { ReleaseUnit(goal); } } unit->Orders[0].Goal=NoUnitP; } unit->SubAction=0; } else { unit->Wait=unit->SubAction; } } else if( i==PF_REACHED ) { unit->SubAction=201; } } } break; } }
/** ** The unit boards a transporter. ** ** @todo FIXME: While waiting for the transporter the units must defend themselves. ** ** @param unit Pointer to unit. */ void HandleActionBoard(CUnit *unit) { int i; CUnit *goal; switch (unit->SubAction) { // // Wait for transporter // case 201: if (WaitForTransporter(unit)) { unit->SubAction = 202; } else { UnitShowAnimation(unit, unit->Type->Animations->Still); } break; // // Enter transporter // case 202: EnterTransporter(unit); break; // // Move to transporter // case 0: if (unit->Wait) { unit->Wait--; return; } NewResetPath(unit); unit->SubAction = 1; // FALL THROUGH default: if (unit->SubAction <= 200) { // FIXME: if near transporter wait for enter if ((i = MoveToTransporter(unit))) { if (i == PF_UNREACHABLE) { if (++unit->SubAction == 200) { unit->ClearAction(); if ((goal = unit->Orders[0]->Goal)) { goal->RefsDecrease(); unit->Orders[0]->Goal = NoUnitP; } } else { // // Try with a bigger range. // if (unit->Orders[0]->Range <= Map.Info.MapWidth || unit->Orders[0]->Range <= Map.Info.MapHeight) { unit->Orders[0]->Range++; unit->SubAction--; } } } else if (i == PF_REACHED) { unit->SubAction = 201; } } } break; } }
/** ** Unit Patrol: ** The unit patrols between two points. ** Any enemy unit in reaction range is attacked. ** @todo FIXME: ** Should do some tries to reach the end-points. ** Should support patrol between more points! ** Patrol between units. ** ** @param unit Patroling unit pointer. */ void HandleActionPatrol(CUnit *unit) { if (unit->Wait) { unit->Wait--; return; } if (!unit->SubAction) { // first entry. NewResetPath(unit); unit->SubAction = 1; } switch (DoActionMove(unit)) { case PF_FAILED: unit->SubAction = 1; break; case PF_UNREACHABLE: // Increase range and try again unit->SubAction = 1; if (unit->Orders[0]->Range <= Map.Info.MapWidth || unit->Orders[0]->Range <= Map.Info.MapHeight) { unit->Orders[0]->Range++; break; } // FALL THROUGH case PF_REACHED: unit->SubAction = 1; unit->Orders[0]->Range = 0; SwapPatrolPoints(unit); break; case PF_WAIT: // Wait for a while then give up unit->SubAction++; if (unit->SubAction == 5) { unit->SubAction = 1; unit->Orders[0]->Range = 0; SwapPatrolPoints(unit); } break; default: // moving unit->SubAction = 1; break; } if (!unit->Anim.Unbreakable) { // // Attack any enemy in reaction range. // If don't set the goal, the unit can then choose a // better goal if moving nearer to enemy. // if (unit->Type->CanAttack) { const CUnit *goal = AttackUnitsInReactRange(unit); if (goal) { DebugPrint("Patrol attack %d\n" _C_ UnitNumber(goal)); CommandAttack(unit, goal->X, goal->Y, NULL, FlushCommands); // Save current command to come back. unit->SavedOrder = *unit->Orders[0]; unit->Orders[0]->Action = UnitActionStill; unit->Orders[0]->Goal = NoUnitP; unit->SubAction = 0; } } } }
/** ** 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; } }