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