void CMiniMap::MouseRelease(int x, int y, int button) { if (mouseMove || mouseResize || mouseLook) { mouseMove = false; mouseResize = false; mouseLook = false; proxyMode = false; return; } if (proxyMode) { ProxyMouseRelease(x, y, button); proxyMode = false; return; } if (selecting) { SelectUnits(x, y); selecting = false; return; } if (button == SDL_BUTTON_LEFT) { if (showButtons && maximizeBox.Inside(x, y)) { ToggleMaximized(!!keys[SDLK_LSHIFT]); return; } if (showButtons && minimizeBox.Inside(x, y)) { minimized = true; return; } } }
/** ** Draw all units on visible map. ** FIXME: Must use the redraw tile flags in this function */ global void DrawUnits(void) { Unit* unit; Unit* table[MAX_UNITS]; int n; int i; // // Select all units touching the viewpoint. // n=SelectUnits(MapX,MapY,MapX+MapWidth,MapY+MapHeight,table); // // 2a) corpse aren't in the cache. // for( i=0; i<NumUnits; ++i ) { unit=Units[i]; if( unit->Type->Vanishes || unit->Command.Action==UnitActionDie ) { DrawUnit(unit); } } // // 2b) buildings // for( i=0; i<n; ++i ) { unit=table[i]; if( !unit->Removed && UnitVisible(unit) ) { if( unit->Type->Building ) { DrawBuilding(unit); table[i]=NoUnitP; } } else { table[i]=NoUnitP; } } // // 3) land/sea units // for( i=0; i<n; ++i ) { if( !(unit=table[i]) ) { continue; } if( unit->Type->UnitType!=UnitTypeFly ) { DrawUnit(unit); table[i]=NoUnitP; } } // // 5) flying units // for( i=0; i<n; ++i ) { if( !(unit=table[i]) ) { continue; } DrawUnit(unit); } }
bool EXCELLON_IMAGE::Execute_HEADER_Command( char*& text ) { EXCELLON_CMD* cmd = NULL; int iprm; double dprm; D_CODE* dcode; wxString msg; // Search command in list EXCELLON_CMD* candidate; for( unsigned ii = 0; ; ii++ ) { candidate = &excellonHeaderCmdList[ii]; int len = candidate->m_Name.size(); if( len == 0 ) // End of list reached break; if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found. { cmd = candidate; text += len; break; } } if( !cmd ) { msg.Printf( wxT( "Unknown Excellon command <%s>" ), text ); ReportMessage( msg ); while( *text ) text++; return false; } // Execute command // some do nothing switch( cmd->m_Code ) { case DRILL_SKIP: case DRILL_M_UNKNOWN: break; case DRILL_M_END: break; case DRILL_M_ENDREWIND: break; case DRILL_M_MESSAGE: break; case DRILL_M_LONGMESSAGE: break; case DRILL_M_HEADER: m_State = READ_HEADER_STATE; break; case DRILL_M_ENDHEADER: m_State = READ_PROGRAM_STATE; break; case DRILL_REWIND_STOP: // TODO: what this command really is ? m_State = READ_PROGRAM_STATE; break; case DRILL_M_METRIC: SelectUnits( true ); break; case DRILL_METRICHEADER: // command like METRIC,TZ or METRIC,LZ SelectUnits( true ); if( *text != ',' ) { ReportMessage( _( "METRIC command has no parameter" ) ); break; } text++; // skip separator if( *text == 'T' ) m_NoTrailingZeros = false; else m_NoTrailingZeros = true; break; case DRILL_M_IMPERIAL: SelectUnits( false ); break; case DRILL_IMPERIALHEADER: // command like INCH,TZ or INCH,LZ SelectUnits( false ); if( *text != ',' ) { ReportMessage( _( "INCH command has no parameter" ) ); break; } text++; // skip separator if( *text == 'T' ) m_NoTrailingZeros = false; else m_NoTrailingZeros = true; break; case DRILL_M_BEGINPATTERN: break; case DRILL_M_ENDPATTERN: break; case DRILL_M_CANNEDTEXT: break; case DRILL_M_TIPCHECK: break; case DRILL_DETECT_BROKEN: break; case DRILL_INCREMENTALHEADER: m_Relative = true; break; case DRILL_TOOL_CHANGE_STOP: break; case DRILL_AUTOMATIC_SPEED: break; case DRILL_AXIS_VERSION: break; case DRILL_RESET_CMD: break; case DRILL_AUTOMATIC_TOOL_CHANGE: break; case DRILL_FMT: break; case DRILL_TOOL_INFORMATION: // Read a tool definition like T1C0.02: // Read tool number: iprm = ReadInt( text, false ); // Read tool shape if( *text != 'C' ) ReportMessage( _( "Tool definition <%c> not supported" ) ); if( *text ) text++; //read tool diameter: dprm = ReadDouble( text, false ); m_Has_DCode = true; // Initialize Dcode to handle this Tool dcode = GetDCODE( iprm + FIRST_DCODE ); // Remember: dcodes are >= FIRST_DCODE if( dcode == NULL ) break; double conv_scale = m_GerbMetric ? PCB_INTERNAL_UNIT / 25.4 : PCB_INTERNAL_UNIT; dcode->m_Size.x = dcode->m_Size.y = wxRound( dprm * conv_scale ); dcode->m_Shape = APT_CIRCLE; break; } while( *text ) text++; return true; }
bool EXCELLON_IMAGE::Execute_HEADER_Command( char*& text ) { EXCELLON_CMD* cmd = NULL; wxString msg; // Search command in list for( unsigned ii = 0; ; ii++ ) { EXCELLON_CMD* candidate = &excellonHeaderCmdList[ii]; int len = candidate->m_Name.size(); if( len == 0 ) // End of list reached break; if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found. { cmd = candidate; text += len; break; } } if( !cmd ) { msg.Printf( wxT( "Unknown Excellon command <%s>" ), text ); AddMessageToList( msg ); while( *text ) text++; return false; } // Execute command // some do nothing switch( cmd->m_Code ) { case DRILL_SKIP: case DRILL_M_UNKNOWN: break; case DRILL_M_END: break; case DRILL_M_ENDREWIND: break; case DRILL_M_MESSAGE: break; case DRILL_M_LONGMESSAGE: break; case DRILL_M_HEADER: m_State = READ_HEADER_STATE; break; case DRILL_M_ENDHEADER: m_State = READ_PROGRAM_STATE; break; case DRILL_REWIND_STOP: // End of header. No action in a viewer m_State = READ_PROGRAM_STATE; break; case DRILL_M_METRIC: SelectUnits( true ); break; case DRILL_METRICHEADER: // command like METRIC,TZ or METRIC,LZ SelectUnits( true ); if( *text != ',' ) { AddMessageToList( _( "METRIC command has no parameter" ) ); break; } text++; // skip separator if( *text == 'T' ) m_NoTrailingZeros = false; else m_NoTrailingZeros = true; break; case DRILL_M_IMPERIAL: SelectUnits( false ); break; case DRILL_IMPERIALHEADER: // command like INCH,TZ or INCH,LZ SelectUnits( false ); if( *text != ',' ) { AddMessageToList( _( "INCH command has no parameter" ) ); break; } text++; // skip separator if( *text == 'T' ) m_NoTrailingZeros = false; else m_NoTrailingZeros = true; break; case DRILL_M_BEGINPATTERN: break; case DRILL_M_ENDPATTERN: break; case DRILL_M_CANNEDTEXT: break; case DRILL_M_TIPCHECK: break; case DRILL_DETECT_BROKEN: break; case DRILL_INCREMENTALHEADER: if( *text != ',' ) { AddMessageToList( _( "ICI command has no parameter" ) ); break; } text++; // skip separator // Parameter should be ON or OFF if( strncasecmp( text, "OFF", 3 ) == 0 ) m_Relative = false; else if( strncasecmp( text, "ON", 2 ) == 0 ) m_Relative = true; else AddMessageToList( _( "ICI command has incorrect parameter" ) ); break; case DRILL_TOOL_CHANGE_STOP: break; case DRILL_AUTOMATIC_SPEED: break; case DRILL_AXIS_VERSION: break; case DRILL_RESET_CMD: break; case DRILL_AUTOMATIC_TOOL_CHANGE: break; case DRILL_FMT: break; case DRILL_TOOL_INFORMATION: readToolInformation( text ); break; } while( *text ) text++; return true; }
bool EXCELLON_IMAGE::Execute_HEADER_Command( char*& text ) { EXCELLON_CMD* cmd = NULL; int iprm; double dprm; D_CODE* dcode; wxString msg; // Search command in list EXCELLON_CMD* candidate; for( unsigned ii = 0; ; ii++ ) { candidate = &excellonHeaderCmdList[ii]; int len = candidate->m_Name.size(); if( len == 0 ) // End of list reached break; if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found. { cmd = candidate; text += len; break; } } if( !cmd ) { msg.Printf( wxT( "Unknown Excellon command <%s>" ), text ); ReportMessage( msg ); while( *text ) text++; return false; } // Execute command // some do nothing switch( cmd->m_Code ) { case DRILL_SKIP: case DRILL_M_UNKNOWN: break; case DRILL_M_END: break; case DRILL_M_ENDREWIND: break; case DRILL_M_MESSAGE: break; case DRILL_M_LONGMESSAGE: break; case DRILL_M_HEADER: m_State = READ_HEADER_STATE; break; case DRILL_M_ENDHEADER: m_State = READ_PROGRAM_STATE; break; case DRILL_REWIND_STOP: // End of header. No action in a viewer m_State = READ_PROGRAM_STATE; break; case DRILL_M_METRIC: SelectUnits( true ); break; case DRILL_METRICHEADER: // command like METRIC,TZ or METRIC,LZ SelectUnits( true ); if( *text != ',' ) { ReportMessage( _( "METRIC command has no parameter" ) ); break; } text++; // skip separator if( *text == 'T' ) m_NoTrailingZeros = false; else m_NoTrailingZeros = true; break; case DRILL_M_IMPERIAL: SelectUnits( false ); break; case DRILL_IMPERIALHEADER: // command like INCH,TZ or INCH,LZ SelectUnits( false ); if( *text != ',' ) { ReportMessage( _( "INCH command has no parameter" ) ); break; } text++; // skip separator if( *text == 'T' ) m_NoTrailingZeros = false; else m_NoTrailingZeros = true; break; case DRILL_M_BEGINPATTERN: break; case DRILL_M_ENDPATTERN: break; case DRILL_M_CANNEDTEXT: break; case DRILL_M_TIPCHECK: break; case DRILL_DETECT_BROKEN: break; case DRILL_INCREMENTALHEADER: if( *text != ',' ) { ReportMessage( _( "ICI command has no parameter" ) ); break; } text++; // skip separator // Parameter should be ON or OFF if( strnicmp( text, "OFF", 3 ) == 0 ) m_Relative = false; else if( strnicmp( text, "ON", 2 ) == 0 ) m_Relative = true; else ReportMessage( _( "ICI command has incorrect parameter" ) ); break; case DRILL_TOOL_CHANGE_STOP: break; case DRILL_AUTOMATIC_SPEED: break; case DRILL_AXIS_VERSION: break; case DRILL_RESET_CMD: break; case DRILL_AUTOMATIC_TOOL_CHANGE: break; case DRILL_FMT: break; case DRILL_TOOL_INFORMATION: // Read a tool definition like T1C0.02: // or T1F00S00C0.02 or T1C0.02F00S00 // Read tool number: iprm = ReadInt( text, false ); // Skip Feed rate and Spindle speed, if any here while( *text && ( *text == 'F' || *text == 'S' ) ) { text++; ReadInt( text, false ); } // Read tool shape if( *text != 'C' ) ReportMessage( wxString:: Format( _( "Tool definition <%c> not supported" ), *text ) ); if( *text ) text++; //read tool diameter: dprm = ReadDouble( text, false ); m_Has_DCode = true; // Initialize Dcode to handle this Tool dcode = GetDCODE( iprm + FIRST_DCODE ); // Remember: dcodes are >= FIRST_DCODE if( dcode == NULL ) break; // conv_scale = scaling factor from inch to Internal Unit double conv_scale = IU_PER_MILS * 1000; if( m_GerbMetric ) conv_scale /= 25.4; dcode->m_Size.x = dcode->m_Size.y = KiROUND( dprm * conv_scale ); dcode->m_Shape = APT_CIRCLE; break; } while( *text ) text++; return true; }
/** ** 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; } }
/** ** Attack units in distance. ** ** If the unit can attack must be handled by caller. ** Choose the best target, that can be attacked. ** ** @param unit Find in distance for this unit. ** @param range Distance range to look. ** ** @return Unit to be attacked. ** ** @note This could be improved, for better performance. */ global Unit* AttackUnitsInDistance(const Unit* unit,int range) { const Unit* dest; const UnitType* type; const UnitType* dtype; Unit* table[UnitMax]; int x; int y; int n; int i; int d; int attackrange; int cost; const Player* player; const Unit* best_unit; int best_cost; DebugLevel3Fn("(%d)%s\n" _C_ UnitNumber(unit) _C_ unit->Type->Ident); // if necessary, take possible damage on allied units into account... if (unit->Type->Missile.Missile->Range>1 && (range+unit->Type->Missile.Missile->Range<15)) { return FindRangeAttack(unit,range); } // // Select all units in range. // x=unit->X; y=unit->Y; n=SelectUnits(x-range,y-range,x+range+1,y+range+1,table); best_unit=NoUnitP; best_cost=INT_MAX; player=unit->Player; type=unit->Type; attackrange=unit->Stats->AttackRange; // // Find the best unit to attack // for( i=0; i<n; ++i ) { dest=table[i]; // // unusable unit // // FIXME: did SelectUnits already filter this. if( dest->Removed || dest->Invisible || !unit->HP || !(dest->Visible&(1<<player->Player)) || dest->Orders[0].Action==UnitActionDie ) { continue; } if( !IsEnemy(player,dest) ) { // a friend or neutral continue; } dtype=dest->Type; if( !CanTarget(type,dtype) ) { // can't be attacked. continue; } // // Calculate the costs to attack the unit. // Unit with the smallest attack costs will be taken. // cost=0; // // Priority 0-255 // cost-=dtype->Priority*PRIORITY_FACTOR; // // Remaining HP (Health) 0-65535 // cost+=dest->HP*HEALTH_FACTOR; // // Unit in attack range? // d=MapDistanceBetweenUnits(unit,dest); if( d<type->MinAttackRange ) { // FIXME: we don't support moving away! continue; } if( d<attackrange && d>type->MinAttackRange ) { cost+=d*INRANGE_FACTOR; cost-=INRANGE_BONUS; } else { cost+=d*DISTANCE_FACTOR; } // // Unit can attack back. // if( CanTarget(dtype,type) ) { cost-=CANATTACK_BONUS; } DebugLevel3Fn("%s -> %s\t%08x\n" _C_ type->Ident _C_ dtype->Ident _C_ cost); // // Take this target? // if( cost<best_cost && (d<attackrange || UnitReachable(unit,dest,attackrange)) ) { best_unit=dest; best_cost=cost; } } /* if( best_unit ) { DebugLevel3Fn("Attacking (%d)%s -> %s\n" _C_ UnitNumber(unit) _C_ unit->Type->Ident _C_ best_unit->Type->Ident); } */ // FIXME: No idea how to make this correct, without cast!! return (Unit*)best_unit; }
/** ** Attack units in distance, with large missile ** ** Choose the best target, that can be attacked. It takes into ** account allied unit which could be hit by the missile ** ** @param u Find in distance for this unit. ** @param range Distance range to look. ** ** @return Unit to be attacked. ** ** @note This could be improved, for better performance / better trade. ** @note Limited to attack range smaller than 16. ** @note Will be moved to unit_ai.c soon. */ local Unit* FindRangeAttack(const Unit* u, int range) { int x, y, n, cost,d,effective_hp,enemy_count; int missile_range,attackrange,hp_damage_evaluate; int good[32][32], bad[32][32]; Unit* table[UnitMax]; Unit* dest; const UnitType* dtype; const UnitType* type; const Player* player; int xx, yy; int best_x, best_y, best_cost; int i, sbad, sgood; Unit* best; type = u->Type; player = u->Player; // If catapult, count units near the target... // FIXME : make it configurable // missile_range = type->Missile.Missile->Range + range - 1; attackrange=u->Stats->AttackRange; // Evaluation of possible damage... hp_damage_evaluate=u->Stats->BasicDamage+u->Stats->PiercingDamage; DebugCheck(2 * missile_range + 1 >= 32); x = u->X; y = u->Y; n = SelectUnits(x - missile_range, y - missile_range, x + missile_range + 1, y + missile_range + 1, table); if (!n) { return NoUnitP; } for (y = 0; y < 2 * missile_range + 1; y++) { for (x = 0; x < 2 * missile_range + 1; x++) { good[y][x] = 0; bad[y][x] = 0; } } enemy_count=0; // FILL good/bad... for (i = 0; i < n; i++) { dest = table[i]; dtype = dest->Type; // // unusable unit // // FIXME: did SelectUnits already filter this. if (dest->Removed || dest->Invisible || !u->HP || !(dest->Visible & (1 << player->Player)) || dest->Orders[0].Action == UnitActionDie) { table[i] = 0; continue; } // won't be a target... if (!CanTarget(type, dtype)) { // can't be attacked. table[i] = 0; continue; } if (!IsEnemy(player, dest)) { // a friend or neutral table[i] = 0; // 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->HP) / (dtype->TileWidth * dtype->TileWidth); if (cost < 1) { cost = 1; } cost = (-cost); } else { // // Calculate the costs to attack the unit. // Unit with the smallest attack costs will be taken. // cost = 0; // // Priority 0-255 // cost += dtype->Priority * PRIORITY_FACTOR; // // 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 // effective_hp=(dest->HP-2*hp_damage_evaluate); // // Unit we won't kill are evaluated the same // if (effective_hp>0){ effective_hp=0; } // // Unit we are sure to kill are all evaluated the same ( except PRIORITY ) // if (effective_hp<(-hp_damage_evaluate)){ effective_hp=(-hp_damage_evaluate); } // // 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 accros multiple cells // cost=cost / (dtype->TileWidth * dtype->TileWidth); if (cost < 1) { cost = 1; } d=MapDistanceBetweenUnits(u,dest); // FIXME: we don't support moving away! if((d<type->MinAttackRange)||(!UnitReachable(u,dest,attackrange))) { table[i]=0; } else { enemy_count++; } } x = dest->X - u->X + missile_range+1; y = dest->Y - u->Y + missile_range+1; // Mark the good/bad array... for (xx = 0; xx < dtype->TileWidth; xx++) { for (yy = 0; yy < dtype->TileWidth; yy++) { if ((x + xx < 0) || (y + yy < 0) || (x + xx >= 2 * missile_range + 1) || (y + yy >= 2 * missile_range + 1)) { continue; } if (cost < 0) { good[y + yy][x + xx] -= cost; } else { bad[y + yy][x + xx] += cost; } } } } if (!enemy_count) { return NoUnitP; } // Find the best area... // The target which provide the best bad/good ratio is choosen... best_x = -1; best_y = -1; best_cost = -1; best = NoUnitP; for (i = 0; i < n; i++) { if (!table[i]) { continue; } dest = table[i]; dtype = dest->Type; // put in x-y the real point which will be hit... // ( only meaningfull when dtype->TileWidth>1 ) if (u->X<dest->X){ x=dest->X; } else if (u->X>dest->X+dtype->TileWidth-1){ x=dest->X+dtype->TileWidth-1; } else { x=u->X; } if (u->Y<dest->Y){ y=dest->Y; } else if(u->Y>dest->Y+dtype->TileWidth-1){ y=dest->Y+dtype->TileWidth-1; } else { y=u->Y; } // Make x,y relative to u->x... x = x - u->X + missile_range+1; y = y - u->Y + missile_range+1; sbad = 0; sgood = 0; for (yy = -(type->Missile.Missile->Range - 1); yy <= type->Missile.Missile->Range - 1; yy++) { for (xx = -(type->Missile.Missile->Range - 1); xx <= type->Missile.Missile->Range - 1; xx++) { if ((x + xx < 0) || (y + yy < 0) || ((x + xx) >= 2 * missile_range + 1) || ((y + yy) >= 2 * missile_range + 1)) { continue; } sbad += bad[y + yy][x + xx]; sgood += good[y + yy][x + xx]; if ((!yy) && (!xx)) { sbad += bad[y + yy][x + xx]; sgood += good[y + yy][x + xx]; } } } // don't consider small damages... if (sgood < 20) { sgood = 20; } cost = sbad / sgood; if (cost > best_cost) { best_cost = cost; best = dest; } } return best; }
/** ** 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; } }