void MissileHandlePierce(Missile &missile, const Vec2i &pos) { CUnit *unit = UnitOnMapTile(pos, -1); if (unit && unit->IsAliveOnMap() && (missile.Type->FriendlyFire || unit->IsEnemy(*missile.SourceUnit->Player))) { missile.MissileHit(unit); } }
/** ** Work for missile hit. */ global void MissileHit(int missile) { Unit* goal; // FIXME: should I move the PlayMissileSound to here? // FIXME: And should the the connected missile be defined in the missile // FIXME: structure switch( Missiles[missile].Type->Type ) { case MissileArrow: case MissileAxe: PlayMissileSound(Missiles+missile, Missiles[missile].Type->ImpactSound.Sound); break; case MissileBallistaBolt: case MissileBigCannon: PlayMissileSound(Missiles+missile, Missiles[missile].Type->ImpactSound.Sound); MakeMissile(MissileImpact ,Missiles[missile].X ,Missiles[missile].Y ,0,0); break; case MissileSubmarineMissile: case MissileTurtleMissile: PlayMissileSound(Missiles+missile, Missiles[missile].Type->ImpactSound.Sound); MakeMissile(MissileImpact ,Missiles[missile].X ,Missiles[missile].Y ,0,0); break; case MissileGreenCross: break; } if( !Missiles[missile].SourceType ) { return; } // FIXME: must choose better goal! // FIXME: what can the missile hit? goal=UnitOnMapTile(Missiles[missile].X/TileSizeX ,Missiles[missile].Y/TileSizeY); if( !goal || !goal->HP ) { return; } HitUnit(goal,CalculateDamage(Missiles[missile].SourceStats,goal)); }
/** ** Handle movement of the cursor. ** ** @param x X map tile position. ** @param y Y map tile position. */ global void HandleMouseMove(int x,int y) { // // Reduce coordinates to window-size. // if( x<0 ) { x=0; } else if( x>=VideoWidth ) { x=VideoWidth-1; } if( y<0 ) { y=0; } else if( y>=VideoHeight ) { y=VideoHeight-1; } CursorX=x; CursorY=y; // // Selecting units. // if( CursorState==CursorStateRectangle ) { return; } // // Move map. // if( CloneCursor==&Cursors[CursorTypeMove] ) { int xo = MapX, yo = MapY; if ( TheUI.ReverseMouseMove ) { if (x < CursorStartX) xo++; else if (x > CursorStartX) xo--; if (y < CursorStartY) yo++; else if (y > CursorStartY) yo--; } else { if (x < CursorStartX) xo--; else if (x > CursorStartX) xo++; if (y < CursorStartY) yo--; else if (y > CursorStartY) yo++; } TheUI.WarpX = CursorStartX; TheUI.WarpY = CursorStartY; if (xo != MapX || yo != MapY) MapSetViewpoint(xo, yo); return; } UnitUnderCursor=NULL; CloneCursor=&Cursors[CursorTypePoint]; // Reset HandleMouseOn(x,y); DebugLevel3("MouseOn %d\n",CursorOn); //cade: this is forbidden for unexplored and not visible space if( CursorOn==CursorOnMap ) { if( MAPVISIBLE(Screen2MapX(x),Screen2MapY(y)) ) { UnitUnderCursor=UnitOnScreen(NULL,x-MAP_X+MapX*TileSizeX ,y-MAP_Y+MapY*TileSizeY); } } else if( CursorOn==CursorOnMinimap ) { if( MAPVISIBLE(Minimap2MapX(x),Minimap2MapY(y)) ) { UnitUnderCursor=UnitOnMapTile(Minimap2MapX(x),Minimap2MapY(y)); } } // // Selecting target. // if( CursorState==CursorStateSelect ) { if( CursorOn==CursorOnMap || CursorOn==CursorOnMinimap ) { CloneCursor=&Cursors[CursorTypeYellowHair]; if( UnitUnderCursor ) { // FIXME: should use IsEnemy here? if( UnitUnderCursor->Player==ThisPlayer ) { CloneCursor=&Cursors[CursorTypeGreenHair]; } else if( UnitUnderCursor->Player->Player!=PlayerNumNeutral ) { CloneCursor=&Cursors[CursorTypeRedHair]; } } if( CursorOn==CursorOnMinimap && (MouseButtons&RightButton) ) { // // Minimap move viewpoint // MapSetViewpoint(Minimap2MapX(CursorX)-MapWidth/2 ,Minimap2MapY(CursorY)-MapHeight/2); } } // FIXME: must move minimap if right button is down ! return; } // // Cursor pointing. // if( CursorOn==CursorOnMap ) { // // Map // if( UnitUnderCursor ) { if( NumSelected==0 ) { MustRedraw|=RedrawTopPanel; } CloneCursor=&Cursors[CursorTypeGlass]; } IfDebug( DrawMouseCoordsOnMap(x,y); ); return; }
/** ** Called when right button is pressed ** ** @param x X map tile position. ** @param y Y map tile position. */ global void DoRightButton(int x,int y) { int i; Unit* dest; Unit* unit; UnitType* type; int action; int acknowledged; // // No unit selected // if( !NumSelected ) { return; } // // Unit selected isn't owned by the player. // You can't select your own units + foreign unit(s). // if( Selected[0]->Player!=ThisPlayer ) { return; } acknowledged=0; for( i=0; i<NumSelected; ++i ) { unit=Selected[i]; DebugCheck( !unit ); type=unit->Type; if( !acknowledged ) { PlayUnitSound(unit,VoiceAcknowledging); acknowledged=1; } action=type->MouseAction; DebugLevel3(__FUNCTION__": Mouse action %d\n",action); // // Enter transporters? // dest=UnitOnMapTile(x,y); if( dest && dest->Type->Transporter && dest->Player==ThisPlayer && unit->Type->UnitType==UnitTypeLand ) { dest->Blink=3; DebugLevel3(__FUNCTION__": Board transporter\n"); SendCommandBoard(unit,dest); continue; } // // Peon/Peasant // if( action==MouseActionHarvest ) { DebugLevel3("Action %x\n",TheMap.ActionMap[x+y*TheMap.Width]); if( type->Type==UnitPeonWithWood || type->Type==UnitPeasantWithWood || type->Type==UnitPeonWithGold || type->Type==UnitPeasantWithGold ) { dest=UnitOnMapTile(x,y); if( dest ) { dest->Blink=3; if( dest->Type->StoresGold && (type->Type==UnitPeonWithGold || type->Type==UnitPeasantWithGold) ) { DebugLevel3("GOLD-DEPOSIT\n"); // FIXME: return to this depot?? SendCommandReturnGoods(unit); continue; } if( (dest->Type->StoresWood || dest->Type->StoresGold) && (type->Type==UnitPeonWithWood || type->Type==UnitPeasantWithWood) ) { DebugLevel3("WOOD-DEPOSIT\n"); // FIXME: return to this depot?? SendCommandReturnGoods(unit); continue; } } } else { if( ForestOnMap(x,y) ) { SendCommandHarvest(unit,x,y); continue; } if( (dest=GoldMineOnMap(x,y)) ) { dest->Blink=3; DebugLevel3("GOLD-MINE\n"); SendCommandMineGold(unit,dest); continue; } } // FIXME: repair/attack/follow/board dest=TargetOnMapTile(unit,x,y); if( dest ) { dest->Blink=3; if( dest->Player==ThisPlayer ) { // FIXME: SendCommandFollow(unit,x,y,dest); // FIXME: continue; } else { // FIXME: can I attack this unit? SendCommandAttack(unit,x,y,dest); continue; } } // cade: this is default repair action dest=UnitOnMapTile(x,y); if( dest && dest->Type && dest->Player==ThisPlayer && dest->HP<dest->Stats[dest->Player->Player].HitPoints && dest->Type->Building ) { SendCommandRepair(unit,x,y); } else { SendCommandMoveUnit(unit,x,y); } continue; } // // Tanker // if( action==MouseActionHaulOil ) { if( type->Type==UnitTankerOrcFull || type->Type==UnitTankerHumanFull ) { DebugLevel2("Should return to oil deposit\n"); } else { if( (dest=PlatformOnMap(x,y)) ) { dest->Blink=3; DebugLevel2("PLATFORM\n"); SendCommandHaulOil(unit,dest); continue; } } SendCommandMoveUnit(unit,x,y); continue; } // // Fighters // if( action==MouseActionAttack ) { // FIXME: more units on same tile dest=TargetOnMapTile(unit,x,y); if( dest ) { dest->Blink=3; if( dest->Player==ThisPlayer ) { // FIXME: SendCommandFollow(unit,x,y,dest); // FIXME: continue; } else { // FIXME: can I attack this unit? SendCommandAttack(unit,x,y,dest); continue; } } if( WallOnMap(x,y) ) { DebugLevel3("WALL ON TILE\n"); if( ThisPlayer->Race==PlayerRaceHuman && OrcWallOnMap(x,y) ) { DebugLevel3("HUMAN ATTACKS ORC\n"); SendCommandAttack(unit,x,y,NoUnitP); } if( ThisPlayer->Race==PlayerRaceOrc && HumanWallOnMap(x,y) ) { DebugLevel3("ORC ATTACKS HUMAN\n"); SendCommandAttack(unit,x,y,NoUnitP); } } SendCommandMoveUnit(unit,x,y); continue; } // FIXME: demolish!!!!!!! // FIXME: attack/follow/board ... if( action==MouseActionMove ) { } // if( !unit->Type->Building ) { SendCommandMoveUnit(unit,x,y); // } } }
/** ** Compute the cost of crossing tile (dx,dy) ** ** @param data The CUnit * that wants to cross the tile. This is ** passed in as void * so that the low-level A* code ** need not know about CUnit. ** @param x X tile where to move. ** @param y Y tile where to move. ** ** @pre The unit's field flags must have been unmarked ** on the map; see UnmarkUnitFieldFlags. ** ** @return -1 -> impossible to cross. ** 0 -> no induced cost, except move ** >0 -> costly tile */ static int STDCALL CostMoveTo(int x, int y, void *data) { int i; int j; int flag; int cost = 0; CUnit *goal; const CUnit *unit = (const CUnit *)data; int mask = unit->Type->MovementMask; cost = 0; // Doesn't cost anything to move to ourselves :) // Used when marking goals mainly. Could cause speed problems if (unit->X == x && unit->Y == y) { return 0; } // verify each tile of the unit. for (i = x; i < x + unit->Type->TileWidth; ++i) { for (j = y; j < y + unit->Type->TileHeight; ++j) { flag = Map.Field(i, j)->Flags & mask; if (flag && (AStarKnowUnseenTerrain || Map.IsFieldExplored(unit->Player, i, j))) { if (flag & ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit)) { // we can't cross fixed units and other unpassable things return -1; } goal = UnitOnMapTile(i, j, unit->Type->UnitType); if (!goal) { // Shouldn't happen, mask says there is something on this tile Assert(0); return -1; } // The unit must not be blocked by itself. // Please use UnmarkUnitFieldFlags and // MarkUnitFieldFlags around pathfinder calls. Assert(goal != unit); if (goal->Moving) { // moving unit are crossable cost += AStarMovingUnitCrossingCost; } else { // for non moving unit Always Fail // FIXME: Need support for moving a fixed unit to add cost return -1; //cost += AStarFixedUnitCrossingCost; } } // Add cost of crossing unknown tiles if required if (!AStarKnowUnseenTerrain && !Map.IsFieldExplored(unit->Player, i, j)) { // Tend against unknown tiles. cost += AStarUnknownTerrainCost; } if (unit->Type->UnitType != UnitTypeFly) { // Add tile movement cost cost += Map.Field(i, j)->Cost; } } } return cost; }
/** ** Unit on map tile. ** ** @param pos position on map, tile-based. ** @param type UnitTypeType, (unsigned)-1 for any type. ** ** @return Returns first found unit on tile. */ CUnit *UnitOnMapTile(const Vec2i &pos, unsigned int type) { return UnitOnMapTile(Map.getIndex(pos), type); }
/** ** Find the closest piece of coast you can unload units on ** ** @param x start location for the search ** @param y start location for the search ** @param resx coast x position ** @param resy coast y position ** ** @return 1 if a location was found, 0 otherwise */ static int ClosestFreeCoast(int x, int y, int *resx, int *resy) { int i; int addx; int addy; int nullx; int nully; int n; addx = addy = 1; if (Map.CoastOnMap(x, y) && FindUnloadPosition(x, y, &nullx, &nully, LandUnitMask)) { *resx = x; *resy = y; return 1; } --x; // The maximum distance to the coast. We have to stop somewhere... n = 20; while (n--) { for (i = addy; i--; ++y) { if (x >= 0 && y >= 0 && x < Map.Info.MapWidth && y < Map.Info.MapHeight && Map.CoastOnMap(x, y) && !UnitOnMapTile(x, y) && FindUnloadPosition(x, y, &nullx, &nully, LandUnitMask)) { *resx = x; *resy = y; return 1; } } ++addx; for (i = addx; i--; ++x) { if (x >= 0 && y >= 0 && x < Map.Info.MapWidth && y < Map.Info.MapHeight && Map.CoastOnMap(x, y) && !UnitOnMapTile(x ,y) && FindUnloadPosition(x, y, &nullx, &nully, LandUnitMask)) { *resx = x; *resy = y; return 1; } } ++addy; for (i = addy; i--; --y) { if (x >= 0 && y >= 0 && x < Map.Info.MapWidth && y < Map.Info.MapHeight && Map.CoastOnMap(x, y) && !UnitOnMapTile(x, y) && FindUnloadPosition(x, y, &nullx, &nully, LandUnitMask)) { *resx = x; *resy = y; return 1; } } ++addx; for (i = addx; i--; --x) { if (x >= 0 && y >= 0 && x < Map.Info.MapWidth && y < Map.Info.MapHeight && Map.CoastOnMap(x, y) && !UnitOnMapTile(x, y) && FindUnloadPosition(x, y, &nullx, &nully, LandUnitMask)) { *resx = x; *resy = y; return 1; } } ++addy; } DebugPrint("Try clicking closer to an actual coast.\n"); return 0; }