DISPLAY.CPP (218047B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: /CounterStrike/DISPLAY.CPP 3 3/09/97 8:04p Joe_bostic $ */ 17 /*********************************************************************************************** 18 *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** 19 *********************************************************************************************** 20 * * 21 * Project Name : Command & Conquer * 22 * * 23 * File Name : DISPLAY.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : September 10, 1993 * 28 * * 29 * Last Update : October 20, 1996 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * DisplayClass::Compute_Start_Pos -- Computes player's start pos from unit coords. * 34 * DisplayClass::AI -- Handles the maintenance tasks for the map display. * 35 * DisplayClass::All_To_Look -- Direct all objects to look around for the player. * 36 * DisplayClass::Calculated_Cell -- Fetch a map cell based on specified method. * 37 * DisplayClass::Cell_Object -- Determines what has been clicked on. * 38 * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * 39 * DisplayClass::Center_Map -- Centers the map about the currently selected objects * 40 * DisplayClass::Click_Cell_Calc -- Determines cell from screen X & Y. * 41 * DisplayClass::Closest_Free_Spot -- Finds the closest cell sub spot that is free. * 42 * DisplayClass::Coord_To_Pixel -- Determines X and Y pixel coordinates. * 43 * DisplayClass::Cursor_Mark -- Set or resets the cursor display flag bits. * 44 * DisplayClass::DisplayClass -- Default constructor for display class. * 45 * DisplayClass::Draw_It -- Draws the tactical map. * 46 * DisplayClass::Encroach_Shadow -- Causes the shadow to creep back by one cell. * 47 * DisplayClass::Flag_Cell -- Flag the specified cell to be redrawn. * 48 * DisplayClass::Flag_To_Redraw -- Flags the display so that it will be redrawn as soon as poss* 49 * DisplayClass::Get_Occupy_Dimensions -- computes width & height of the given occupy list * 50 * DisplayClass::Good_Reinforcement_Cell -- Checks cell for renforcement legality. * 51 * DisplayClass::In_View -- Determines if cell is visible on screen. * 52 * DisplayClass::Init_Clear -- Clears the display to a known state. * 53 * DisplayClass::Init_IO -- Creates the map's button list * 54 * DisplayClass::Init_Theater -- Theater-specific initialization * 55 * DisplayClass::Is_Spot_Free -- Determines if cell sub spot is free of occupation. * 56 * DisplayClass::Map_Cell -- Mark specified cell as having been mapped. * 57 * DisplayClass::Mouse_Left_Held -- Handles the left button held down. * 58 * DisplayClass::Mouse_Left_Press -- Handles the left mouse button press. * 59 * DisplayClass::Mouse_Left_Release -- Handles the left mouse button release. * 60 * DisplayClass::Mouse_Left_Up -- Handles the left mouse "cruising" over the map. * 61 * DisplayClass::Mouse_Right_Press -- Handles the right mouse button press. * 62 * DisplayClass::Next_Object -- Searches for next object on display. * 63 * DisplayClass::One_Time -- Performs any special one time initializations. * 64 * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* 65 * DisplayClass::Pixel_To_Coord -- converts screen coord to COORDINATE * 66 * DisplayClass::Prev_Object -- Searches for the previous object on the map. * 67 * DisplayClass::Read_INI -- Reads map control data from INI file. * 68 * DisplayClass::Redraw_Icons -- Draws all terrain icons necessary. * 69 * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. * 70 * DisplayClass::Refresh_Band -- Causes all cells under the rubber band to be redrawn. * 71 * DisplayClass::Refresh_Cells -- Redraws all cells in list. * 72 * DisplayClass::Remove -- Removes a game object from the rendering system. * 73 * DisplayClass::Repair_Mode_Control -- Controls the repair mode. * 74 * DisplayClass::Scroll_Map -- Scroll the tactical map in desired direction. * 75 * DisplayClass::Select_These -- All selectable objects in region are selected. * 76 * DisplayClass::Sell_Mode_Control -- Controls the sell mode. * 77 * DisplayClass::Set_Cursor_Pos -- Controls the display and animation of the tac cursor. * 78 * DisplayClass::Set_Cursor_Shape -- Changes the shape of the terrain square cursor. * 79 * DisplayClass::Set_Tactical_Position -- Sets the tactical view position. * 80 * DisplayClass::Set_View_Dimensions -- Sets the tactical display screen coordinates. * 81 * DisplayClass::Shroud_Cell -- Returns the specified cell into the shrouded condition. * 82 * DisplayClass::Submit -- Adds a game object to the map rendering system. * 83 * DisplayClass::TacticalClass::Action -- Processes input for the tactical map. * 84 * DisplayClass::Text_Overlap_List -- Creates cell overlap list for specified text string. * 85 * DisplayClass::Write_INI -- Write the map data to the INI file specified. * 86 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 87 88 #include "function.h" 89 #include "vortex.h" 90 91 /* 92 ** These layer control elements are used to group the displayable objects 93 ** so that proper overlap can be obtained. 94 */ 95 LayerClass DisplayClass::Layer[LAYER_COUNT]; 96 97 /* 98 ** Fading tables 99 */ 100 unsigned char DisplayClass::FadingBrighten[256]; 101 unsigned char DisplayClass::FadingShade[256]; 102 unsigned char DisplayClass::FadingWayDark[256]; 103 unsigned char DisplayClass::FadingLight[256]; 104 unsigned char DisplayClass::FadingGreen[256]; 105 unsigned char DisplayClass::FadingYellow[256]; 106 unsigned char DisplayClass::FadingRed[256]; 107 unsigned char DisplayClass::TranslucentTable[(MAGIC_COL_COUNT+1)*256]; 108 unsigned char DisplayClass::WhiteTranslucentTable[(1+1)*256]; 109 unsigned char DisplayClass::MouseTranslucentTable[(4+1)*256]; 110 void const * DisplayClass::TransIconset; 111 unsigned char DisplayClass::UnitShadow[(USHADOW_COL_COUNT+1)*256]; 112 unsigned char DisplayClass::UnitShadowAir[(USHADOW_COL_COUNT+1)*256]; 113 unsigned char DisplayClass::SpecialGhost[2*256]; 114 115 void const * DisplayClass::ShadowShapes; 116 unsigned char DisplayClass::ShadowTrans[(SHADOW_COL_COUNT+1)*256]; 117 118 /* 119 ** Bit array of cell redraw flags 120 */ 121 BooleanVectorClass DisplayClass::CellRedraw; 122 123 /* 124 ** The main button that intercepts user input to the map 125 */ 126 DisplayClass::TacticalClass DisplayClass::TacButton; 127 128 // 129 // We need a way to bypass visible view checks when we are running in the context of GlyphX without using the 130 // internal C&C renderer. We shouldn't know or care what the user is actually looking at 131 // ST - 4/17/2019 9:01AM 132 // 133 bool DisplayClass::IgnoreViewConstraints = false; 134 135 136 static int const TEX_X = 0; 137 static int const TEX_Y = 6; 138 static int const TEX_W = 14; 139 140 //Added for getting the input for special character keys from the client 141 // - 6/26/2019 JAS 142 extern bool DLL_Export_Get_Input_Key_State(KeyNumType key); 143 144 145 /*********************************************************************************************** 146 * DisplayClass::DisplayClass -- Default constructor for display class. * 147 * * 148 * This constructor for the display class just initializes some of the display settings. * 149 * Most settings are initialized with the correct values at the time that the Init function * 150 * is called. There are some cases where default values are wise and this routine fills * 151 * those particular ones in. * 152 * * 153 * INPUT: none * 154 * * 155 * OUTPUT: none * 156 * * 157 * WARNINGS: none * 158 * * 159 * HISTORY: * 160 * 12/06/1994 JLB : Created. * 161 *=============================================================================================*/ 162 DisplayClass::DisplayClass(void) : 163 TacticalCoord(0), 164 TacLeptonWidth(0), 165 TacLeptonHeight(0), 166 ZoneCell(0), 167 ZoneOffset(0), 168 CursorSize(0), 169 ProximityCheck(false), 170 PendingObjectPtr(0), 171 PendingObject(0), 172 PendingHouse(HOUSE_NONE), 173 TacPixelX(0), 174 TacPixelY(0), 175 DesiredTacticalCoord(0), 176 IsToRedraw(true), 177 IsRepairMode(false), 178 IsSellMode(false), 179 IsTargettingMode(SPC_NONE), 180 IsRubberBand(false), 181 IsTentative(false), 182 IsShadowPresent(false), 183 BandX(0), 184 BandY(0), 185 NewX(0), 186 NewY(0) 187 { 188 ShadowShapes = 0; 189 TransIconset = 0; 190 191 Set_View_Dimensions(0, 8, 320/CELL_PIXEL_W, 200/CELL_PIXEL_H); 192 } 193 194 195 /*********************************************************************************************** 196 * DisplayClass::One_Time -- Performs any special one time initializations. * 197 * * 198 * This routine is called from the game initialization process. It is to perform any one * 199 * time initializations necessary for the map display system. It allocates the staging * 200 * buffer needed for the radar map. * 201 * * 202 * INPUT: none * 203 * * 204 * OUTPUT: none * 205 * * 206 * WARNINGS: This routine must be called ONCE and only once. * 207 * * 208 * HISTORY: * 209 * 05/31/1994 JLB : Created. * 210 * 05/31/1994 JLB : Handles layer system now. * 211 * 06/02/1994 JLB : Takes care of misc display tables and data allocation. * 212 *=============================================================================================*/ 213 void DisplayClass::One_Time(void) 214 { 215 MapClass::One_Time(); 216 217 /* 218 ** Init the CellRedraw bit array. Do not do this in the constructor, since the 219 ** BooleanVector may not have been constructed yet. 220 */ 221 CellRedraw.Resize(MAP_CELL_TOTAL); 222 223 for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { 224 Layer[layer].One_Time(); 225 } 226 227 /* 228 ** Load the generic transparent icon set. 229 */ 230 TransIconset = MFCD::Retrieve("TRANS.ICN"); 231 232 #ifndef NDEBUG 233 RawFileClass file("SHADOW.SHP"); 234 if (file.Is_Available()) { 235 ShadowShapes = Load_Alloc_Data(file); 236 } else { 237 ShadowShapes = MFCD::Retrieve("SHADOW.SHP"); 238 } 239 #else 240 ShadowShapes = MFCD::Retrieve("SHADOW.SHP"); 241 #endif 242 243 //PG Set_View_Dimensions(0, 8 * RESFACTOR); 244 Set_View_Dimensions(0, 0); 245 } 246 247 248 /*********************************************************************************************** 249 * DisplayClass::Init_Clear -- clears the display to a known state * 250 * * 251 * INPUT: * 252 * none. * 253 * * 254 * OUTPUT: * 255 * none. * 256 * * 257 * WARNINGS: * 258 * none. * 259 * * 260 * HISTORY: * 261 * 03/17/1995 BRR : Created. * 262 *=============================================================================================*/ 263 void DisplayClass::Init_Clear(void) 264 { 265 MapClass::Init_Clear(); 266 267 /* 268 ** Clear any object being placed 269 */ 270 PendingObjectPtr = 0; 271 PendingObject = 0; 272 PendingHouse = HOUSE_NONE; 273 CursorSize = 0; 274 IsTargettingMode = SPC_NONE; 275 IsRepairMode = false; 276 IsRubberBand = false; 277 IsTentative = false; 278 IsSellMode = false; 279 280 /* 281 ** Empty all the display's layers 282 */ 283 for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { 284 Layer[layer].Init(); 285 } 286 } 287 288 289 /*********************************************************************************************** 290 * DisplayClass::Init_IO -- clears & re-builds the map's button list * 291 * * 292 * INPUT: * 293 * none. * 294 * * 295 * OUTPUT: * 296 * none. * 297 * * 298 * WARNINGS: * 299 * none. * 300 * * 301 * HISTORY: * 302 * 03/17/1995 BRR : Created. * 303 *=============================================================================================*/ 304 void DisplayClass::Init_IO(void) 305 { 306 MapClass::Init_IO(); 307 /* 308 ** Re-attach our buttons to the main map button list, only in non-edit mode. 309 */ 310 if (!Debug_Map) { 311 TacButton.Zap(); 312 Add_A_Button(TacButton); 313 } 314 } 315 316 317 /*********************************************************************************************** 318 * DisplayClass::Init_Theater -- Performs theater-specific initialization (mixfiles, etc) * 319 * * 320 * INPUT: * 321 * theater new theater * 322 * * 323 * OUTPUT: * 324 * none. * 325 * * 326 * WARNINGS: * 327 * none. * 328 * * 329 * HISTORY: * 330 * 03/17/1995 BRR : Created. * 331 * 05/07/1996 JLB : Added translucent tables. * 332 *=============================================================================================*/ 333 void DisplayClass::Init_Theater(TheaterType theater) 334 { 335 char fullname[16]; 336 static TLucentType const MouseCols[4] = { 337 {BLACK, BLACK, 110, 0}, 338 {WHITE, WHITE, 110, 0}, 339 {LTGREY, LTGREY, 110, 0}, 340 {DKGREY, DKGREY, 110, 0} 341 }; 342 static TLucentType const MagicCols[MAGIC_COL_COUNT] = { 343 {32,32,110,0}, 344 {33,33,110,0}, 345 {34,34,110,0}, 346 {35,35,110,0}, 347 {36,36,110,0}, 348 {37,37,110,0}, 349 {38,38,110,0}, 350 {39,39,110,0}, 351 {BLACK, BLACK, 200, 0}, 352 {WHITE, BLACK, 40, 0}, 353 {LTGREY, BLACK, 80, 0}, 354 {DKGREY, BLACK, 140, 0}, 355 {LTGREEN, BLACK,130,0} 356 }; 357 static TLucentType const WhiteCols[1] = { 358 {1, WHITE, 80, 0} 359 }; 360 static TLucentType const ShadowCols[SHADOW_COL_COUNT] = { 361 {WHITE+1, BLACK,130,0}, 362 {WHITE, BLACK,170,0}, 363 {LTGRAY, BLACK,250,0}, 364 {DKGRAY, BLACK,250,0} 365 }; 366 static TLucentType const UShadowCols[USHADOW_COL_COUNT] = { 367 {LTGREEN, BLACK,130,0} 368 }; 369 static TLucentType const UShadowColsAir[USHADOW_COL_COUNT] = { 370 {LTGREEN, WHITE,0,0} 371 }; 372 static TLucentType const UShadowColsSnow[USHADOW_COL_COUNT] = { 373 {LTGREEN, BLACK,75,0} 374 }; 375 376 /* 377 ** Invoke parent's init routine. 378 */ 379 MapClass::Init_Theater(theater); 380 381 /* 382 ** Save the new theater value 383 */ 384 Scen.Theater = theater; 385 386 /* 387 ** Unload old mixfiles, and cache the new ones 388 */ 389 sprintf(fullname, "%s.MIX", Theaters[theater].Root); 390 391 #ifndef WIN32 392 LastTheater = THEATER_NONE; 393 #endif 394 395 if (Scen.Theater != LastTheater) { 396 if (TheaterData != NULL) { 397 delete TheaterData; 398 } 399 TheaterData = new MFCD(fullname, &FastKey); 400 assert(TheaterData != NULL); 401 402 bool theaterload = TheaterData->Cache(TheaterBuffer); 403 assert(theaterload); 404 // LastTheater = Scen.Theater; 405 } 406 407 /* 408 ** Load the custom palette associated with this theater. 409 ** The fading palettes will have to be generated as well. 410 */ 411 sprintf(fullname, "%s.PAL", Theaters[theater].Root); 412 PaletteClass const * ptr = (PaletteClass *)MFCD::Retrieve(fullname); 413 414 GamePalette = * ptr; 415 416 OriginalPalette = GamePalette; 417 418 Build_Fading_Table(GamePalette.Get_Data(), FadingGreen, GREEN, 110); 419 420 Build_Fading_Table(GamePalette.Get_Data(), FadingYellow, YELLOW, 140); 421 422 Build_Fading_Table(GamePalette.Get_Data(), FadingRed, RED, 140); 423 424 Build_Translucent_Table(GamePalette, &MouseCols[0], 4, MouseTranslucentTable); 425 426 Build_Translucent_Table(GamePalette, &MagicCols[0], MAGIC_COL_COUNT, TranslucentTable); 427 428 Build_Translucent_Table(GamePalette, &WhiteCols[0], 1, WhiteTranslucentTable); 429 430 Build_Translucent_Table(GamePalette, &ShadowCols[0], SHADOW_COL_COUNT, ShadowTrans); 431 432 Conquer_Build_Translucent_Table(GamePalette, &UShadowColsAir[0], USHADOW_COL_COUNT, UnitShadowAir); 433 memcpy(&UnitShadowAir[256], ColorRemaps[PCOLOR_GOLD].RemapTable, sizeof(ColorRemaps[PCOLOR_GOLD].RemapTable)); 434 if (theater == THEATER_SNOW) { 435 Conquer_Build_Translucent_Table(GamePalette, &UShadowColsSnow[0], USHADOW_COL_COUNT, UnitShadow); 436 } else { 437 Conquer_Build_Translucent_Table(GamePalette, &UShadowCols[0], USHADOW_COL_COUNT, UnitShadow); 438 } 439 440 if (theater == THEATER_SNOW) { 441 Conquer_Build_Fading_Table(GamePalette, FadingShade, BLACK, 75); 442 } else { 443 Conquer_Build_Fading_Table(GamePalette, FadingShade, BLACK, 130); 444 } 445 446 Conquer_Build_Fading_Table(GamePalette, FadingLight, WHITE, 85); 447 448 /* 449 ** Create the shadow color used by aircraft. 450 */ 451 Conquer_Build_Fading_Table(GamePalette, &SpecialGhost[256], BLACK, 100); 452 for (int index = 0; index < 256; index++) { 453 SpecialGhost[index] = 0; 454 } 455 456 Make_Fading_Table(GamePalette, FadingBrighten, WHITE, 25); 457 458 Make_Fading_Table(GamePalette, FadingWayDark, DKGRAY, 192); 459 460 /* 461 ** Adjust the palette according to the visual control option settings. 462 */ 463 Options.Fixup_Palette(); 464 } 465 466 467 /*********************************************************************************************** 468 * DisplayClass::Text_Overlap_List -- Creates cell overlap list for specified text string. * 469 * * 470 * This routine is used to create an overlap list that specifies all the cells that are * 471 * covered by the specified text string. This overlap list is used to handle map refresh * 472 * logic. * 473 * * 474 * INPUT: text -- Pointer to the text that would appear on the map and must have an * 475 * overlap list generated. * 476 * * 477 * x,y -- The coordinates that the text would appear (upper left corner). * 478 * * 479 * OUTPUT: Returns with a pointer to an overlap list that covers all cells "under" the text * 480 * if were displayed at the coordinates specified. The list is actually a series of * 481 * offsets from the display's upper left corner cell number. * 482 * * 483 * WARNINGS: none * 484 * * 485 * HISTORY: * 486 * 12/06/1994 JLB : Created. * 487 * 12/07/1994 JLB : Sidebar fixup. * 488 * 08/13/1995 JLB : Optimized for variable sized help text. * 489 *=============================================================================================*/ 490 short const * DisplayClass::Text_Overlap_List(char const * text, int x, int y) const 491 { 492 static short _list[60]; 493 int count = ARRAY_SIZE(_list); 494 495 if (text != NULL) { 496 short * ptr = &_list[0]; 497 int len = String_Pixel_Width(text)+CELL_PIXEL_W; 498 int right = TacPixelX + Lepton_To_Pixel(TacLeptonWidth); 499 500 /* 501 ** If the help text would spill into the sidebar, then flag this fact, but 502 ** shorten the apparent length so that the icon list calculation will 503 ** function correctly. 504 */ 505 if (x+len >= TacPixelX+Lepton_To_Pixel(TacLeptonWidth)) { 506 len = right-x; 507 *ptr++ = REFRESH_SIDEBAR; 508 count--; 509 } 510 511 /* 512 ** Build the list of overlap cell offset values according to the text 513 ** coordinate and the length. 514 */ 515 if (x <= right) { 516 CELL ul = Click_Cell_Calc(x, y-1); 517 CELL lr = Click_Cell_Calc(x+len-1, Bound(y+24, TacPixelY, TacPixelY+Lepton_To_Pixel(TacLeptonHeight) - 1)); 518 519 if (ul == -1) ul = Click_Cell_Calc(x, y); 520 521 if (ul != -1 && lr != -1) { 522 for (int yy = Cell_Y(ul); yy <= Cell_Y(lr); yy++) { 523 for (int xx = Cell_X(ul); xx <= Cell_X(lr); xx++) { 524 *ptr++ = XY_Cell(xx, yy) - Coord_Cell(TacticalCoord); 525 count--; 526 if (count < 2) break; 527 } 528 if (count < 2) break; 529 } 530 } 531 } 532 533 *ptr = REFRESH_EOL; 534 } 535 return(_list); 536 } 537 538 539 /*********************************************************************************************** 540 * DisplayClass::Set_View_Dimensions -- Sets the tactical display screen coordinates. * 541 * * 542 * Use this routine to set the tactical map screen coordinates and dimensions. This routine * 543 * is typically used when the screen size or position changes as a result of the sidebar * 544 * changing position or appearance. * 545 * * 546 * INPUT: x,y -- The X and Y pixel position on the screen for the tactical map upper left * 547 * corner. * 548 * * 549 * width -- The width of the tactical display (in icons). If this parameter is * 550 * omitted, then the width will be as wide as the screen will allow. * 551 * * 552 * height-- The height of the tactical display (in icons). If this parameter is * 553 * omitted, then the width will be as wide as the screen will allow. * 554 * * 555 * OUTPUT: none * 556 * * 557 * WARNINGS: none * 558 * * 559 * HISTORY: * 560 * 12/06/1994 JLB : Created. * 561 * 06/27/1995 JLB : Adjusts tactical map position if necessary. * 562 *=============================================================================================*/ 563 void DisplayClass::Set_View_Dimensions(int x, int y, int width, int height) 564 { 565 if (width == -1) { 566 TacLeptonWidth = Pixel_To_Lepton(SeenBuff.Get_Width()-x); 567 } else { 568 TacLeptonWidth = width * CELL_LEPTON_W; 569 } 570 571 // ST - 3/1/2019 12:05PM 572 // Made the below code more consistent with the width calculation. This is needed if we aren't going to draw the tabs at the top of the screen 573 // 574 if (height == -1) { 575 TacLeptonHeight = Pixel_To_Lepton(SeenBuff.Get_Height() - y); 576 //height = (SeenBuff.Get_Height()-y) / CELL_PIXEL_H; 577 } 578 else { 579 TacLeptonHeight = height * CELL_LEPTON_H; 580 } 581 //TacLeptonHeight = height * CELL_LEPTON_H; 582 583 /* 584 ** Adjust the tactical cell if it is now in an invalid position 585 ** because of the changed dimensions. 586 */ 587 int xx = 0;// Coord_X(TacticalCoord) - (MapCellX * CELL_LEPTON_W); 588 int yy = 0;// Coord_Y(TacticalCoord) - (MapCellY * CELL_LEPTON_H); 589 590 Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, MapCellWidth * CELL_LEPTON_W, MapCellHeight * CELL_LEPTON_H); 591 592 Set_Tactical_Position(XY_Coord(xx + (MapCellX * CELL_LEPTON_W), yy + (MapCellY * CELL_LEPTON_H))); 593 594 TacPixelX = x; 595 TacPixelY = y; 596 WindowList[WINDOW_TACTICAL][WINDOWX] = x; 597 WindowList[WINDOW_TACTICAL][WINDOWY] = y; 598 WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = Lepton_To_Pixel(TacLeptonWidth); 599 WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = Lepton_To_Pixel(TacLeptonHeight); 600 if (Window == WINDOW_TACTICAL) { 601 Change_Window(0); 602 Change_Window(Window); 603 } 604 IsToRedraw = true; 605 Flag_To_Redraw(false); 606 607 TacButton.X = TacPixelX; 608 TacButton.Y = TacPixelY; 609 TacButton.Width = Lepton_To_Pixel(TacLeptonWidth); 610 TacButton.Height = Lepton_To_Pixel(TacLeptonHeight); 611 } 612 613 614 /*********************************************************************************************** 615 * DisplayClass::Set_Cursor_Shape -- Changes the shape of the terrain square cursor. * 616 * * 617 * This routine is used to set up the terrain cursor according to the size of the object * 618 * that is to be placed down. The terrain cursor looks like an arbitrary collection of * 619 * hatched square overlays. Typical use is when placing buildings. * 620 * * 621 * INPUT: list -- A pointer to the list that contains offsets to the cells that are to * 622 * be marked. * 623 * * 624 * OUTPUT: none * 625 * * 626 * WARNINGS: none * 627 * * 628 * HISTORY: * 629 * 06/03/1994 JLB : Created. * 630 * 06/26/1995 JLB : Puts placement cursor into static buffer. * 631 *=============================================================================================*/ 632 void DisplayClass::Set_Cursor_Shape(short const * list) 633 { 634 if (CursorSize) { 635 Cursor_Mark(ZoneCell+ZoneOffset, false); 636 } 637 ZoneOffset = 0; 638 639 if (list) { 640 int w,h; 641 static short _list[50]; 642 643 memcpy(_list, list, sizeof(_list)); 644 CursorSize = _list; 645 Get_Occupy_Dimensions (w, h, CursorSize); 646 ZoneOffset = -(((h/2)*MAP_CELL_W)+(w/2)); 647 Cursor_Mark(ZoneCell+ZoneOffset, true); 648 } else { 649 CursorSize = 0; 650 } 651 } 652 653 654 /*********************************************************************************************** 655 * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* 656 * * 657 * This routine is used by the building placement cursor logic to determine whether the * 658 * at the current cursor position if the building would be adjacent to another friendly * 659 * building. In cases where this is not true, then the building cannot be placed at all. * 660 * This determination is returned by the function. * 661 * * 662 * INPUT: object -- The building object that the current placement system is examining. * 663 * * 664 * house -- The house to base the proximity check upon. Typically this is the * 665 * player's house, but in multiplay, the computer needs to check for * 666 * proximity as well. * 667 * * 668 * list -- Pointer to the building's offset list. * 669 * * 670 * trycell -- The cell to base the offset list on. * 671 * * 672 * OUTPUT: bool; Can the pending building object be placed at the present cursor location * 673 * checking only for proximity to friendly buildings? If this isn't for a * 674 * building type object, then this routine always returns true. * 675 * * 676 * WARNINGS: none * 677 * * 678 * HISTORY: * 679 * 06/06/1994 JLB : Created. * 680 * 06/07/1994 JLB : Handles concrete check. * 681 * 10/11/1994 BWG : Added IsProximate check for ore refineries * 682 *=============================================================================================*/ 683 bool DisplayClass::Passes_Proximity_Check(ObjectTypeClass const * object, HousesType house, short const * list, CELL trycell) const 684 { 685 short const * ptr; 686 int retval = -1; 687 bool noradar = false; 688 //bool nomapped = false; // Not used. ST - 8/6/2019 10:51AM 689 bool shipyard = false; 690 691 if (house == PlayerPtr->Class->House) { 692 PassedProximity = false; 693 } 694 695 /* 696 ** In editor mode, the proximity check always passes. 697 */ 698 if (Debug_Map) { 699 return(true); 700 } 701 702 if (list == NULL || trycell == 0) { 703 return(true); 704 } 705 706 if (object == NULL || object->What_Am_I() != RTTI_BUILDINGTYPE) { 707 return(true); 708 } 709 710 BuildingTypeClass const * building = (BuildingTypeClass const *)object; 711 712 /* 713 ** Scan through all cells that the building foundation would cover. If any adjacent 714 ** cells to these are of friendly persuasion, then consider the proximity check to 715 ** have been a success. 716 */ 717 ptr = list; 718 // ptr = CursorSize; 719 CELL cell = trycell; 720 // CELL cell = ZoneCell; 721 if (building->Adjacent == 1) { 722 while (*ptr != REFRESH_EOL && (retval == -1) ) { 723 cell = trycell + *ptr++; 724 // cell = ZoneCell + ZoneOffset + *ptr++; 725 726 if (!In_Radar(cell)) { 727 retval = false; 728 noradar = true; 729 break; 730 } 731 732 for (FacingType facing = FACING_FIRST; facing < FACING_COUNT; facing++) { 733 CELL newcell = Adjacent_Cell(cell, facing); 734 735 if (!In_Radar(newcell)) continue; 736 737 // Code has no effect. ST - 8/6/2019 10:51AM 738 //if (!(*this)[newcell].IsMapped) { 739 // nomapped = true; 740 //} 741 BuildingClass * base = (*this)[newcell].Cell_Building(); 742 743 /* 744 ** The special cell ownership flag allows building adjacent 745 ** to friendly walls and bibs even though there is no official 746 ** building located there. 747 */ 748 //BG: Modified so only walls can be placed next to walls - buildings can't. 749 //JLB: Except for bibs, in which case buildings can be placed next to these. 750 if (building->IsWall || 751 ((*this)[newcell].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference((*this)[newcell].Smudge).IsBib)) { 752 753 if ((*this)[newcell].Owner == house) { 754 retval = true; 755 break; 756 } 757 } 758 759 // we've found a building... 760 if (base != NULL && base->House->Class->House == house && base->Class->IsBase) { 761 retval = true; 762 break; 763 } 764 765 /* BG: modifications to allow buildings one cell away from other buildings. 766 ** This is done by scanning each cell that fails the check (hence getting 767 ** to this point) and looking at the n/s/e/w adjacent cells to see if they 768 ** have buildings in them. If they do, and they match us, then succeed. 769 */ 770 if (retval != -1) break; 771 772 for (FacingType newface = FACING_N; newface < FACING_COUNT; newface++) { 773 CELL newercell = Adjacent_Cell(newcell, newface); 774 775 if (building->IsWall || 776 ((*this)[newercell].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference((*this)[newercell].Smudge).IsBib)) { 777 778 if ((*this)[newercell].Owner == house) { 779 retval = true; 780 break; 781 } 782 } 783 784 TechnoClass * newbase = (*this)[newercell].Cell_Techno(); 785 786 // we've found a building... 787 if (newbase != NULL && newbase->What_Am_I() == RTTI_BUILDING && newbase->House->Class->House == house && ((BuildingClass const *)newbase)->Class->IsBase) { 788 retval = true; 789 break; 790 } 791 } 792 if (retval != -1) break; 793 } 794 } 795 } 796 797 if (retval == -1) retval = false; 798 799 if (house == PlayerPtr->Class->House) { 800 PassedProximity = (retval != false); 801 } 802 803 /* 804 ** If this object has special dispensation to be placed further than one cell from 805 ** other regular buildings, then check for this case now. Only bother to check if 806 ** it hasn't already been given permission to be placed down. 807 */ 808 if (!retval && !noradar && object->What_Am_I() == RTTI_BUILDINGTYPE) { 809 810 // For land mines, let's make it check proximity within 10 squares 811 if (building->Adjacent > 1) { 812 for (int index = 0; index < Buildings.Count(); index++) { 813 BuildingClass * obj = Buildings.Ptr(index); 814 if (obj != NULL && !obj->IsInLimbo && obj->House->Class->House == house && obj->Class->IsBase) { 815 int centdist = ::Distance(obj->Center_Coord(), Cell_Coord(cell)); 816 centdist /= CELL_LEPTON_W; 817 centdist -= (obj->Class->Width() + obj->Class->Height()) / 2; 818 if (centdist <= building->Adjacent) { 819 retval = true; 820 break; 821 } 822 } 823 } 824 } 825 } 826 827 return((bool)retval); 828 } 829 830 831 /*********************************************************************************************** 832 * DisplayClass::Set_Cursor_Pos -- Controls the display and animation of the tac cursor. * 833 * * 834 * This routine controls the location, display, and animation of the * 835 * tactical map cursor. * 836 * * 837 * INPUT: pos -- Position to move the cursor do. If -1 is passed then * 838 * the cursor will just be hidden. If the position * 839 * passed is the same as the last position passed in, * 840 * then animation could occur (based on timers). * 841 * * 842 * OUTPUT: none * 843 * * 844 * WARNINGS: none * 845 * * 846 * HISTORY: * 847 * 05/22/1991 JLB : Created. * 848 * 06/02/1994 JLB : Converted to member function. * 849 * 06/08/1994 JLB : If position is -1, then follow mouse. * 850 * 02/28/1995 JLB : Forces placement cursor to fit on map. * 851 *=============================================================================================*/ 852 CELL DisplayClass::Set_Cursor_Pos(CELL pos) 853 { 854 CELL prevpos; // Last position of cursor (for jump-back reasons). 855 856 /* 857 ** Follow the mouse position if no cell number is provided. 858 */ 859 if (pos == -1) { 860 pos = Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y()); 861 } 862 863 if (CursorSize == NULL) { 864 prevpos = ZoneCell; 865 ZoneCell = pos; 866 return(prevpos); 867 } 868 869 /* 870 ** Adjusts the position so that the placement cursor is never part way off the 871 ** tactical map. 872 */ 873 int w,h; 874 Get_Occupy_Dimensions(w, h, CursorSize); 875 876 int x = Cell_X(pos + ZoneOffset); 877 int y = Cell_Y(pos + ZoneOffset); 878 879 if (x < Coord_XCell(TacticalCoord)) x = Coord_XCell(TacticalCoord); 880 if (y < Coord_YCell(TacticalCoord)) y = Coord_YCell(TacticalCoord); 881 if (x+w >= Coord_XCell(TacticalCoord) + Lepton_To_Cell(TacLeptonWidth)) x = Coord_XCell(TacticalCoord)+Lepton_To_Cell(TacLeptonWidth)-w; 882 if (y+h >= Coord_YCell(TacticalCoord) + Lepton_To_Cell(TacLeptonHeight)) y = Coord_YCell(TacticalCoord)+Lepton_To_Cell(TacLeptonHeight)-h; 883 pos = XY_Cell(x, y) - ZoneOffset; 884 885 /* 886 ** This checks to see if NO animation or drawing is to occur and, if so, 887 ** exits. 888 */ 889 if (pos == ZoneCell) return(pos); 890 891 prevpos = ZoneCell; 892 893 /* 894 ** If the cursor is visible, then handle the graphic update. 895 ** Otherwise, just update the global position of the cursor. 896 */ 897 if (CursorSize != NULL) { 898 899 /* 900 ** Erase the old cursor (if it exists) AND the cursor is moving. 901 */ 902 if (pos != ZoneCell && ZoneCell != -1) { 903 Cursor_Mark(ZoneCell+ZoneOffset, false); 904 } 905 906 /* 907 ** Render the cursor (could just be animation). 908 */ 909 if (pos != -1) { 910 Cursor_Mark(pos+ZoneOffset, true); 911 } 912 } 913 ZoneCell = pos; 914 ProximityCheck = Passes_Proximity_Check(PendingObject, PendingHouse, CursorSize, ZoneCell+ZoneOffset); 915 916 return(prevpos); 917 } 918 919 920 /*********************************************************************************************** 921 * DisplayClass::Get_Occupy_Dimensions -- computes width & height of the given occupy list * 922 * * 923 * INPUT: * 924 * w ptr to fill in with height * 925 * h ptr to fill in with width * 926 * * 927 * OUTPUT: * 928 * none. * 929 * * 930 * WARNINGS: * 931 * none. * 932 * * 933 * HISTORY: * 934 * 03/31/1995 BRR : Created. * 935 *=============================================================================================*/ 936 void DisplayClass::Get_Occupy_Dimensions(int & w, int & h, short const * list) const 937 { 938 int min_x = MAP_CELL_W; 939 int max_x = -MAP_CELL_W; 940 int min_y = MAP_CELL_H; 941 int max_y = -MAP_CELL_H; 942 int x,y; 943 944 w = 0; 945 h = 0; 946 947 if (!list) { 948 /* 949 ** Loop through all cell offsets, accumulating max & min x- & y-coords 950 */ 951 while (*list != REFRESH_EOL) { 952 /* 953 ** Compute x & y coords of the current cell offset. We can't use Cell_X() 954 ** & Cell_Y(), because they use shifts to compute the values, and if the 955 ** offset is negative we'll get a bogus coordinate! 956 */ 957 x = (*list) % MAP_CELL_W; 958 y = (*list) / MAP_CELL_H; 959 960 max_x = max(max_x, x); 961 min_x = min(min_x, x); 962 max_y = max(max_y, y); 963 min_y = min(min_y, y); 964 965 list++; 966 } 967 968 w = max(1, max_x - min_x + 1); 969 h = min(1, max_y - min_y + 1); 970 } 971 } 972 973 974 /*********************************************************************************************** 975 * DisplayClass::Cursor_Mark -- Set or resets the cursor display flag bits. * 976 * * 977 * This routine will clear or set the cursor display bits on the map. * 978 * If the bit is set, then the cursor will be rendered on that map * 979 * icon. * 980 * * 981 * INPUT: pos -- Position of the upper left corner of the cursor. * 982 * * 983 * on -- Should the bit be turned on? * 984 * * 985 * OUTPUT: none * 986 * * 987 * WARNINGS: Be sure that every call to set the bits is matched by a * 988 * corresponding call to clear the bits. * 989 * * 990 * HISTORY: * 991 * 09/04/1991 JLB : Created. * 992 * 06/02/1994 JLB : Converted to member function. * 993 *=============================================================================================*/ 994 void DisplayClass::Cursor_Mark(CELL pos, bool on) 995 { 996 CELL const * ptr; 997 CellClass * cellptr; 998 999 if ((unsigned)pos >= MAP_CELL_TOTAL) return; 1000 1001 /* 1002 ** For every cell in the CursorSize list, invoke its Redraw_Objects and 1003 ** toggle its IsCursorHere flag 1004 */ 1005 ptr = CursorSize; 1006 while (*ptr != REFRESH_EOL) { 1007 CELL cell = pos + *ptr++; 1008 if (In_Radar(cell)) { 1009 cellptr = &(*this)[cell]; 1010 cellptr->Redraw_Objects(); 1011 if (on) { 1012 cellptr->IsCursorHere = true; 1013 } else { 1014 cellptr->IsCursorHere = false; 1015 } 1016 } 1017 } 1018 1019 /* 1020 ** For every cell in the PendingObjectPtr's Overlap_List, invoke its 1021 ** Redraw_Objects routine. 1022 */ 1023 if (PendingObjectPtr && PendingObjectPtr->IsActive) { 1024 ptr = PendingObjectPtr->Overlap_List(); 1025 while (*ptr != REFRESH_EOL) { 1026 CELL cell = pos + *ptr++; 1027 if (In_Radar(cell)) { 1028 cellptr = &(*this)[cell]; 1029 cellptr->Redraw_Objects(); 1030 } 1031 } 1032 } 1033 } 1034 1035 1036 /*********************************************************************************************** 1037 * DisplayClass::AI -- Handles the maintenance tasks for the map display. * 1038 * * 1039 * This routine is called once per game display frame (15 times per second). It handles * 1040 * the mouse shape tracking and map scrolling as necessary. * 1041 * * 1042 * INPUT: input -- The next key just fetched from the input queue. * 1043 * * 1044 * x,y -- Mouse coordinates. * 1045 * * 1046 * OUTPUT: Modifies the input code if necessary. When the input code is consumed, it gets * 1047 * set to 0. * 1048 * * 1049 * WARNINGS: none * 1050 * * 1051 * HISTORY: * 1052 * 06/01/1994 JLB : Created. * 1053 * 06/02/1994 JLB : Filters mouse click input. * 1054 * 06/07/1994 JLB : Fixed so template click will behave right. * 1055 * 10/14/1994 JLB : Changing cursor shape over target. * 1056 * 12/31/1994 JLB : Takes mouse coordinates as parameters. * 1057 * 06/27/1995 JLB : Breaks out of rubber band mode if mouse leaves map. * 1058 *=============================================================================================*/ 1059 void DisplayClass::AI(KeyNumType & input, int x, int y) 1060 { 1061 if ( 1062 IsRubberBand && 1063 (Get_Mouse_X() < TacPixelX || 1064 Get_Mouse_Y() < TacPixelY || 1065 Get_Mouse_X() >= (TacPixelX + Lepton_To_Pixel(TacLeptonWidth)) || 1066 Get_Mouse_Y() >= (TacPixelY + Lepton_To_Pixel(TacLeptonHeight)))) { 1067 Mouse_Left_Release(-1, Get_Mouse_X(), Get_Mouse_Y(), NULL, ACTION_NONE); 1068 } 1069 1070 MapClass::AI(input, x, y); 1071 } 1072 1073 1074 /*********************************************************************************************** 1075 * DisplayClass::Submit -- Adds a game object to the map rendering system. * 1076 * * 1077 * This routine is used to add an arbitrary (but tangible) game object to the map. It will * 1078 * be rendered (made visible) once it is submitted to this function. This function builds * 1079 * the list of game objects that get rendered each frame as necessary. It is possible to * 1080 * submit the game object to different rendering layers. All objects in a layer get drawn * 1081 * at the same time. Using this layer method it becomes possible to have objects "below" * 1082 * other objects. * 1083 * * 1084 * INPUT: object -- Pointer to the object to add. * 1085 * * 1086 * layer -- The layer to add the object to. * 1087 * * 1088 * OUTPUT: none * 1089 * * 1090 * WARNINGS: none * 1091 * * 1092 * HISTORY: * 1093 * 05/31/1994 JLB : Created. * 1094 * 05/31/1994 JLB : Improved layer system. * 1095 * 05/31/1994 JLB : Sorts object position if this is for the ground layer. * 1096 *=============================================================================================*/ 1097 void DisplayClass::Submit(ObjectClass const * object, LayerType layer) 1098 { 1099 if (object) { 1100 Layer[layer].Submit(object, (layer == LAYER_GROUND)); 1101 } 1102 } 1103 1104 1105 /*********************************************************************************************** 1106 * DisplayClass::Remove -- Removes a game object from the rendering system. * 1107 * * 1108 * Every object that is to disappear from the map must be removed from the rendering * 1109 * system. * 1110 * * 1111 * INPUT: object -- The object to remove. * 1112 * * 1113 * layer -- The layer to remove it from. * 1114 * * 1115 * OUTPUT: none * 1116 * * 1117 * WARNINGS: none * 1118 * * 1119 * HISTORY: * 1120 * 05/31/1994 JLB : Created. * 1121 * 05/31/1994 JLB : Improved layer system. * 1122 *=============================================================================================*/ 1123 void DisplayClass::Remove(ObjectClass const * object, LayerType layer) 1124 { 1125 assert(object != 0); 1126 assert(object->IsActive); 1127 1128 if (object) { 1129 Layer[layer].Delete((ObjectClass *)object); 1130 } 1131 } 1132 1133 1134 /*********************************************************************************************** 1135 * DisplayClass::Click_Cell_Calc -- Determines cell from screen X & Y. * 1136 * * 1137 * This routine is used to determine the cell that is located at the * 1138 * screen pixel coordinates given. Typical use is when the player * 1139 * clicks with the mouse on the tactical map. * 1140 * * 1141 * INPUT: x,y -- Screen pixel coordinates. * 1142 * * 1143 * OUTPUT: Returns with cell that is under the coordinates specified. * 1144 * If the coordinate specified is outside of the tactical * 1145 * map, then -1 is returned. * 1146 * * 1147 * WARNINGS: none * 1148 * * 1149 * HISTORY: * 1150 * 05/27/1994 JLB : Created. * 1151 *=============================================================================================*/ 1152 CELL DisplayClass::Click_Cell_Calc(int x, int y) const 1153 { 1154 x -= TacPixelX; 1155 x = Pixel_To_Lepton(x); 1156 y -= TacPixelY; 1157 y = Pixel_To_Lepton(y); 1158 1159 // Possibly ignore the view constraints if we aren't using the internal renderer. ST - 8/5/2019 11:56AM 1160 if (IgnoreViewConstraints || (unsigned)x < TacLeptonWidth && (unsigned)y < TacLeptonHeight) { 1161 COORDINATE tcoord = XY_Coord(Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))), Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord)))); 1162 return(Coord_Cell(Coord_Add(tcoord, XY_Coord(x, y)))); 1163 } 1164 return(-1); 1165 } 1166 1167 1168 /*********************************************************************************************** 1169 * DisplayClass::Scroll_Map -- Scroll the tactical map in desired direction. * 1170 * * 1171 * This routine is used to scroll the tactical map view in the desired * 1172 * direction. It can also be used to determine if scrolling would be * 1173 * legal without actually performing any scrolling action. * 1174 * * 1175 * INPUT: facing -- The direction to scroll the tactical map. * 1176 * * 1177 * distance -- The distance in leptons to scroll the map. * 1178 * * 1179 * really -- Should the map actually be scrolled? If false, * 1180 * then only the legality of a scroll is checked. * 1181 * * 1182 * OUTPUT: bool; Would scrolling in the desired direction be possible? * 1183 * * 1184 * WARNINGS: none * 1185 * * 1186 * HISTORY: * 1187 * 10/07/1992 JLB : Created. * 1188 * 05/20/1994 JLB : Converted to member function. * 1189 * 08/09/1995 JLB : Added distance parameter. * 1190 * 08/10/1995 JLB : Any direction scrolling. * 1191 *=============================================================================================*/ 1192 bool DisplayClass::Scroll_Map(DirType facing, int & distance, bool really) 1193 { 1194 /* 1195 ** If the distance is invalid then no further checking is required. Bail 1196 ** with a no-can-do flag. 1197 */ 1198 if (distance == 0) return(false); 1199 FacingType crude = Dir_Facing(facing); 1200 1201 if (Coord_X(TacticalCoord) == Cell_To_Lepton(MapCellX) && crude != FACING_W) { 1202 if (crude == FACING_SW) facing = DIR_S; 1203 if (crude == FACING_NW) facing = DIR_N; 1204 } 1205 if (Coord_Y(TacticalCoord) == Cell_To_Lepton(MapCellY) && crude != FACING_N) { 1206 if (crude == FACING_NW) facing = DIR_W; 1207 if (crude == FACING_NE) facing = DIR_E; 1208 } 1209 if (Coord_X(TacticalCoord) + TacLeptonWidth == Cell_To_Lepton(MapCellX+MapCellWidth) && crude != FACING_E) { 1210 if (crude == FACING_NE) facing = DIR_N; 1211 if (crude == FACING_SE) facing = DIR_S; 1212 } 1213 if (Coord_Y(TacticalCoord) + TacLeptonHeight == Cell_To_Lepton(MapCellY+MapCellHeight) && crude != FACING_S) { 1214 if (crude == FACING_SE) facing = DIR_E; 1215 if (crude == FACING_SW) facing = DIR_W; 1216 } 1217 1218 /* 1219 ** Determine the coordinate that it wants to scroll to. 1220 */ 1221 COORDINATE coord = Coord_Move(TacticalCoord, facing, distance); 1222 1223 /* 1224 ** Clip the new coordinate to the edges of the game world. 1225 */ 1226 int xx = (int)(short)Coord_X(coord) - (short)Cell_To_Lepton(MapCellX); 1227 int yy = (int)(short)Coord_Y(coord) - (short)Cell_To_Lepton(MapCellY); 1228 bool shifted = Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth), Cell_To_Lepton(MapCellHeight)); 1229 if (xx < 0) { 1230 xx = 0; 1231 shifted = true; 1232 } 1233 if (yy < 0) { 1234 yy = 0; 1235 shifted = true; 1236 } 1237 coord = XY_Coord(xx + Cell_To_Lepton(MapCellX), yy + Cell_To_Lepton(MapCellY)); 1238 1239 /* 1240 ** If the desired scroll was bound by the edge of the map, then adjust the distance to more accurately 1241 ** reflect the actual distance moved. 1242 */ 1243 if (shifted) { 1244 distance = Distance(TacticalCoord, coord); 1245 } 1246 1247 /* 1248 ** If the new coordinate is the same as the old, then no scrolling would occur. 1249 */ 1250 if (!distance || coord == TacticalCoord) return(false); 1251 1252 /* 1253 ** Since the new coordinate is different than the old one, possibly adjust the real 1254 ** tactical map accordingly. 1255 */ 1256 if (really) { 1257 Set_Tactical_Position(coord); 1258 IsToRedraw = true; 1259 Flag_To_Redraw(false); 1260 1261 /* 1262 ** Scrolled map REQUIRES all top layer units to be redrawn. 1263 */ 1264 int index; 1265 for (index = 0; index < Layer[LAYER_TOP].Count(); index++) { 1266 Layer[LAYER_TOP][index]->Mark(MARK_CHANGE); 1267 } 1268 for (index = 0; index < Layer[LAYER_AIR].Count(); index++) { 1269 Layer[LAYER_AIR][index]->Mark(MARK_CHANGE); 1270 } 1271 1272 1273 } 1274 return(true); 1275 } 1276 1277 1278 /*********************************************************************************************** 1279 * DisplayClass::Refresh_Cells -- Redraws all cells in list. * 1280 * * 1281 * This routine is used to flag all cells in the specified list for * 1282 * redrawing. * 1283 * * 1284 * INPUT: cell -- The origin cell that the list is offset from. * 1285 * * 1286 * list -- Pointer to a list of offsets from the origin cell. * 1287 * Each cell so specified is flagged for redraw. * 1288 * * 1289 * OUTPUT: none * 1290 * * 1291 * WARNINGS: This routine is rather slow (by definition). * 1292 * * 1293 * HISTORY: * 1294 * 05/14/1994 JLB : Created. * 1295 * 08/01/1994 JLB : Simplified. * 1296 *=============================================================================================*/ 1297 void DisplayClass::Refresh_Cells(CELL cell, short const * list) 1298 { 1299 short tlist[36]; 1300 1301 if (*list == REFRESH_SIDEBAR) { 1302 list++; 1303 } 1304 1305 List_Copy(list, ARRAY_SIZE(tlist), tlist); 1306 short * tt = tlist; 1307 int count = 0; 1308 while (*tt != REFRESH_EOL) { 1309 if (count >= ARRAY_SIZE(tlist)) { // Added overrun check. ST - 8/14/2019 3:14PM 1310 break; 1311 } 1312 CELL newcell = cell + *tt++; 1313 if (In_Radar(newcell)) { 1314 (*this)[newcell].Redraw_Objects(); 1315 } 1316 count++; 1317 } 1318 } 1319 1320 1321 /*********************************************************************************************** 1322 * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * 1323 * * 1324 * This routine will examine the specified cell and adjacent cells to * 1325 * determine what shadow icon to use. * 1326 * * 1327 * INPUT: cell -- The cell to examine. * 1328 * * 1329 * OUTPUT: Returns with the shadow icon to use. -2= all black. * 1330 * -1= map cell. * 1331 * * 1332 * WARNINGS: none * 1333 * * 1334 * HISTORY: * 1335 * 03/01/1994 JLB : Created. * 1336 * 04/04/1994 JLB : Revamped for new shadow icon method. * 1337 * 04/30/1994 JLB : Converted to member function. * 1338 * 08/05/2019 ST : Added house parameter so we can do this per player ** 1339 *=============================================================================================*/ 1340 int DisplayClass::Cell_Shadow(CELL cell, HouseClass *house) const 1341 { 1342 static char const _shadow[256]={ 1343 -1,33, 2, 2,34,37, 2, 2, 1344 4,26, 6, 6, 4,26, 6, 6, 1345 35,45,17,17,38,41,17,17, 1346 4,26, 6, 6, 4,26, 6, 6, 1347 8,21,10,10,27,31,10,10, 1348 12,23,14,14,12,23,14,14, 1349 8,21,10,10,27,31,10,10, 1350 12,23,14,14,12,23,14,14, 1351 1352 32,36,25,25,44,40,25,25, 1353 19,30,20,20,19,30,20,20, 1354 39,43,29,29,42,46,29,29, 1355 19,30,20,20,19,30,20,20, 1356 8,21,10,10,27,31,10,10, 1357 12,23,14,14,12,23,14,14, 1358 8,21,10,10,27,31,10,10, 1359 12,23,14,14,12,23,14,14, 1360 1361 1, 1, 3, 3,16,16, 3, 3, 1362 5, 5, 7, 7, 5, 5, 7, 7, 1363 24,24,18,18,28,28,18,18, 1364 5, 5, 7, 7, 5, 5, 7, 7, 1365 9, 9,11,11,22,22,11,11, 1366 13,13,-2,-2,13,13,-2,-2, 1367 9, 9,11,11,22,22,11,11, 1368 13,13,-2,-2,13,13,-2,-2, 1369 1370 1, 1, 3, 3,16,16, 3, 3, 1371 5, 5, 7, 7, 5, 5, 7, 7, 1372 24,24,18,18,28,28,18,18, 1373 5, 5, 7, 7, 5, 5, 7, 7, 1374 9, 9,11,11,22,22,11,11, 1375 13,13,-2,-2,13,13,-2,-2, 1376 9, 9,11,11,22,22,11,11, 1377 13,13,-2,-2,13,13,-2,-2 1378 }; 1379 1380 int index = 0, value = -1; 1381 1382 /* 1383 ** Don't map cells that are at the edges. This solves 1384 ** problem of accessing cells off the bounds of map and into 1385 ** who-knows-what memory. 1386 */ 1387 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1388 if ((unsigned)(Cell_X(cell)-1) >= MAP_CELL_W-2) return(-1); 1389 if ((unsigned)(Cell_Y(cell)-1) >= MAP_CELL_H-2) return(-1); 1390 #else 1391 if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-1); 1392 #endif 1393 //if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-2); 1394 1395 CellClass const * cellptr = &(*this)[cell]; 1396 1397 /* 1398 ** Presume solid black if that is what is here already. 1399 */ 1400 if (!cellptr->Is_Visible(house) && !cellptr->Is_Mapped(house)) value = -2; 1401 1402 if (cellptr->Is_Mapped(house) /*&& !cellptr->IsVisible*/) { 1403 /* 1404 ** Build an index into the lookup table using all 8 surrounding cells. 1405 ** We're mapping a revealed cell and we only care about the existence 1406 ** of black cells. Bit numbering starts at the upper-right corner and 1407 ** goes around the cell clockwise, so 0x80 = directly north. 1408 */ 1409 cell -= MAP_CELL_W + 1; cellptr -= MAP_CELL_W + 1; 1410 if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x40; 1411 cell++; cellptr++; 1412 if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x80; 1413 cell++; cellptr++; 1414 if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x01; 1415 cell += MAP_CELL_W - 2; cellptr += MAP_CELL_W - 2; 1416 if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x20; 1417 cell += 2; cellptr += 2; 1418 if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x02; 1419 cell += MAP_CELL_W - 2; cellptr += MAP_CELL_W - 2; 1420 if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x10; 1421 cell++; cellptr++; 1422 if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x08; 1423 cell++; cellptr++; 1424 if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x04; 1425 1426 value = _shadow[index]; 1427 } 1428 return(value); 1429 } 1430 1431 1432 #if (0) // Old code for reference. ST - 8/15/2019 10:25AM 1433 /*********************************************************************************************** 1434 * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * 1435 * * 1436 * This routine will examine the specified cell and adjacent cells to * 1437 * determine what shadow icon to use. * 1438 * * 1439 * INPUT: cell -- The cell to examine. * 1440 * * 1441 * OUTPUT: Returns with the shadow icon to use. -2= all black. * 1442 * -1= map cell. * 1443 * * 1444 * WARNINGS: none * 1445 * * 1446 * HISTORY: * 1447 * 03/01/1994 JLB : Created. * 1448 * 04/04/1994 JLB : Revamped for new shadow icon method. * 1449 * 04/30/1994 JLB : Converted to member function. * 1450 *=============================================================================================*/ 1451 int DisplayClass::Cell_Shadow(CELL cell) const 1452 { 1453 static char const _shadow[256]={ 1454 -1,33, 2, 2,34,37, 2, 2, 1455 4,26, 6, 6, 4,26, 6, 6, 1456 35,45,17,17,38,41,17,17, 1457 4,26, 6, 6, 4,26, 6, 6, 1458 8,21,10,10,27,31,10,10, 1459 12,23,14,14,12,23,14,14, 1460 8,21,10,10,27,31,10,10, 1461 12,23,14,14,12,23,14,14, 1462 1463 32,36,25,25,44,40,25,25, 1464 19,30,20,20,19,30,20,20, 1465 39,43,29,29,42,46,29,29, 1466 19,30,20,20,19,30,20,20, 1467 8,21,10,10,27,31,10,10, 1468 12,23,14,14,12,23,14,14, 1469 8,21,10,10,27,31,10,10, 1470 12,23,14,14,12,23,14,14, 1471 1472 1, 1, 3, 3,16,16, 3, 3, 1473 5, 5, 7, 7, 5, 5, 7, 7, 1474 24,24,18,18,28,28,18,18, 1475 5, 5, 7, 7, 5, 5, 7, 7, 1476 9, 9,11,11,22,22,11,11, 1477 13,13,-2,-2,13,13,-2,-2, 1478 9, 9,11,11,22,22,11,11, 1479 13,13,-2,-2,13,13,-2,-2, 1480 1481 1, 1, 3, 3,16,16, 3, 3, 1482 5, 5, 7, 7, 5, 5, 7, 7, 1483 24,24,18,18,28,28,18,18, 1484 5, 5, 7, 7, 5, 5, 7, 7, 1485 9, 9,11,11,22,22,11,11, 1486 13,13,-2,-2,13,13,-2,-2, 1487 9, 9,11,11,22,22,11,11, 1488 13,13,-2,-2,13,13,-2,-2 1489 }; 1490 1491 int index = 0, value = -1; 1492 1493 /* 1494 ** Don't map cells that are at the top or bottom edge. This solves 1495 ** problem of accessing cells off the top or bottom of the map and into 1496 ** who-knows-what memory. 1497 */ 1498 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1499 if ((unsigned)(Cell_Y(cell)-1) >= MAP_CELL_H-2) return(-1); 1500 #else 1501 if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-1); 1502 #endif 1503 //if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-2); 1504 1505 CellClass const * cellptr = &(*this)[cell]; 1506 1507 /* 1508 ** Presume solid black if that is what is here already. 1509 */ 1510 if (!cellptr->IsVisible && !cellptr->IsMapped) value = -2; 1511 1512 if (cellptr->IsMapped /*&& !cellptr->IsVisible*/) { 1513 /* 1514 ** Build an index into the lookup table using all 8 surrounding cells. 1515 ** We're mapping a revealed cell and we only care about the existence 1516 ** of black cells. Bit numbering starts at the upper-right corner and 1517 ** goes around the cell clockwise, so 0x80 = directly north. 1518 */ 1519 cellptr-= MAP_CELL_W + 1; 1520 if (!cellptr->IsMapped) index |= 0x40; 1521 cellptr++; 1522 if (!cellptr->IsMapped) index |= 0x80; 1523 cellptr++; 1524 if (!cellptr->IsMapped) index |= 0x01; 1525 cellptr += MAP_CELL_W - 2; 1526 if (!cellptr->IsMapped) index |= 0x20; 1527 cellptr += 2; 1528 if (!cellptr->IsMapped) index |= 0x02; 1529 cellptr += MAP_CELL_W - 2; 1530 if (!cellptr->IsMapped) index |= 0x10; 1531 cellptr++; 1532 if (!cellptr->IsMapped) index |= 0x08; 1533 cellptr++; 1534 if (!cellptr->IsMapped) index |= 0x04; 1535 1536 value = _shadow[index]; 1537 } 1538 return(value); 1539 } 1540 #endif 1541 1542 /*********************************************************************************************** 1543 * DisplayClass::Map_Cell -- Mark specified cell as having been mapped. * 1544 * * 1545 * This routine maps the specified cell. The cell must not already * 1546 * have been mapped and the mapping player must be the human. * 1547 * This routine will update any adjacent cell map icon as appropriate. * 1548 * * 1549 * INPUT: cell -- The cell to be mapped. * 1550 * * 1551 * house -- The player that is doing the mapping. * 1552 * * 1553 * OUTPUT: bool; Was action taken to map this cell? * 1554 * * 1555 * WARNINGS: none. * 1556 * * 1557 * HISTORY: * 1558 * 08/05/1992 JLB : Created. * 1559 * 04/30/1994 JLB : Converted to member function. * 1560 * 05/24/1994 JLB : Takes pointer to HouseClass. * 1561 * 02/20/1996 JLB : Allied units reveal the map for the player. * 1562 * 08/05/2019 ST : Use per-player mapping so we can track the shroud for all players * 1563 *=============================================================================================*/ 1564 bool DisplayClass::Map_Cell(CELL cell, HouseClass * house, bool check_radar_spied, bool and_for_allies) 1565 { 1566 // OK for house not to be PlayerPtr. ST - 8/6/2019 10:05AM 1567 //if (house != PlayerPtr || !In_Radar(cell)) return(false); 1568 if (house == NULL || !In_Radar(cell)) return(false); 1569 1570 if (!house->IsHuman) { 1571 if (!ShareAllyVisibility || !and_for_allies && !check_radar_spied) { 1572 return false; 1573 } 1574 } 1575 1576 /* 1577 ** First check for the condition where we're spying on a house's radar 1578 ** facility, to see if his mapping is applicable to us. 1579 */ 1580 if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { 1581 // Original code. ST - 8/15/2019 10:26AM 1582 if (house && house != PlayerPtr) { 1583 if (house->RadarSpied & (1<<(PlayerPtr->Class->House))) house = PlayerPtr; 1584 if (Session.Type == GAME_NORMAL && house->Is_Ally(PlayerPtr)) house = PlayerPtr; 1585 } 1586 } else { 1587 // Version to work with any human player, not just PlayerPtr 1588 if (house && check_radar_spied) { 1589 1590 for (int i=0 ; i<Session.Players.Count() ; i++) { 1591 HouseClass *player_ptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); 1592 if (player_ptr->IsHuman) { 1593 if (house->RadarSpied & (1<<(player_ptr->Class->House))) { 1594 Map_Cell(cell, player_ptr, false, false); 1595 } 1596 } 1597 } 1598 } 1599 } 1600 1601 /* 1602 ** Maybe also recurse to map for allies 1603 */ 1604 if (ShareAllyVisibility && and_for_allies && Session.Type == GAME_GLYPHX_MULTIPLAYER) { 1605 1606 for (int i=0 ; i<Session.Players.Count() ; i++) { 1607 HouseClass *player_ptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); 1608 if (player_ptr && player_ptr->IsActive && player_ptr->IsHuman) { 1609 if (player_ptr != house && house->Is_Ally(player_ptr)) { 1610 Map_Cell(cell, player_ptr, check_radar_spied, false); 1611 } 1612 } 1613 } 1614 } 1615 1616 CellClass * cellptr = &(*this)[cell]; 1617 1618 /* 1619 ** Don't bother remapping this cell if it is already mapped. 1620 */ 1621 #if (1) 1622 if (cellptr->Is_Mapped(house)) { 1623 if (!cellptr->Is_Visible(house)) { 1624 cellptr->Redraw_Objects(); 1625 } 1626 return(false); 1627 } 1628 #else 1629 if (cellptr->IsMapped) { 1630 if (!cellptr->IsVisible) { 1631 cellptr->Redraw_Objects(); 1632 } 1633 return(false); 1634 } 1635 #endif 1636 /* 1637 ** Mark the cell as being mapped. This must be done first because 1638 ** if the IsVisible flag must be set, then it might affect the 1639 ** adjacent cell processing. 1640 */ 1641 // Set per player. ST - 8/6/2019 10:18AM 1642 cellptr->Set_Mapped(house); 1643 cellptr->Redraw_Objects(); 1644 if (Cell_Shadow(cell, house) == -1) { 1645 cellptr->Set_Visible(house); 1646 } 1647 1648 /* 1649 ** Check out all adjacent cells to see if they need 1650 ** to be mapped as well. This is necessary because of the 1651 ** "unique" method of showing shadowed cells. Many combinations 1652 ** are not allowed, and to fix this, just map the cells until 1653 ** all is ok. 1654 */ 1655 int xx = Cell_X(cell); 1656 for (FacingType dir = FACING_FIRST; dir < FACING_COUNT; dir++) { 1657 int shadow; 1658 CELL c; 1659 int xdiff; 1660 1661 c = Adjacent_Cell(cell, dir); 1662 1663 /* 1664 ** Determine if the map edge has been wrapped. If so, 1665 ** then don't process the cell. 1666 */ 1667 if ((unsigned)c >= MAP_CELL_TOTAL) continue; 1668 xdiff = Cell_X(c) - xx; 1669 xdiff = ABS(xdiff); 1670 if (xdiff > 1) continue; 1671 1672 CellClass * cptr = &(*this)[c]; 1673 cptr->Redraw_Objects(); 1674 1675 #if (1) 1676 // New client/server friendly code 1677 if (c != cell && !cptr->Is_Visible(house)) { 1678 shadow = Cell_Shadow(c, house); 1679 1680 if (shadow == -1) { 1681 if (!cptr->Is_Mapped(house)) { 1682 Map_Cell(c, house, check_radar_spied, false); 1683 } else { 1684 cptr->Set_Visible(house); 1685 } 1686 } else { 1687 if (shadow != -2 && !cptr->Is_Mapped(house)) { 1688 Map_Cell(c, house, check_radar_spied, false); 1689 } 1690 } 1691 } 1692 #else 1693 // Old peer/peer code 1694 if (c != cell && !cptr->IsVisible) { 1695 shadow = Cell_Shadow(c); 1696 1697 if (shadow == -1) { 1698 if (!cptr->IsMapped) { 1699 Map_Cell(c, house); 1700 } else { 1701 cptr->IsVisible = true; 1702 } 1703 } else { 1704 if (shadow != -2 && !cptr->IsMapped) { 1705 Map_Cell(c, house); 1706 } 1707 } 1708 } 1709 #endif 1710 } 1711 1712 TechnoClass * tech = (*this)[cell].Cell_Techno(); 1713 if (tech) { 1714 tech->Revealed(house); 1715 } 1716 return(true); 1717 } 1718 1719 1720 /*********************************************************************************************** 1721 * DisplayClass::Coord_To_Pixel -- Determines X and Y pixel coordinates. * 1722 * * 1723 * This is the routine that figures out the location on the screen for * 1724 * a specified coordinate. It is one of the fundamental routines * 1725 * necessary for rendering the game objects. It performs some quick * 1726 * tests to see if the coordinate is in a visible region and returns * 1727 * this check as a boolean value. * 1728 * * 1729 * INPUT: coord -- The coordinate to check. * 1730 * * 1731 * x,y -- Reference to the pixel coordinates that this * 1732 * coordinate would be when rendered. * 1733 * * 1734 * OUTPUT: bool; Is this coordinate in a visible portion of the map? * 1735 * * 1736 * WARNINGS: If the coordinate is not in a visible portion of the * 1737 * map, then this X and Y parameters are not set. * 1738 * * 1739 * HISTORY: * 1740 * 05/14/1994 JLB : Created. * 1741 * 12/15/1994 JLB : Converted to member function. * 1742 * 01/07/1995 JLB : Uses inline functions to extract coord components. * 1743 * 08/09/1995 JLB : Uses new coordinate system. * 1744 *=============================================================================================*/ 1745 #define EDGE_ZONE (CELL_LEPTON_W*2) 1746 bool DisplayClass::Coord_To_Pixel(COORDINATE coord, int &x, int &y) const 1747 { 1748 int xtac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))); 1749 int xoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(coord))); 1750 xoff = (xoff + EDGE_ZONE) - xtac; 1751 1752 int ytac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord))); 1753 int yoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(coord))); 1754 yoff = (yoff + EDGE_ZONE) - ytac; 1755 1756 x = Lepton_To_Pixel(xoff) - CELL_PIXEL_W * 2; 1757 y = Lepton_To_Pixel(yoff) - CELL_PIXEL_H * 2; 1758 1759 // Possibly ignore the view constraints if we aren't using the internal renderer. ST - 4/17/2019 9:06AM 1760 return(coord && (IgnoreViewConstraints || ((xoff <= TacLeptonWidth + EDGE_ZONE * 2) && (yoff <= TacLeptonHeight + EDGE_ZONE * 2)))); 1761 } 1762 1763 #if (0) //reference. ST - 5/8/2019 1764 bool DisplayClass::Coord_To_Pixel(COORDINATE coord, int &x, int &y) const 1765 { 1766 if (coord) { 1767 int xtac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))); 1768 int xoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(coord))); 1769 1770 xoff = (xoff+EDGE_ZONE) - xtac; 1771 if ((unsigned)xoff <= TacLeptonWidth + EDGE_ZONE*2) { 1772 int ytac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord))); 1773 int yoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(coord))); 1774 1775 yoff = (yoff+EDGE_ZONE) - ytac; 1776 if ((unsigned)yoff <= TacLeptonHeight + EDGE_ZONE*2) { 1777 x = Lepton_To_Pixel(xoff)-CELL_PIXEL_W*2; 1778 y = Lepton_To_Pixel(yoff)-CELL_PIXEL_H*2; 1779 return(true); 1780 } 1781 } 1782 } 1783 return(false); 1784 } 1785 #endif 1786 1787 /*********************************************************************************************** 1788 * DisplayClass::Push_Onto_TacMap -- Moves x & y coords to being on tactical map * 1789 * * 1790 * This routine expects a line to be drawn between SOURCE & DEST, so it pushes the coords to * 1791 * be within the region bounded by TacMapX,Y - + TacMapW,H. * 1792 * * 1793 * INPUT: source, dest -- References to the coordinates to check. * 1794 * * 1795 * * 1796 * OUTPUT: bool; Are these coordinates in a visible portion of the map? * 1797 * Returns true if the pushed source & dest are visible, but if neither are * 1798 * within the map, then it returns false. * 1799 * * 1800 * * 1801 * HISTORY: * 1802 * 03/27/1995 BWG : Created. * 1803 *=============================================================================================*/ 1804 bool DisplayClass::Push_Onto_TacMap(COORDINATE & source, COORDINATE & dest) 1805 { 1806 if (!source || !dest) return(false); 1807 1808 int x1 = Coord_X(source); 1809 int y1 = Coord_Y(source); 1810 int x2 = Coord_X(dest); 1811 int y2 = Coord_Y(dest); 1812 int left = Coord_X(TacticalCoord); 1813 int right = Coord_X(TacticalCoord) + TacLeptonWidth; 1814 int top = Coord_Y(TacticalCoord); 1815 int bottom = Coord_Y(TacticalCoord) + TacLeptonHeight; 1816 1817 if (x1 < left && x2 < left) return(false); 1818 if (x1 > right && x2 > right) return(false); 1819 if (y1 < top && y2 < top) return(false); 1820 if (y1 > bottom && y2 > bottom) return(false); 1821 1822 x1 = Bound(x1, left, right); 1823 x2 = Bound(x2, left, right); 1824 y1 = Bound(y1, top, bottom); 1825 y2 = Bound(y2, top, bottom); 1826 1827 source = XY_Coord(x1, y1); 1828 dest = XY_Coord(x2, y2); 1829 return(true); 1830 } 1831 1832 1833 /*********************************************************************************************** 1834 * DisplayClass::Cell_Object -- Determines what has been clicked on. * 1835 * * 1836 * This routine is used to determine what the player has clicked on. * 1837 * It is passed the cell that the click was on and it then examines * 1838 * the cell and returns with a pointer to the object that is there. * 1839 * * 1840 * INPUT: cell -- The cell that has been clicked upon. * 1841 * * 1842 * x,y -- Optional offsets from the upper left corner of the cell to be used in * 1843 * determining exactly which object in the cell is desired. * 1844 * * 1845 * OUTPUT: Returns with a pointer to the object that is "clickable" in * 1846 * the specified cell. * 1847 * * 1848 * WARNINGS: none * 1849 * * 1850 * HISTORY: * 1851 * 05/14/1994 JLB : Created. * 1852 *=============================================================================================*/ 1853 ObjectClass * DisplayClass::Cell_Object(CELL cell, int x, int y) const 1854 { 1855 return(*this)[cell].Cell_Object(x, y); 1856 } 1857 1858 1859 /*********************************************************************************************** 1860 * DisplayClass::Draw_It -- Draws the tactical map. * 1861 * * 1862 * This will draw the tactical map at the recorded position. This * 1863 * routine is used whenever the tactical map moves or needs to be * 1864 * completely redrawn. It will handle making the necessary adjustments * 1865 * to accomodate a moving cursor. * 1866 * * 1867 * INPUT: forced -- bool; force redraw of the entire display? * 1868 * * 1869 * OUTPUT: none * 1870 * * 1871 * WARNINGS: none * 1872 * * 1873 * HISTORY: * 1874 * 04/15/1991 JLB : Created. (benchmark = 292) * 1875 * 04/15/1991 JLB : Added _cell2meta[] reference array (206) * 1876 * 04/15/1991 JLB : Added actual map reference for terrain (207) * 1877 * 04/16/1991 JLB : _cell2meta converted to int (194) * 1878 * 04/16/1991 JLB : References actual CellIcon[] array (204) * 1879 * 04/16/1991 JLB : Cell size increased to 16 x 16 (167) * 1880 * 04/17/1991 JLB : Cell based tactical map rendering (165) * 1881 * 04/22/1991 JLB : Uses Draw_Stamp() for icon rendering (426) * 1882 * 04/22/1991 JLB : Draw_Stamp uses LogicPage now (276) * 1883 * 04/23/1991 JLB : Map active location cursor (334) * 1884 * 05/02/1991 JLB : Added smoothing and 3 icons sets (431) * 1885 * 05/22/1991 JLB : Broken into Draw_Map() and Refresh_Map(). * 1886 * 09/14/1991 JLB : Uses Refresh_Cell when new cells scroll onto display. * 1887 * 05/12/1992 JLB : Destination page support. * 1888 * 02/14/1994 JLB : Revamped. * 1889 * 05/01/1994 JLB : Converted to member function. * 1890 * 12/15/1994 JLB : Updated to work with display hierarchy. * 1891 * 12/24/1994 JLB : Examines redraw bit intelligently. * 1892 * 12/24/1994 JLB : Combined with old Refresh_Map() function. * 1893 * 01/10/1995 JLB : Rubber band drawing. * 1894 *=============================================================================================*/ 1895 void DisplayClass::Draw_It(bool forced) 1896 { 1897 int x,y; // Working cell index values. 1898 1899 MapClass::Draw_It(forced); 1900 1901 if (IsToRedraw || forced) { 1902 BStart(BENCH_TACTICAL); 1903 IsToRedraw = false; 1904 1905 /* 1906 ** In rubber band mode, mark all cells under the "rubber band" to be 1907 ** redrawn. 1908 */ 1909 Refresh_Band(); 1910 1911 /* 1912 ** Mark all cells under the vortex to be redrawn 1913 */ 1914 ChronalVortex.Set_Redraw(); 1915 1916 1917 /* 1918 ** If the multiplayer message system is displaying one or more messages, 1919 ** flag all cells covered by the messages to redraw. This will prevent 1920 ** messages from smearing the map if it scrolls. 1921 */ 1922 int num = Session.Messages.Num_Messages(); 1923 if (num > 0) { 1924 CELL cell; 1925 for (cell = Coord_Cell(TacticalCoord); cell < Coord_Cell(TacticalCoord) + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { 1926 (*this)[cell].Redraw_Objects(); 1927 } 1928 for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W; 1929 cell < Coord_Cell(TacticalCoord) + MAP_CELL_W + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { 1930 (*this)[cell].Redraw_Objects(); 1931 } 1932 if (num > 1) { 1933 for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*2; 1934 cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*2 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { 1935 (*this)[cell].Redraw_Objects(); 1936 } 1937 } 1938 if (num > 2) { 1939 for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*3; 1940 cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*3 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { 1941 (*this)[cell].Redraw_Objects(); 1942 } 1943 } 1944 if (num > 3) { 1945 for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*4; 1946 cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*4 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { 1947 (*this)[cell].Redraw_Objects(); 1948 } 1949 } 1950 } 1951 1952 /* 1953 ** Check for a movement of the tactical map. If there has been some 1954 ** movement, then part (or all) of the icons must be redrawn. 1955 */ 1956 if (Lepton_To_Pixel(Coord_X(DesiredTacticalCoord)) != Lepton_To_Pixel(Coord_X(TacticalCoord)) || 1957 Lepton_To_Pixel(Coord_Y(DesiredTacticalCoord)) != Lepton_To_Pixel(Coord_Y(TacticalCoord))) { 1958 1959 int xmod = Lepton_To_Pixel(Coord_X(DesiredTacticalCoord)); 1960 int ymod = Lepton_To_Pixel(Coord_Y(DesiredTacticalCoord)); 1961 1962 int oldx = Lepton_To_Pixel(Coord_X(TacticalCoord))-xmod; // Old relative offset. 1963 int oldy = Lepton_To_Pixel(Coord_Y(TacticalCoord))-ymod; 1964 1965 int oldw = Lepton_To_Pixel(TacLeptonWidth)-ABS(oldx); // Replicable width. 1966 int oldh = Lepton_To_Pixel(TacLeptonHeight)-ABS(oldy); // Replicable height. 1967 1968 if (oldw < 1) forced = true; 1969 if (oldh < 1) forced = true; 1970 1971 1972 #ifdef WIN32 //For WIN32 only redraw the edges of the map that move into view 1973 1974 /* 1975 ** Work out which map edges need to be redrawn 1976 */ 1977 BOOL redraw_right = (oldx < 0) ? true : false; //Right hand edge 1978 BOOL redraw_left = (oldx > 0) ? true : false; //Left hand edge 1979 BOOL redraw_bottom= (oldy < 0) ? true : false; //Bottom edge 1980 BOOL redraw_top = (oldy > 0) ? true : false; //Top edge 1981 1982 /* 1983 ** Blit any replicable block to avoid having to drawstamp. 1984 */ 1985 if (!forced && (oldw != Lepton_To_Pixel(TacLeptonWidth) || oldh != Lepton_To_Pixel(TacLeptonHeight))) { 1986 Set_Cursor_Pos(-1); 1987 1988 /* 1989 ** If hid page is in video memory then blit from the seen page to avoid blitting 1990 ** an overlapped region. 1991 */ 1992 if (HidPage.Get_IsDirectDraw()) { 1993 Hide_Mouse(); 1994 SeenBuff.Blit(HidPage, 1995 ((oldx < 0) ? -oldx : 0) +TacPixelX, 1996 ((oldy < 0) ? -oldy : 0) +TacPixelY, 1997 ((oldx < 0) ? 0 : oldx) +TacPixelX, 1998 ((oldy < 0) ? 0 : oldy) +TacPixelY, 1999 oldw, 2000 oldh); 2001 Show_Mouse(); 2002 } else { 2003 HidPage.Blit(HidPage, 2004 ((oldx < 0) ? -oldx : 0) +TacPixelX, 2005 ((oldy < 0) ? -oldy : 0) +TacPixelY, 2006 ((oldx < 0) ? 0 : oldx) +TacPixelX, 2007 ((oldy < 0) ? 0 : oldy) +TacPixelY, 2008 oldw, 2009 oldh); 2010 } 2011 2012 } else { 2013 forced = true; 2014 } 2015 2016 if (oldx < 0) oldx = 0; 2017 if (oldy < 0) oldy = 0; 2018 2019 /* 2020 ** Record new map position for future reference. 2021 */ 2022 ScenarioInit++; 2023 Set_Tactical_Position(DesiredTacticalCoord); 2024 ScenarioInit--; 2025 2026 if (!forced) { 2027 2028 /* 2029 ** 2030 ** Set the 'redraw stamp' bit for any cells that could not be copied. 2031 ** 2032 */ 2033 int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); 2034 int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); 2035 oldw -= 24; 2036 oldh -= 24; 2037 2038 if (abs(oldx) < 0x25 && abs(oldy) < 0x25) { 2039 2040 /* 2041 ** The width of the area we redraw depends on the scroll speed 2042 */ 2043 int extra_x = (abs(oldx)>=16) ? 2 : 1; 2044 int extra_y = (abs(oldy)>=16) ? 2 : 1; 2045 2046 /* 2047 ** Flag the cells across the top of the visible area if required 2048 */ 2049 if (redraw_top) { 2050 for (y = starty; y <= starty+CELL_PIXEL_H*extra_y; y += CELL_PIXEL_H) { 2051 for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { 2052 CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, 2053 Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); 2054 2055 if (c > 0) (*this)[c].Redraw_Objects(true); 2056 } 2057 } 2058 } 2059 2060 /* 2061 ** Flag the cells across the bottom of the visible area if required 2062 */ 2063 if (redraw_bottom) { 2064 for (y = Lepton_To_Pixel(TacLeptonHeight)-CELL_PIXEL_H*(1+extra_y); y <= Lepton_To_Pixel(TacLeptonHeight)+CELL_PIXEL_H*3; y += CELL_PIXEL_H) { 2065 for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { 2066 CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, 2067 Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); 2068 2069 if (c > 0) (*this)[c].Redraw_Objects(true); 2070 } 2071 } 2072 } 2073 2074 /* 2075 ** Flag the cells down the left of the visible area if required 2076 */ 2077 if (redraw_left) { 2078 for (x = startx; x <= startx + CELL_PIXEL_W*extra_x; x += CELL_PIXEL_W) { 2079 for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { 2080 CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, 2081 Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); 2082 2083 if (c > 0) (*this)[c].Redraw_Objects(true); 2084 } 2085 } 2086 } 2087 2088 /* 2089 ** Flag the cells down the right of the visible area if required 2090 */ 2091 if (redraw_right) { 2092 for (x = Lepton_To_Pixel(TacLeptonWidth)-CELL_PIXEL_W*(extra_x+1); x <= Lepton_To_Pixel(TacLeptonWidth)+CELL_PIXEL_W*3; x += CELL_PIXEL_W) { 2093 for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { 2094 CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, 2095 Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); 2096 2097 if (c > 0) (*this)[c].Redraw_Objects(true); 2098 } 2099 } 2100 } 2101 2102 } else { 2103 2104 /* 2105 ** Set the 'redraw stamp' bit for any cells that could not be copied. 2106 */ 2107 int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); 2108 int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); 2109 oldw -= 24; 2110 oldh -= 24; 2111 for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { 2112 for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { 2113 if (x <= oldx || x >= oldx+oldw || y <= oldy || y >= oldy+oldh) { 2114 CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, 2115 Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); 2116 2117 if (c > 0) { 2118 (*this)[c].Redraw_Objects(true); 2119 } 2120 } 2121 } 2122 } 2123 } 2124 } 2125 2126 } else { 2127 2128 /* 2129 ** Set the tactical coordinate just in case the desired tactical has changed but 2130 ** not enough to result in any visible map change. This is likely to occur with very 2131 ** slow scroll rates. 2132 */ 2133 ScenarioInit++; 2134 if (DesiredTacticalCoord != TacticalCoord) { 2135 Set_Tactical_Position(DesiredTacticalCoord); 2136 } 2137 ScenarioInit--; 2138 } 2139 2140 2141 #else //WIN32 2142 /* 2143 ** Blit any replicable block to avoid having to drawstamp. 2144 */ 2145 if (!forced && (oldw != Lepton_To_Pixel(TacLeptonWidth) || oldh != Lepton_To_Pixel(TacLeptonHeight))) { 2146 Set_Cursor_Pos(-1); 2147 2148 HidPage.Blit(HidPage, 2149 ((oldx < 0) ? -oldx : 0) +TacPixelX, 2150 ((oldy < 0) ? -oldy : 0) +TacPixelY, 2151 ((oldx < 0) ? 0 : oldx) +TacPixelX, 2152 ((oldy < 0) ? 0 : oldy) +TacPixelY, 2153 oldw, 2154 oldh); 2155 } else { 2156 forced = true; 2157 } 2158 2159 if (oldx < 0) oldx = 0; 2160 if (oldy < 0) oldy = 0; 2161 2162 /* 2163 ** Record new map position for future reference. 2164 */ 2165 ScenarioInit++; 2166 Set_Tactical_Position(DesiredTacticalCoord); 2167 ScenarioInit--; 2168 2169 if (!forced) { 2170 2171 /* 2172 ** Set the 'redraw stamp' bit for any cells that could not be copied. 2173 */ 2174 int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); 2175 int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); 2176 oldw -= 24; 2177 oldh -= 24; 2178 for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { 2179 for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { 2180 if (x <= oldx || x >= oldx+oldw || y <= oldy || y >= oldy+oldh) { 2181 CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, 2182 Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); 2183 2184 if (c > 0) { 2185 (*this)[c].Redraw_Objects(true); 2186 } 2187 } 2188 } 2189 } 2190 } 2191 2192 } else { 2193 2194 /* 2195 ** Set the tactical coordinate just in case the desired tactical has changed but 2196 ** not enough to result in any visible map change. This is likely to occur with very 2197 ** slow scroll rates. 2198 */ 2199 ScenarioInit++; 2200 if (DesiredTacticalCoord != TacticalCoord) { 2201 Set_Tactical_Position(DesiredTacticalCoord); 2202 } 2203 ScenarioInit--; 2204 } 2205 #endif 2206 2207 /* 2208 ** If the entire tactical map is forced to be redrawn, then set all the redraw flags 2209 ** and let the normal processing take care of the rest. 2210 */ 2211 if (forced) { 2212 CellRedraw.Set(); 2213 } 2214 2215 /* 2216 ** The first order of business is to redraw all the underlying icons that are 2217 ** flagged to be redrawn. 2218 */ 2219 if (HidPage.Lock()) { 2220 Redraw_Icons(); 2221 2222 /* 2223 ** Draw the infantry bodies in this special layer. 2224 */ 2225 // for (int index = 0; index < Anims.Count(); index++) { 2226 // AnimClass * anim = Anims.Ptr(index); 2227 // if (*anim >= ANIM_CORPSE1 && *anim <= ANIM_CORPSE3) { 2228 // anim->Render(forced); 2229 // } 2230 // } 2231 2232 #ifdef SORTDRAW 2233 /* 2234 ** Draw the vortex effect over the terrain 2235 */ 2236 ChronalVortex.Render(); 2237 2238 Redraw_OIcons(); 2239 #endif 2240 2241 HidPage.Unlock(); 2242 } 2243 2244 #ifndef WIN32 2245 /* 2246 ** Once the icons are drawn, duplicate the bottom line of the screen into the phantom 2247 ** area one line below the screen. This causes the predator effect to work on any 2248 ** shape drawn at the bottom of the screen. 2249 */ 2250 HidPage.Blit(HidPage, 0, HidPage.Get_Height()-1, 0, HidPage.Get_Height(), HidPage.Get_Width(), 1, false); 2251 #endif 2252 2253 if (HidPage.Lock()) { 2254 2255 #ifndef SORTDRAW 2256 /* 2257 ** Draw the vortex effect over the terrain 2258 */ 2259 ChronalVortex.Render(); 2260 #endif 2261 2262 /* 2263 ** Redraw the game objects layer by layer. The layer drawing occurs on the ground layer 2264 ** first and then followed by all the layers in increasing altitude. 2265 */ 2266 BStart(BENCH_OBJECTS); 2267 for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { 2268 for (int index = 0; index < Layer[layer].Count(); index++) { 2269 ObjectClass * ptr = Layer[layer][index]; 2270 2271 #ifdef SORTDRAW 2272 /* 2273 ** Techno objects are drawn as part of the cell redraw process since techno 2274 ** objects in the ground layer are handled by the Occupier and Overlapper 2275 ** pointer lists. 2276 */ 2277 if (!Debug_Map && ptr->Is_Techno() && layer == LAYER_GROUND && ((TechnoClass*)ptr)->Visual_Character() == VISUAL_NORMAL) continue; 2278 #endif 2279 2280 // if (ptr->What_Am_I() == RTTI_ANIM && *((AnimClass*)ptr) >= ANIM_CORPSE1 && *((AnimClass*)ptr) <= ANIM_CORPSE3) { 2281 // continue; 2282 // } 2283 assert(ptr->IsActive); 2284 ptr->Render(forced); 2285 } 2286 } 2287 BEnd(BENCH_OBJECTS); 2288 2289 //ChronalVortex.Render(); 2290 /* 2291 ** Finally, redraw the shadow overlay as necessary. 2292 */ 2293 BStart(BENCH_SHROUD); 2294 Redraw_Shadow(); 2295 BEnd(BENCH_SHROUD); 2296 } 2297 HidPage.Unlock(); 2298 2299 #ifdef SORTDRAW 2300 for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { 2301 Layer[LAYER_GROUND][index]->IsToDisplay = false; 2302 } 2303 #endif 2304 2305 /* 2306 ** Draw the rubber band over the top of it all. 2307 */ 2308 if (IsRubberBand) { 2309 LogicPage->Draw_Rect(BandX+TacPixelX, BandY+TacPixelY, NewX+TacPixelX, NewY+TacPixelY, WHITE); 2310 } 2311 2312 /* 2313 ** Clear the redraw flags so that normal redraw flag setting can resume. 2314 */ 2315 CellRedraw.Reset(); 2316 2317 #ifdef SCENARIO_EDITOR 2318 /* 2319 ** If we're placing an object (PendingObject is non-NULL), and that object 2320 ** is NOT an icon, smudge, or overlay, draw it here. 2321 ** Terrain, Buildings & Aircraft aren't drawn at the cell's center coord; 2322 ** they're drawn at the upper left coord, so I have to AND the coord value 2323 ** with 0xFF00FF00 to strip off the lepton coordinates, but leave the 2324 ** cell coordinates. 2325 */ 2326 if (Debug_Map && PendingObjectPtr) { 2327 PendingObjectPtr->Coord = PendingObjectPtr->Class_Of().Coord_Fixup(Cell_Coord(ZoneCell + ZoneOffset)); 2328 PendingObjectPtr->Render(true); 2329 } 2330 #endif 2331 BEnd(BENCH_TACTICAL); 2332 } 2333 } 2334 2335 2336 /*********************************************************************************************** 2337 * DisplayClass::Redraw_Icons -- Draws all terrain icons necessary. * 2338 * * 2339 * This routine will redraw all of the terrain icons that are flagged * 2340 * to be redrawn. * 2341 * * 2342 * INPUT: none * 2343 * * 2344 * OUTPUT: none * 2345 * * 2346 * WARNINGS: none. * 2347 * * 2348 * HISTORY: * 2349 * 02/14/1994 JLB : Created. * 2350 * 05/01/1994 JLB : Converted to member function. * 2351 * 06/20/1994 JLB : Uses cell drawing support function. * 2352 * 12/06/1994 JLB : Scans tactical view in separate row/column loops * 2353 * 12/24/1994 JLB : Uses the cell bit flag array to determine what to redraw. * 2354 *=============================================================================================*/ 2355 void DisplayClass::Redraw_Icons(void) 2356 { 2357 IsShadowPresent = false; 2358 for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { 2359 for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { 2360 COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); 2361 CELL cell = Coord_Cell(coord); 2362 coord = Coord_Whole(Cell_Coord(cell)); 2363 2364 /* 2365 ** Only cells flagged to be redraw are examined. 2366 */ 2367 if (In_View(cell) && Is_Cell_Flagged(cell)) { 2368 int xpixel; 2369 int ypixel; 2370 2371 if (Coord_To_Pixel(coord, xpixel, ypixel)) { 2372 CellClass * cellptr = &(*this)[coord]; 2373 2374 /* 2375 ** If there is a portion of the underlying icon that could be visible, 2376 ** then draw it. Also draw the cell if the shroud is off. 2377 */ 2378 //if (cellptr->IsMapped || Debug_Unshroud) { 2379 if (cellptr->Is_Mapped(PlayerPtr) || Debug_Unshroud) { // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM 2380 cellptr->Draw_It(xpixel, ypixel); 2381 } 2382 2383 /* 2384 ** If any cell is not fully mapped, then flag it so that the shadow drawing 2385 ** process will occur. Only draw the shadow if Debug_Unshroud is false. 2386 */ 2387 //if (!cellptr->IsVisible && !Debug_Unshroud) { 2388 if (!cellptr->Is_Visible(PlayerPtr) && !Debug_Unshroud) { // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM 2389 IsShadowPresent = true; 2390 } 2391 } 2392 } 2393 } 2394 } 2395 } 2396 2397 2398 #ifdef SORTDRAW 2399 void DisplayClass::Redraw_OIcons(void) 2400 { 2401 for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { 2402 for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { 2403 COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); 2404 CELL cell = Coord_Cell(coord); 2405 coord = Coord_Whole(Cell_Coord(cell)); 2406 2407 /* 2408 ** Only cells flagged to be redraw are examined. 2409 */ 2410 if (In_View(cell) && Is_Cell_Flagged(cell)) { 2411 int xpixel; 2412 int ypixel; 2413 2414 if (Coord_To_Pixel(coord, xpixel, ypixel)) { 2415 CellClass * cellptr = &(*this)[coord]; 2416 2417 /* 2418 ** If there is a portion of the underlying icon that could be visible, 2419 ** then draw it. Also draw the cell if the shroud is off. 2420 */ 2421 //if (cellptr->IsMapped || Debug_Unshroud) { 2422 if (cellptr->Is_Mapped(PlayerPtr) || Debug_Unshroud) { // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM 2423 cellptr->Draw_It(xpixel, ypixel, true); 2424 } 2425 } 2426 } 2427 } 2428 } 2429 } 2430 #endif 2431 2432 2433 /*********************************************************************************************** 2434 * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. * 2435 * * 2436 * This routine is called after all other tactical map rendering takes place. It draws * 2437 * the shadow map over the tactical map. * 2438 * * 2439 * INPUT: none * 2440 * * 2441 * OUTPUT: none * 2442 * * 2443 * WARNINGS: none * 2444 * * 2445 * HISTORY: * 2446 * 01/01/1995 JLB : Created. * 2447 * 08/06/1995 JLB : Clips the fill rect if necessary. * 2448 *=============================================================================================*/ 2449 void DisplayClass::Redraw_Shadow(void) 2450 { 2451 if (IsShadowPresent) { 2452 for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { 2453 for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { 2454 COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); 2455 CELL cell = Coord_Cell(coord); 2456 coord = Coord_Whole(Cell_Coord(cell)); 2457 2458 /* 2459 ** Only cells flagged to be redrawn are examined. 2460 */ 2461 if (In_View(cell) && Is_Cell_Flagged(cell)) { 2462 int xpixel; 2463 int ypixel; 2464 2465 if (Coord_To_Pixel(coord, xpixel, ypixel)) { 2466 CellClass * cellptr = &(*this)[coord]; 2467 //if (cellptr->IsVisible) continue; 2468 if (cellptr->Is_Visible(PlayerPtr)) continue; // Use PlayerPtr since we won't be rendering in MP. ST - 8/6/2019 10:44AM 2469 int shadow = -2; 2470 //if (cellptr->IsMapped) { 2471 if (cellptr->Is_Mapped(PlayerPtr)) { // Use PlayerPtr since we won't be rendering in MP. ST - 8/6/2019 10:44AM 2472 shadow = Cell_Shadow(cell, PlayerPtr); // Use PlayerPtr since we won't be rendering in MP. ST - 8/6/2019 10:44AM 2473 } 2474 if (shadow >= 0) { 2475 CC_Draw_Shape(ShadowShapes, shadow, xpixel, ypixel, WINDOW_TACTICAL, SHAPE_GHOST, NULL, ShadowTrans); 2476 } else { 2477 if (shadow != -1) { 2478 int ww = CELL_PIXEL_W; 2479 int hh = CELL_PIXEL_H; 2480 2481 if (Clip_Rect(&xpixel, &ypixel, &ww, &hh, Lepton_To_Pixel(TacLeptonWidth), Lepton_To_Pixel(TacLeptonHeight)) >= 0) { 2482 LogicPage->Fill_Rect(TacPixelX+xpixel, TacPixelY+ypixel, TacPixelX+xpixel+ww-1, TacPixelY+ypixel+hh-1, BLACK); 2483 } 2484 } 2485 } 2486 } 2487 } 2488 } 2489 } 2490 } 2491 } 2492 2493 2494 /*********************************************************************************************** 2495 * DisplayClass::Next_Object -- Searches for next object on display. * 2496 * * 2497 * This utility routine is used to find the "next" object from the object specified. This * 2498 * is typically used when <TAB> is pressed and the current object shifts. * 2499 * * 2500 * INPUT: object -- The current object to base the "next" calculation off of. * 2501 * * 2502 * OUTPUT: Returns with a pointer to the next object. If there is no objects available, * 2503 * then NULL is returned. * 2504 * * 2505 * WARNINGS: none * 2506 * * 2507 * HISTORY: * 2508 * 06/20/1994 JLB : Created. * 2509 *=============================================================================================*/ 2510 ObjectClass * DisplayClass::Next_Object(ObjectClass * object) const 2511 { 2512 ObjectClass * firstobj = NULL; 2513 bool foundmatch = false; 2514 2515 if (object == NULL) { 2516 foundmatch = true; 2517 } 2518 for (unsigned uindex = 0; uindex < (unsigned)Layer[LAYER_GROUND].Count(); uindex++) { 2519 ObjectClass * obj = Layer[LAYER_GROUND][uindex]; 2520 2521 /* 2522 ** Verify that the object can be selected by and is owned by the player. 2523 */ 2524 if (obj != NULL && obj->Is_Players_Army()) { 2525 if (firstobj == NULL) firstobj = obj; 2526 if (foundmatch) return(obj); 2527 if (object == obj) foundmatch = true; 2528 } 2529 } 2530 return(firstobj); 2531 } 2532 2533 2534 /*********************************************************************************************** 2535 * DisplayClass::Prev_Object -- Searches for the previous object on the map. * 2536 * * 2537 * This routine will search for the previous object. Previous is defined as the one listed * 2538 * before the specified object in the ground layer. If there is no specified object, then * 2539 * the last object in the ground layer is returned. * 2540 * * 2541 * INPUT: object -- Pointer to the object that "previous" is to be defined from. * 2542 * * 2543 * OUTPUT: Returns with a pointer to the object previous to the specified one. * 2544 * * 2545 * WARNINGS: none * 2546 * * 2547 * HISTORY: * 2548 * 08/24/1995 JLB : Created. * 2549 *=============================================================================================*/ 2550 ObjectClass * DisplayClass::Prev_Object(ObjectClass * object) const 2551 { 2552 ObjectClass * firstobj = NULL; 2553 bool foundmatch = false; 2554 2555 if (object == NULL) { 2556 foundmatch = true; 2557 } 2558 for (int uindex = Layer[LAYER_GROUND].Count()-1; uindex >= 0; uindex--) { 2559 ObjectClass * obj = Layer[LAYER_GROUND][uindex]; 2560 2561 /* 2562 ** Verify that the object can be selected by and is owned by the player. 2563 */ 2564 if (obj != NULL && obj->Is_Players_Army()) { 2565 if (firstobj == NULL) firstobj = obj; 2566 if (foundmatch) return(obj); 2567 if (object == obj) foundmatch = true; 2568 } 2569 } 2570 2571 return(firstobj); 2572 } 2573 2574 2575 /*********************************************************************************************** 2576 * DisplayClass::Pixel_To_Coord -- converts screen coord to COORDINATE * 2577 * * 2578 * INPUT: * 2579 * x,y pixel coordinates to convert * 2580 * * 2581 * OUTPUT: * 2582 * COORDINATE of pixel * 2583 * * 2584 * WARNINGS: * 2585 * none. * 2586 * * 2587 * HISTORY: * 2588 * 11/09/1994 BR : Created. * 2589 * 12/06/1994 JLB : Uses map dimension variables in display class. * 2590 * 12/10/1994 JLB : Uses union to speed building coordinate value. * 2591 *=============================================================================================*/ 2592 COORDINATE DisplayClass::Pixel_To_Coord(int x, int y) const 2593 { 2594 /* 2595 ** Normalize the pixel coordinates to be relative to the upper left corner 2596 ** of the tactical map. The coordinates are expressed in leptons. 2597 */ 2598 x -= TacPixelX; 2599 x = Pixel_To_Lepton(x); 2600 y -= TacPixelY; 2601 y = Pixel_To_Lepton(y); 2602 2603 /* 2604 ** If pixel coordinate is over the tactical map, then translate it into a coordinate 2605 ** value. If not, then just return with NULL. 2606 */ 2607 // Possibly ignore the view constraints if we aren't using the internal renderer. ST - 8/6/2019 10:47AM 2608 //if ((unsigned)x < TacLeptonWidth && (unsigned)y < TacLeptonHeight) { 2609 if (IgnoreViewConstraints || ((unsigned)x < TacLeptonWidth && (unsigned)y < TacLeptonHeight)) { 2610 return(Coord_Add(TacticalCoord, XY_Coord(x, y))); 2611 } 2612 return(0); 2613 } 2614 2615 2616 /*********************************************************************************************** 2617 * DisplayClass::Calculated_Cell -- Fetch a map cell based on specified method. * 2618 * * 2619 * Find a cell meeting the specified requirements. This function is * 2620 * used for scenario reinforcements. * 2621 * * 2622 * INPUT: dir -- Method of picking a map cell. * 2623 * * 2624 * waypoint -- Closest waypoint to use for finding appropriate map edge. * 2625 * * 2626 * cell -- Cell to find closest edge to if waypoint not specified. * 2627 * * 2628 * loco -- The locomotion of the reinforcements that are trying to enter. * 2629 * * 2630 * zonecheck -- Is zone checking required? * 2631 * * 2632 * mzone -- The movement zone type to check against (only if zone checking). * 2633 * * 2634 * OUTPUT: Returns with the calculated cell. If 0, then this indicates * 2635 * that no legal cell was found. * 2636 * * 2637 * WARNINGS: none * 2638 * * 2639 * HISTORY: * 2640 * 10/07/1992 JLB : Created. * 2641 * 04/11/1994 JLB : Revamped. * 2642 * 05/18/1994 JLB : Converted to member function. * 2643 * 12/18/1995 JLB : Handles edge preference scan. * 2644 * 06/24/1996 JLB : Removed Dune II legacy code. * 2645 * 06/25/1996 JLB : Rewrote and greatly simplified. * 2646 * 10/05/1996 JLB : Checks for zone and crushable status. * 2647 *=============================================================================================*/ 2648 CELL DisplayClass::Calculated_Cell(SourceType dir, WAYPOINT waypoint, CELL cell, SpeedType loco, bool zonecheck, MZoneType mzone) const 2649 { 2650 bool vert = false; 2651 bool horz = false; 2652 int x = 0; 2653 int y = 0; 2654 CELL punt = 0; // If all else fails, return this cell location. 2655 int zone = -1; // Tentative zone for legality checking. 2656 2657 /* 2658 ** Waypoint edge detection for ground based reinforcements that have a waypoint origin are 2659 ** determined by finding the closest map edge to the waypoint. Reinforcement location 2660 ** scanning starts from that position. 2661 */ 2662 CELL trycell = -1; 2663 if (waypoint != -1) { 2664 trycell = Scen.Waypoint[waypoint]; 2665 } 2666 if (trycell == -1) { 2667 trycell = cell; 2668 } 2669 2670 /* 2671 ** If zone checking is requested, then find the correct zone to use. 2672 */ 2673 if (zonecheck && trycell != -1) { 2674 zone = (*this)[trycell].Zones[mzone]; 2675 } 2676 2677 /* 2678 ** If the cell or waypoint specified as been detected as legal, then set up the map edge 2679 ** scanning values accordingly. 2680 */ 2681 if (trycell != -1) { 2682 x = Cell_X(trycell) - MapCellX; 2683 x = min(x, (-Cell_X(trycell) + (MapCellX+MapCellWidth))); 2684 2685 y = Cell_Y(trycell) - MapCellY; 2686 y = min(y, (-Cell_Y(trycell) + (MapCellY+MapCellHeight))); 2687 2688 if (x < y) { 2689 vert = true; 2690 horz = false; 2691 if ((Cell_X(trycell)-MapCellX) < MapCellWidth/2) { 2692 x = -1; 2693 } else { 2694 x = MapCellWidth; 2695 } 2696 y = Cell_Y(trycell) - MapCellY; 2697 2698 } else { 2699 2700 vert = false; 2701 horz = true; 2702 if ((Cell_Y(trycell)-MapCellY) < MapCellHeight/2) { 2703 y = -1; 2704 } else { 2705 y = MapCellHeight; 2706 } 2707 x = Cell_X(trycell) - MapCellX; 2708 } 2709 } 2710 2711 /* 2712 ** If no map edge can be inferred from the waypoint, then go with the 2713 ** map edge specified by the edge parameter. 2714 */ 2715 if (!vert && !horz) { 2716 switch (dir) { 2717 default: 2718 case SOURCE_NORTH: 2719 horz = true; 2720 y = -1; 2721 x = Random_Pick(0, MapCellWidth-1); 2722 break; 2723 2724 case SOURCE_SOUTH: 2725 horz = true; 2726 y = MapCellHeight; 2727 x = Random_Pick(0, MapCellWidth-1); 2728 break; 2729 2730 case SOURCE_EAST: 2731 vert = true; 2732 x = MapCellWidth; 2733 y = Random_Pick(0, MapCellHeight-1); 2734 break; 2735 2736 case SOURCE_WEST: 2737 vert = true; 2738 x = -1; 2739 y = Random_Pick(0, MapCellHeight-1); 2740 break; 2741 } 2742 } 2743 2744 /* 2745 ** Determine the default reinforcement cell if all else fails. 2746 */ 2747 punt = XY_Cell(x + MapCellX, y + MapCellY); 2748 2749 /* 2750 ** Scan through the vertical and horizontal edges of the map looking for 2751 ** a relatively clear cell for object placement. The cell scanned is 2752 ** from the edge position specified by the X and Y variables. 2753 */ 2754 if (vert) { 2755 int modifier = (x > MapCellX) ? -1 : 1; 2756 2757 for (int index = 0; index < MapCellHeight; index++) { 2758 CELL trycell = XY_Cell(x + MapCellX, ((y + index) % MapCellHeight) + MapCellY); 2759 2760 if (Good_Reinforcement_Cell(trycell, trycell+modifier, loco, zone, mzone)) { 2761 return(trycell); 2762 } 2763 } 2764 } 2765 2766 if (horz) { 2767 int modifier = (y > MapCellY) ? -MAP_CELL_W : MAP_CELL_W; 2768 2769 for (int index = 0; index < MapCellWidth; index++) { 2770 CELL trycell = XY_Cell(((x + index) % MapCellWidth) + MapCellX, y + MapCellY); 2771 2772 if (Good_Reinforcement_Cell(trycell, trycell+modifier, loco, zone, mzone)) { 2773 return(trycell); 2774 } 2775 } 2776 } 2777 2778 /* 2779 ** If there was no success in finding a suitable reinforcement edge cell, then return 2780 ** with the default 'punt' cell location. 2781 */ 2782 return(punt); 2783 } 2784 2785 2786 /*********************************************************************************************** 2787 * DisplayClass::Good_Reinforcement_Cell -- Checks cell for renforcement legality. * 2788 * * 2789 * This routine will check the secified cell (given the specified conditions) and determine * 2790 * if that is a good cell for reinforcement purposes. It checks for passability of the cell * 2791 * as well as zone and whether blocking walls can be crushed. * 2792 * * 2793 * INPUT: outcell -- The cell that is just outside the edge of the map. * 2794 * * 2795 * incell -- The cell that is just inside the edge of the map. * 2796 * * 2797 * loco -- The locomotion type of the reinforcement. * 2798 * * 2799 * zone -- The zone that the eventual movement destination lies. A reinforcement * 2800 * edge must fall within the same zone. * 2801 * * 2802 * mzone -- The zone check type to check against (if zone checking required) * 2803 * * 2804 * OUTPUT: bool; Is the specified cell good for reinforcement purposes? * 2805 * * 2806 * WARNINGS: none * 2807 * * 2808 * HISTORY: * 2809 * 10/05/1996 JLB : Created. * 2810 *=============================================================================================*/ 2811 bool DisplayClass::Good_Reinforcement_Cell(CELL outcell, CELL incell, SpeedType loco, int zone, MZoneType mzone) const 2812 { 2813 /* 2814 ** If the map edge location is not clear for object placement, then this is not 2815 ** a good cell for reinforcement purposes. 2816 */ 2817 if (!(*this)[outcell].Is_Clear_To_Move(loco, false, false)) { 2818 return(false); 2819 } 2820 2821 /* 2822 ** If it looks like the on-map cell cannot be driven on to, then return with 2823 ** the failure code. 2824 */ 2825 if (!(*this)[incell].Is_Clear_To_Move(loco, false, false, zone, mzone)) { 2826 return(false); 2827 } 2828 2829 /* 2830 ** If the reinforcement cell is already occupied, then return a failure code. 2831 */ 2832 if ((*this)[outcell].Cell_Techno() != NULL) { 2833 return(false); 2834 } 2835 if ((*this)[incell].Cell_Techno() != NULL) return(false); 2836 2837 /* 2838 ** All tests have passed, return with success code. 2839 */ 2840 //Mono_Printf("<%04X>\n", incell);Keyboard->Get(); 2841 return(true); 2842 } 2843 2844 2845 /*********************************************************************************************** 2846 * DisplayClass::Select_These -- All selectable objects in region are selected. * 2847 * * 2848 * Use this routine to simultaneously select all objects within the coordinate region * 2849 * specified. This routine is used by the multi-select rubber band handler. * 2850 * * 2851 * INPUT: coord1 -- Coordinate of one corner of the selection region. * 2852 * * 2853 * coord2 -- The opposite corner of the selection region. * 2854 * * 2855 * additive -- Does this add to the existing selection or replace it. * 2856 * * 2857 * OUTPUT: none * 2858 * * 2859 * WARNINGS: none * 2860 * * 2861 * HISTORY: * 2862 * 01/19/1995 JLB : Created. * 2863 * 04/25/1995 JLB : Limited to non-building type. * 2864 * 03/06/1996 JLB : Allows selection of aircraft with bounding box. * 2865 *=============================================================================================*/ 2866 static bool should_exclude_from_selection(ObjectClass* obj) 2867 { 2868 return (obj->What_Am_I() == RTTI_UNIT) && 2869 (((UnitClass *)obj)->Class->IsToHarvest || 2870 *((UnitClass *)obj) == UNIT_MCV); 2871 } 2872 2873 void DisplayClass::Select_These(COORDINATE coord1, COORDINATE coord2, bool additive) 2874 { 2875 COORDINATE tcoord = TacticalCoord; //Cell_Coord(TacticalCell) & 0xFF00FF00L; 2876 2877 coord1 = Coord_Add(tcoord, coord1); 2878 coord2 = Coord_Add(tcoord, coord2); 2879 int x1 = Coord_X(coord1); 2880 int x2 = Coord_X(coord2); 2881 int y1 = Coord_Y(coord1); 2882 int y2 = Coord_Y(coord2); 2883 2884 /* 2885 ** Ensure that coordinate number one represents the upper left corner 2886 ** and coordinate number two represents the lower right corner. 2887 */ 2888 if (x1 > x2) { 2889 int temp = x1; 2890 x1 = x2; 2891 x2 = temp; 2892 } 2893 if (y1 > y2) { 2894 int temp = y1; 2895 y1 = y2; 2896 y2 = temp; 2897 } 2898 2899 /* 2900 ** Sweep through all ground layer objects and select the ones within the 2901 ** bounding box. 2902 */ 2903 if (!additive) { 2904 Unselect_All(); 2905 } 2906 AllowVoice = true; 2907 for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { 2908 ObjectClass * obj = Layer[LAYER_GROUND][index]; 2909 COORDINATE ocoord = obj->Center_Coord(); 2910 int x = Coord_X(ocoord); 2911 int y = Coord_Y(ocoord); 2912 2913 /* 2914 ** Only try to select objects that are allowed to be selected, and are within the bounding box. 2915 */ 2916 HouseClass * hptr = HouseClass::As_Pointer(obj->Owner()); 2917 if ( obj->Class_Of().IsSelectable && 2918 obj->What_Am_I() != RTTI_BUILDING && 2919 (!obj->Is_Techno() || !((TechnoClass*)obj)->Is_Cloaked(PlayerPtr)) && 2920 x >= x1 && x <= x2 && y >= y1 && y <= y2) { 2921 bool old_allow_voice = AllowVoice; 2922 bool is_player_controlled = (hptr != NULL) && hptr->IsPlayerControl; 2923 AllowVoice &= is_player_controlled; 2924 if (obj->Select(true)) { 2925 if (is_player_controlled) { 2926 old_allow_voice = false; 2927 } 2928 } 2929 AllowVoice = old_allow_voice; 2930 } 2931 } 2932 2933 /* 2934 ** Select any aircraft with the bounding box. 2935 */ 2936 for (int air_index = 0; air_index < Aircraft.Count(); air_index++) { 2937 AircraftClass * aircraft = Aircraft.Ptr(air_index); 2938 COORDINATE ocoord = aircraft->Center_Coord(); 2939 int x = Coord_X(ocoord); 2940 int y = Coord_Y(ocoord); 2941 2942 /* 2943 ** Only try to select objects that are allowed to be selected, and are within the bounding box. 2944 */ 2945 if ( aircraft->Class->IsSelectable && 2946 !aircraft->Is_Cloaked(PlayerPtr) && 2947 !aircraft->Is_Selected_By_Player() && 2948 x >= x1 && x <= x2 && y >= y1 && y <= y2) { 2949 bool old_allow_voice = AllowVoice; 2950 bool is_player_controlled = aircraft->House->IsPlayerControl; 2951 AllowVoice &= is_player_controlled; 2952 if (aircraft->Select(true)) { 2953 if (is_player_controlled) { 2954 old_allow_voice = false; 2955 } 2956 } 2957 AllowVoice = old_allow_voice; 2958 } 2959 } 2960 2961 /* 2962 ** If a mix of player and non-player controlled units were selected, make sure non-player controlled units are de-selected 2963 */ 2964 bool player_controlled_units = false, non_player_controlled_units = false; 2965 for (int i = 0; (i < CurrentObject.Count()) && (!player_controlled_units || !non_player_controlled_units); ++i) { 2966 HouseClass * hptr = HouseClass::As_Pointer(CurrentObject[i]->Owner()); 2967 if (hptr->IsPlayerControl) { 2968 player_controlled_units = true; 2969 } 2970 else { 2971 non_player_controlled_units = true; 2972 } 2973 } 2974 if (player_controlled_units && non_player_controlled_units) { 2975 for (int i = 0; i < CurrentObject.Count(); ++i) { 2976 HouseClass * hptr = HouseClass::As_Pointer(CurrentObject[i]->Owner()); 2977 if (!hptr->IsPlayerControl) { 2978 int count_before = CurrentObject.Count(); 2979 CurrentObject[i]->Unselect(); 2980 if (count_before <= CurrentObject.Count()) { 2981 GlyphX_Debug_Print("Select_These failed to remove an object"); 2982 CurrentObject.Delete(CurrentObject[i]); 2983 } 2984 --i; 2985 } 2986 } 2987 } 2988 2989 /* 2990 ** If player-controlled units are non-additively selected, remove harvesters and MCVs if they aren't the only types of units selected 2991 */ 2992 if (!additive && player_controlled_units) { 2993 bool any_to_exclude = false, all_to_exclude = true; 2994 for (int i = 0; i < CurrentObject.Count(); ++i) { 2995 bool exclude = should_exclude_from_selection(CurrentObject[i]); 2996 any_to_exclude |= exclude; 2997 all_to_exclude &= exclude; 2998 } 2999 if (any_to_exclude && !all_to_exclude) { 3000 for (int i = 0; i < CurrentObject.Count(); ++i) { 3001 if (should_exclude_from_selection(CurrentObject[i])) { 3002 int count_before = CurrentObject.Count(); 3003 CurrentObject[i]->Unselect(); 3004 if (count_before <= CurrentObject.Count()) { 3005 GlyphX_Debug_Print("Select_These failed to remove an object"); 3006 CurrentObject.Delete(CurrentObject[i]); 3007 } 3008 --i; 3009 } 3010 } 3011 } 3012 } 3013 3014 AllowVoice = true; 3015 } 3016 3017 3018 /*********************************************************************************************** 3019 * DisplayClass::Refresh_Band -- Causes all cells under the rubber band to be redrawn. * 3020 * * 3021 * Use this routine to flag all cells that are covered in some fashion by the multi-unit * 3022 * select "rubber band" to be redrawn. This is necessary whenever the rubber band changes * 3023 * size or is being removed. * 3024 * * 3025 * INPUT: none * 3026 * * 3027 * OUTPUT: none * 3028 * * 3029 * WARNINGS: none * 3030 * * 3031 * HISTORY: * 3032 * 01/19/1995 JLB : Created. * 3033 *=============================================================================================*/ 3034 void DisplayClass::Refresh_Band(void) 3035 { 3036 if (IsRubberBand) { 3037 3038 /* 3039 ** In rubber band mode, mark all cells under the "rubber band" to be 3040 ** redrawn. 3041 */ 3042 int x1 = BandX+TacPixelX; 3043 int y1 = BandY+TacPixelY; 3044 int x2 = NewX+TacPixelX; 3045 int y2 = NewY+TacPixelY; 3046 3047 if (x1 > x2) { 3048 int temp = x1; 3049 x1 = x2; 3050 x2 = temp; 3051 } 3052 if (y1 > y2) { 3053 int temp = y1; 3054 y1 = y2; 3055 y2 = temp; 3056 } 3057 3058 CELL cell; 3059 for (int y = y1; y <= y2+CELL_PIXEL_H; y += CELL_PIXEL_H) { 3060 cell = Click_Cell_Calc(x1, Bound(y, 0, TacPixelY+Lepton_To_Pixel(TacLeptonHeight))); 3061 if (cell != -1) (*this)[cell].Redraw_Objects(); 3062 3063 cell = Click_Cell_Calc(x2, Bound(y, 0, TacPixelY+Lepton_To_Pixel(TacLeptonHeight))); 3064 if (cell != -1) (*this)[cell].Redraw_Objects(); 3065 } 3066 3067 for (int x = x1; x <= x2+CELL_PIXEL_W; x += CELL_PIXEL_W) { 3068 cell = Click_Cell_Calc(Bound(x, 0, TacPixelX+Lepton_To_Pixel(TacLeptonWidth)), y1); 3069 if (cell != -1) (*this)[cell].Redraw_Objects(); 3070 3071 cell = Click_Cell_Calc(Bound(x, 0, TacPixelX+Lepton_To_Pixel(TacLeptonWidth)), y2); 3072 if (cell != -1) (*this)[cell].Redraw_Objects(); 3073 } 3074 3075 /* 3076 ** Stretching the rubber band requires all objects to be redrawn. 3077 */ 3078 int index; 3079 for (index = 0; index < Layer[LAYER_TOP].Count(); index++) { 3080 Layer[LAYER_TOP][index]->Mark(MARK_CHANGE); 3081 } 3082 for (index = 0; index < Layer[LAYER_AIR].Count(); index++) { 3083 Layer[LAYER_AIR][index]->Mark(MARK_CHANGE); 3084 } 3085 } 3086 } 3087 3088 3089 /*********************************************************************************************** 3090 * DisplayClass::TacticalClass::Action -- Processes input for the tactical map. * 3091 * * 3092 * This routine handles the input directed at the tactical map. Since input, in this * 3093 * regard, includes even the presence of the mouse over the tactical map, this routine * 3094 * is called nearly every game frame. It handles adjusting the mouse shape as well as * 3095 * giving orders to units. * 3096 * * 3097 * INPUT: flags -- The gadget event flags that triggered the call to this function. * 3098 * * 3099 * key -- A reference to the keyboard event (if any). * 3100 * * 3101 * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? * 3102 * * 3103 * WARNINGS: none * 3104 * * 3105 * HISTORY: * 3106 * 02/17/1995 JLB : Created. * 3107 *=============================================================================================*/ 3108 int DisplayClass::TacticalClass::Action(unsigned flags, KeyNumType & key) 3109 { 3110 int x,y; // Sub cell pixel coordinates. 3111 bool shadow; 3112 ObjectClass * object = 0; 3113 ActionType action = ACTION_NONE; // Action possible with currently selected object. 3114 3115 /* 3116 ** Set some working variables that depend on the mouse position. For the press 3117 ** or release event, special mouse queuing storage variables are used. Other 3118 ** events must use the current mouse position globals. 3119 */ 3120 if (flags & (LEFTPRESS|LEFTRELEASE|RIGHTPRESS|RIGHTRELEASE)) { 3121 x = Keyboard->MouseQX; 3122 y = Keyboard->MouseQY; 3123 } else { 3124 x = Get_Mouse_X(); 3125 y = Get_Mouse_Y(); 3126 } 3127 bool edge = (y == 0 || x == 0 || x == SeenBuff.Get_Width()-1 || y == SeenBuff.Get_Height()-1); 3128 COORDINATE coord = Map.Pixel_To_Coord(x, y); 3129 CELL cell = Coord_Cell(coord); 3130 if (coord) { 3131 3132 //shadow = (!Map[cell].IsMapped && !Debug_Unshroud); 3133 shadow = (!Map[cell].Is_Mapped(PlayerPtr) && !Debug_Unshroud); // Use PlayerPtr since we won't be rendering in MP. ST - 8/6/2019 10:49AM 3134 x -= Map.TacPixelX; 3135 y -= Map.TacPixelY; 3136 3137 /* 3138 ** Cause any displayed cursor to move along with the mouse cursor. 3139 */ 3140 if (cell != Map.ZoneCell) { 3141 Map.Set_Cursor_Pos(cell); 3142 } 3143 3144 /* 3145 ** Determine the object that the mouse is currently over. 3146 */ 3147 if (!shadow) { 3148 object = Map.Close_Object(coord); 3149 3150 /* 3151 ** Special case check to ignore cloaked object if not allied with the player. 3152 */ 3153 if (object != NULL && object->Is_Techno() && ((TechnoClass *)object)->Is_Cloaked(PlayerPtr, true)) { 3154 object = NULL; 3155 } 3156 } 3157 3158 /* 3159 ** If there is a currently selected object, then the action to perform if 3160 ** the left mouse button were clicked must be determined. 3161 */ 3162 if (CurrentObject.Count()) { 3163 if (object != NULL) { 3164 action = Best_Object_Action(object); 3165 } else { 3166 action = Best_Object_Action(cell); 3167 } 3168 3169 } else { 3170 3171 if (object && object->Class_Of().IsSelectable) { 3172 action = ACTION_SELECT; 3173 } 3174 3175 if (Map.IsRepairMode) { 3176 if (object && object->Owner() == PlayerPtr->Class->House && object->Can_Repair()) { 3177 action = ACTION_REPAIR; 3178 } else { 3179 action = ACTION_NO_REPAIR; 3180 } 3181 } 3182 3183 if (Map.IsSellMode) { 3184 if (object && object->Owner() == PlayerPtr->Class->House && object->Can_Demolish()) { 3185 if (object->What_Am_I() == RTTI_BUILDING) { 3186 action = ACTION_SELL; 3187 } else { 3188 action = ACTION_SELL_UNIT; 3189 } 3190 } else { 3191 3192 /* 3193 ** Check to see if the cursor is over an owned wall. 3194 */ 3195 if (Map[cell].Overlay != OVERLAY_NONE && 3196 OverlayTypeClass::As_Reference(Map[cell].Overlay).IsWall && 3197 Map[cell].Owner == PlayerPtr->Class->House) { 3198 action = ACTION_SELL; 3199 } else { 3200 action = ACTION_NO_SELL; 3201 } 3202 } 3203 } 3204 3205 if (Map.IsTargettingMode == SPC_NUCLEAR_BOMB) { 3206 action = ACTION_NUKE_BOMB; 3207 } 3208 3209 if (Map.IsTargettingMode == SPC_PARA_BOMB) { 3210 action = ACTION_PARA_BOMB; 3211 } 3212 3213 if (Map.IsTargettingMode == SPC_PARA_INFANTRY) { 3214 action = ACTION_PARA_INFANTRY; 3215 } 3216 3217 if (Map.IsTargettingMode == SPC_SPY_MISSION) { 3218 action = ACTION_SPY_MISSION; 3219 } 3220 3221 if (Map.IsTargettingMode == SPC_IRON_CURTAIN) { 3222 action = ACTION_IRON_CURTAIN; 3223 } 3224 3225 if (Map.IsTargettingMode == SPC_CHRONOSPHERE) { 3226 action = ACTION_CHRONOSPHERE; 3227 } 3228 3229 if (Map.IsTargettingMode == SPC_CHRONO2) { 3230 action = ACTION_CHRONO2; 3231 if (shadow) action = ACTION_NOMOVE; 3232 ObjectClass const * tobject = As_Object(PlayerPtr->UnitToTeleport); 3233 3234 /* 3235 ** Determine if the object can be teleported to the destination cell. 3236 */ 3237 if (tobject != NULL && tobject->Is_Techno()) { 3238 TechnoClass const * uobject = (TechnoClass const *)tobject; 3239 if (!uobject->Can_Teleport_Here(cell)) { 3240 // if (((UnitClass *)As_Object(PlayerPtr->UnitToTeleport))->Can_Enter_Cell(cell, FACING_NONE) != MOVE_OK) { 3241 action = ACTION_NOMOVE; 3242 } 3243 3244 } 3245 #ifdef FIXIT_CSII // checked - ajw 9/28/98 3246 else { // If the object is no longer valid, cancel targetting mode. 3247 action = ACTION_NOMOVE; 3248 Map.IsTargettingMode = SPC_NONE; 3249 } 3250 #endif 3251 } 3252 3253 if (Map.PendingObject) { 3254 action = ACTION_NONE; 3255 } 3256 } 3257 3258 /* 3259 ** Move any cursor displayed. 3260 */ 3261 if (cell != Map.ZoneCell) { 3262 Map.Set_Cursor_Pos(cell); 3263 } 3264 3265 /* 3266 ** A right mouse button press cancels the current action or selection. 3267 */ 3268 if (flags & RIGHTPRESS) { 3269 Map.Mouse_Right_Press(); 3270 } 3271 3272 /* 3273 ** Make sure that if the mouse button has been released and the map doesn't know about it, 3274 ** then it must be informed. Do this by faking a mouse release event. 3275 */ 3276 if ((flags & LEFTUP) && Map.IsRubberBand) { 3277 flags |= LEFTRELEASE; 3278 } 3279 3280 /* 3281 ** When the mouse buttons aren't pressed, only the mouse cursor shape is processed. 3282 ** The shape changes depending on what object the mouse is currently over and what 3283 ** object is currently selected. 3284 */ 3285 if (!edge) { 3286 if (flags & LEFTUP) { 3287 Map.Mouse_Left_Up(cell, shadow, object, action); 3288 } 3289 } 3290 3291 /* 3292 ** Normal actions occur when the mouse button is released. The press event is 3293 ** intercepted and possible rubber-band mode is flagged. 3294 */ 3295 if (flags & LEFTRELEASE) { 3296 Map.Mouse_Left_Release(cell, x, y, object, action); 3297 } 3298 3299 /* 3300 ** When the mouse is first pressed on the map, then record the mouse 3301 ** position so that a proper check before going into rubber band 3302 ** mode can be made. Rubber band mode starts when the mouse is 3303 ** held down and moved a certain minimum distance. 3304 */ 3305 if (!edge && (flags & LEFTPRESS)) { 3306 Map.Mouse_Left_Up(cell, shadow, object, action); 3307 Map.Mouse_Left_Press(x, y); 3308 } 3309 3310 /* 3311 ** While the mouse is being held down, determine if rubber band mode should 3312 ** start. If rubber band mode is already active, then update the size 3313 ** and flag the map to redraw it. 3314 */ 3315 if (flags & LEFTHELD) { 3316 Map.Mouse_Left_Held(x, y); 3317 } 3318 } 3319 3320 return(GadgetClass::Action(0, key)); 3321 } 3322 3323 3324 /*********************************************************************************************** 3325 * DisplayClass::TacticalClass::Selection_At_Mouse -- Object selection * 3326 * * 3327 * Selects any objects at the current mouse position. * 3328 * * 3329 * * 3330 * INPUT: flags -- The gadget event flags that triggered the call to this function. * 3331 * * 3332 * key -- A reference to the keyboard event (if any). * 3333 * * 3334 * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? * 3335 * * 3336 * WARNINGS: none * 3337 * * 3338 * HISTORY: * 3339 * 2019/09/17 JAS * 3340 *=============================================================================================*/ 3341 int DisplayClass::TacticalClass::Selection_At_Mouse(unsigned flags, KeyNumType & key) 3342 { 3343 int x, y; // Sub cell pixel coordinates. 3344 bool edge = false; 3345 if (flags & (LEFTPRESS | LEFTRELEASE | RIGHTPRESS | RIGHTRELEASE)) { 3346 x = Keyboard->MouseQX; 3347 y = Keyboard->MouseQY; 3348 } 3349 else { 3350 x = Get_Mouse_X(); 3351 y = Get_Mouse_Y(); 3352 3353 if (x == 0 || y == 199 || x == 319) edge = true; 3354 } 3355 COORDINATE coord = Map.Pixel_To_Coord(x, y); 3356 CELL cell = Coord_Cell(coord); 3357 3358 if (coord) { 3359 bool shadow = (!Map[cell].Is_Visible(PlayerPtr) && !Debug_Unshroud); // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM 3360 x -= Map.TacPixelX; 3361 y -= Map.TacPixelY; 3362 3363 /* 3364 ** Cause any displayed cursor to move along with the mouse cursor. 3365 */ 3366 if (cell != Map.ZoneCell) { 3367 Map.Set_Cursor_Pos(cell); 3368 } 3369 3370 ObjectClass* object = nullptr; 3371 3372 /* 3373 ** Determine the object that the mouse is currently over. 3374 */ 3375 if (!shadow) { 3376 object = Map.Close_Object(coord); 3377 } 3378 3379 if (object != nullptr) 3380 { 3381 bool shiftdown = DLL_Export_Get_Input_Key_State(KN_LSHIFT); 3382 3383 if (shiftdown) 3384 { 3385 Map.Mouse_Left_Release(cell, x, y, object, ACTION_TOGGLE_SELECT); 3386 } 3387 else 3388 { 3389 Map.Mouse_Left_Release(cell, x, y, object, ACTION_SELECT); 3390 } 3391 3392 } 3393 else 3394 { 3395 Unselect_All(); 3396 } 3397 } 3398 3399 return 0; 3400 } 3401 3402 /*********************************************************************************************** 3403 * DisplayClass::TacticalClass::Command_Object -- Commanding Units * 3404 * * 3405 * Issues a command to the currently selected unit. * 3406 * * 3407 * * 3408 * INPUT: flags -- The gadget event flags that triggered the call to this function. * 3409 * * 3410 * key -- A reference to the keyboard event (if any). * 3411 * * 3412 * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? * 3413 * * 3414 * WARNINGS: none * 3415 * * 3416 * HISTORY: * 3417 * 2019/09/17 JAS * 3418 *=============================================================================================*/ 3419 int DisplayClass::TacticalClass::Command_Object(unsigned flags, KeyNumType & key) 3420 { 3421 int x, y; // Sub cell pixel coordinates. 3422 bool edge = false; 3423 if (flags & (LEFTPRESS | LEFTRELEASE | RIGHTPRESS | RIGHTRELEASE)) { 3424 x = Keyboard->MouseQX; 3425 y = Keyboard->MouseQY; 3426 } 3427 else { 3428 x = Get_Mouse_X(); 3429 y = Get_Mouse_Y(); 3430 3431 if (x == 0 || y == 199 || x == 319) edge = true; 3432 } 3433 COORDINATE coord = Map.Pixel_To_Coord(x, y); 3434 CELL cell = Coord_Cell(coord); 3435 3436 ActionType action = ACTION_NONE; 3437 3438 if (coord) { 3439 bool shadow = (!Map[cell].Is_Mapped(PlayerPtr) && !Debug_Unshroud); // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM 3440 x -= Map.TacPixelX; 3441 y -= Map.TacPixelY; 3442 3443 /* 3444 ** Cause any displayed cursor to move along with the mouse cursor. 3445 */ 3446 if (cell != Map.ZoneCell) { 3447 Map.Set_Cursor_Pos(cell); 3448 } 3449 3450 ObjectClass* object = nullptr; 3451 3452 /* 3453 ** Determine the object that the mouse is currently over. 3454 */ 3455 if (!shadow) { 3456 object = Map.Close_Object(coord); 3457 } 3458 3459 if (CurrentObject.Count()) { 3460 if (object) { 3461 action = Best_Object_Action(object); 3462 } 3463 else { 3464 action = Best_Object_Action(cell); 3465 } 3466 } 3467 3468 if (action != ACTION_SELECT) 3469 { 3470 Map.Mouse_Left_Release(cell, x, y, object, action); 3471 } 3472 } 3473 return 0; 3474 } 3475 3476 3477 /*********************************************************************************************** 3478 * DisplayClass::Mouse_Right_Press -- Handles the right mouse button press. * 3479 * * 3480 * This routine is called when the right mouse button is pressed. This action is supposed * 3481 * to cancel whatever mode or process is active. If there is nothing to cancel, then it * 3482 * will default to unselecting any units that might be currently selected. * 3483 * * 3484 * INPUT: none * 3485 * * 3486 * OUTPUT: none * 3487 * * 3488 * WARNINGS: none * 3489 * * 3490 * HISTORY: * 3491 * 02/24/1995 JLB : Created. * 3492 *=============================================================================================*/ 3493 void DisplayClass::Mouse_Right_Press(void) 3494 { 3495 if (PendingObjectPtr && PendingObjectPtr->Is_Techno()) { 3496 //PendingObjectPtr->Transmit_Message(RADIO_OVER_OUT); 3497 PendingObjectPtr = 0; 3498 PendingObject = 0; 3499 PendingHouse = HOUSE_NONE; 3500 Set_Cursor_Shape(0); 3501 } else { 3502 if (IsRepairMode) { 3503 IsRepairMode = false; 3504 } else { 3505 if (IsSellMode) { 3506 IsSellMode = false; 3507 } else { 3508 if (IsTargettingMode != SPC_NONE) { 3509 IsTargettingMode = SPC_NONE; 3510 } else { 3511 Unselect_All(); 3512 } 3513 } 3514 } 3515 } 3516 3517 // If it breaks... call 228. 3518 Set_Default_Mouse(MOUSE_NORMAL, Map.IsSmall); 3519 } 3520 3521 3522 /*********************************************************************************************** 3523 * DisplayClass::Mouse_Left_Up -- Handles the left mouse "cruising" over the map. * 3524 * * 3525 * This routine is called continuously while the mouse is over the tactical map but there * 3526 * are no mouse buttons pressed. Typically, this adjusts the mouse shape and the pop-up * 3527 * help text. * 3528 * * 3529 * INPUT: shadow -- Is the mouse hovering over shadowed terrain? * 3530 * * 3531 * object -- Pointer to the object that the mouse is currently over (may be NULL). * 3532 * * 3533 * action -- This is the action that the currently selected object (if any) will * 3534 * perform if the left mouse button were clicked at this location. * 3535 * * 3536 * OUTPUT: none * 3537 * * 3538 * WARNINGS: none * 3539 * * 3540 * HISTORY: * 3541 * 02/24/1995 JLB : Created. * 3542 * 07/05/1995 JLB : Removed pop up help text for shadow and terrain after #3. * 3543 *=============================================================================================*/ 3544 void DisplayClass::Mouse_Left_Up(CELL cell, bool shadow, ObjectClass * object, ActionType action, bool wsmall) 3545 { 3546 IsTentative = false; 3547 3548 TARGET target = TARGET_NONE; 3549 if (object != NULL) { 3550 target = object->As_Target(); 3551 } else { 3552 if (cell != -1) { 3553 target = As_Target(cell); 3554 } 3555 } 3556 3557 /* 3558 ** Don't allow selection of an object that is located in shadowed terrain. 3559 ** In fact, just show the normal move cursor in order to keep the shadowed 3560 ** terrain a mystery. 3561 */ 3562 if (shadow) { 3563 switch (action) { 3564 case ACTION_NO_DEPLOY: 3565 Set_Default_Mouse(MOUSE_NO_DEPLOY, wsmall); 3566 break; 3567 3568 case ACTION_NO_ENTER: 3569 Set_Default_Mouse(MOUSE_NO_ENTER, wsmall); 3570 break; 3571 3572 case ACTION_NO_GREPAIR: 3573 Set_Default_Mouse(MOUSE_NO_GREPAIR, wsmall); 3574 break; 3575 3576 case ACTION_DAMAGE: 3577 Set_Default_Mouse(MOUSE_NORMAL, wsmall); 3578 break; 3579 3580 case ACTION_GREPAIR: 3581 Set_Default_Mouse(MOUSE_NORMAL, wsmall); 3582 break; 3583 3584 case ACTION_GUARD_AREA: 3585 Set_Default_Mouse(MOUSE_AREA_GUARD, wsmall); 3586 break; 3587 3588 case ACTION_NONE: 3589 Set_Default_Mouse(MOUSE_NORMAL, wsmall); 3590 break; 3591 3592 case ACTION_NO_SELL: 3593 case ACTION_SELL: 3594 case ACTION_SELL_UNIT: 3595 Set_Default_Mouse(MOUSE_NO_SELL_BACK, wsmall); 3596 break; 3597 3598 case ACTION_NO_REPAIR: 3599 case ACTION_REPAIR: 3600 Set_Default_Mouse(MOUSE_NO_REPAIR, wsmall); 3601 break; 3602 3603 case ACTION_NUKE_BOMB: 3604 Set_Default_Mouse(MOUSE_NUCLEAR_BOMB, wsmall); 3605 break; 3606 3607 case ACTION_AIR_STRIKE: 3608 case ACTION_PARA_BOMB: 3609 case ACTION_PARA_INFANTRY: 3610 case ACTION_SPY_MISSION: 3611 case ACTION_IRON_CURTAIN: 3612 Set_Default_Mouse(MOUSE_AIR_STRIKE, wsmall); 3613 break; 3614 3615 case ACTION_CHRONOSPHERE: 3616 Set_Default_Mouse(MOUSE_CHRONO_SELECT, wsmall); 3617 break; 3618 3619 case ACTION_CHRONO2: 3620 Set_Default_Mouse(MOUSE_CHRONO_DEST, wsmall); 3621 break; 3622 3623 case ACTION_HEAL: 3624 Set_Default_Mouse(MOUSE_HEAL, wsmall); 3625 break; 3626 3627 case ACTION_NOMOVE: 3628 if (CurrentObject.Count()) { 3629 MouseType mouse_type = MOUSE_NO_MOVE; 3630 for (int i = 0; i < CurrentObject.Count(); ++i) { 3631 if (CurrentObject[i]->What_Am_I() != RTTI_AIRCRAFT) { 3632 mouse_type = MOUSE_CAN_MOVE; 3633 break; 3634 } 3635 } 3636 Set_Default_Mouse(mouse_type, wsmall); 3637 break; 3638 } 3639 // Fall into next case for non aircraft object types. 3640 3641 default: 3642 Set_Default_Mouse(MOUSE_CAN_MOVE, wsmall); 3643 break; 3644 3645 } 3646 } else { 3647 3648 /* 3649 ** Change the mouse shape according to the default action that will occur 3650 ** if the mouse button were clicked at this location. 3651 */ 3652 switch (action) { 3653 case ACTION_NO_DEPLOY: 3654 Set_Default_Mouse(MOUSE_NO_DEPLOY, wsmall); 3655 break; 3656 3657 case ACTION_NO_ENTER: 3658 Set_Default_Mouse(MOUSE_NO_ENTER, wsmall); 3659 break; 3660 3661 case ACTION_NO_GREPAIR: 3662 Set_Default_Mouse(MOUSE_NO_GREPAIR, wsmall); 3663 break; 3664 3665 case ACTION_DAMAGE: 3666 Set_Default_Mouse(MOUSE_DAMAGE, wsmall); 3667 break; 3668 3669 case ACTION_GREPAIR: 3670 Set_Default_Mouse(MOUSE_GREPAIR, wsmall); 3671 break; 3672 3673 case ACTION_TOGGLE_SELECT: 3674 case ACTION_SELECT: 3675 Set_Default_Mouse(MOUSE_CAN_SELECT, wsmall); 3676 break; 3677 3678 case ACTION_MOVE: 3679 Set_Default_Mouse(MOUSE_CAN_MOVE, wsmall); 3680 break; 3681 3682 case ACTION_GUARD_AREA: 3683 Set_Default_Mouse(MOUSE_AREA_GUARD, wsmall); 3684 break; 3685 3686 case ACTION_ATTACK: 3687 if (Target_Legal(target) && CurrentObject.Count() == 1 && CurrentObject[0]->Is_Techno() && ((TechnoClass *)CurrentObject[0])->In_Range(target, 0)) { 3688 Set_Default_Mouse(MOUSE_STAY_ATTACK, wsmall); 3689 break; 3690 } 3691 // fall into next case. 3692 3693 case ACTION_HARVEST: 3694 Set_Default_Mouse(MOUSE_CAN_ATTACK, wsmall); 3695 break; 3696 3697 case ACTION_SABOTAGE: 3698 Set_Default_Mouse(MOUSE_DEMOLITIONS, wsmall); 3699 break; 3700 3701 case ACTION_ENTER: 3702 case ACTION_CAPTURE: 3703 Set_Default_Mouse(MOUSE_ENTER, wsmall); 3704 break; 3705 3706 case ACTION_NOMOVE: 3707 Set_Default_Mouse(MOUSE_NO_MOVE, wsmall); 3708 break; 3709 3710 case ACTION_NO_SELL: 3711 Set_Default_Mouse(MOUSE_NO_SELL_BACK, wsmall); 3712 break; 3713 3714 case ACTION_NO_REPAIR: 3715 Set_Default_Mouse(MOUSE_NO_REPAIR, wsmall); 3716 break; 3717 3718 case ACTION_SELF: 3719 Set_Default_Mouse(MOUSE_DEPLOY, wsmall); 3720 break; 3721 3722 case ACTION_REPAIR: 3723 Set_Default_Mouse(MOUSE_REPAIR, wsmall); 3724 break; 3725 3726 case ACTION_SELL_UNIT: 3727 Set_Default_Mouse(MOUSE_SELL_UNIT, wsmall); 3728 break; 3729 3730 case ACTION_SELL: 3731 Set_Default_Mouse(MOUSE_SELL_BACK, wsmall); 3732 break; 3733 3734 case ACTION_NUKE_BOMB: 3735 Set_Default_Mouse(MOUSE_NUCLEAR_BOMB, wsmall); 3736 break; 3737 3738 case ACTION_AIR_STRIKE: 3739 case ACTION_PARA_BOMB: 3740 case ACTION_PARA_INFANTRY: 3741 case ACTION_SPY_MISSION: 3742 case ACTION_IRON_CURTAIN: 3743 Set_Default_Mouse(MOUSE_AIR_STRIKE, wsmall); 3744 break; 3745 3746 case ACTION_CHRONOSPHERE: 3747 Set_Default_Mouse(MOUSE_CHRONO_SELECT, wsmall); 3748 break; 3749 3750 case ACTION_CHRONO2: 3751 Set_Default_Mouse(MOUSE_CHRONO_DEST, wsmall); 3752 break; 3753 3754 case ACTION_HEAL: 3755 Set_Default_Mouse(MOUSE_HEAL, wsmall); 3756 break; 3757 3758 case ACTION_TOGGLE_PRIMARY: 3759 Set_Default_Mouse(MOUSE_DEPLOY, wsmall); 3760 break; 3761 3762 default: 3763 Set_Default_Mouse(MOUSE_NORMAL, wsmall); 3764 break; 3765 } 3766 } 3767 #if 0 3768 /* 3769 ** Never display help text if the mouse is held over the radar map. 3770 */ 3771 if (wsmall) { 3772 return; 3773 } 3774 3775 /* 3776 ** Give a generic help message when over shadow terrain. 3777 */ 3778 if (shadow) { 3779 // if (Scen.Scenario < 4) { 3780 Help_Text(TXT_SHADOW); 3781 // } else { 3782 // Help_Text(TXT_NONE); 3783 // } 3784 } else { 3785 3786 /* 3787 ** If the mouse is held over objects on the map, then help text may 3788 ** pop up that tells what the object is. This call informs the help 3789 ** system of the text name for the object under the mouse. 3790 */ 3791 if (object != NULL) { 3792 int text; 3793 int color = LTGREY; 3794 3795 /* 3796 ** Fetch the appropriate background color for help text. 3797 */ 3798 if (PlayerPtr->Is_Ally(object)) { 3799 color = GREEN; 3800 } else { 3801 if (object->Owner() == HOUSE_NONE || object->Owner() == HOUSE_NEUTRAL) { 3802 color = LTGREY; 3803 } else { 3804 color = PINK; 3805 } 3806 } 3807 3808 /* 3809 ** Fetch the name of the object. If it is an enemy object, then 3810 ** the exact identity is glossed over with a generic text. 3811 */ 3812 text = object->Full_Name(); 3813 if (object->Is_Techno() && !((TechnoTypeClass const &)object->Class_Of()).IsNominal) { 3814 3815 if (!((TechnoClass *)object)->House->Is_Ally(PlayerPtr)) { 3816 // if (!PlayerPtr->Is_Ally(object)) { 3817 switch (object->What_Am_I()) { 3818 case RTTI_INFANTRY: 3819 text = TXT_ENEMY_SOLDIER; 3820 break; 3821 3822 case RTTI_UNIT: 3823 text = TXT_ENEMY_VEHICLE; 3824 break; 3825 3826 case RTTI_BUILDING: 3827 text = TXT_ENEMY_STRUCTURE; 3828 break; 3829 } 3830 } 3831 } 3832 3833 if (/*Scen.Scenario > 3 ||*/ object->What_Am_I() != RTTI_TERRAIN) { 3834 Help_Text(text, -1, -1, color); 3835 } else { 3836 Help_Text(TXT_NONE); 3837 } 3838 } else { 3839 if ((*this)[cell].Land_Type() == LAND_TIBERIUM) { 3840 Help_Text(TXT_MINERALS); 3841 } else { 3842 Help_Text(TXT_NONE); 3843 } 3844 } 3845 } 3846 #endif 3847 } 3848 3849 3850 /*********************************************************************************************** 3851 * DisplayClass::Mouse_Left_Release -- Handles the left mouse button release. * 3852 * * 3853 * This routine is called when the left mouse button is released over the tactical map. * 3854 * The release event is the workhorse of the game. Most actions occur at the moment of * 3855 * mouse release. * 3856 * * 3857 * INPUT: cell -- The cell that the mouse is over. * 3858 * * 3859 * x,y -- The mouse pixel coordinate. * 3860 * * 3861 * object -- Pointer to the object that the mouse is over. * 3862 * * 3863 * action -- The action that the currently selected object (if any) will * 3864 * perform. * 3865 * * 3866 * OUTPUT: none * 3867 * * 3868 * WARNINGS: none * 3869 * * 3870 * HISTORY: * 3871 * 02/24/1995 JLB : Created. * 3872 * 03/27/1995 JLB : Handles sell and repair actions. * 3873 *=============================================================================================*/ 3874 void DisplayClass::Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * object, ActionType action, bool wsmall) 3875 { 3876 if (PendingObjectPtr) { 3877 /* 3878 ** Try to place the pending object onto the map. 3879 */ 3880 if (ProximityCheck) { 3881 OutList.Add(EventClass(EventClass::PLACE, PendingObjectPtr->What_Am_I(), cell + ZoneOffset)); 3882 } else { 3883 Speak(VOX_DEPLOY); 3884 } 3885 3886 } else { 3887 3888 if (IsRubberBand) { 3889 Refresh_Band(); 3890 Select_These(XYP_Coord(BandX, BandY), XYP_Coord(x, y)); 3891 3892 Set_Default_Mouse(MOUSE_NORMAL, wsmall); 3893 IsRubberBand = false; 3894 IsTentative = false; 3895 Map.DisplayClass::IsToRedraw = true; 3896 Map.Flag_To_Redraw(false); 3897 3898 } else { 3899 3900 /* 3901 ** Toggle the select state of the object. 3902 */ 3903 if (action == ACTION_TOGGLE_SELECT) { 3904 if (!object || !CurrentObject.Count()) { 3905 action = ACTION_SELECT; 3906 } else { 3907 if (object->Is_Selected_By_Player()) { 3908 object->Unselect(); 3909 } else { 3910 object->Select(); 3911 } 3912 } 3913 } 3914 3915 /* 3916 ** Selection of other object action. 3917 */ 3918 if (action == ACTION_SELECT || (action == ACTION_NONE && object && object->Class_Of().IsSelectable && !object->Is_Selected_By_Player())) { 3919 if (object->Is_Selected_By_Player()) { 3920 object->Unselect(); 3921 } 3922 if (object->Select()) { 3923 Unselect_All_Except(object); 3924 Set_Default_Mouse(MOUSE_NORMAL, wsmall); 3925 } 3926 } 3927 3928 /* 3929 ** If an action was detected as possible, then pass this action event 3930 ** to all selected objects. 3931 */ 3932 if (action != ACTION_NONE && action != ACTION_SELECT && action != ACTION_TOGGLE_SELECT) { 3933 3934 /* 3935 ** Pass the action to all the selected objects. But first, redetermine 3936 ** what action that object should perform. This, seemingly redundant 3937 ** process, is necessary since multiple objects could be selected and each 3938 ** might perform a different action when the click occurs. 3939 */ 3940 bool doflash = true; 3941 AllowVoice = true; 3942 FormMove = false; 3943 FormSpeed = SPEED_WHEEL; 3944 FormMaxSpeed = MPH_LIGHT_SPEED; 3945 3946 if ( (action == ACTION_MOVE || action == ACTION_NOMOVE) && CurrentObject.Count()) { 3947 3948 /* 3949 ** Scan all units. If any are selected that shouldn't be, or aren't 3950 ** selected but should be, then this is not a formation move. 3951 */ 3952 int group = 254; // init to invalid group # 3953 3954 if (CurrentObject[0]->Is_Foot()) { 3955 group = ((FootClass *)CurrentObject[0])->Group; 3956 } 3957 3958 /* 3959 ** Presume this is a formation move unless something is detected 3960 ** that will prevent it. 3961 */ 3962 FormMove = true; 3963 3964 /* 3965 ** First scan through all the selected units to make sure that they 3966 ** are all of the same team and have been assigned a particular formation 3967 */ 3968 for (int index = 0; index < CurrentObject.Count(); index++) { 3969 ObjectClass const * tobject = CurrentObject[index]; 3970 3971 /* 3972 ** Only moveable (i.e., FootClass) objects can ever be in a formation 3973 ** so if a selected object isn't of a FootClass type then it can't be 3974 ** a formation move. 3975 */ 3976 if (tobject->Is_Foot() == false) { 3977 FormMove = false; 3978 break; 3979 } 3980 3981 /* 3982 ** If the object is not part of the same team as the rest of the 3983 ** selected group, or it just plain has never been assigned a 3984 ** formation offset, then it can't be a formation move. 3985 */ 3986 FootClass const * foot = (FootClass *)tobject; 3987 if (foot->Group != group || foot->XFormOffset == 0x80000000) { 3988 FormMove = false; 3989 break; 3990 } 3991 3992 /* 3993 ** Determine the formation speed on the presumption that this 3994 ** will turn out to be a formation move. 3995 */ 3996 MPHType maxspeed = foot->Techno_Type_Class()->MaxSpeed; 3997 if (maxspeed < FormMaxSpeed) { 3998 FormMaxSpeed = maxspeed; 3999 FormSpeed = foot->Techno_Type_Class()->Speed; 4000 } 4001 } 4002 4003 /* 4004 ** Loop through all objects (that can theoretically be part of a team) and 4005 ** if there are any that are part of the currently selected team, but 4006 ** are not currently selected themselves, then this will force this move 4007 ** to NOT be a formation move. 4008 */ 4009 if (FormMove) { 4010 for (int index = 0; index < ::Logic.Count(); index++) { 4011 ObjectClass const * obj = ::Logic[index]; 4012 4013 /* 4014 ** If the object is selected, then it has already been scanned 4015 ** by the previous loop. 4016 */ 4017 if (obj->Is_Selected_By_Player()) continue; 4018 4019 /* 4020 ** Only consider footclass objects. 4021 */ 4022 if (!obj->Is_Foot()) continue; 4023 4024 FootClass const * foot = (FootClass *)obj; 4025 4026 /* 4027 ** Only consider objects that are owned by the player. 4028 */ 4029 if (!foot->Is_Owned_By_Player()) continue; 4030 4031 /* 4032 ** If another member of this team has been discovered and 4033 ** it isn't selected, then the formation move cannot take 4034 ** place. 4035 */ 4036 if (foot->Group == group) { 4037 FormMove = false; 4038 break; 4039 } 4040 } 4041 } 4042 } 4043 4044 for (int index = 0; index < CurrentObject.Count(); index++) { 4045 ObjectClass * tobject = CurrentObject[index]; 4046 4047 if (object != NULL) { 4048 tobject->Active_Click_With(tobject->What_Action(object), object); 4049 } else { 4050 4051 /* 4052 ** Trap for formation moves: if this unit is part of a 4053 ** formation (being part of a team qualifies) and they're 4054 ** told to move, adjust the target destination so they stay 4055 ** in formation when they arrive. 4056 */ 4057 CELL newmove = cell; 4058 int whatami = tobject->What_Am_I(); 4059 if (action == ACTION_MOVE && tobject->Is_Foot()) { 4060 int oldisform; 4061 FootClass * foot = (FootClass *)tobject; 4062 oldisform = foot->IsFormationMove; 4063 foot->IsFormationMove = FormMove; 4064 if (FormMove && foot->Group != 255 ) { 4065 newmove = foot->Adjust_Dest(cell); 4066 } 4067 foot->IsFormationMove = oldisform; 4068 } 4069 tobject->Active_Click_With(tobject->What_Action(cell), newmove); 4070 } 4071 AllowVoice = false; 4072 } 4073 AllowVoice = true; 4074 FormMove = false; 4075 4076 if (action == ACTION_REPAIR && object->What_Am_I() == RTTI_BUILDING) { 4077 OutList.Add(EventClass(EventClass::REPAIR, TargetClass(object))); 4078 } 4079 if (action == ACTION_SELL_UNIT && object) { 4080 switch (object->What_Am_I()) { 4081 case RTTI_AIRCRAFT: 4082 case RTTI_UNIT: 4083 OutList.Add(EventClass(EventClass::SELL, TargetClass(object))); 4084 break; 4085 4086 default: 4087 break; 4088 } 4089 4090 } 4091 if (action == ACTION_SELL) { 4092 if (object) { 4093 OutList.Add(EventClass(EventClass::SELL, TargetClass(object))); 4094 } else { 4095 OutList.Add(EventClass(EventClass::SELLCELL, cell)); 4096 // OutList.Add(EventClass(EventClass::SELL, ::As_Target(cell))); 4097 } 4098 } 4099 4100 if (action == ACTION_NUKE_BOMB) { 4101 OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_NUCLEAR_BOMB, cell)); 4102 } 4103 4104 if (action == ACTION_PARA_BOMB) { 4105 OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_PARA_BOMB, cell)); 4106 } 4107 if (action == ACTION_PARA_INFANTRY) { 4108 OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_PARA_INFANTRY, cell)); 4109 } 4110 if (action == ACTION_SPY_MISSION) { 4111 OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_SPY_MISSION, cell)); 4112 } 4113 if (action == ACTION_IRON_CURTAIN) { 4114 OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_IRON_CURTAIN, cell)); 4115 } 4116 if (action == ACTION_CHRONOSPHERE) { 4117 OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_CHRONOSPHERE, cell)); 4118 } 4119 if (action == ACTION_CHRONO2) { 4120 OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_CHRONO2, cell)); 4121 } 4122 } 4123 4124 IsTentative = false; 4125 } 4126 } 4127 } 4128 4129 4130 /*********************************************************************************************** 4131 * DisplayClass::Mouse_Left_Press -- Handles the left mouse button press. * 4132 * * 4133 * Handle the left mouse button press while over the tactical map. If it isn't is * 4134 * repair or sell mode, then a tentative transition to rubber band mode is flagged. If the * 4135 * mouse moves a sufficient distance from this recorded position, then rubber band mode * 4136 * is officially started. * 4137 * * 4138 * INPUT: x,y -- The mouse coordinates at the time of the press. * 4139 * * 4140 * OUTPUT: none * 4141 * * 4142 * WARNINGS: none * 4143 * * 4144 * HISTORY: * 4145 * 02/24/1995 JLB : Created. * 4146 *=============================================================================================*/ 4147 void DisplayClass::Mouse_Left_Press(int x, int y) 4148 { 4149 if (!IsRepairMode && !IsSellMode && IsTargettingMode == SPC_NONE && !PendingObject) { 4150 IsTentative = true; 4151 BandX = x; 4152 BandY = y; 4153 NewX = x; 4154 NewY = y; 4155 } 4156 } 4157 4158 4159 /*********************************************************************************************** 4160 * DisplayClass::Mouse_Left_Held -- Handles the left button held down. * 4161 * * 4162 * This routine is called continuously while the left mouse button is held down over * 4163 * the tactical map. This handles the rubber band mode detection and dragging. * 4164 * * 4165 * INPUT: x,y -- The mouse coordinate. * 4166 * * 4167 * OUTPUT: none * 4168 * * 4169 * WARNINGS: none * 4170 * * 4171 * HISTORY: * 4172 * 02/24/1995 JLB : Created. * 4173 *=============================================================================================*/ 4174 void DisplayClass::Mouse_Left_Held(int x, int y) 4175 { 4176 if (IsRubberBand) { 4177 if (x != NewX || y != NewY) { 4178 x = Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1); 4179 y = Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1); 4180 Refresh_Band(); 4181 NewX = x; 4182 NewY = y; 4183 IsToRedraw = true; 4184 Flag_To_Redraw(false); 4185 } 4186 } else { 4187 4188 /* 4189 ** If the mouse is still held down while a tentative extended select is possible, then 4190 ** check to see if the mouse has moved a sufficient distance in order to activate 4191 ** extended select mode. 4192 */ 4193 if (IsTentative) { 4194 4195 /* 4196 ** The mouse must have moved a minimum distance before rubber band mode can be 4197 ** initiated. 4198 */ 4199 if (ABS(x - BandX) > 4 || ABS(y - BandY) > 4) { 4200 IsRubberBand = true; 4201 x = Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1); 4202 y = Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1); 4203 NewX = x; 4204 NewY = y; 4205 IsToRedraw = true; 4206 Flag_To_Redraw(false); 4207 4208 /* 4209 ** Stretching the rubber band requires all objects to be redrawn. 4210 */ 4211 int index; 4212 for (index = 0; index < Layer[LAYER_TOP].Count(); index++) { 4213 Layer[LAYER_TOP][index]->Mark(MARK_CHANGE); 4214 } 4215 for (index = 0; index < Layer[LAYER_AIR].Count(); index++) { 4216 Layer[LAYER_AIR][index]->Mark(MARK_CHANGE); 4217 } 4218 4219 } 4220 } 4221 } 4222 } 4223 4224 4225 // Needed to accomodate Glyphx client sidebar. ST - 4/12/2019 5:29PM 4226 extern int GlyphXClientSidebarWidthInLeptons; 4227 4228 /*********************************************************************************************** 4229 * DisplayClass::Set_Tactical_Position -- Sets the tactical view position. * 4230 * * 4231 * This routine is used to set the tactical view position. The requested position is * 4232 * clipped to the map dimensions as necessary. * 4233 * * 4234 * INPUT: coord -- The coordinate desired for the upper left corner. * 4235 * * 4236 * OUTPUT: none * 4237 * * 4238 * WARNINGS: none * 4239 * * 4240 * HISTORY: * 4241 * 08/13/1995 JLB : Created. * 4242 *=============================================================================================*/ 4243 void DisplayClass::Set_Tactical_Position(COORDINATE coord) 4244 { 4245 /* 4246 ** Bound the desired location to fit the legal map edges. 4247 */ 4248 int xx = 0;// (int)Coord_X(coord) - (int)Cell_To_Lepton(MapCellX); 4249 int yy = 0;// (int)Coord_Y(coord) - (int)Cell_To_Lepton(MapCellY); 4250 4251 Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth) + GlyphXClientSidebarWidthInLeptons, Cell_To_Lepton(MapCellHeight)); // Needed to accomodate Glyphx client sidebar. ST - 4/12/2019 5:29PM 4252 // Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth), Cell_To_Lepton(MapCellHeight)); 4253 coord = XY_Coord(xx + Cell_To_Lepton(MapCellX), yy + Cell_To_Lepton(MapCellY)); 4254 4255 if (ScenarioInit) { 4256 TacticalCoord = coord; 4257 } 4258 DesiredTacticalCoord = coord; 4259 4260 IsToRedraw = true; 4261 Flag_To_Redraw(false); 4262 } 4263 4264 4265 /*********************************************************************************************** 4266 * DisplayClass::Compute_Start_Pos -- Computes player's start pos from unit coords. * 4267 * * 4268 * Use this function in multiplayer games, to compute the scenario starting Tactical Pos. * 4269 * * 4270 * INPUT: none * 4271 * * 4272 * OUTPUT: x, y -- Player starting location * 4273 * * 4274 * WARNINGS: none * 4275 * * 4276 * HISTORY: * 4277 * 02/28/1995 JLB : Commented. * 4278 * 06/26/1995 JLB : Fixed building loop. * 4279 * 10/20/1996 JLB : Doesn't wrap. * 4280 *=============================================================================================*/ 4281 void DisplayClass::Compute_Start_Pos(long& x, long& y) 4282 { 4283 /* 4284 ** Find the summation cell-x & cell-y for all the player's units, infantry, 4285 ** and buildings. Buildings are weighted so that they count 16 times more 4286 ** than units or infantry. 4287 */ 4288 x = 0; 4289 y = 0; 4290 long num = 0; 4291 int i; 4292 for (i = 0; i < Infantry.Count(); i++) { 4293 InfantryClass * infp = Infantry.Ptr(i); 4294 if (!infp->IsInLimbo && infp->Is_Owned_By_Player()) { 4295 x += (long)Coord_XCell(infp->Coord); 4296 y += (long)Coord_YCell(infp->Coord); 4297 num++; 4298 } 4299 } 4300 4301 for (i = 0; i < Units.Count(); i++) { 4302 UnitClass * unitp = Units.Ptr(i); 4303 if (!unitp->IsInLimbo && unitp->Is_Owned_By_Player()) { 4304 x += (long)Coord_XCell(unitp->Coord); 4305 y += (long)Coord_YCell(unitp->Coord); 4306 num++; 4307 } 4308 } 4309 4310 for (i = 0; i < Buildings.Count(); i++) { 4311 BuildingClass * bldgp = Buildings.Ptr(i); 4312 if (!bldgp->IsInLimbo && bldgp->Is_Owned_By_Player()) { 4313 x += (((long)Coord_XCell(bldgp->Coord)) * 16); 4314 y += (((long)Coord_YCell(bldgp->Coord)) * 16); 4315 num += 16; 4316 } 4317 } 4318 4319 for (i = 0; i < Vessels.Count(); i++) { 4320 VesselClass * bldgp = Vessels.Ptr(i); 4321 if (!bldgp->IsInLimbo && bldgp->Is_Owned_By_Player()) { 4322 x += (((long)Coord_XCell(bldgp->Coord))); 4323 y += (((long)Coord_YCell(bldgp->Coord))); 4324 num++; 4325 } 4326 } 4327 4328 /* 4329 ** Divide each coord by 'num' to compute the average value 4330 */ 4331 if (num > 0) { 4332 x /= num; 4333 } else { 4334 x = 0; 4335 } 4336 4337 if (num > 0) { 4338 y /= num; 4339 } else { 4340 y = 0; 4341 } 4342 } 4343 4344 4345 /*********************************************************************************************** 4346 * DisplayClass::Sell_Mode_Control -- Controls the sell mode. * 4347 * * 4348 * This routine will control the sell mode for the player. * 4349 * * 4350 * INPUT: control -- The mode to set the sell state to. * 4351 * 0 = Turn sell mode off. * 4352 * 1 = Turn sell mode on. * 4353 * -1 = Toggle sell mode. * 4354 * * 4355 * OUTPUT: none * 4356 * * 4357 * WARNINGS: none * 4358 * * 4359 * HISTORY: * 4360 * 07/08/1995 JLB : Created. * 4361 *=============================================================================================*/ 4362 void DisplayClass::Sell_Mode_Control(int control) 4363 { 4364 bool mode = IsSellMode; 4365 switch (control) { 4366 case 0: 4367 mode = false; 4368 break; 4369 4370 case -1: 4371 mode = (IsSellMode == false); 4372 break; 4373 4374 case 1: 4375 mode = true; 4376 break; 4377 } 4378 4379 if (mode != IsSellMode && !PendingObject) { 4380 IsRepairMode = false; 4381 if (mode && PlayerPtr->BScan) { 4382 IsSellMode = true; 4383 Unselect_All(); 4384 } else { 4385 IsSellMode = false; 4386 Revert_Mouse_Shape(); 4387 } 4388 } 4389 } 4390 4391 4392 /*********************************************************************************************** 4393 * DisplayClass::Repair_Mode_Control -- Controls the repair mode. * 4394 * * 4395 * This routine is used to control the repair mode for the player. * 4396 * * 4397 * INPUT: control -- The mode to set the repair to. * 4398 * 0 = Turn repair off. * 4399 * 1 = Turn repair on. * 4400 * -1= Toggle repair state. * 4401 * * 4402 * OUTPUT: none * 4403 * * 4404 * WARNINGS: none * 4405 * * 4406 * HISTORY: * 4407 * 07/08/1995 JLB : Created. * 4408 *=============================================================================================*/ 4409 void DisplayClass::Repair_Mode_Control(int control) 4410 { 4411 bool mode = IsRepairMode; 4412 switch (control) { 4413 case 0: 4414 mode = false; 4415 break; 4416 4417 case -1: 4418 mode = (IsRepairMode == false); 4419 break; 4420 4421 case 1: 4422 mode = true; 4423 break; 4424 } 4425 4426 if (mode != IsRepairMode && !PendingObject) { 4427 IsSellMode = false; 4428 if (mode && PlayerPtr->BScan) { 4429 IsRepairMode = true; 4430 Unselect_All(); 4431 } else { 4432 IsRepairMode = false; 4433 Revert_Mouse_Shape(); 4434 } 4435 } 4436 } 4437 4438 4439 /*********************************************************************************************** 4440 * DisplayClass::In_View -- Determines if cell is visible on screen. * 4441 * * 4442 * Use this routine to determine if the specified cell is visible on * 4443 * the display. This is a useful fact, since many display operations * 4444 * can be skipped if the cell is not visible. * 4445 * * 4446 * INPUT: cell -- The cell number to check. * 4447 * * 4448 * OUTPUT: bool; Is this cell visible on the display? * 4449 * * 4450 * WARNINGS: none * 4451 * * 4452 * HISTORY: * 4453 * 04/30/1994 JLB : Created. * 4454 * 04/30/1994 JLB : Converted to member function. * 4455 *=============================================================================================*/ 4456 bool DisplayClass::In_View(register CELL cell) const 4457 { 4458 if (cell & 0xC000) return(false); 4459 4460 COORDINATE coord = Coord_Whole(Cell_Coord(cell)); 4461 COORDINATE tcoord = Coord_Whole(TacticalCoord); 4462 4463 if ((Coord_X(coord) - Coord_X(tcoord)) > TacLeptonWidth+CELL_LEPTON_W-1) return(false); 4464 if ((Coord_Y(coord) - Coord_Y(tcoord)) > TacLeptonHeight+CELL_LEPTON_H-1) return(false); 4465 return(true); 4466 } 4467 4468 4469 /*********************************************************************************************** 4470 * DisplayClass::Closest_Free_Spot -- Finds the closest cell sub spot that is free. * 4471 * * 4472 * Use this routine to find the sub cell spot closest to the coordinate specified that is * 4473 * free from occupation. Typical use of this is for infantry destination calculation. * 4474 * * 4475 * INPUT: coord -- The coordinate to use as the starting point when finding the closest * 4476 * free spot. * 4477 * * 4478 * any -- Ignore occupation and just return the closest sub cell spot? * 4479 * * 4480 * OUTPUT: Returns with the coordinate of the closest free (possibly) sub cell location. * 4481 * * 4482 * WARNINGS: none * 4483 * * 4484 * HISTORY: * 4485 * 09/22/1995 JLB : Created. * 4486 *=============================================================================================*/ 4487 COORDINATE DisplayClass::Closest_Free_Spot(COORDINATE coord, bool any) const 4488 { 4489 if (coord & HIGH_COORD_MASK) { 4490 return(0x00800080); 4491 } 4492 return Map[coord].Closest_Free_Spot(coord, any); 4493 } 4494 4495 4496 /*********************************************************************************************** 4497 * DisplayClass::Is_Spot_Free -- Determines if cell sub spot is free of occupation. * 4498 * * 4499 * Use this routine to determine if the coordinate (rounded to the nearest sub cell * 4500 * position) is free for placement. Typical use of this would be for infantry placement. * 4501 * * 4502 * INPUT: coord -- The coordinate to examine for "freeness". The coordinate is rounded to * 4503 * the nearest free sub cell spot. * 4504 * * 4505 * OUTPUT: Is the sub spot indicated by the coordinate free from previous occupation? * 4506 * * 4507 * WARNINGS: none * 4508 * * 4509 * HISTORY: * 4510 * 09/22/1995 JLB : Created. * 4511 *=============================================================================================*/ 4512 bool DisplayClass::Is_Spot_Free(COORDINATE coord) const 4513 { 4514 // This can't be right. Copy/paste error, maybe? ST - 5/8/2019 4515 //if (coord & HIGH_COORD_MASK) { 4516 // return(0x00800080); 4517 //} 4518 return Map[coord].Is_Spot_Free(CellClass::Spot_Index(coord)); 4519 } 4520 4521 4522 /*********************************************************************************************** 4523 * DisplayClass::Center_Map -- Centers the map about the currently selected objects * 4524 * * 4525 * This routine will average the position of all the selected objects and then center * 4526 * the map about those objects. * 4527 * * 4528 * INPUT: center -- The is an optional center about override coordinate. If specified, * 4529 * then the map will be centered about that coordinate. Otherwise it * 4530 * will center about the average location of all selected objects. * 4531 * * 4532 * OUTPUT: The center coordinate. * 4533 * * 4534 * WARNINGS: The map position changes by this routine. * 4535 * * 4536 * HISTORY: * 4537 * 08/22/1995 JLB : Created. * 4538 * 09/16/1996 JLB : Takes coordinate to center about (as override). * 4539 *=============================================================================================*/ 4540 COORDINATE DisplayClass::Center_Map(COORDINATE center) 4541 { 4542 int x = 0; 4543 // unsigned x = 0; 4544 int y = 0; 4545 // unsigned y = 0; 4546 bool centerit = false; 4547 4548 if (CurrentObject.Count()) { 4549 4550 for (int index = 0; index < CurrentObject.Count(); index++) { 4551 COORDINATE coord = CurrentObject[index]->Center_Coord(); 4552 4553 x += Coord_X(coord); 4554 y += Coord_Y(coord); 4555 } 4556 4557 x /= CurrentObject.Count(); 4558 y /= CurrentObject.Count(); 4559 centerit = true; 4560 } 4561 4562 if (center != 0L) { 4563 x = Coord_X(center); 4564 y = Coord_Y(center); 4565 centerit = true; 4566 } 4567 4568 if (centerit) { 4569 center = XY_Coord(x, y); 4570 4571 x = x - (int)TacLeptonWidth/2; 4572 if (x < Cell_To_Lepton(MapCellX)) x = Cell_To_Lepton(MapCellX); 4573 4574 y = y - (int)TacLeptonHeight/2; 4575 if (y < Cell_To_Lepton(MapCellY)) y = Cell_To_Lepton(MapCellY); 4576 4577 Set_Tactical_Position(XY_Coord(x, y)); 4578 4579 return center; 4580 } 4581 4582 return 0; 4583 } 4584 4585 4586 /*********************************************************************************************** 4587 * DisplayClass::Encroach_Shadow -- Causes the shadow to creep back by one cell. * 4588 * * 4589 * This routine will cause the shadow to creep back by one cell. Multiple calls to this * 4590 * routine will result in the shadow becoming more and more invasive until only the sight * 4591 * range of player controlled units will keep the shadow pushed back. * 4592 * * 4593 * INPUT: none * 4594 * house -- Player to apply shroud to * 4595 * * 4596 * OUTPUT: none * 4597 * * 4598 * WARNINGS: none * 4599 * * 4600 * HISTORY: * 4601 * 10/16/1995 JLB : Created. * 4602 * 08/06/2019 ST: Added house parameter for multiplayer * 4603 *=============================================================================================*/ 4604 void DisplayClass::Encroach_Shadow(HouseClass * house) 4605 { 4606 CELL cell; 4607 for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { 4608 if (!In_Radar(cell)) continue; 4609 4610 CellClass * cellptr = &(*this)[cell]; 4611 if (cellptr->Is_Visible(house) || !cellptr->Is_Mapped(house)) continue; 4612 4613 cellptr->IsToShroud = true; // IsToShroud isn't used outside this function. ST - 8/6/2019 2:28PM 4614 } 4615 4616 /* 4617 ** Mark all shadow edge cells to be fully shrouded. All adjacent mapped 4618 ** cell should become partially shrouded. 4619 */ 4620 for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { 4621 if (!In_Radar(cell)) continue; 4622 4623 if ((*this)[cell].IsToShroud) { 4624 (*this)[cell].IsToShroud = false; 4625 Shroud_Cell(cell, house); 4626 } 4627 } 4628 4629 All_To_Look(house); 4630 4631 Flag_To_Redraw(true); 4632 } 4633 4634 4635 /*********************************************************************************************** 4636 * DisplayClass::Shroud_Cell -- Returns the specified cell into the shrouded condition. * 4637 * * 4638 * This routine is called to add the shroud back to the cell specified. Typical of this * 4639 * would be when the shroud is to regenerate. * 4640 * * 4641 * INPUT: cell -- The cell that the shroud is to be regenerated upon. * 4642 * house -- Player to apply shroud to * 4643 * * 4644 * OUTPUT: none * 4645 * * 4646 * WARNINGS: Adjacent cells might be affected by this routine. The affect is determined * 4647 * according to the legality of the partial shadow artwork. In the illegal cases * 4648 * the adjacent cell might become shrouded as well. * 4649 * * 4650 * HISTORY: * 4651 * 10/17/1995 JLB : Created. * 4652 * 06/17/1996 JLB : Modified to handle the new shadow pieces. * 4653 * 08/06/2019 ST: Added house parameter for multiplayer * 4654 *=============================================================================================*/ 4655 void DisplayClass::Shroud_Cell(CELL cell, HouseClass * house) 4656 { 4657 if (house->IsGPSActive) { 4658 if ( (*this)[cell].Is_Jamming(house) ) { 4659 return; 4660 } 4661 } 4662 if (!In_Radar(cell)) return; 4663 4664 CellClass * cellptr = &(*this)[cell]; 4665 if (cellptr->Is_Mapped(house)) { 4666 4667 cellptr->Set_Mapped(house, false); 4668 cellptr->Set_Visible(house, false); 4669 cellptr->Redraw_Objects(); 4670 4671 /* 4672 ** Check adjacent cells. There might be some weird combination of 4673 ** shrouded cells such that more cells must be shrouded in order for 4674 ** this to work. 4675 */ 4676 for (FacingType dir = FACING_FIRST; dir < FACING_COUNT; dir++) { 4677 CELL c = Adjacent_Cell(cell, dir); 4678 CellClass * cptr = &(*this)[c]; 4679 4680 /* 4681 ** If this adjacent cell must be completely shrouded as a result 4682 ** of the map change, yet it isn't already shrouded, then recursively 4683 ** shroud that cell. 4684 */ 4685 if (c != cell) { 4686 cptr->Set_Visible(house, false); 4687 } 4688 4689 /* 4690 ** Always redraw the cell because, more than likely, the shroud 4691 ** edge will change shape because of the map change. 4692 */ 4693 cptr->Redraw_Objects(); 4694 } 4695 } 4696 } 4697 4698 4699 /*********************************************************************************************** 4700 * DisplayClass::Read_INI -- Reads map control data from INI file. * 4701 * * 4702 * This routine is used to read the map control data from the INI * 4703 * file. * 4704 * * 4705 * INPUT: buffer -- Pointer to the loaded INI file data. * 4706 * * 4707 * OUTPUT: none * 4708 * * 4709 * WARNINGS: The TriggerClass INI data must have been read before calling this function. * 4710 * * 4711 * HISTORY: * 4712 * 05/27/1994 JLB : Created. * 4713 *=============================================================================================*/ 4714 void DisplayClass::Read_INI(CCINIClass & ini) 4715 { 4716 /* 4717 ** Read the map dimensions. 4718 */ 4719 char const * const name = "Map"; 4720 int x = ini.Get_Int(name, "X", 1); 4721 int y = ini.Get_Int(name, "Y", 1); 4722 int w = ini.Get_Int(name, "Width", MAP_CELL_W-2); 4723 int h = ini.Get_Int(name, "Height", MAP_CELL_H-2); 4724 4725 #ifndef FIXIT_VERSION_3 // Map size no longer restricted. 4726 4727 #ifdef FIXIT_CSII // checked - ajw 4728 if(Session.Type >= GAME_MODEM && Session.Type <= GAME_INTERNET && PlayingAgainstVersion < VERSION_AFTERMATH_CS) { 4729 /* 4730 ** HACK ALERT: 4731 ** Force the map to be limited to the size that 96x96 would be. If the 4732 ** size is greater (due to hacking?) then shrink it down to legal size. 4733 ** BG Note: only do this for multiplayer games against non-AfterMath. 4734 */ 4735 if (w * h > 96 * 96) { 4736 h -= (((w*h) - (96*96)) / w) + 1; 4737 } 4738 } 4739 #else 4740 /* 4741 ** HACK ALERT: 4742 ** Force the map to be limited to the size that 96x96 would be. If the 4743 ** size is greater (due to hacking?) then shrink it down to legal size. 4744 */ 4745 if (w * h > 96 * 96) { 4746 h -= (((w*h) - (96*96)) / w) + 1; 4747 } 4748 #endif 4749 4750 #endif // !FIXIT_VERSION_3 4751 4752 Set_Map_Dimensions( x, y, w, h ); 4753 4754 /* 4755 ** The theater is determined at this point. There is specific data that 4756 ** is custom to this data. Load the custom data (as it related to terrain) 4757 ** at this point. 4758 */ 4759 Scen.Theater = ini.Get_TheaterType(name, "Theater", THEATER_TEMPERATE); 4760 if (Scen.Theater == THEATER_NONE) { 4761 Scen.Theater = THEATER_TEMPERATE; 4762 } 4763 4764 /* 4765 ** Remove any old theater specific uncompressed shapes 4766 */ 4767 #ifdef WIN32 4768 if (Scen.Theater != LastTheater) { 4769 Reset_Theater_Shapes(); 4770 } 4771 #endif //WIN32 4772 4773 /* 4774 ** Now that the theater is known, init the entire map hierarchy 4775 */ 4776 Init(Scen.Theater); 4777 4778 /* 4779 ** Special initializations occur when the theater is known. 4780 */ 4781 TerrainTypeClass::Init(Scen.Theater); 4782 TemplateTypeClass::Init(Scen.Theater); 4783 OverlayTypeClass::Init(Scen.Theater); 4784 UnitTypeClass::Init(Scen.Theater); 4785 InfantryTypeClass::Init(Scen.Theater); 4786 BuildingTypeClass::Init(Scen.Theater); 4787 BulletTypeClass::Init(Scen.Theater); 4788 AnimTypeClass::Init(Scen.Theater); 4789 AircraftTypeClass::Init(Scen.Theater); 4790 VesselTypeClass::Init(Scen.Theater); 4791 SmudgeTypeClass::Init(Scen.Theater); 4792 4793 /* 4794 ** Read the Waypoint entries. 4795 */ 4796 for (int i = 0; i < WAYPT_COUNT; i++) { 4797 char buf[20]; 4798 sprintf(buf, "%d", i); 4799 Scen.Waypoint[i] = ini.Get_Int("Waypoints", buf, -1); 4800 4801 if (Scen.Waypoint[i] != -1) { 4802 (*this)[Scen.Waypoint[i]].IsWaypoint = 1; 4803 } 4804 } 4805 4806 /* 4807 ** Set the starting position (do this after Init(), which clears the cells' 4808 ** IsWaypoint flags). 4809 */ 4810 if (Scen.Waypoint[WAYPT_HOME] == -1) { 4811 Scen.Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + 5*RESFACTOR, MapCellY + 4*RESFACTOR); 4812 } 4813 4814 Scen.Views[0] = Scen.Views[1] = Scen.Views[2] = Scen.Views[3] = Scen.Waypoint[WAYPT_HOME]; 4815 Set_Tactical_Position(Cell_Coord((Scen.Waypoint[WAYPT_HOME] - (MAP_CELL_W * 4 * RESFACTOR)) - (5*RESFACTOR))); 4816 4817 /* 4818 ** Loop through all CellTrigger entries. 4819 */ 4820 int len = ini.Entry_Count("CellTriggers"); 4821 for (int index = 0; index < len; index++) { 4822 4823 /* 4824 ** Get a cell trigger and cell assignment. 4825 */ 4826 char const * cellentry = ini.Get_Entry("CellTriggers", index); 4827 TriggerTypeClass * tp = ini.Get_TriggerType("CellTriggers", cellentry); 4828 CELL cell = atoi(cellentry); 4829 4830 if (tp != NULL && !(*this)[cell].Trigger.Is_Valid()) { 4831 TriggerClass * tt = Find_Or_Make(tp); 4832 if (tt) { 4833 tt->AttachCount++; 4834 tt->Cell = cell; 4835 (*this)[cell].Trigger = tt; 4836 } 4837 } 4838 } 4839 4840 /* 4841 ** Read the map template data. 4842 */ 4843 static char const * const MAPPACK = "MapPack"; 4844 len = ini.Get_UUBlock(MAPPACK, _staging_buffer, sizeof(_staging_buffer)); 4845 BufferStraw bstraw(_staging_buffer, len); 4846 Map.Read_Binary(bstraw); 4847 4848 LastTheater = Scen.Theater; 4849 } 4850 4851 4852 /*********************************************************************************************** 4853 * DisplayClass::Write_INI -- Write the map data to the INI file specified. * 4854 * * 4855 * This routine will output all the data of this map to the INI database specified. * 4856 * * 4857 * INPUT: ini -- Reference to the INI handler to store the map data to. * 4858 * * 4859 * OUTPUT: none * 4860 * * 4861 * WARNINGS: Any existing map data in the INI database will be replaced by this function. * 4862 * * 4863 * HISTORY: * 4864 * 07/03/1996 JLB : Created. * 4865 *=============================================================================================*/ 4866 void DisplayClass::Write_INI(CCINIClass & ini) 4867 { 4868 char entry[20]; 4869 4870 /* 4871 ** Save the map parameters. 4872 */ 4873 static char const * const NAME = "Map"; 4874 ini.Clear(NAME); 4875 ini.Put_TheaterType(NAME, "Theater", Scen.Theater); 4876 ini.Put_Int(NAME, "X", MapCellX); 4877 ini.Put_Int(NAME, "Y", MapCellY); 4878 ini.Put_Int(NAME, "Width", MapCellWidth); 4879 ini.Put_Int(NAME, "Height", MapCellHeight); 4880 4881 /* 4882 ** Save the Waypoint entries. 4883 */ 4884 static char const * const WAYNAME = "Waypoints"; 4885 ini.Clear(WAYNAME); 4886 for (int i = 0; i < WAYPT_COUNT; i++) { 4887 if (Scen.Waypoint[i] != -1) { 4888 sprintf(entry, "%d", i); 4889 ini.Put_Int(WAYNAME, entry, Scen.Waypoint[i]); 4890 } 4891 } 4892 4893 /* 4894 ** Save the cell's triggers. 4895 */ 4896 static char const * const CELLTRIG = "CellTriggers"; 4897 ini.Clear(CELLTRIG); 4898 for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { 4899 if ((*this)[cell].Trigger.Is_Valid()) { 4900 TriggerClass * tp = (*this)[cell].Trigger; 4901 if (tp != NULL) { 4902 4903 /* 4904 ** Generate entry name. 4905 */ 4906 sprintf(entry, "%d", cell); 4907 4908 /* 4909 ** Save entry. 4910 */ 4911 ini.Put_TriggerType(CELLTRIG, entry, tp->Class); 4912 } 4913 } 4914 } 4915 4916 /* 4917 ** Write the map template data out to the ini file. 4918 */ 4919 static char const * const MAPPACK = "MapPack"; 4920 BufferPipe bpipe(_staging_buffer, sizeof(_staging_buffer)); 4921 int len = Map.Write_Binary(bpipe); 4922 ini.Clear(MAPPACK); 4923 if (len) { 4924 ini.Put_UUBlock(MAPPACK, _staging_buffer, len); 4925 } 4926 } 4927 4928 4929 /*********************************************************************************************** 4930 * DisplayClass::All_To_Look -- Direct all objects to look around for the player. * 4931 * * 4932 * This routine will scan through all objects and tell them to look if they are supposed * 4933 * to be able to reveal the map for the player. This routine may be necessary in cases * 4934 * of gap generator reshroud logic. * 4935 * * 4936 * INPUT: none * 4937 * * 4938 * OUTPUT: none * 4939 * * 4940 * WARNINGS: none * 4941 * * 4942 * HISTORY: * 4943 * 09/23/1996 JLB : Created. * 4944 * 08/06/2019 ST : Added house parameter so it can work for multiple players * 4945 *=============================================================================================*/ 4946 void DisplayClass::All_To_Look(HouseClass *house, bool units_only) 4947 { 4948 for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { 4949 ObjectClass * object = Layer[LAYER_GROUND][index]; 4950 if (object != NULL && object->Is_Techno()) { 4951 TechnoClass * tech = ((TechnoClass *)object); 4952 4953 if (tech->What_Am_I() == RTTI_BUILDING && units_only) continue; 4954 4955 if (tech->House == house) { 4956 if (tech->Is_Discovered_By_Player(house)) { 4957 object->Look(); 4958 } 4959 } else { 4960 //if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr)) { 4961 if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(house)) { 4962 tech->Look(); 4963 } 4964 } 4965 } 4966 } 4967 } 4968 4969 4970 /* 4971 ** Added house parameter for client/server multiplayer. ST - 8/12/2019 11:48AM 4972 ** 4973 ** 4974 */ 4975 void DisplayClass::Constrained_Look(COORDINATE center, LEPTON distance, HouseClass *house) 4976 { 4977 for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { 4978 ObjectClass * object = Layer[LAYER_GROUND][index]; 4979 if (object != NULL && object->Is_Techno()) { 4980 TechnoClass * tech = ((TechnoClass *)object); 4981 4982 // if (tech->What_Am_I() == RTTI_BUILDING && units_only) continue; 4983 4984 if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { 4985 if (tech->House->IsPlayerControl) { 4986 if (tech->IsDiscoveredByPlayer && Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) { 4987 object->Look(); 4988 } 4989 } else { 4990 4991 if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr) && 4992 Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) { 4993 4994 tech->Look(); 4995 } 4996 } 4997 } else { 4998 4999 if (tech->House->IsHuman) { 5000 5001 if (tech->House == house) { 5002 if (tech->Is_Discovered_By_Player(house) && Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) { 5003 object->Look(); 5004 } 5005 } else { 5006 if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(house) && 5007 Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) { 5008 tech->Look(); 5009 } 5010 } 5011 } 5012 } 5013 } 5014 } 5015 } 5016 5017 5018 /*********************************************************************************************** 5019 * DisplayClass::Flag_Cell -- Flag the specified cell to be redrawn. * 5020 * * 5021 * This will flag the cell to be redrawn. * 5022 * * 5023 * INPUT: cell -- The cell to be flagged. * 5024 * * 5025 * OUTPUT: none * 5026 * * 5027 * WARNINGS: none * 5028 * * 5029 * HISTORY: * 5030 * 10/20/1996 JLB : Created. * 5031 *=============================================================================================*/ 5032 void DisplayClass::Flag_Cell(CELL cell) 5033 { 5034 Flag_To_Redraw(false); 5035 IsToRedraw = true; 5036 CellRedraw[cell] = true; 5037 } 5038 5039 static ActionType _priority_actions[] = { 5040 ACTION_ATTACK, 5041 ACTION_ENTER, 5042 ACTION_HEAL, 5043 ACTION_REPAIR, 5044 ACTION_SABOTAGE, 5045 ACTION_CAPTURE, 5046 ACTION_MOVE 5047 }; 5048 5049 static int get_action_priority(ActionType action) 5050 { 5051 for (int i = 0; i < ARRAY_LENGTH(_priority_actions); ++i) { 5052 if (_priority_actions[i] == action) { 5053 return i; 5054 } 5055 } 5056 return INT_MAX; 5057 } 5058 5059 template <typename T> 5060 static int index_of(const DynamicVectorClass<T*>& list, T* object) 5061 { 5062 for (int i = 0; i < list.Count(); i++) { 5063 if (list[i] == object) { 5064 return i; 5065 } 5066 } 5067 return -1; 5068 } 5069 5070 template <typename T> 5071 static ObjectClass* Best_Object_With_ActionT(DynamicVectorClass<ObjectClass*>& objects, T subject) 5072 { 5073 DynamicVectorClass<const ObjectTypeClass*> checked_types; 5074 5075 if (objects.Count()) { 5076 int best_priority = INT_MAX; 5077 ObjectClass* best_object = objects[0]; 5078 for (int i = 0; i < objects.Count(); ++i) { 5079 ObjectClass* object = objects[i]; 5080 const ObjectTypeClass* type = &object->Class_Of(); 5081 if (index_of(checked_types, type) != -1) { 5082 continue; 5083 } 5084 checked_types.Add(type); 5085 ActionType action = object->What_Action(subject); 5086 int priority = get_action_priority(action); 5087 if (priority < best_priority) { 5088 best_priority = priority; 5089 best_object = object; 5090 if (best_priority == 0) { 5091 break; 5092 } 5093 } 5094 } 5095 return best_object; 5096 } 5097 return NULL; 5098 } 5099 5100 ObjectClass* Best_Object_With_Action(DynamicVectorClass<ObjectClass*>& objects, const ObjectClass* object) 5101 { 5102 return Best_Object_With_ActionT(objects, object); 5103 } 5104 5105 ObjectClass* Best_Object_With_Action(DynamicVectorClass<ObjectClass*>& objects, CELL cell) 5106 { 5107 return Best_Object_With_ActionT(objects, cell); 5108 } 5109 5110 ActionType Best_Object_Action(DynamicVectorClass<ObjectClass*>& objects, const ObjectClass* object) 5111 { 5112 ObjectClass* obj = Best_Object_With_Action(objects, object); 5113 return (obj != NULL) ? obj->What_Action(object) : ACTION_NONE; 5114 } 5115 5116 ActionType Best_Object_Action(DynamicVectorClass<ObjectClass*>& objects, CELL cell) 5117 { 5118 ObjectClass* obj = Best_Object_With_Action(objects, cell); 5119 return (obj != NULL) ? obj->What_Action(cell) : ACTION_NONE; 5120 } 5121 5122 ObjectClass* Best_Object_With_Action(const ObjectClass* object) 5123 { 5124 return Best_Object_With_Action(CurrentObject.Raw(), object); 5125 } 5126 5127 ObjectClass* Best_Object_With_Action(CELL cell) 5128 { 5129 return Best_Object_With_Action(CurrentObject.Raw(), cell); 5130 } 5131 5132 ActionType Best_Object_Action(const ObjectClass* object) 5133 { 5134 return Best_Object_Action(CurrentObject.Raw(), object); 5135 } 5136 5137 ActionType Best_Object_Action(CELL cell) 5138 { 5139 return Best_Object_Action(CurrentObject.Raw(), cell); 5140 }