DLLInterface.cpp (227144B)
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 17 /* 18 ** DLLInterfac.cpp 19 ** 20 ** This is where we implement the API expected by the Instance Server. 21 ** 22 ** The Instance Server will pass in requests for loading and starting maps, control input from players, 23 ** and requests for game simulation and rendering states. 24 ** 25 ** 26 */ 27 28 29 30 #include <stdio.h> 31 32 #include "function.h" 33 #include "externs.h" 34 #include "DLLInterface.h" 35 #include "Gadget.h" 36 #include "defines.h" // VOC_COUNT, VOX_COUNT 37 #include "SidebarGlyphx.h" 38 39 40 41 42 /* 43 ** Externs 44 */ 45 extern int DLL_Startup(const char * command_line); 46 extern void Reallocate_Big_Shape_Buffer(void); 47 extern bool ProgEndCalled; 48 extern int Write_PCX_File(char* name, GraphicViewPortClass& pic, unsigned char* palette ); 49 extern bool Color_Cycle(void); 50 51 52 53 54 /* 55 ** Prototypes and constants 56 */ 57 bool Debug_Write_Shape_Type(const ObjectTypeClass *type, int shapenum); 58 bool Debug_Write_Shape(const char *file_name, void const * shapefile, int shapenum, int flags = 0, void const * ghostdata = NULL); 59 60 typedef void (__cdecl* CNC_Event_Callback_Type)(const EventCallbackStruct &event); 61 typedef unsigned __int64 uint64; 62 typedef __int64 int64; 63 64 65 66 67 /* 68 ** Audio defines 69 ** 70 ** 71 ** 72 ** 73 ** 74 */ 75 // For compatibility with Watcom in audio enums 76 #pragma warning (disable : 4091) 77 78 // From TiberianDawn\Audio.cpp 79 enum ContextType; 80 extern struct SoundEffectNameStruct { 81 char const *Name; // Digitized voice file name. 82 int Priority; // Playback priority of this sample. 83 ContextType Where; // In what game context does this sample exist. 84 } SoundEffectName[VOC_COUNT]; 85 86 // From TiberianDawn\Audio.cpp 87 extern char const* Speech[VOX_COUNT]; 88 89 // From TiberianDawn\Audio.cpp 90 typedef enum { 91 IN_NOVAR, // No variation or alterations allowed. 92 IN_JUV, // Juvenile sound effect alternate option. 93 IN_VAR, // Infantry variance response modification. 94 }; 95 96 97 98 99 /* 100 ** Misc defines 101 ** 102 ** 103 ** 104 ** 105 ** 106 */ 107 #define RANDOM_START_POSITION 0x7f 108 109 #define KILL_PLAYER_ON_DISCONNECT 1 110 111 112 113 /* 114 ** DLL Interface 115 ** 116 ** 117 ** 118 ** 119 ** 120 */ 121 extern "C" __declspec(dllexport) unsigned int __cdecl CNC_Version(unsigned int version_in); 122 extern "C" __declspec(dllexport) void __cdecl CNC_Init(const char *command_line, CNC_Event_Callback_Type event_callback); 123 extern "C" __declspec(dllexport) void __cdecl CNC_Config(const CNCRulesDataStruct& rules); 124 extern "C" __declspec(dllexport) void __cdecl CNC_Add_Mod_Path(const char *mod_path); 125 extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Visible_Page(unsigned char *buffer_in, unsigned int &width, unsigned int &height); 126 extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Palette(unsigned char(&palette_in)[256][3]); 127 extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Instance(int scenario_index, int build_level, const char *faction, const char *game_type, const char *content_directory, int sabotaged_structure, const char *override_map_name); 128 extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Instance_Variation(int scenario_index, int scenario_variation, int scenario_direction, int build_level, const char *faction, const char *game_type, const char *content_directory, int sabotaged_structure, const char *override_map_name); 129 extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Custom_Instance(const char* content_directory, const char* directory_path, const char* scenario_name, int build_level, bool multiplayer); 130 extern "C" __declspec(dllexport) bool __cdecl CNC_Advance_Instance(uint64 player_id); 131 extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Game_State(GameStateRequestEnum state_type, uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); 132 extern "C" __declspec(dllexport) bool __cdecl CNC_Read_INI(int scenario_index, int scenario_variation, int scenario_direction, const char *content_directory, const char *override_map_name, char *ini_buffer, int _ini_buffer_size); 133 extern "C" __declspec(dllexport) void __cdecl CNC_Set_Home_Cell(int x, int y, uint64 player_id); 134 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Request(GameRequestEnum request_type); 135 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Settings_Request(int health_bar_display_mode, int resource_bar_display_mode); 136 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Input(InputRequestEnum mouse_event, unsigned char special_key_flags, uint64 player_id, int x1, int y1, int x2, int y2); 137 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Structure_Request(StructureRequestEnum request_type, uint64 player_id, int object_id); 138 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Unit_Request(UnitRequestEnum request_type, uint64 player_id); 139 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Sidebar_Request(SidebarRequestEnum request_type, uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y); 140 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_SuperWeapon_Request(SuperWeaponRequestEnum request_type, uint64 player_id, int buildable_type, int buildable_id, int x1, int y1); 141 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_ControlGroup_Request(ControlGroupRequestEnum request_type, uint64 player_id, unsigned char control_group_index); 142 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Debug_Request(DebugRequestEnum debug_request_type, uint64 player_id, const char *object_name, int x, int y, bool unshroud, bool enemy); 143 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Beacon_Request(BeaconRequestEnum beacon_request_type, uint64 player_id, int pixel_x, int pixel_y); 144 extern "C" __declspec(dllexport) bool __cdecl CNC_Set_Multiplayer_Data(int scenario_index, CNCMultiplayerOptionsStruct &game_options, int num_players, CNCPlayerInfoStruct *player_list, int max_players); 145 extern "C" __declspec(dllexport) bool __cdecl CNC_Clear_Object_Selection(uint64 player_id); 146 extern "C" __declspec(dllexport) bool __cdecl CNC_Select_Object(uint64 player_id, int object_type_id, int object_to_select_id); 147 extern "C" __declspec(dllexport) bool __cdecl CNC_Save_Load(bool save, const char *file_path_and_name, const char *game_type); 148 extern "C" __declspec(dllexport) void __cdecl CNC_Set_Difficulty(int difficulty); 149 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uint64 player_id); 150 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Human_Team_Wins(uint64 player_id); 151 extern "C" __declspec(dllexport) void __cdecl CNC_Start_Mission_Timer(int time); 152 extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Start_Game_Info(uint64 player_id, int &start_location_waypoint_index); 153 154 155 /* 156 ** Class to implement the interface, and contain additional game state required by the conversion from peer/peer to client/server 157 ** 158 ** 159 ** 160 ** 161 ** 162 */ 163 class DLLExportClass { 164 public: 165 166 static void Init(void); 167 static void Shutdown(void); 168 static void Config(const CNCRulesDataStruct& rules); 169 static void Add_Mod_Path(const char *mod_path); 170 static void Set_Home_Cell(int x, int y, uint64 player_id); 171 static void Set_Content_Directory(const char *dir); 172 173 static bool Get_Layer_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); 174 static bool Get_Sidebar_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); 175 static bool Start_Construction(uint64 player_id, int buildable_type, int buildable_id); 176 static bool Hold_Construction(uint64 player_id, int buildable_type, int buildable_id); 177 static bool Cancel_Construction(uint64 player_id, int buildable_type, int buildable_id); 178 static bool Start_Placement(uint64 player_id, int buildable_type, int buildable_id); 179 static BuildingClass *Get_Pending_Placement_Object(uint64 player_id, int buildable_type, int buildable_id); 180 static bool Get_Placement_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); 181 static void Convert_Type(const ObjectClass *object, CNCObjectStruct &object_out); 182 static void DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, ObjectClass *object, const char *shape_file_name = NULL, char override_owner = HOUSE_NONE, int scale = 0x100); 183 static void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip); 184 static void DLL_Draw_Line_Intercept(int x, int y, int x1, int y1, unsigned char color, int frame); 185 static bool Place(uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y); 186 static bool Cancel_Placement(uint64 player_id, int buildable_type, int buildable_id); 187 static bool Place_Super_Weapon(uint64 player_id, int buildable_type, int buildable_id, int x, int y); 188 static bool Create_Control_Group(unsigned char control_group_index); 189 static bool Add_To_Control_Group(unsigned char control_group_index); 190 static bool Toggle_Control_Group_Selection(unsigned char control_group_index); 191 static bool Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id); 192 static bool MP_Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id); 193 static bool Passes_Proximity_Check(CELL cell_in, BuildingTypeClass *placement_type, unsigned char* placement_distance); 194 static void Calculate_Start_Positions(void); 195 static void Computer_Message(bool last_player_taunt); 196 197 static void Repair_Mode(uint64 player_id); 198 static void Repair(uint64 player_id, int object_id); 199 static void Sell_Mode(uint64 player_id); 200 static void Sell(uint64 player_id, int object_id); 201 static void Repair_Sell_Cancel(uint64 player_id); 202 203 static void Scatter_Selected(uint64 player_id); 204 static void Select_Next_Unit(uint64 player_id); 205 static void Select_Previous_Unit(uint64 player_id); 206 static void Selected_Guard_Mode(uint64 player_id); 207 static void Selected_Stop(uint64 player_id); 208 static void Team_Units_Formation_Toggle_On(uint64 player_id); 209 static void Units_Queued_Movement_Toggle(uint64 player_id, bool toggle); 210 211 static void Cell_Class_Draw_It(CNCDynamicMapStruct *dynamic_map, int &entry_index, CellClass *cell_ptr, int xpixel, int ypixel, bool debug_output); 212 static bool Get_Dynamic_Map_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); 213 static bool Get_Shroud_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); 214 static bool Get_Occupier_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); 215 static bool Get_Player_Info_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); 216 217 218 static void Set_Event_Callback(CNC_Event_Callback_Type event_callback) {EventCallback = event_callback;} 219 static void Debug_Spawn_Unit(const char *object_name, int x, int y, bool enemy = false); 220 static void Debug_Spawn_All(int x, int y); 221 static bool Try_Debug_Spawn_Unlimbo(TechnoClass *techno, int &cell_x, int &cell_y); 222 static void Debug_Kill_Unit(int x, int y); 223 static void Debug_Heal_Unit(int x, int y); 224 225 static void On_Play_Movie(const char * movie_name, ThemeType theme, bool immediate); 226 static void On_Display_Briefing_Text(); 227 228 static void On_Sound_Effect(const HouseClass* player_ptr, int sound_effect_index, const char* extension, int variation, COORDINATE coord); 229 static void On_Speech(const HouseClass* player_ptr, int speech_index); 230 231 static void On_Game_Over(uint64 glyphx_player_id, bool player_wins); 232 static void On_Multiplayer_Game_Over(void); 233 234 static void On_Message(const HouseClass* player_ptr, const char* message, float timeout_seconds, EventCallbackMessageEnum message_type, int64 message_id); 235 236 static void On_Debug_Output(const char *debug_text); 237 238 static void On_Achievement(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason); 239 240 static void On_Center_Camera(const HouseClass* player_ptr, int coord_x, int coord_y); 241 242 static void On_Ping(const HouseClass* player_ptr, COORDINATE coord); 243 244 static void Glyphx_Queue_AI(); 245 246 static void Force_Human_Team_Wins(uint64 quitting_player_id); 247 248 /* 249 ** Player context switching for input/output 250 */ 251 static bool Set_Player_Context(uint64 glyphx_player, bool force = false); 252 static void Reset_Player_Context(void); 253 static void Adjust_Internal_View(bool force_ignore_view_constraints = false); 254 static void Logic_Switch_Player_Context(ObjectClass *object); 255 static void Logic_Switch_Player_Context(HouseClass *house); 256 static __int64 Get_GlyphX_Player_ID(const HouseClass *house); 257 258 static void Recalculate_Placement_Distances(); 259 260 static void Reset_Sidebars(void); 261 262 static SidebarGlyphxClass *Get_Current_Context_Sidebar(HouseClass *player_ptr = NULL); 263 264 static uint64 GlyphxPlayerIDs[MAX_PLAYERS]; 265 266 267 268 static const void *Get_Shadow_Shapes(void) {return Map.ShadowShapes;} 269 static const unsigned char *Get_Shadow_Trans(void) {return &Map.ShadowTrans[0];} 270 271 static bool Legacy_Render_Enabled(void); 272 273 static bool Get_Input_Key_State(KeyNumType key); 274 275 static void Set_Special_Key_Flags(unsigned char special_key_flags); 276 static void Clear_Special_Key_Flags(); 277 278 static bool Load(FileClass & file); 279 static bool Save(FileClass & file); 280 static void Code_Pointers(void); 281 static void Decode_Pointers(void); 282 283 static bool Get_Game_Over() { return GameOver; } 284 285 286 private: 287 static void Calculate_Single_Player_Score(EventCallbackStruct&); 288 289 static unsigned int TD_Calculate_Efficiency( unsigned int harvested_credits, unsigned int initial_credits, unsigned int available_credits ); 290 static unsigned int TD_Calculate_Leadership( int house, unsigned int units_lost, unsigned int buildings_lost ); 291 static unsigned int TD_Calculate_Score( unsigned int leadership, unsigned int efficiency, unsigned int build_level ); 292 293 static void Convert_Action_Type(ActionType type, ObjectClass* object, TARGET target, DllActionTypeEnum& dll_type); 294 295 static void Calculate_Placement_Distances(BuildingTypeClass* placement_type, unsigned char* placement_distance); 296 297 static int CurrentDrawCount; 298 static int TotalObjectCount; 299 static int SortOrder; 300 static int ExportLayer; 301 static CNCObjectListStruct *ObjectList; 302 303 static CNC_Event_Callback_Type EventCallback; 304 305 306 static int CurrentLocalPlayerIndex; 307 308 static bool GameOver; 309 310 /* 311 ** Pseudo sidebars for players in multiplayer 312 */ 313 static SidebarGlyphxClass MultiplayerSidebars[MAX_PLAYERS]; 314 315 static CELL MultiplayerStartPositions[MAX_PLAYERS]; 316 317 static BuildingTypeClass *PlacementType[MAX_PLAYERS]; 318 319 static unsigned char PlacementDistance[MAX_PLAYERS][MAP_CELL_TOTAL]; 320 321 static unsigned char SpecialKeyFlags[MAX_PLAYERS]; 322 323 /* 324 ** Mod directories 325 */ 326 static DynamicVectorClass<char *> ModSearchPaths; 327 328 }; 329 330 331 /* 332 ** DLLExportClass static data 333 ** 334 ** 335 ** 336 ** 337 ** 338 */ 339 int DLLExportClass::CurrentDrawCount = 0; 340 int DLLExportClass::TotalObjectCount = 0; 341 int DLLExportClass::SortOrder = 0; 342 int DLLExportClass::ExportLayer = 0; 343 CNCObjectListStruct *DLLExportClass::ObjectList = NULL; 344 SidebarGlyphxClass DLLExportClass::MultiplayerSidebars [MAX_PLAYERS]; 345 uint64 DLLExportClass::GlyphxPlayerIDs[MAX_PLAYERS] = {0xffffffffl}; 346 int DLLExportClass::CurrentLocalPlayerIndex = -1; 347 CELL DLLExportClass::MultiplayerStartPositions[MAX_PLAYERS]; 348 BuildingTypeClass *DLLExportClass::PlacementType[MAX_PLAYERS]; 349 unsigned char DLLExportClass::PlacementDistance[MAX_PLAYERS][MAP_CELL_TOTAL]; 350 unsigned char DLLExportClass::SpecialKeyFlags[MAX_PLAYERS] = { 0U }; 351 DynamicVectorClass<char *> DLLExportClass::ModSearchPaths; 352 bool DLLExportClass::GameOver = false; 353 354 355 /* 356 ** Global variables 357 ** 358 ** 359 ** 360 ** 361 ** 362 */ 363 int DLLForceMouseX = 0; 364 int DLLForceMouseY = 0; 365 366 CNC_Event_Callback_Type DLLExportClass::EventCallback = NULL; 367 368 // Needed to accomodate Glyphx client sidebar. ST - 4/12/2019 5:29PM 369 int GlyphXClientSidebarWidthInLeptons = 0; 370 371 bool MPlayerIsHuman[MAX_PLAYERS]; 372 int MPlayerTeamIDs[MAX_PLAYERS]; 373 int MPlayerStartLocations[MAX_PLAYERS]; 374 375 bool ShareAllyVisibility = true; 376 377 378 379 380 381 382 void Play_Movie_GlyphX(const char * movie_name, ThemeType theme) 383 { 384 if ((movie_name[0] == 'x' || movie_name[0] == 'X') && movie_name[1] == 0) { 385 return; 386 } 387 388 DLLExportClass::On_Play_Movie(movie_name, theme, false); 389 } 390 391 392 void On_Sound_Effect(int sound_index, int variation, COORDINATE coord) 393 { 394 int voc = sound_index; 395 if (voc == VOC_NONE) 396 { 397 return; 398 } 399 400 // Borrowed from AUDIO.CPP Sound_Effect() 401 // 402 #if 1 403 char const * ext = ""; // ".AUD"; 404 if (Special.IsJuvenile && SoundEffectName[voc].Where == IN_JUV) { 405 ext = ".JUV"; 406 } else { 407 if (SoundEffectName[voc].Where == IN_VAR) { 408 /* 409 ** For infantry, use a variation on the response. For vehicles, always 410 ** use the vehicle response table. 411 */ 412 if (variation < 0) { 413 if (ABS(variation) % 2) { 414 ext = ".V00"; 415 } else { 416 ext = ".V02"; 417 } 418 } else { 419 if (variation % 2) { 420 ext = ".V01"; 421 } else { 422 ext = ".V03"; 423 } 424 } 425 } 426 } 427 #endif 428 429 DLLExportClass::On_Sound_Effect(PlayerPtr, sound_index, ext, variation, coord); 430 } 431 432 void On_Speech(int speech_index, HouseClass *house) 433 { 434 if (house == NULL) { 435 DLLExportClass::On_Speech(PlayerPtr, speech_index); 436 } 437 else 438 { 439 DLLExportClass::On_Speech(house, speech_index); 440 } 441 } 442 443 444 void On_Ping(const HouseClass* player_ptr, COORDINATE coord) 445 { 446 DLLExportClass::On_Ping(player_ptr, coord); 447 } 448 449 450 void GlyphX_Debug_Print(const char *debug_text) 451 { 452 DLLExportClass::On_Debug_Output(debug_text); 453 } 454 455 456 void On_Achievement_Event(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason) 457 { 458 DLLExportClass::On_Achievement(player_ptr, achievement_type, achievement_reason); 459 } 460 461 462 463 464 /************************************************************************************************** 465 * CNC_Version -- Check DLL/Server version 466 * 467 * In: Version expected 468 * 469 * Out: Actual version 470 * 471 * 472 * 473 * History: 4/9/2020 2:12PM - ST 474 **************************************************************************************************/ 475 extern "C" __declspec(dllexport) unsigned int __cdecl CNC_Version(unsigned int version_in) 476 { 477 // Unreferenced, but potentially useful to know which version the server is expecting 478 version_in; 479 480 return CNC_DLL_API_VERSION; 481 } 482 483 484 485 486 /************************************************************************************************** 487 * CNC_Init -- Initialize the .DLL 488 * 489 * In: Command line 490 * 491 * Out: 492 * 493 * 494 * 495 * History: 1/3/2019 11:33AM - ST 496 **************************************************************************************************/ 497 extern "C" __declspec(dllexport) void __cdecl CNC_Init(const char *command_line, CNC_Event_Callback_Type event_callback) 498 { 499 DLLExportClass::Set_Content_Directory(NULL); 500 501 DLL_Startup(command_line); 502 503 DLLExportClass::Set_Event_Callback( event_callback ); 504 505 DLLExportClass::Init(); 506 } 507 508 509 510 /************************************************************************************************** 511 * DLL_Shutdown -- Shutdown the .DLL 512 * 513 * In: 514 * 515 * Out: 516 * 517 * 518 * 519 * History: 2/20/2020 1:58PM - ST 520 **************************************************************************************************/ 521 void DLL_Shutdown(void) 522 { 523 DLLExportClass::Shutdown(); 524 } 525 526 527 528 529 530 /************************************************************************************************** 531 * CNC_Config -- Configure the plugin 532 * 533 * In: Configuration data 534 * 535 * Out: 536 * 537 * 538 * 539 * History: 10/03/2019 - SKY 540 **************************************************************************************************/ 541 extern "C" __declspec(dllexport) void __cdecl CNC_Config(const CNCRulesDataStruct& rules) 542 { 543 DLLExportClass::Config(rules); 544 } 545 546 547 548 549 /************************************************************************************************** 550 * CNC_Add_Mod_Path -- Add a path to load mod files from 551 * 552 * In: Path to load mods from 553 * 554 * Out: 555 * 556 * 557 * 558 * History: 2/20/2020 2:04PM - ST 559 **************************************************************************************************/ 560 extern "C" __declspec(dllexport) void __cdecl CNC_Add_Mod_Path(const char *mod_path) 561 { 562 DLLExportClass::Add_Mod_Path(mod_path); 563 } 564 565 566 567 568 569 /************************************************************************************************** 570 * CNC_Get_Visible_Page -- Get the screen buffer 'SeenBuff' from the game 571 * 572 * In: If buffer_in is null, just return info about page 573 * 574 * Out: false if not changed since last call 575 * 576 * 577 * 578 * History: 1/3/2019 11:33AM - ST 579 **************************************************************************************************/ 580 extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Visible_Page(unsigned char *buffer_in, unsigned int &width, unsigned int &height) 581 { 582 if (!DLLExportClass::Legacy_Render_Enabled() || (buffer_in == NULL)) { 583 return false; 584 } 585 586 /* 587 ** Assume the seen page viewport is the same size as the page 588 */ 589 590 GraphicBufferClass *gbuffer = HidPage.Get_Graphic_Buffer(); 591 if (gbuffer == NULL) { 592 return false; 593 } 594 595 int view_port_width = Map.MapCellWidth * CELL_PIXEL_W; 596 int view_port_height = Map.MapCellHeight * CELL_PIXEL_H; 597 598 if (view_port_width == 0 || view_port_height == 0) { 599 return false; 600 } 601 602 unsigned char *raw_buffer = (unsigned char*) gbuffer->Get_Buffer(); 603 long raw_size = gbuffer->Get_Size(); 604 if (raw_buffer == NULL || gbuffer->Get_Width() < view_port_width || gbuffer->Get_Height() < view_port_height) { 605 return false; 606 } 607 608 width = view_port_width; 609 height = view_port_height; 610 611 int pitch = gbuffer->Get_Width(); 612 for (int i = 0; i < view_port_height; ++i, buffer_in += view_port_width, raw_buffer += pitch) { 613 memcpy(buffer_in, raw_buffer, view_port_width); 614 } 615 616 return true; 617 } 618 619 620 621 622 extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Palette(unsigned char(&palette_in)[256][3]) 623 { 624 memcpy(palette_in, CurrentPalette, sizeof(palette_in)); 625 return true; 626 } 627 628 629 630 631 /************************************************************************************************** 632 * CNC_Set_Multiplayer_Data -- Set up for a multiplayer match 633 * 634 * In: Multiplayer data 635 * 636 * Out: false if data is bad 637 * 638 * 639 * 640 * History: 1/7/2019 5:20PM - ST 641 **************************************************************************************************/ 642 extern "C" __declspec(dllexport) bool __cdecl CNC_Set_Multiplayer_Data(int scenario_index, CNCMultiplayerOptionsStruct &game_options, int num_players, CNCPlayerInfoStruct *player_list, int max_players) 643 { 644 645 if (num_players <= 0) { 646 return false; 647 } 648 649 if (num_players > min(MAX_PLAYERS, max_players)) { 650 return false; 651 } 652 653 DLLExportClass::Init(); 654 655 //MPlayerPrefColor; // preferred color index for this player 656 //MPlayerColorIdx; // actual color index of this player 657 //MPlayerHouse; // House of this player (GDI/NOD) 658 //MPlayerLocalID; // ID of this player 659 MPlayerCount = num_players; // # of human players in this game 660 MPlayerBases = game_options.MPlayerBases; // 1 = bases are on for this scenario 661 MPlayerCredits = game_options.MPlayerCredits; // # credits everyone gets 662 MPlayerTiberium = game_options.MPlayerTiberium; // 1 = tiberium enabled for this scenario 663 MPlayerGoodies = game_options.MPlayerGoodies; // 1 = goodies enabled for this scenario 664 MPlayerGhosts = game_options.MPlayerGhosts; // 1 = houses with no players will still play 665 MPlayerSolo = game_options.MPlayerSolo; // 1 = allows a single-player net game 666 MPlayerUnitCount = game_options.MPlayerUnitCount; // # units for non-base multiplayer scenarios 667 668 Special.IsMCVDeploy = game_options.IsMCVDeploy; 669 Special.IsVisceroids = game_options.SpawnVisceroids; 670 Special.IsCaptureTheFlag = game_options.CaptureTheFlag; 671 Special.IsEarlyWin = game_options.DestroyStructures; 672 Special.ModernBalance = game_options.ModernBalance; 673 674 Rule.AllowSuperWeapons = game_options.EnableSuperweapons; // Are superweapons available 675 676 if (MPlayerTiberium) { 677 Special.IsTGrowth = 1; 678 Special.IsTSpread = 1; 679 } else { 680 Special.IsTGrowth = 0; 681 Special.IsTSpread = 0; 682 } 683 684 Scenario = scenario_index; 685 MPlayerCount = 0; 686 687 for (int i=0 ; i<num_players ; i++) { 688 CNCPlayerInfoStruct &player_info = player_list[i]; 689 MPlayerHouses[i] = (HousesType) player_info.House; 690 strncpy(MPlayerNames[i], player_info.Name, MPLAYER_NAME_MAX); 691 MPlayerNames[i][MPLAYER_NAME_MAX - 1] = 0; // Make sure it's terminated 692 693 MPlayerID[i] = Build_MPlayerID(player_info.ColorIndex, (HousesType)player_info.House); 694 695 DLLExportClass::GlyphxPlayerIDs[i] = player_info.GlyphxPlayerID; 696 697 MPlayerIsHuman[i] = !player_info.IsAI; 698 if (player_info.IsAI) { 699 MPlayerGhosts = true; 700 } 701 702 MPlayerTeamIDs[i] = player_info.Team; 703 MPlayerStartLocations[i] = player_info.StartLocationIndex; 704 705 /* 706 ** Temp fix for custom maps that don't have valid start positions set from matchmaking 707 */ 708 if (i > 0 && MPlayerStartLocations[i] == 0 && MPlayerStartLocations[0] == 0) { 709 MPlayerStartLocations[i] = i; 710 } 711 712 MPlayerCount++; 713 } 714 715 /* 716 ** We need some default for the local ID in order to have a valid PlayerPtr during scenario load. ST - 4/24/2019 10:33AM 717 */ 718 MPlayerLocalID = MPlayerID[0]; 719 720 return true; 721 } 722 723 extern "C" __declspec(dllexport) bool __cdecl CNC_Clear_Object_Selection(uint64 player_id) 724 { 725 if (!DLLExportClass::Set_Player_Context(player_id)) { 726 return false; 727 } 728 729 Unselect_All(); 730 731 return true; 732 } 733 734 extern "C" __declspec(dllexport) bool __cdecl CNC_Select_Object(uint64 player_id, int object_type_id, int object_to_select_id) 735 { 736 if (!DLLExportClass::Set_Player_Context(player_id)) { 737 return false; 738 } 739 740 switch (object_type_id) 741 { 742 case INFANTRY: 743 { 744 for (int index = 0; index < Infantry.Count(); index++) { 745 InfantryClass * obj = Infantry.Ptr(index); 746 747 if (obj 748 && !obj->IsInLimbo 749 && obj->House == PlayerPtr 750 && Infantry.ID((InfantryClass*)obj) == object_to_select_id) 751 { 752 if (!obj->Is_Selected_By_Player()) 753 { 754 obj->Select(); 755 AllowVoice = false; 756 } 757 return true; 758 } 759 } 760 } 761 break; 762 case UNIT: 763 { 764 for (int index = 0; index < Units.Count(); index++) { 765 UnitClass * obj = Units.Ptr(index); 766 767 if (obj 768 && !obj->IsInLimbo 769 && obj->House == PlayerPtr 770 && Units.ID((UnitClass*)obj) == object_to_select_id) 771 { 772 if (!obj->Is_Selected_By_Player()) 773 { 774 obj->Select(); 775 AllowVoice = false; 776 } 777 return true; 778 } 779 } 780 } 781 break; 782 case AIRCRAFT: 783 { 784 for (int index = 0; index < Aircraft.Count(); index++) { 785 AircraftClass * obj = Aircraft.Ptr(index); 786 787 if (obj 788 && !obj->IsInLimbo 789 && obj->House == PlayerPtr 790 && Aircraft.ID((AircraftClass*)obj) == object_to_select_id) 791 { 792 if (!obj->Is_Selected_By_Player()) 793 { 794 obj->Select(); 795 AllowVoice = false; 796 } 797 return true; 798 } 799 } 800 } 801 break; 802 case BUILDING: 803 { 804 for (int index = 0; index < Buildings.Count(); index++) { 805 BuildingClass * obj = Buildings.Ptr(index); 806 if (obj 807 && !obj->IsInLimbo 808 && obj->House == PlayerPtr 809 && Buildings.ID((BuildingClass*)obj) == object_to_select_id) 810 { 811 if (!obj->Is_Selected_By_Player()) 812 { 813 obj->Select(); 814 AllowVoice = false; 815 } 816 return true; 817 } 818 } 819 } 820 break; 821 } 822 823 return false; 824 } 825 826 827 /************************************************************************************************** 828 * GlyphX_Assign_Houses -- Replacement for Assign_Houses in INI.CPP 829 * 830 * In: 831 * 832 * Out: 833 * 834 * 835 * 836 * History: 6/25/2019 11:09AM - ST 837 **************************************************************************************************/ 838 void GlyphX_Assign_Houses(void) 839 { 840 HousesType house; 841 HousesType pref_house; 842 HouseClass *housep; 843 bool house_used[MAX_PLAYERS]; // true = this house is in use 844 bool color_used[16]; // true = this color is in use. We have more than 6 color options now, so bumped this to 16. ST - 6/19/2019 5:18PM 845 bool preassigned; 846 int i,j,random_start_location; 847 PlayerColorType color; 848 HousesType house2; 849 HouseClass *housep2; 850 851 srand(timeGetTime()); 852 853 /* 854 ** Init the 'used' flag for all houses & colors to 0 855 */ 856 for (i = 0; i < MAX_PLAYERS; i++) { 857 house_used[i] = false; 858 } 859 for (i = 0; i < 16; i++) { 860 color_used[i] = false; 861 } 862 863 /* 864 ** Assign random start positions if needed. 865 */ 866 int random_start_locations[26]; 867 int num_start_locations = 0; 868 int num_random_start_locations = 0; 869 for (i = 0; i < 26; i++) { 870 if (Waypoint[i] != -1) { 871 preassigned = false; 872 for (j = 0; !preassigned && (j < MPlayerCount); j++) { 873 if (MPlayerStartLocations[j] == num_start_locations) { 874 preassigned = true; 875 } 876 } 877 if (!preassigned && i < MAX_PLAYERS) { 878 random_start_locations[num_random_start_locations] = num_start_locations; 879 num_random_start_locations++; 880 } 881 num_start_locations++; 882 } 883 } 884 885 if (num_random_start_locations > 1) { 886 for (i = 0; i < num_random_start_locations - 1; i++) { 887 j = i + rand() / (RAND_MAX / (num_random_start_locations - i) + 1); 888 int t = random_start_locations[j]; 889 random_start_locations[j] = random_start_locations[i]; 890 random_start_locations[i] = t; 891 } 892 } 893 894 /* 895 ** For each player, randomly pick a house 896 */ 897 random_start_location = 0; 898 for (i = 0; i < MPlayerCount; i++) { 899 j = Random_Pick(0, MPlayerMax-1); 900 901 /* 902 ** If this house was already selected, decrement 'i' & keep looping. 903 */ 904 if (house_used[j]) { 905 i--; 906 continue; 907 } 908 909 /* 910 ** Set the house, preferred house (GDI/NOD), color, and actual house; 911 ** get a pointer to the house instance 912 */ 913 house = (HousesType)(j + (int)HOUSE_MULTI1); 914 pref_house = MPlayerID_To_HousesType(MPlayerID[i]); 915 color = MPlayerID_To_ColorIndex(MPlayerID[i]); 916 housep = HouseClass::As_Pointer(house); 917 MPlayerHouses[i] = house; 918 919 /* 920 ** Mark this house & color as used 921 */ 922 house_used[j] = true; 923 color_used[color] = true; 924 925 /* 926 ** Set the house's IsHuman, Credits, ActLike, & RemapTable 927 */ 928 memset((char *)housep->Name, 0, MPLAYER_NAME_MAX); 929 strncpy((char *)housep->Name, MPlayerNames[i], MPLAYER_NAME_MAX-1); 930 housep->IsHuman = MPlayerIsHuman[i]; 931 housep->Init_Data(color, pref_house, MPlayerCredits); 932 933 /* 934 ** Set the start location override 935 */ 936 if (MPlayerStartLocations[i] != RANDOM_START_POSITION) { 937 housep->StartLocationOverride = MPlayerStartLocations[i]; 938 } else { 939 if (random_start_location < num_random_start_locations) { 940 housep->StartLocationOverride = random_start_locations[random_start_location++]; 941 } else { 942 housep->StartLocationOverride = -1; 943 } 944 } 945 946 /* 947 ** If this ID is for myself, set up PlayerPtr 948 */ 949 if (MPlayerID[i] == MPlayerLocalID) { 950 PlayerPtr = housep; 951 } 952 } 953 954 /* 955 ** From INI.CPP. Remove unused AI players. 956 */ 957 for (int i=0 ; i<MAX_PLAYERS ; i++) { 958 959 if (house_used[i]) { 960 continue; 961 } 962 963 house = (HousesType)(i + (int)HOUSE_MULTI1); 964 housep = HouseClass::As_Pointer (house); 965 if (housep && housep->IsHuman == false) { 966 housep->Clobber_All(); 967 } 968 } 969 970 for (i = 0; i < MPlayerCount; i++) { 971 972 house = MPlayerHouses[i]; 973 housep = HouseClass::As_Pointer(house); 974 975 if (housep) { 976 977 int team = MPlayerTeamIDs[i]; 978 979 for (int j=0 ; j<MPlayerCount ; j++) { 980 981 if (i != j) { 982 983 if (team == MPlayerTeamIDs[j]) { 984 985 house2 = MPlayerHouses[j]; 986 housep2 = HouseClass::As_Pointer(house2); 987 988 if (housep2) { 989 housep->Make_Ally(house2); 990 } 991 } 992 } 993 } 994 } 995 } 996 } 997 998 /************************************************************************************************** 999 * CNC_Start_Instance -- Load and start a cnc map to use WITHOUT a sceanrio variation (SCEN_VAR) or scenarion direction (SCEN_DIR) 1000 * 1001 * In: Map initialization parameters 1002 * 1003 * Out: false if map load failed 1004 * 1005 * 1006 * 1007 * History: 7/10/2019 - LLL 1008 **************************************************************************************************/ 1009 extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Instance(int scenario_index, int build_level, const char *faction, const char *game_type, const char *content_directory, int sabotaged_structure, const char *override_map_name) 1010 { 1011 return CNC_Start_Instance_Variation(scenario_index, (int)SCEN_VAR_NONE, (int)SCEN_DIR_EAST, build_level, faction, game_type, content_directory, sabotaged_structure, override_map_name); 1012 } 1013 1014 1015 /************************************************************************************************** 1016 * HandleSabotagedStructure 1017 * 1018 * A port of the code from the original game code which is suppose to remove the main previously sabatoged building. 1019 * From what I can tell since it only stores the type it might remove a different building of the same type. 1020 * Watching the GDI longplay on YouTube the player destroys the refinery and yet it exists in the next level. Perhaps there are 2 refineries. 1021 * 1022 * History: 7/10/2019 - LLL 1023 **************************************************************************************************/ 1024 void HandleSabotagedStructure(int structure_type) 1025 { 1026 SabotagedType = (StructType) structure_type; 1027 1028 int index; 1029 if (SabotagedType != STRUCT_NONE && Scenario == 7 && PlayerPtr->Class->House == HOUSE_GOOD) { 1030 for (index = 0; index < Buildings.Count(); index++) { 1031 BuildingClass * building = Buildings.Ptr(index); 1032 1033 if (building && !building->IsInLimbo && building->House != PlayerPtr && building->Class->Type == SabotagedType) { 1034 building->Limbo(); 1035 delete building; 1036 break; 1037 } 1038 } 1039 1040 /* 1041 ** Remove the building from the prebuild list. 1042 */ 1043 for (index = 0; index < Base.Nodes.Count(); index++) { 1044 BaseNodeClass * node = Base.Get_Node(index); 1045 1046 if (node && node->Type == SabotagedType) { 1047 Base.Nodes.Delete(index); 1048 break; 1049 } 1050 } 1051 } 1052 SabotagedType = STRUCT_NONE; 1053 } 1054 1055 1056 1057 /************************************************************************************************** 1058 * CNC_Read_INI -- Load an ini file into the supplied buffer 1059 * 1060 * In: Map initialization parameters 1061 * 1062 * Out: false if ini load failed 1063 * 1064 * 1065 * History: 12/16/2019 11:44AM - ST 1066 **************************************************************************************************/ 1067 extern "C" __declspec(dllexport) bool __cdecl CNC_Read_INI(int scenario_index, int scenario_variation, int scenario_direction, const char *content_directory, const char *override_map_name, char *ini_buffer, int _ini_buffer_size) 1068 { 1069 if (content_directory == NULL) { 1070 return false; 1071 } 1072 1073 DLLExportClass::Set_Content_Directory(content_directory); 1074 1075 // Hack a fix for scenario 21 since the same mission number is used in Covert Ops and N64 1076 Scenario = (scenario_index == 81) ? 21 : scenario_index; 1077 1078 ScenVar = (ScenarioVarType)scenario_variation; 1079 ScenDir = (ScenarioDirType)scenario_direction; 1080 1081 GameToPlay = GAME_GLYPHX_MULTIPLAYER; 1082 ScenPlayer = SCEN_PLAYER_MPLAYER; 1083 1084 if (override_map_name && strlen(override_map_name)) { 1085 strcpy(ScenarioName, override_map_name); 1086 } else { 1087 Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, (ScenarioDirType)scenario_direction, (ScenarioVarType)scenario_variation); 1088 } 1089 1090 1091 if (_ini_buffer_size < _ShapeBufferSize) { 1092 GlyphX_Debug_Print("INI file buffer may be too small"); 1093 return false; 1094 } 1095 1096 if (!ini_buffer) { 1097 GlyphX_Debug_Print("No INI file buffer"); 1098 return false; 1099 } 1100 1101 memset(ini_buffer, _ini_buffer_size, 0); 1102 1103 char fname[_MAX_PATH]; 1104 1105 sprintf(fname,"%s.INI", ScenarioName); 1106 CCFileClass file(fname); 1107 if (!file.Is_Available()) { 1108 GlyphX_Debug_Print("Failed to find scenario file"); 1109 GlyphX_Debug_Print(fname); 1110 return(false); 1111 1112 } else { 1113 1114 GlyphX_Debug_Print("Opened scenario file"); 1115 GlyphX_Debug_Print(fname); 1116 1117 int bytes_read = file.Read(ini_buffer, _ini_buffer_size-1); 1118 if (bytes_read == _ini_buffer_size - 1) { 1119 GlyphX_Debug_Print("INI file buffer is too small"); 1120 return false; 1121 } 1122 } 1123 1124 /* 1125 ** Ini buffer should be zero terminated 1126 */ 1127 if ((int) strlen(ini_buffer) >= _ini_buffer_size) { 1128 GlyphX_Debug_Print("INI file buffer overrun"); 1129 return false; 1130 } 1131 1132 return true; 1133 } 1134 1135 1136 /************************************************************************************************** 1137 * CNC_Set_Home_Cell -- Allows overriding the start position for the camera 1138 * 1139 * 1140 * History: 2/14/2020 - LLL 1141 **************************************************************************************************/ 1142 extern "C" __declspec(dllexport) void __cdecl CNC_Set_Home_Cell(int x, int y, uint64 player_id) 1143 { 1144 DLLExportClass::Set_Home_Cell(x, y, player_id); 1145 } 1146 1147 1148 /************************************************************************************************** 1149 * CNC_Start_Instance -- Load and start a cnc map 1150 * 1151 * In: Map initialization parameters 1152 * 1153 * Out: false if map load failed 1154 * 1155 * 1156 * Renamed and modified to accept a scenario variation 7/10/2019 - LLL 1157 * Modified to accept a scenario direction 7/12/2019 - LLL 1158 * 1159 * History: 1/7/2019 5:20PM - ST 1160 **************************************************************************************************/ 1161 extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Instance_Variation(int scenario_index, int scenario_variation, int scenario_direction, int build_level, const char *faction, const char *game_type, const char *content_directory, int sabotaged_structure, const char *override_map_name) 1162 { 1163 if (game_type == NULL) { 1164 return false; 1165 } 1166 1167 if (faction == NULL) { 1168 return false; 1169 } 1170 1171 if (content_directory == NULL) { 1172 return false; 1173 } 1174 1175 if (stricmp(faction, "GDI") == 0) { 1176 ScenPlayer = SCEN_PLAYER_GDI; 1177 Whom = HOUSE_GOOD; 1178 } 1179 1180 if (stricmp(faction, "NOD") == 0) { 1181 ScenPlayer = SCEN_PLAYER_NOD; 1182 Whom = HOUSE_BAD; 1183 } 1184 1185 if (stricmp(faction, "Jurassic") == 0) { 1186 ScenPlayer = SCEN_PLAYER_JP; 1187 Whom = HOUSE_JP; 1188 Special.IsJurassic = true; 1189 AreThingiesEnabled = true; 1190 } 1191 1192 DLLExportClass::Set_Content_Directory(content_directory); 1193 1194 // Hack a fix for scenario 21 since the same mission number is used in Covert Ops and N64 1195 Scenario = (scenario_index == 81) ? 21 : scenario_index; 1196 BuildLevel = build_level; 1197 1198 SabotagedType = (StructType)sabotaged_structure; 1199 1200 ScenVar = (ScenarioVarType)scenario_variation; 1201 ScenDir = (ScenarioDirType)scenario_direction; 1202 1203 if (stricmp(game_type, "GAME_NORMAL") == 0) { 1204 GameToPlay = GAME_NORMAL; 1205 } else { 1206 if (stricmp(game_type, "GAME_GLYPHX_MULTIPLAYER") == 0) { 1207 GameToPlay = GAME_GLYPHX_MULTIPLAYER; 1208 ScenPlayer = SCEN_PLAYER_MPLAYER; 1209 } else { 1210 return false; 1211 } 1212 } 1213 1214 if (override_map_name && strlen(override_map_name)) { 1215 strcpy(ScenarioName, override_map_name); 1216 } else { 1217 Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, (ScenarioDirType)scenario_direction, (ScenarioVarType)scenario_variation); 1218 } 1219 1220 HiddenPage.Clear(); 1221 VisiblePage.Clear(); 1222 1223 /* 1224 ** Set the mouse to some position where it's not going to scroll, or do something else wierd. 1225 */ 1226 DLLForceMouseX = 100; 1227 DLLForceMouseY = 100; 1228 _Kbd->MouseQX = 100; 1229 _Kbd->MouseQY = 100; 1230 1231 GlyphXClientSidebarWidthInLeptons = 0; 1232 1233 Seed = timeGetTime(); 1234 1235 if (!Start_Scenario(ScenarioName)) { 1236 return(false); 1237 } 1238 1239 HandleSabotagedStructure(sabotaged_structure); 1240 1241 DLLExportClass::Reset_Sidebars(); 1242 DLLExportClass::Reset_Player_Context(); 1243 1244 /* 1245 ** Make sure the scroll constraints are applied. This is important for GDI 1 where the map isn't wide enough for the screen 1246 */ 1247 COORDINATE origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(1, 0)); 1248 Map.Set_Tactical_Position(origin_coord); 1249 origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(-1, 0)); 1250 Map.Set_Tactical_Position(origin_coord); 1251 1252 DLLExportClass::Calculate_Start_Positions(); 1253 1254 /* 1255 ** Hide the SeenBuff; force the map to render one frame. The caller can 1256 ** then fade the palette in. 1257 ** (If we loaded a game, this step will fade out the title screen. If we 1258 ** started a scenario, Start_Scenario() will have played a couple of VQ 1259 ** movies, which will have cleared the screen to black already.) 1260 */ 1261 1262 //Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); 1263 HiddenPage.Clear(); 1264 VisiblePage.Clear(); 1265 Set_Logic_Page(SeenBuff); 1266 Map.Flag_To_Redraw(true); 1267 Map.Render(); 1268 1269 Set_Palette(GamePalette); 1270 1271 return true; 1272 } 1273 1274 1275 1276 1277 1278 /************************************************************************************************** 1279 * CNC_Start_Custom_Instance -- 1280 * 1281 * 1282 * 1283 * 1284 * History: 2019/10/17 - JAS 1285 **************************************************************************************************/ 1286 extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Custom_Instance(const char* content_directory, const char* directory_path, 1287 const char* scenario_name, int build_level, bool multiplayer) 1288 { 1289 if (content_directory == NULL) { 1290 return false; 1291 } 1292 1293 DLLExportClass::Set_Content_Directory(content_directory); 1294 1295 if (multiplayer) { 1296 GameToPlay = GAME_GLYPHX_MULTIPLAYER; 1297 ScenPlayer = SCEN_PLAYER_MPLAYER; 1298 } else { 1299 GameToPlay = GAME_NORMAL; 1300 ScenPlayer = SCEN_PLAYER_GDI; // Don't think it matters since we are specifying the exact file to load 1301 } 1302 1303 BuildLevel = build_level; 1304 1305 const int MAX_FILE_PATH = 1024; 1306 char scenario_file_name[MAX_FILE_PATH]; 1307 char bin_file_name[MAX_FILE_PATH]; 1308 snprintf(scenario_file_name, MAX_FILE_PATH, "%s%s.INI", directory_path, scenario_name); 1309 snprintf(bin_file_name, MAX_FILE_PATH, "%s%s.BIN", directory_path, scenario_name); 1310 1311 Seed = timeGetTime(); 1312 1313 Clear_Scenario(); 1314 1315 if (!Read_Scenario_Ini_File(scenario_file_name, bin_file_name, scenario_name, true)) { 1316 return false; 1317 } 1318 1319 HiddenPage.Clear(); 1320 VisiblePage.Clear(); 1321 1322 /* 1323 ** Set the mouse to some position where it's not going to scroll, or do something else wierd. 1324 */ 1325 DLLForceMouseX = 100; 1326 DLLForceMouseY = 100; 1327 _Kbd->MouseQX = 100; 1328 _Kbd->MouseQY = 100; 1329 1330 GlyphXClientSidebarWidthInLeptons = 0; 1331 1332 Play_Movie(IntroMovie); 1333 Play_Movie(BriefMovie); 1334 Play_Movie(ActionMovie, TransitTheme); 1335 1336 /* 1337 if (!Start_Scenario(ScenarioName)) { 1338 return(false); 1339 } 1340 */ 1341 1342 DLLExportClass::Reset_Sidebars(); 1343 DLLExportClass::Reset_Player_Context(); 1344 1345 /* 1346 ** Make sure the scroll constraints are applied. This is important for GDI 1 where the map isn't wide enough for the screen 1347 */ 1348 COORDINATE origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(1, 0)); 1349 Map.Set_Tactical_Position(origin_coord); 1350 origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(-1, 0)); 1351 Map.Set_Tactical_Position(origin_coord); 1352 1353 DLLExportClass::Calculate_Start_Positions(); 1354 1355 /* 1356 ** Hide the SeenBuff; force the map to render one frame. The caller can 1357 ** then fade the palette in. 1358 ** (If we loaded a game, this step will fade out the title screen. If we 1359 ** started a scenario, Start_Scenario() will have played a couple of VQ 1360 ** movies, which will have cleared the screen to black already.) 1361 */ 1362 1363 //Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); 1364 HiddenPage.Clear(); 1365 VisiblePage.Clear(); 1366 Set_Logic_Page(SeenBuff); 1367 Map.Flag_To_Redraw(true); 1368 Map.Render(); 1369 1370 Set_Palette(GamePalette); 1371 1372 return true; 1373 } 1374 1375 1376 bool Debug_Write_Shape_Type(const ObjectTypeClass *type, int shapenum) 1377 { 1378 char fullname[_MAX_FNAME+_MAX_EXT]; 1379 char buffer[_MAX_FNAME]; 1380 CCFileClass file; 1381 1382 if (type->ImageData != NULL) { 1383 1384 sprintf(buffer, "%s_%d", type->IniName, shapenum); 1385 _makepath(fullname, NULL, NULL, buffer, ".PCX"); 1386 1387 return Debug_Write_Shape(fullname, type->ImageData, shapenum); 1388 } 1389 1390 return false; 1391 } 1392 1393 1394 bool Debug_Write_Shape(const char *file_name, void const * shapefile, int shapenum, int flags, void const * ghostdata) 1395 { 1396 /* 1397 ** Build frame returns a pointer now instead of the shapes length 1398 */ 1399 char *shape_pointer = (char*) Build_Frame(shapefile , shapenum , _ShapeBuffer); 1400 if (shape_pointer == NULL) { 1401 return false;; 1402 } 1403 if (Get_Last_Frame_Length() > _ShapeBufferSize) { 1404 return false;; 1405 } 1406 1407 int width = Get_Build_Frame_Width(shapefile); 1408 int height = Get_Build_Frame_Height(shapefile); 1409 1410 GraphicBufferClass temp_gbuffer(width, height); 1411 GraphicViewPortClass temp_viewport(&temp_gbuffer, 0, 0, width, height); 1412 1413 WindowList[WINDOW_CUSTOM][WINDOWX] = 0; 1414 WindowList[WINDOW_CUSTOM][WINDOWY] = 0; 1415 WindowList[WINDOW_CUSTOM][WINDOWWIDTH] = width; 1416 WindowList[WINDOW_CUSTOM][WINDOWHEIGHT] = height; 1417 1418 static const char _shape_trans = 0x40; 1419 1420 if (flags == 0) { 1421 Buffer_Frame_To_Page(0, 0, width, height, shape_pointer, temp_viewport, SHAPE_NORMAL|SHAPE_WIN_REL|_shape_trans); //, ghostdata, predoffset); 1422 } else { 1423 Buffer_Frame_To_Page(0, 0, width, height, shape_pointer, temp_viewport, flags, ghostdata); 1424 } 1425 Write_PCX_File((char*)file_name, temp_viewport, GamePalette); 1426 1427 return true; 1428 } 1429 1430 1431 1432 /************************************************************************************************** 1433 * CNC_Advance_Instance -- Process one logic frame 1434 * 1435 * In: 1436 * 1437 * Out: Is game still playing? 1438 * 1439 * 1440 * 1441 * History: 1/7/2019 5:20PM - ST 1442 **************************************************************************************************/ 1443 extern "C" __declspec(dllexport) bool __cdecl CNC_Advance_Instance(uint64 player_id) 1444 { 1445 //DLLExportClass::Set_Event_Callback(event_callback); 1446 1447 InMainLoop = true; 1448 1449 if (Frame <= 10) { // Don't spam forever, but useful to know that we actually started advancing 1450 GlyphX_Debug_Print("CNC_Advance_Instance - TD"); 1451 } 1452 1453 /* 1454 ** Shouldn't really need to do this, but I like the idea of always running the main loop in the context of the same player. 1455 ** Might make tbe bugs more repeatable and consistent. ST - 3/15/2019 11:58AM 1456 */ 1457 if (player_id != 0) { 1458 DLLExportClass::Set_Player_Context(player_id); 1459 } else { 1460 DLLExportClass::Set_Player_Context(DLLExportClass::GlyphxPlayerIDs[0]); 1461 } 1462 1463 /* 1464 ** Allocate extra memory for uncompressed shapes as needed 1465 */ 1466 Reallocate_Big_Shape_Buffer(); 1467 1468 /* 1469 ** If there is no theme playing, but it looks like one is required, then start one 1470 ** playing. This is usually the symptom of there being no transition score. 1471 */ 1472 //if (SampleType && Theme.What_Is_Playing() == THEME_NONE) { 1473 // Theme.Queue_Song(THEME_PICK_ANOTHER); 1474 //} 1475 1476 /* 1477 ** Update the display, unless we're inside a dialog. 1478 */ 1479 //if (SpecialDialog == SDLG_NONE && GameInFocus) { 1480 1481 //WWMouse->Erase_Mouse(&HidPage, TRUE); 1482 //Map.Input(input, x, y); 1483 //if (input) { 1484 // Keyboard_Process(input); 1485 //} 1486 /* 1487 ** The main loop passes these in uninitialized. ST - 2/7/2019 4:36PM 1488 */ 1489 KeyNumType input = KN_NONE; // Player input. 1490 int x = 0; 1491 int y = 0; 1492 Map.Input(input, x, y); 1493 //if (input) { 1494 // Keyboard_Process(input); 1495 //} 1496 1497 if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { 1498 /* 1499 ** Process the sidebar. ST - 4/18/2019 11:59AM 1500 */ 1501 HouseClass *old_player_ptr = PlayerPtr; 1502 for (int i=0 ; i<MPlayerCount ; i++) { 1503 HouseClass *player_ptr = HouseClass::As_Pointer(MPlayerHouses[i]); //HouseClass::As_Pointer(HOUSE_MULTI2); 1504 DLLExportClass::Logic_Switch_Player_Context(player_ptr); 1505 Sidebar_Glyphx_AI(player_ptr, input); 1506 } 1507 DLLExportClass::Logic_Switch_Player_Context(old_player_ptr); 1508 } 1509 //} 1510 1511 /* 1512 ** Sort the map's ground layer by y-coordinate value. This is done 1513 ** outside the IsToRedraw check, for the purposes of game sync'ing 1514 ** between machines; this way, all machines will sort the Map's 1515 ** layer in the same way, and any processing done that's based on 1516 ** the order of this layer will sync on different machines. 1517 */ 1518 Map.Layer[LAYER_GROUND].Sort(); 1519 1520 /* 1521 ** AI logic operations are performed here. 1522 */ 1523 //Skip this block of code on first update of single-player games. This helps prevents trigger generated messages on the first update from being lost during loading screen or movie. - LLL 1524 static bool FirstUpdate = GameToPlay != GAME_GLYPHX_MULTIPLAYER;; 1525 if (!FirstUpdate) 1526 { 1527 HouseClass *old_player_ptr = PlayerPtr; 1528 Logic.Clear_Recently_Created_Bits(); 1529 Logic.AI(); 1530 DLLExportClass::Logic_Switch_Player_Context(old_player_ptr); 1531 } 1532 FirstUpdate = false; 1533 1534 /* 1535 ** Manage the inter-player message list. If Manage() returns true, it means 1536 ** a message has expired & been removed, and the entire map must be updated. 1537 */ 1538 //if (Messages.Manage()) { 1539 // HiddenPage.Clear(); 1540 // Map.Flag_To_Redraw(true); 1541 //} 1542 1543 /* 1544 ** Process all commands that are ready to be processed. 1545 */ 1546 if (GameToPlay == GAME_NORMAL) { 1547 Queue_AI(); 1548 } else { 1549 if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { 1550 DLLExportClass::Glyphx_Queue_AI(); 1551 1552 /* 1553 ** Process the sidebar. ST - 3/22/2019 2:07PM 1554 */ 1555 for (int i=0 ; i<MPlayerCount ; i++) { 1556 HouseClass *player_ptr = HouseClass::As_Pointer(MPlayerHouses[i]); //HouseClass::As_Pointer(HOUSE_MULTI2); 1557 Sidebar_Glyphx_Recalc(player_ptr); 1558 } 1559 } 1560 } 1561 1562 /* 1563 ** Keep track of elapsed time in the game. 1564 */ 1565 //Score.ElapsedTime += TIMER_SECOND / TICKS_PER_SECOND; 1566 1567 /* 1568 ** Perform any win/lose code as indicated by the global control flags. 1569 */ 1570 if (EndCountDown) EndCountDown--; 1571 1572 /* 1573 ** Check for player wins or loses according to global event flag. 1574 */ 1575 if (PlayerWins) { 1576 //WWMouse->Erase_Mouse(&HidPage, TRUE); 1577 PlayerLoses = false; 1578 PlayerWins = false; 1579 PlayerRestarts = false; 1580 Map.Help_Text(TXT_NONE); 1581 1582 InMainLoop = false; 1583 1584 GlyphX_Debug_Print("PlayerWins = true"); 1585 1586 if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { 1587 DLLExportClass::On_Multiplayer_Game_Over(); 1588 } else { 1589 DLLExportClass::On_Game_Over(player_id, true); 1590 } 1591 1592 return false; 1593 } 1594 if (PlayerLoses) { 1595 1596 //WWMouse->Erase_Mouse(&HidPage, TRUE); 1597 PlayerWins = false; 1598 PlayerLoses = false; 1599 PlayerRestarts = false; 1600 Map.Help_Text(TXT_NONE); 1601 1602 //Do_Lose(); //Old C&C code 1603 GlyphX_Debug_Print("PlayerLoses = true"); 1604 if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { 1605 DLLExportClass::On_Multiplayer_Game_Over(); 1606 } else { 1607 DLLExportClass::On_Game_Over(player_id, false); 1608 } 1609 1610 InMainLoop = false; 1611 1612 return false; 1613 } 1614 1615 /* 1616 ** The frame logic has been completed. Increment the frame 1617 ** counter. 1618 */ 1619 Frame++; 1620 1621 /* 1622 ** Very rarely, the human players will get a message from the computer. 1623 */ 1624 if (GameToPlay != GAME_NORMAL && MPlayerGhosts && IRandom(0,10000) == 1) { 1625 DLLExportClass::Computer_Message(false); 1626 } 1627 1628 /* 1629 ** The code is often leaving dangling pointers in overlappers. We can afford the CPU time to just clean them up. I suspect 1630 ** the underlying cause was probably fixed in RA. 1631 ** ST - 4/14/2020 11:45AM 1632 */ 1633 Map.Clean(); 1634 1635 #ifndef NDEBUG 1636 /* 1637 ** Is there a memory trasher altering the map?? 1638 */ 1639 if (!Map.Validate()) { 1640 GlyphX_Debug_Print("Map.Validate() failed"); 1641 1642 //if (CCMessageBox().Process ("Map Error!","Stop","Continue")==0) { 1643 // GameActive = false; 1644 //} 1645 Map.Validate(); // give debugger a chance to catch it 1646 } 1647 #endif NDEBUG 1648 1649 InMainLoop = false; 1650 1651 if (ProgEndCalled) { 1652 GlyphX_Debug_Print("ProgEndCalled - GameActive = false"); 1653 GameActive = false; 1654 } 1655 1656 if (DLLExportClass::Legacy_Render_Enabled()) { 1657 Map.Render(); 1658 } 1659 1660 //Sync_Delay(); 1661 Color_Cycle(); 1662 //DLLExportClass::Set_Event_Callback(NULL); 1663 return(GameActive); 1664 } 1665 1666 1667 /************************************************************************************************** 1668 * CNC_Save_Load -- Process a save or load game action 1669 * 1670 * In: 1671 * 1672 * Out: Success? 1673 * 1674 * 1675 * 1676 * History: 1/7/2019 5:20PM - ST 1677 **************************************************************************************************/ 1678 extern "C" __declspec(dllexport) bool __cdecl CNC_Save_Load(bool save, const char *file_path_and_name, const char *game_type) 1679 { 1680 bool result = false; 1681 1682 if (save) { 1683 result = Save_Game(file_path_and_name, "internal"); 1684 } else { 1685 1686 if (game_type == NULL) { 1687 return false; 1688 } 1689 1690 if (stricmp(game_type, "GAME_NORMAL") == 0) { 1691 GameToPlay = GAME_NORMAL; 1692 } else { 1693 if (stricmp(game_type, "GAME_GLYPHX_MULTIPLAYER") == 0) { 1694 GameToPlay = GAME_GLYPHX_MULTIPLAYER; 1695 ScenPlayer = SCEN_PLAYER_MPLAYER; 1696 } else { 1697 return false; 1698 } 1699 } 1700 1701 result = Load_Game(file_path_and_name); 1702 1703 if (result == false) 1704 { 1705 return false; 1706 } 1707 1708 DLLExportClass::Set_Player_Context(DLLExportClass::GlyphxPlayerIDs[0], true); 1709 DLLExportClass::Cancel_Placement(DLLExportClass::GlyphxPlayerIDs[0], -1, -1); 1710 Set_Logic_Page(SeenBuff); 1711 VisiblePage.Clear(); 1712 Map.Flag_To_Redraw(true); 1713 if (DLLExportClass::Legacy_Render_Enabled()) { 1714 Map.Render(); 1715 } 1716 Set_Palette(GamePalette); 1717 } 1718 1719 return result; 1720 } 1721 1722 1723 1724 /************************************************************************************************** 1725 * CNC_Set_Difficulty -- Set game difficulty 1726 * 1727 * In: 1728 * 1729 * Out: 1730 * 1731 * 1732 * 1733 * History: 10/02/2019 - SKY 1734 **************************************************************************************************/ 1735 extern "C" __declspec(dllexport) void __cdecl CNC_Set_Difficulty(int difficulty) 1736 { 1737 if (GameToPlay == GAME_NORMAL) { 1738 Set_Scenario_Difficulty(difficulty); 1739 } 1740 } 1741 1742 1743 /************************************************************************************************** 1744 * CNC_Handle_Player_Switch_To_AI -- Renamed 3/9/20202 - LLL 1745 * previously named: CNC_Handle_Player_Disconnect -- Handle player disconnected during multiplayuer game 1746 * 1747 * In: 1748 * 1749 * Out: 1750 * 1751 * 1752 * 1753 * History: 12/3/2019 1:46PM - ST 1754 **************************************************************************************************/ 1755 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uint64 player_id) 1756 { 1757 if (PlayerWins || PlayerLoses || DLLExportClass::Get_Game_Over()) { 1758 return; 1759 } 1760 1761 GlyphX_Debug_Print("CNC_Handle_Player_Switch_To_AI"); 1762 1763 if (GameToPlay == GAME_NORMAL) { 1764 return; 1765 } 1766 1767 #ifdef KILL_PLAYER_ON_DISCONNECT 1768 1769 /* 1770 ** Kill player's units on disconnect. 1771 */ 1772 if (player_id != 0) { 1773 DLLExportClass::Set_Player_Context(player_id); 1774 1775 if (PlayerPtr) { 1776 PlayerPtr->Flag_To_Die(); 1777 } 1778 } 1779 1780 #else //KILL_PLAYER_ON_DISCONNECT 1781 1782 if (player_id != 0) { 1783 1784 HousesType house; 1785 HouseClass *ptr; 1786 1787 DLLExportClass::Set_Player_Context(player_id); 1788 1789 if (PlayerPtr) { 1790 PlayerPtr->WasHuman = true; 1791 PlayerPtr->IsHuman = false; 1792 PlayerPtr->IsStarted = true; 1793 PlayerPtr->IQ = Rule.MaxIQ; 1794 PlayerPtr->IsBaseBuilding = true; 1795 1796 /* 1797 ** Start the unload mission for MCVs 1798 */ 1799 for (int index = 0; index < Units.Count(); index++) { 1800 UnitClass * obj = Units.Ptr(index); 1801 1802 if (obj && !obj->IsInLimbo && obj->House == PlayerPtr) { 1803 if (*obj == UNIT_MCV) { 1804 obj->Assign_Mission(MISSION_GUARD); 1805 obj->Assign_Target(TARGET_NONE); 1806 obj->Assign_Destination(TARGET_NONE); 1807 obj->Assign_Mission(MISSION_UNLOAD); 1808 obj->Commence(); 1809 } 1810 } 1811 } 1812 1813 DLLExportClass::On_Message(PlayerPtr, "", 60.0f, MESSAGE_TYPE_PLAYER_DISCONNECTED, -1); 1814 1815 /* 1816 ** Send the disconnect taunt message 1817 */ 1818 int human_count = 0; 1819 for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MPlayerMax); house++) { 1820 ptr = HouseClass::As_Pointer(house); 1821 1822 if (ptr && ptr->IsHuman && !ptr->IsDefeated) { 1823 human_count++; 1824 } 1825 } 1826 1827 if (human_count == 1) { 1828 DLLExportClass::Computer_Message(true); 1829 } 1830 } 1831 } 1832 1833 #endif //KILL_PLAYER_ON_DISCONNECT 1834 1835 } 1836 1837 1838 /************************************************************************************************** 1839 * CNC_Handle_Human_Team_Wins 1840 * 1841 * History: 3/10/2020 - LLL 1842 **************************************************************************************************/ 1843 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Human_Team_Wins(uint64 quitting_player_id) 1844 { 1845 GlyphX_Debug_Print("CNC_Handle_Human_Team_Wins"); 1846 DLLExportClass::Force_Human_Team_Wins(quitting_player_id); 1847 } 1848 1849 1850 /************************************************************************************************** 1851 * CNC_Start_Mission_Timer 1852 * 1853 * History: 11/25/2019 - LLL 1854 **************************************************************************************************/ 1855 extern "C" __declspec(dllexport) void __cdecl CNC_Start_Mission_Timer(int time) 1856 { 1857 //Only implemented in Red Alert. 1858 } 1859 1860 1861 1862 /************************************************************************************************** 1863 * CNC_Get_Start_Game_Info 1864 * 1865 * History: 8/31/2020 11:37AM - ST 1866 **************************************************************************************************/ 1867 extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Start_Game_Info(uint64 player_id, int &start_location_waypoint_index) 1868 { 1869 start_location_waypoint_index = 0; 1870 if (!DLLExportClass::Set_Player_Context(player_id)) { 1871 return false; 1872 } 1873 1874 start_location_waypoint_index = PlayerPtr->StartLocationOverride; 1875 return true; 1876 } 1877 1878 1879 1880 /************************************************************************************************** 1881 * DLLExportClass::Init -- Init the class 1882 * 1883 * In: 1884 * 1885 * Out: 1886 * 1887 * 1888 * 1889 * History: 3/12/2019 10:52AM - ST 1890 **************************************************************************************************/ 1891 void DLLExportClass::Init(void) 1892 { 1893 for (int i=0 ; i<MAX_PLAYERS ; i++) { 1894 GlyphxPlayerIDs[i] = 0xffffffffull; 1895 } 1896 1897 CurrentLocalPlayerIndex = 0; 1898 1899 } 1900 1901 1902 1903 /************************************************************************************************** 1904 * DLLExportClass::Shutdown -- Shutdown 1905 * 1906 * In: 1907 * 1908 * Out: 1909 * 1910 * 1911 * 1912 * History: 2/20/2020 1:59PM - ST 1913 **************************************************************************************************/ 1914 void DLLExportClass::Shutdown(void) 1915 { 1916 for (int i=0 ; i<ModSearchPaths.Count() ; i++) { 1917 delete [] ModSearchPaths[i]; 1918 } 1919 ModSearchPaths.Clear(); 1920 } 1921 1922 1923 1924 1925 /************************************************************************************************** 1926 * DLLExportClass::Add_Mod_Path -- Add a path to load mod files from 1927 * 1928 * In: Mod path 1929 * 1930 * Out: 1931 * 1932 * 1933 * 1934 * History: 2/20/2020 2:03PM - ST 1935 **************************************************************************************************/ 1936 void DLLExportClass::Add_Mod_Path(const char *mod_path) 1937 { 1938 char *copy_path = strdup(mod_path); 1939 ModSearchPaths.Add(copy_path); 1940 } 1941 1942 1943 1944 1945 /************************************************************************************************** 1946 * DLLExportClass::Set_Content_Directory -- Update the locations that the original code will load from 1947 * 1948 * In: Main (official) content directory 1949 * 1950 * Out: 1951 * 1952 * 1953 * 1954 * History: 2/20/2020 2:03PM - ST 1955 **************************************************************************************************/ 1956 void DLLExportClass::Set_Content_Directory(const char *content_directory) 1957 { 1958 CCFileClass::Clear_Search_Drives(); 1959 CCFileClass::Reset_Raw_Path(); 1960 1961 if ((content_directory == NULL || strlen(content_directory) == 0) && ModSearchPaths.Count() == 0) { 1962 return; 1963 } 1964 1965 char *all_paths = new char [_MAX_PATH * 100]; 1966 *all_paths = 0; 1967 1968 for (int i=0 ; i<ModSearchPaths.Count() ; i++) { 1969 if (i != 0) { 1970 strcat(all_paths, ";"); 1971 } 1972 strcat(all_paths, ModSearchPaths[i]); 1973 } 1974 1975 if (ModSearchPaths.Count() && content_directory && strlen(content_directory)) { 1976 strcat(all_paths, ";"); 1977 } 1978 1979 if (content_directory) { 1980 strcat(all_paths, content_directory); 1981 } 1982 1983 CCFileClass::Set_Search_Drives(all_paths); 1984 delete [] all_paths; 1985 } 1986 1987 1988 1989 1990 1991 1992 /************************************************************************************************** 1993 * DLLExportClass::Config 1994 * 1995 * History: 1/16/2020 - SKY 1996 **************************************************************************************************/ 1997 void DLLExportClass::Config(const CNCRulesDataStruct& rules) 1998 { 1999 for (int i = 0; i < 3; ++i) 2000 { 2001 Rule.Diff[i].FirepowerBias = rules.Difficulties[i].FirepowerBias; 2002 Rule.Diff[i].GroundspeedBias = rules.Difficulties[i].GroundspeedBias; 2003 Rule.Diff[i].AirspeedBias = rules.Difficulties[i].AirspeedBias; 2004 Rule.Diff[i].ArmorBias = rules.Difficulties[i].ArmorBias; 2005 Rule.Diff[i].ROFBias = rules.Difficulties[i].ROFBias; 2006 Rule.Diff[i].CostBias = rules.Difficulties[i].CostBias; 2007 Rule.Diff[i].BuildSpeedBias = rules.Difficulties[i].BuildSpeedBias; 2008 Rule.Diff[i].RepairDelay = rules.Difficulties[i].RepairDelay; 2009 Rule.Diff[i].BuildDelay = rules.Difficulties[i].BuildDelay; 2010 Rule.Diff[i].IsBuildSlowdown = rules.Difficulties[i].IsBuildSlowdown ? 1 : 0; 2011 Rule.Diff[i].IsWallDestroyer = rules.Difficulties[i].IsWallDestroyer ? 1 : 0; 2012 Rule.Diff[i].IsContentScan = rules.Difficulties[i].IsContentScan ? 1 : 0; 2013 } 2014 } 2015 2016 2017 /************************************************************************************************** 2018 * DLLExportClass::Set_Home_Cell 2019 * 2020 * History: 2/14/2020 - LLL 2021 **************************************************************************************************/ 2022 extern CELL Views[4]; 2023 void DLLExportClass::Set_Home_Cell(int x, int y, uint64 player_id) 2024 { 2025 if (GameToPlay == GAME_NORMAL) { 2026 MultiplayerStartPositions[0] = Views[0] = XY_Cell(x, y); 2027 } 2028 else { 2029 for (int i = 0; i < MPlayerCount && i < 4; i++) { 2030 if (GlyphxPlayerIDs[i] == player_id) { 2031 Views[i] = MultiplayerStartPositions[i] = XY_Cell(x, y); 2032 } 2033 } 2034 } 2035 } 2036 2037 2038 /************************************************************************************************** 2039 * DLLExportClass::On_Play_Movie 2040 * 2041 * History: 7/23/2019 - LLL 2042 **************************************************************************************************/ 2043 void DLLExportClass::On_Play_Movie(const char * movie_name, ThemeType theme, bool immediate) 2044 { 2045 if (EventCallback == NULL) { 2046 return; 2047 } 2048 2049 InMainLoop = false; 2050 2051 EventCallbackStruct new_event; 2052 new_event.EventType = CALLBACK_EVENT_MOVIE; 2053 new_event.Movie.MovieName = movie_name; 2054 new_event.Movie.Theme = (int)theme; 2055 new_event.Movie.Immediate = immediate; 2056 2057 EventCallback(new_event); 2058 } 2059 2060 /************************************************************************************************** 2061 * DLLExportClass::On_Display_Briefing_Text 2062 * 2063 * Called when Red Alert wants to display the mission breifing screen before a mission. 2064 * 2065 * History: 12/04/2019 - LLL 2066 **************************************************************************************************/ 2067 void DLLExportClass::On_Display_Briefing_Text() 2068 { 2069 //Only implemeneted for Red Alert - LLL 2070 } 2071 2072 2073 /************************************************************************************************** 2074 * DLLExportClass::On_Sound_Effect -- Called when C&C wants to play a sound effect 2075 * 2076 * In: 2077 * 2078 * Out: 2079 * 2080 * 2081 * 2082 * History: 2/20/2019 2:39PM - ST 2083 **************************************************************************************************/ 2084 void DLLExportClass::On_Sound_Effect(const HouseClass* player_ptr, int sound_effect_index, const char* extension, int variation, COORDINATE coord) 2085 { 2086 // player_ptr could be NULL 2087 2088 if (EventCallback == NULL) { 2089 return; 2090 } 2091 2092 EventCallbackStruct new_event; 2093 new_event.EventType = CALLBACK_EVENT_SOUND_EFFECT; 2094 new_event.SoundEffect.SFXIndex = sound_effect_index; 2095 new_event.SoundEffect.Variation = variation; 2096 2097 new_event.GlyphXPlayerID = 0; 2098 if ( player_ptr != NULL ) 2099 { 2100 new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); 2101 } 2102 2103 if ( coord == 0 ) 2104 { 2105 new_event.SoundEffect.PixelX = -1; 2106 new_event.SoundEffect.PixelY = -1; 2107 } 2108 else 2109 { 2110 // Use world pixel coordinates 2111 new_event.SoundEffect.PixelX = Lepton_To_Pixel(Coord_X(coord)); 2112 new_event.SoundEffect.PixelY = Lepton_To_Pixel(Coord_Y(coord)); 2113 } 2114 2115 if ( sound_effect_index >= VOC_FIRST && sound_effect_index < VOC_COUNT ) 2116 { 2117 strncpy( new_event.SoundEffect.SoundEffectName, SoundEffectName[ sound_effect_index ].Name, CNC_OBJECT_ASSET_NAME_LENGTH); 2118 new_event.SoundEffect.SoundEffectName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; // strncpy can leave strings unterminated 2119 if ( extension != NULL ) 2120 { 2121 strncat( new_event.SoundEffect.SoundEffectName, extension, CNC_OBJECT_ASSET_NAME_LENGTH); 2122 new_event.SoundEffect.SoundEffectName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; // strncat can leave strings unterminated 2123 } 2124 new_event.SoundEffect.SoundEffectPriority = SoundEffectName[ sound_effect_index ].Priority; 2125 new_event.SoundEffect.SoundEffectContext = SoundEffectName[ sound_effect_index ].Where; 2126 } 2127 else 2128 { 2129 strncpy( new_event.SoundEffect.SoundEffectName, "BADINDEX", CNC_OBJECT_ASSET_NAME_LENGTH); 2130 new_event.SoundEffect.SoundEffectPriority = -1; 2131 new_event.SoundEffect.SoundEffectContext = -1; 2132 } 2133 2134 EventCallback(new_event); 2135 } 2136 2137 2138 2139 /************************************************************************************************** 2140 * DLLExportClass::On_Speech -- Called when C&C wants to play a speech line 2141 * 2142 * In: 2143 * 2144 * Out: 2145 * 2146 * 2147 * 2148 * History: 2/20/2019 2:39PM - ST 2149 **************************************************************************************************/ 2150 void DLLExportClass::On_Speech(const HouseClass* player_ptr, int speech_index) 2151 { 2152 // player_ptr could be NULL 2153 2154 if (EventCallback == NULL) { 2155 return; 2156 } 2157 2158 EventCallbackStruct new_event; 2159 new_event.EventType = CALLBACK_EVENT_SPEECH; 2160 new_event.Speech.SpeechIndex = speech_index; 2161 2162 new_event.GlyphXPlayerID = 0; 2163 if ( player_ptr != NULL ) 2164 { 2165 new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); 2166 } 2167 2168 if ( speech_index >= VOX_FIRST && speech_index < VOX_COUNT ) 2169 { 2170 strncpy( new_event.Speech.SpeechName, Speech[ speech_index ], CNC_OBJECT_ASSET_NAME_LENGTH); 2171 new_event.Speech.SpeechName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; // strncpy can leave strings unterminated 2172 } 2173 else 2174 { 2175 strncpy( new_event.Speech.SpeechName, "BAD_SPEECH_INDEX", CNC_OBJECT_ASSET_NAME_LENGTH); 2176 } 2177 2178 EventCallback(new_event); 2179 } 2180 2181 /************************************************************************************************** 2182 * DLLExportClass::TD_Calculate_Efficiency -- 2183 * 2184 * History: 10.29.2019 MBL (Based on LLL's Calculate_Single_Player_Score()) 2185 **************************************************************************************************/ 2186 unsigned int DLLExportClass::TD_Calculate_Efficiency( unsigned int harvested_credits, unsigned int initial_credits, unsigned int available_credits ) 2187 { 2188 unsigned efficiency = Cardinal_To_Fixed( harvested_credits + (initial_credits + 1), (available_credits + 1) ); 2189 if ( efficiency == 0 ) { 2190 efficiency++; 2191 } 2192 2193 efficiency = Fixed_To_Cardinal(100, efficiency); 2194 if (efficiency > 100) { 2195 efficiency = 100; 2196 } 2197 2198 return efficiency; 2199 } 2200 2201 /************************************************************************************************** 2202 * DLLExportClass::TD_Calculate_Leadership -- 2203 * 2204 * History: 10.29.2019 MBL (Based on LLL's Calculate_Single_Player_Score()) 2205 **************************************************************************************************/ 2206 unsigned int DLLExportClass::TD_Calculate_Leadership( int house, unsigned int units_lost, unsigned int buildings_lost ) 2207 { 2208 unsigned int leadership = 0; 2209 2210 for (int index = 0; index < Logic.Count(); index++) { 2211 ObjectClass * object = Logic[index]; 2212 if (object->Owner() == house) { 2213 leadership++; 2214 } 2215 } 2216 2217 if (leadership == 0) { 2218 leadership = 1; 2219 } 2220 2221 leadership = Cardinal_To_Fixed(units_lost + buildings_lost + leadership, leadership); 2222 2223 leadership = Fixed_To_Cardinal(100, leadership); 2224 if (leadership > 100) { 2225 leadership = 100; 2226 } 2227 2228 return leadership; 2229 } 2230 2231 /************************************************************************************************** 2232 * DLLExportClass::TD_Calculate_Score -- 2233 * 2234 * History: 10.29.2019 MBL (Based on LLL's Calculate_Single_Player_Score()) 2235 **************************************************************************************************/ 2236 unsigned int DLLExportClass::TD_Calculate_Score( unsigned int leadership, unsigned int efficiency, unsigned int build_level ) 2237 { 2238 long total = ((leadership * 40) + (4600) + (efficiency * 14)) / 100; 2239 if (!total) total++; 2240 total *= (build_level + 1); 2241 2242 return total; 2243 } 2244 2245 void DLLExportClass::Calculate_Single_Player_Score(EventCallbackStruct& event) 2246 { 2247 //Adapted from Tiberian Dawn SCORE.CPP Presentation() - LLL 2248 int house = PlayerPtr->Class->House; // 0 or 1 2249 2250 HouseClass *houses[3]; 2251 for (int index = 0; index < 3; index++) { 2252 houses[index] = (HouseClass::As_Pointer((HousesType)(HOUSE_GOOD + index))); 2253 } 2254 2255 int gdi_units_lost = (HouseClass::As_Pointer(HOUSE_GOOD))->UnitsLost; 2256 int nod_units_lost = (HouseClass::As_Pointer(HOUSE_BAD))->UnitsLost; 2257 int civilians_killed = (HouseClass::As_Pointer(HOUSE_NEUTRAL))->UnitsLost; 2258 int gdi_buildings_lost = (HouseClass::As_Pointer(HOUSE_GOOD))->BuildingsLost; 2259 int nod_buildings_lost = (HouseClass::As_Pointer(HOUSE_BAD))->BuildingsLost; 2260 int civilian_buildings_lost = (HouseClass::As_Pointer(HOUSE_NEUTRAL))->BuildingsLost; 2261 int gdi_harvested = (HouseClass::As_Pointer(HOUSE_GOOD))->HarvestedCredits; 2262 int nod_harvested = (HouseClass::As_Pointer(HOUSE_BAD))->HarvestedCredits; 2263 2264 // unsigned leadership = 0; 2265 // int index; 2266 // for (index = 0; index < Logic.Count(); index++) { 2267 // ObjectClass * object = Logic[index]; 2268 // if (object->Owner() == house) { 2269 // leadership++; 2270 // } 2271 // } 2272 // 2273 // if (leadership == 0) { 2274 // leadership = 1; 2275 // } 2276 // 2277 // leadership = Cardinal_To_Fixed(gdi_units_lost + gdi_buildings_lost + leadership, leadership); 2278 // leadership = Fixed_To_Cardinal(100, leadership); 2279 // if (leadership > 100) { 2280 // leadership = 100; 2281 // } 2282 // 2283 unsigned leadership = TD_Calculate_Leadership( house, (house == HOUSE_GOOD ? gdi_units_lost : nod_units_lost), (house == HOUSE_GOOD ? gdi_buildings_lost : nod_buildings_lost) ); 2284 2285 /* 2286 ** Determine efficiency rating. 2287 */ 2288 // int gharv = gdi_harvested; 2289 // int init = PlayerPtr->InitialCredits; 2290 // int cred = PlayerPtr->Available_Money(); 2291 // 2292 // unsigned efficiency = Cardinal_To_Fixed((house == HOUSE_GOOD ? gdi_harvested : nod_harvested) + (unsigned)PlayerPtr->InitialCredits + 1, (unsigned)PlayerPtr->Available_Money() + 1); 2293 // if (!efficiency) efficiency++; 2294 // efficiency = Fixed_To_Cardinal(100, efficiency); 2295 // if (efficiency > 100) { 2296 // efficiency = 100; 2297 // } 2298 // 2299 unsigned efficiency = TD_Calculate_Efficiency((house == HOUSE_GOOD ? gdi_harvested : nod_harvested), PlayerPtr->InitialCredits, PlayerPtr->Available_Money()); 2300 2301 /* 2302 ** Calculate total score 2303 */ 2304 // long total_score = ((leadership * 40) + (4600) + (efficiency * 14)) / 100; 2305 // if (!total_score) total_score++; 2306 // total_score *= (BuildLevel + 1); 2307 // 2308 unsigned total_score = TD_Calculate_Score( leadership, efficiency, BuildLevel ); 2309 2310 //Score Stats 2311 event.GameOver.Leadership = leadership; 2312 event.GameOver.Efficiency = efficiency; 2313 event.GameOver.Score = total_score; 2314 event.GameOver.CategoryTotal = 0; //Only used in Red Alert 2315 event.GameOver.NODKilled = nod_units_lost; 2316 event.GameOver.GDIKilled = gdi_units_lost; 2317 event.GameOver.CiviliansKilled = civilians_killed; 2318 event.GameOver.NODBuildingsDestroyed = nod_buildings_lost; 2319 event.GameOver.GDIBuildingsDestroyed = gdi_buildings_lost; 2320 event.GameOver.CiviliansBuildingsDestroyed = civilian_buildings_lost; 2321 event.GameOver.RemainingCredits = PlayerPtr->Available_Money(); 2322 } 2323 2324 void DLLExportClass::Convert_Action_Type(ActionType type, ObjectClass* object, TARGET target, DllActionTypeEnum& dll_type) 2325 { 2326 switch (type) 2327 { 2328 case ACTION_NONE: 2329 default: 2330 dll_type = DAT_NONE; 2331 break; 2332 case ACTION_MOVE: 2333 dll_type = DAT_MOVE; 2334 break; 2335 case ACTION_NOMOVE: 2336 dll_type = DAT_NOMOVE; 2337 break; 2338 case ACTION_ENTER: 2339 dll_type = DAT_ENTER; 2340 break; 2341 case ACTION_SELF: 2342 dll_type = DAT_SELF; 2343 break; 2344 case ACTION_ATTACK: 2345 if (Target_Legal(target) && (object != NULL) && object->Is_Techno() && ((TechnoClass*)object)->In_Range(target, 0)) { 2346 dll_type = DAT_ATTACK; 2347 } 2348 else { 2349 dll_type = DAT_ATTACK_OUT_OF_RANGE; 2350 } 2351 break; 2352 case ACTION_GUARD_AREA: 2353 dll_type = DAT_GUARD; 2354 break; 2355 case ACTION_HARVEST: 2356 dll_type = DAT_ATTACK; 2357 break; 2358 case ACTION_SELECT: 2359 case ACTION_TOGGLE_SELECT: 2360 dll_type = DAT_SELECT; 2361 break; 2362 case ACTION_CAPTURE: 2363 dll_type = DAT_CAPTURE; 2364 break; 2365 case ACTION_SABOTAGE: 2366 dll_type = DAT_SABOTAGE; 2367 break; 2368 case ACTION_TOGGLE_PRIMARY: 2369 dll_type = DAT_TOGGLE_PRIMARY; 2370 break; 2371 case ACTION_NO_DEPLOY: 2372 dll_type = DAT_CANT_DEPLOY; 2373 break; 2374 } 2375 } 2376 2377 2378 2379 2380 /************************************************************************************************** 2381 * DLLExportClass::On_Game_Over -- Called when the C&C campaign game finishes 2382 * 2383 * 2384 * History: 6/19/2019 - LLL 2385 **************************************************************************************************/ 2386 void DLLExportClass::On_Game_Over(uint64 glyphx_Player_id, bool player_wins) 2387 { 2388 if (EventCallback == NULL) { 2389 return; 2390 } 2391 2392 GameOver = true; 2393 2394 EventCallbackStruct new_event; 2395 new_event.EventType = CALLBACK_EVENT_GAME_OVER; 2396 new_event.GlyphXPlayerID = glyphx_Player_id; 2397 new_event.GameOver.PlayerWins = player_wins; 2398 new_event.GameOver.MovieName = player_wins ? WinMovie : LoseMovie; 2399 new_event.GameOver.AfterScoreMovieName = ""; 2400 new_event.GameOver.Multiplayer = false; 2401 new_event.GameOver.MultiPlayerTotalPlayers = 0; 2402 2403 Calculate_Single_Player_Score(new_event); 2404 2405 if (player_wins) 2406 { 2407 if (PlayerPtr->Class->House == HOUSE_BAD && Scenario == 13) { 2408 //TODO: Nod_Ending() Also looks like it plays some audio that might need to be integrated. 2409 new_event.GameOver.MovieName = ""; 2410 new_event.GameOver.AfterScoreMovieName = "NODFINAL"; 2411 } 2412 else if (PlayerPtr->Class->House == HOUSE_GOOD && Scenario == 15) { 2413 if (TempleIoned) { 2414 new_event.GameOver.MovieName = "GDIFINB"; 2415 new_event.GameOver.AfterScoreMovieName = "GDIEND2"; 2416 } 2417 else { 2418 new_event.GameOver.MovieName = "GDIFINA"; 2419 new_event.GameOver.AfterScoreMovieName = "GDIEND1"; 2420 } 2421 } 2422 } 2423 2424 if (strcmp(new_event.GameOver.MovieName, "x") == 0 || strcmp(new_event.GameOver.MovieName, "X") == 0) { 2425 new_event.GameOver.MovieName = ""; 2426 } 2427 2428 new_event.GameOver.MovieName2 = WinMovie2; 2429 if (strcmp(new_event.GameOver.MovieName2, "x") == 0 || strcmp(new_event.GameOver.MovieName2, "X") == 0) { 2430 new_event.GameOver.MovieName2 = ""; 2431 } 2432 2433 new_event.GameOver.MovieName3 = WinMovie3; 2434 if (strcmp(new_event.GameOver.MovieName3, "x") == 0 || strcmp(new_event.GameOver.MovieName3, "X") == 0) { 2435 new_event.GameOver.MovieName3 = ""; 2436 } 2437 2438 new_event.GameOver.MovieName4 = WinMovie4; 2439 if (strcmp(new_event.GameOver.MovieName4, "x") == 0 || strcmp(new_event.GameOver.MovieName4, "X") == 0) { 2440 new_event.GameOver.MovieName4 = ""; 2441 } 2442 2443 new_event.GameOver.SabotagedStructureType = SabotagedType; 2444 new_event.GameOver.TimerRemaining = -1; //Used in RA 2445 2446 EventCallback(new_event); 2447 } 2448 2449 2450 /************************************************************************************************** 2451 * DLLExportClass::On_Multiplayer_Game_Over -- Called when the C&C multiplayer game finishes 2452 * 2453 * 2454 * History: 6/19/2019 - LLL 2455 * History: 10/25/2019 - MBL - Adding the multi-player score stats support for debrief 2456 **************************************************************************************************/ 2457 void DLLExportClass::On_Multiplayer_Game_Over(void) 2458 { 2459 if (EventCallback == NULL) { 2460 return; 2461 } 2462 2463 GameOver = true; 2464 2465 EventCallbackStruct event; 2466 2467 event.EventType = CALLBACK_EVENT_GAME_OVER; 2468 2469 // Multiplayer players data for debrief stats 2470 2471 event.GameOver.Multiplayer = true; 2472 event.GameOver.MultiPlayerTotalPlayers = MPlayerCount; 2473 2474 for ( int player_index = 0; player_index < MPlayerCount; player_index ++ ) 2475 { 2476 HouseClass* player_ptr = HouseClass::As_Pointer( MPlayerHouses[player_index] ); //HouseClass::As_Pointer(HOUSE_MULTI2); 2477 if ( player_ptr != NULL ) 2478 { 2479 int house = player_ptr->Class->House; 2480 unsigned int leadership = TD_Calculate_Leadership( house, player_ptr->UnitsLost, player_ptr->BuildingsLost ); 2481 2482 unsigned int efficiency = TD_Calculate_Efficiency( player_ptr->HarvestedCredits, player_ptr->InitialCredits, player_ptr->Available_Money() ); 2483 2484 unsigned int total_score = TD_Calculate_Score( leadership, efficiency, BuildLevel ); 2485 2486 int units_killed = 0; 2487 int structures_killed = 0; 2488 for ( unsigned int house_index = 0; house_index < HOUSE_COUNT; house_index ++ ) 2489 { 2490 units_killed += player_ptr->UnitsKilled[ house_index ]; 2491 structures_killed += player_ptr->BuildingsKilled[ house_index ]; 2492 } 2493 2494 // Populate and copy the multiplayer player data structure 2495 2496 GameOverMultiPlayerStatsStruct multi_player_data; 2497 2498 multi_player_data.GlyphXPlayerID = Get_GlyphX_Player_ID( player_ptr ); 2499 multi_player_data.IsHuman = (player_ptr->IsHuman || player_ptr->WasHuman); 2500 multi_player_data.WasHuman = player_ptr->WasHuman; 2501 multi_player_data.IsWinner = !player_ptr->IsDefeated; 2502 multi_player_data.Efficiency = efficiency; 2503 multi_player_data.Score = total_score; 2504 multi_player_data.ResourcesGathered = player_ptr->HarvestedCredits; 2505 multi_player_data.TotalUnitsKilled = units_killed; 2506 multi_player_data.TotalStructuresKilled = structures_killed; 2507 2508 if ( player_index < GAME_OVER_MULTIPLAYER_MAX_PLAYERS_TRACKED ) 2509 { 2510 event.GameOver.MultiPlayerPlayersData[ player_index ] = multi_player_data; 2511 } 2512 } 2513 } 2514 for ( int player_index = MPlayerCount; player_index < GAME_OVER_MULTIPLAYER_MAX_PLAYERS_TRACKED; player_index ++ ) 2515 { 2516 memset( &event.GameOver.MultiPlayerPlayersData[ player_index ], 0, sizeof( GameOverMultiPlayerStatsStruct ) ); 2517 } 2518 2519 // Single-player N/A stuff 2520 2521 event.GameOver.MovieName = ""; 2522 event.GameOver.MovieName2 = ""; 2523 event.GameOver.MovieName3 = ""; 2524 event.GameOver.MovieName4 = ""; 2525 event.GameOver.AfterScoreMovieName = ""; 2526 event.GameOver.Leadership = 0; 2527 event.GameOver.Efficiency = 0; 2528 event.GameOver.Score = 0; 2529 event.GameOver.NODKilled = 0; 2530 event.GameOver.GDIKilled = 0; 2531 event.GameOver.CiviliansKilled = 0; 2532 event.GameOver.NODBuildingsDestroyed = 0; 2533 event.GameOver.GDIBuildingsDestroyed = 0; 2534 event.GameOver.CiviliansBuildingsDestroyed = 0; 2535 event.GameOver.RemainingCredits = 0; 2536 event.GameOver.SabotagedStructureType = 0; 2537 event.GameOver.TimerRemaining = -1; 2538 2539 // Trigger an event for each human player, winner first (even if it's an AI) 2540 for (int i = 0; i < MPlayerCount; i++) { 2541 HouseClass* player_ptr = HouseClass::As_Pointer(MPlayerHouses[i]); //HouseClass::As_Pointer(HOUSE_MULTI2); 2542 if (player_ptr != NULL && !player_ptr->IsDefeated) { 2543 event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); 2544 event.GameOver.IsHuman = player_ptr->IsHuman; 2545 event.GameOver.PlayerWins = true; 2546 event.GameOver.RemainingCredits = player_ptr->Available_Money(); 2547 EventCallback(event); 2548 } 2549 } 2550 2551 for (int i = 0; i < MPlayerCount; i++) { 2552 HouseClass* player_ptr = HouseClass::As_Pointer(MPlayerHouses[i]); //HouseClass::As_Pointer(HOUSE_MULTI2); 2553 if (player_ptr != NULL && player_ptr->IsHuman && player_ptr->IsDefeated) { 2554 event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); 2555 event.GameOver.IsHuman = true; 2556 event.GameOver.PlayerWins = false; 2557 event.GameOver.RemainingCredits = player_ptr->Available_Money(); 2558 EventCallback(event); 2559 } 2560 } 2561 } 2562 2563 2564 /************************************************************************************************** 2565 * DLLExportClass::On_Message -- Called when the game wants to display a message (ex. tutorial text) 2566 * 2567 * In: 2568 * 2569 * Out: 2570 * 2571 * 2572 * 2573 * History: 10/16/2019 - SKY 2574 **************************************************************************************************/ 2575 void DLLExportClass::On_Message(const HouseClass* player_ptr, const char* message, float timeout_seconds, EventCallbackMessageEnum message_type, int64 message_id) 2576 { 2577 if (EventCallback == NULL) 2578 { 2579 return; 2580 } 2581 2582 const char* p_msg = message; 2583 if (message_id != -1) { 2584 if (message_id == TXT_LOW_POWER) { 2585 p_msg = "TEXT_LOW_POWER_MESSAGE_001"; 2586 } 2587 else if (message_id == TXT_INSUFFICIENT_FUNDS) { 2588 p_msg = "TEXT_INSUFFICIENT_FUNDS_MESSAGE"; 2589 } 2590 } 2591 2592 EventCallbackStruct new_event; 2593 new_event.EventType = CALLBACK_EVENT_MESSAGE; 2594 new_event.Message.Message = p_msg; 2595 new_event.Message.TimeoutSeconds = timeout_seconds; 2596 new_event.Message.MessageType = message_type; 2597 new_event.Message.MessageParam1 = message_id; 2598 2599 new_event.GlyphXPlayerID = 0; 2600 if (player_ptr != NULL) 2601 { 2602 new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); 2603 } 2604 2605 EventCallback(new_event); 2606 } 2607 2608 void On_Message(const char* message, float timeout_seconds, EventCallbackMessageEnum message_type, int64 message_id) 2609 { 2610 DLLExportClass::On_Message(PlayerPtr, message, timeout_seconds, message_type, message_id); 2611 } 2612 2613 void On_Message(const char* message, float timeout_seconds, int64 message_id) 2614 { 2615 DLLExportClass::On_Message(PlayerPtr, message, timeout_seconds, MESSAGE_TYPE_DIRECT, message_id); 2616 } 2617 2618 void On_Defeated_Message(const char* message, float timeout_seconds) 2619 { 2620 DLLExportClass::On_Message(PlayerPtr, message, timeout_seconds, MESSAGE_TYPE_PLAYER_DEFEATED, -1); 2621 } 2622 2623 2624 2625 /************************************************************************************************** 2626 * DLLExportClass::On_Achievement -- Called when something achievement-related happens 2627 * 2628 * In: Type of achievement, reason this happened 2629 * 2630 * Out: 2631 * 2632 * 2633 * 2634 * History: 11/11/2019 11:37AM - ST 2635 **************************************************************************************************/ 2636 void DLLExportClass::On_Achievement(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason) 2637 { 2638 if (EventCallback == NULL) { 2639 return; 2640 } 2641 2642 EventCallbackStruct new_event; 2643 new_event.EventType = CALLBACK_EVENT_ACHIEVEMENT; 2644 new_event.Achievement.AchievementType = achievement_type; 2645 new_event.Achievement.AchievementReason = achievement_reason; 2646 2647 new_event.GlyphXPlayerID = 0; 2648 if (player_ptr != NULL) { 2649 new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); 2650 } 2651 2652 EventCallback(new_event); 2653 } 2654 2655 2656 void DLLExportClass::On_Center_Camera(const HouseClass* player_ptr, int coord_x, int coord_y) 2657 { 2658 if (EventCallback == NULL) { 2659 return; 2660 } 2661 2662 EventCallbackStruct new_event; 2663 new_event.EventType = CALLBACK_EVENT_CENTER_CAMERA; 2664 new_event.CenterCamera.CoordX = coord_x; 2665 new_event.CenterCamera.CoordY = coord_y; 2666 2667 new_event.GlyphXPlayerID = 0; 2668 if (player_ptr != NULL) { 2669 new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); 2670 } 2671 2672 EventCallback(new_event); 2673 } 2674 2675 2676 void DLLExportClass::On_Ping(const HouseClass* player_ptr, COORDINATE coord) 2677 { 2678 if (EventCallback == NULL) { 2679 return; 2680 } 2681 2682 EventCallbackStruct new_event; 2683 new_event.EventType = CALLBACK_EVENT_PING; 2684 new_event.Ping.CoordX = Coord_X(coord); 2685 new_event.Ping.CoordY = Coord_Y(coord); 2686 2687 new_event.GlyphXPlayerID = 0; 2688 if (player_ptr != NULL) { 2689 new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); 2690 } 2691 2692 EventCallback(new_event); 2693 } 2694 2695 2696 /************************************************************************************************** 2697 * DLLExportClass::On_Debug_Output -- Called when C&C wants to print debug output 2698 * 2699 * In: String to print to GlyphX log system 2700 * 2701 * Out: 2702 * 2703 * 2704 * 2705 * History: 2/20/2019 2:39PM - ST 2706 **************************************************************************************************/ 2707 void DLLExportClass::On_Debug_Output(const char *debug_text) 2708 { 2709 if (EventCallback == NULL) { 2710 return; 2711 } 2712 2713 EventCallbackStruct new_event; 2714 new_event.EventType = CALLBACK_EVENT_DEBUG_PRINT; 2715 new_event.DebugPrint.PrintString = debug_text; 2716 EventCallback(new_event); 2717 } 2718 2719 2720 /************************************************************************************************** 2721 * DLLExportClass::Force_Human_Team_Wins 2722 * 2723 * History: 3/10/2020 - LL 2724 **************************************************************************************************/ 2725 void DLLExportClass::Force_Human_Team_Wins(uint64 quitting_player_id) 2726 { 2727 if (PlayerWins || PlayerLoses || GameOver) { 2728 return; 2729 } 2730 2731 int winning_team = -1; 2732 2733 //Find the first human's multiplayer team. 2734 for (int i = 0; i < MPlayerCount; i++) 2735 { 2736 if (GlyphxPlayerIDs[i] != quitting_player_id) { 2737 HousesType house_type = MPlayerHouses[i]; 2738 HouseClass* house_class = HouseClass::As_Pointer(house_type); 2739 if (house_class && house_class->IsHuman && !house_class->IsDefeated) { 2740 winning_team = MPlayerTeamIDs[i]; 2741 break; 2742 } 2743 } 2744 } 2745 2746 //Mark all players not on that team as defeated. 2747 for (int i = 0; i < MPlayerCount; i++) 2748 { 2749 HousesType house_type = MPlayerHouses[i]; 2750 HouseClass* house_class = HouseClass::As_Pointer(house_type); 2751 if (house_class) { 2752 house_class->IsDefeated = MPlayerTeamIDs[i] != winning_team; 2753 } 2754 } 2755 2756 PlayerWins = true; 2757 } 2758 2759 2760 /************************************************************************************************** 2761 * CNC_Get_Game_State -- Get game state 2762 * 2763 * In: Type of state requested 2764 * Player perspective 2765 * Buffer to contain game state 2766 * Size of buffer 2767 * 2768 * Out: Game state returned in buffer 2769 * 2770 * 2771 * 2772 * History: 1/7/2019 5:20PM - ST 2773 **************************************************************************************************/ 2774 extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Game_State(GameStateRequestEnum state_type, uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) 2775 { 2776 bool got_state = false; 2777 2778 switch (state_type) { 2779 2780 case GAME_STATE_LAYERS: 2781 { 2782 got_state = DLLExportClass::Get_Layer_State(player_id, buffer_in, buffer_size); 2783 break; 2784 } 2785 2786 case GAME_STATE_SIDEBAR: 2787 { 2788 got_state = DLLExportClass::Get_Sidebar_State(player_id, buffer_in, buffer_size); 2789 break; 2790 } 2791 2792 case GAME_STATE_PLACEMENT: 2793 { 2794 got_state = DLLExportClass::Get_Placement_State(player_id, buffer_in, buffer_size); 2795 break; 2796 } 2797 2798 case GAME_STATE_DYNAMIC_MAP: 2799 got_state = DLLExportClass::Get_Dynamic_Map_State(player_id, buffer_in, buffer_size); 2800 break; 2801 2802 case GAME_STATE_SHROUD: 2803 got_state = DLLExportClass::Get_Shroud_State(player_id, buffer_in, buffer_size); 2804 break; 2805 2806 case GAME_STATE_OCCUPIER: 2807 got_state = DLLExportClass::Get_Occupier_State(player_id, buffer_in, buffer_size); 2808 break; 2809 2810 case GAME_STATE_PLAYER_INFO: 2811 got_state = DLLExportClass::Get_Player_Info_State(player_id, buffer_in, buffer_size); 2812 break; 2813 2814 case GAME_STATE_STATIC_MAP: 2815 { 2816 if (buffer_size < sizeof(CNCMapDataStruct)) { 2817 got_state = false; 2818 break; 2819 } 2820 2821 int map_cell_x = Map.MapCellX; 2822 int map_cell_y = Map.MapCellY; 2823 int map_cell_width = Map.MapCellWidth; 2824 int map_cell_height = Map.MapCellHeight; 2825 2826 CNCMapDataStruct *map_data = (CNCMapDataStruct *)buffer_in; 2827 2828 map_data->OriginalMapCellX = map_cell_x; 2829 map_data->OriginalMapCellY = map_cell_y; 2830 map_data->OriginalMapCellWidth = map_cell_width; 2831 map_data->OriginalMapCellHeight = map_cell_height; 2832 2833 if (map_cell_x > 0) { 2834 map_cell_x--; 2835 map_cell_width++; 2836 } 2837 2838 if (map_cell_width < MAP_MAX_CELL_WIDTH) { 2839 map_cell_width++; 2840 } 2841 2842 if (map_cell_y > 0) { 2843 map_cell_y--; 2844 map_cell_height++; 2845 } 2846 2847 if (map_cell_height < MAP_MAX_CELL_HEIGHT) { 2848 map_cell_height++; 2849 } 2850 2851 map_data->MapCellX = map_cell_x; 2852 map_data->MapCellY = map_cell_y; 2853 map_data->MapCellWidth = map_cell_width; 2854 map_data->MapCellHeight = map_cell_height; 2855 2856 map_data->Theater = (CnCTheaterType) Map.Theater; 2857 2858 // Hack a fix for scenario 21 since the same mission number is used in Covert Ops and N64 2859 memset(map_data->ScenarioName, 0, sizeof(map_data->ScenarioName)); 2860 if ((Map.Theater == CNC_THEATER_DESERT) && (Scenario == 21)) { 2861 strncpy(map_data->ScenarioName, "SCB81EA", sizeof(map_data->ScenarioName) - 1); 2862 } else { 2863 strncpy(map_data->ScenarioName, ScenarioName, sizeof(map_data->ScenarioName) - 1); 2864 } 2865 2866 int cell_index = 0; 2867 char cell_name[_MAX_PATH]; 2868 char icon_number[32]; 2869 2870 for (int y = 0 ; y < map_cell_height ; y++) { 2871 for (int x = 0 ; x < map_cell_width ; x++) { 2872 CELL cell = XY_Cell(map_cell_x+x, map_cell_y+y); 2873 CellClass * cellptr = &Map[cell]; 2874 2875 cell_name[0] = 0; 2876 int icon = 0; 2877 void *image_data = 0; 2878 if (cellptr->Get_Template_Info(cell_name, icon, image_data)) { 2879 itoa(icon, icon_number, 10); 2880 strncat(cell_name, "_i", 32); 2881 strncat(cell_name, icon_number, 32); 2882 strncat(cell_name, ".tga", 32); 2883 cell_name[31] = 0; 2884 2885 CNCStaticCellStruct &cell_info = map_data->StaticCells[cell_index++]; 2886 strncpy(cell_info.TemplateTypeName, cell_name, 32); 2887 cell_info.TemplateTypeName[31] = 0; 2888 cell_info.IconNumber = icon; 2889 } 2890 } 2891 } 2892 2893 got_state = true; 2894 break; 2895 } 2896 2897 default: 2898 { 2899 got_state = false; 2900 break; 2901 } 2902 } 2903 2904 return got_state; 2905 } 2906 2907 /************************************************************************************************** 2908 * CNC_Handle_Game_Request 2909 * 2910 * Callback for when the requested movie is done playing. 2911 * 2912 * 7/23/2019 - LLL 2913 **************************************************************************************************/ 2914 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Request(GameRequestEnum request_type) 2915 { 2916 switch (request_type) 2917 { 2918 case INPUT_GAME_MOVIE_DONE: 2919 2920 InMainLoop = true; 2921 break; 2922 } 2923 } 2924 2925 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Settings_Request(int health_bar_display_mode, int resource_bar_display_mode) 2926 { 2927 if (!DLLExportClass::Legacy_Render_Enabled()) { 2928 return; 2929 } 2930 2931 SpecialClass::eHealthBarDisplayMode new_hb_mode = (SpecialClass::eHealthBarDisplayMode)health_bar_display_mode; 2932 if (new_hb_mode != Special.HealthBarDisplayMode) { 2933 Special.HealthBarDisplayMode = new_hb_mode; 2934 Map.Flag_To_Redraw(true); 2935 } 2936 2937 SpecialClass::eResourceBarDisplayMode new_rb_mode = (SpecialClass::eResourceBarDisplayMode)resource_bar_display_mode; 2938 if (new_rb_mode != Special.ResourceBarDisplayMode) { 2939 Special.ResourceBarDisplayMode = new_rb_mode; 2940 Map.Flag_To_Redraw(true); 2941 } 2942 } 2943 2944 2945 2946 2947 2948 void DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, ObjectClass *object, const char *shape_file_name = NULL, char override_owner = HOUSE_NONE, int scale = 0x100) 2949 { 2950 DLLExportClass::DLL_Draw_Intercept(shape_number, x, y, width, height, flags, object, shape_file_name, override_owner, scale); 2951 } 2952 2953 2954 void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip) 2955 { 2956 DLLExportClass::DLL_Draw_Pip_Intercept(object, pip); 2957 } 2958 2959 2960 void DLL_Draw_Line_Intercept(int x, int y, int x1, int y1, unsigned char color, int frame) 2961 { 2962 DLLExportClass::DLL_Draw_Line_Intercept(x, y, x1, y1, color, frame); 2963 } 2964 2965 2966 void DLLExportClass::DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, ObjectClass *object, const char *shape_file_name, char override_owner, int scale) 2967 { 2968 CNCObjectStruct& new_object = ObjectList->Objects[TotalObjectCount + CurrentDrawCount]; 2969 memset(&new_object, 0, sizeof(new_object)); 2970 Convert_Type(object, new_object); 2971 if (new_object.Type == UNKNOWN) { 2972 return; 2973 } 2974 2975 CNCObjectStruct* base_object = NULL; 2976 for (int i = 0; i < CurrentDrawCount; ++i) { 2977 CNCObjectStruct& draw_object = ObjectList->Objects[TotalObjectCount + i]; 2978 if (draw_object.CNCInternalObjectPointer == object) { 2979 base_object = &draw_object; 2980 break; 2981 } 2982 } 2983 2984 new_object.CNCInternalObjectPointer = (void*)object; 2985 new_object.OccupyListLength = 0; 2986 if (CurrentDrawCount == 0) { 2987 new_object.SortOrder = (ExportLayer << 29) + (object->Sort_Y() >> 3); 2988 } else { 2989 new_object.SortOrder = ObjectList->Objects[TotalObjectCount].SortOrder + CurrentDrawCount; 2990 } 2991 2992 strncpy(new_object.TypeName, object->Class_Of().IniName, CNC_OBJECT_ASSET_NAME_LENGTH); 2993 2994 if (shape_file_name != NULL) { 2995 strncpy(new_object.AssetName, shape_file_name, CNC_OBJECT_ASSET_NAME_LENGTH); 2996 } 2997 else { 2998 strncpy(new_object.AssetName, object->Class_Of().IniName, CNC_OBJECT_ASSET_NAME_LENGTH); 2999 } 3000 3001 new_object.TypeName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; 3002 new_object.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; 3003 new_object.Owner = ((base_object != NULL) && (override_owner != HOUSE_NONE)) ? override_owner : (char)object->Owner(); 3004 3005 HouseClass* owner_house = nullptr; 3006 for (int i = 0; i < Houses.Count(); ++i) { 3007 HouseClass* hptr = Houses.Ptr(i); 3008 if ((hptr != nullptr) && (hptr->Class->House == new_object.Owner)) { 3009 owner_house = hptr; 3010 break; 3011 } 3012 } 3013 3014 new_object.RemapColor = (owner_house != nullptr) ? owner_house->RemapColor : -1; 3015 3016 if (base_object == NULL) { 3017 CNCObjectStruct& root_object = ObjectList->Objects[TotalObjectCount]; 3018 3019 if (new_object.Type == BUILDING) { 3020 BuildingClass *building = (BuildingClass*)object; 3021 if (building->BState == BSTATE_CONSTRUCTION) { 3022 strncat(new_object.AssetName, "MAKE", CNC_OBJECT_ASSET_NAME_LENGTH); 3023 new_object.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; 3024 } 3025 const BuildingTypeClass *building_type = building->Class; 3026 short const *occupy_list = building_type->Occupy_List(); 3027 if (occupy_list) { 3028 while (*occupy_list != REFRESH_EOL && new_object.OccupyListLength < MAX_OCCUPY_CELLS) { 3029 new_object.OccupyList[new_object.OccupyListLength] = *occupy_list; 3030 new_object.OccupyListLength++; 3031 occupy_list++; 3032 } 3033 } 3034 } 3035 3036 COORDINATE coord = object->Render_Coord(); 3037 CELL cell = Coord_Cell(coord); 3038 int dimx, dimy; 3039 object->Class_Of().Dimensions(dimx, dimy); 3040 3041 short sim_lepton_x = 0; 3042 short sim_lepton_y = 0; 3043 3044 if (new_object.Type == UNIT) { 3045 sim_lepton_x = ((DriveClass*)object)->SimLeptonX; 3046 sim_lepton_y = ((DriveClass*)object)->SimLeptonY; 3047 } 3048 3049 new_object.PositionX = x; 3050 new_object.PositionY = y; 3051 new_object.Width = width; 3052 new_object.Height = height; 3053 new_object.Altitude = 0; 3054 new_object.DrawFlags = flags; 3055 new_object.SubObject = 0; 3056 new_object.ShapeIndex = (unsigned short)shape_number; 3057 new_object.IsTheaterSpecific = IsTheaterShape; 3058 new_object.Scale = scale; 3059 new_object.Rotation = 0; 3060 new_object.FlashingFlags = 0; 3061 new_object.Cloak = (CurrentDrawCount > 0) ? root_object.Cloak : UNCLOAKED; 3062 new_object.VisibleFlags = CNCObjectStruct::VISIBLE_FLAGS_ALL; 3063 new_object.SpiedByFlags = 0U; 3064 3065 new_object.SortOrder = SortOrder++; 3066 new_object.IsSelectable = object->Class_Of().IsSelectable; 3067 new_object.IsSelectedMask = object->IsSelectedMask; 3068 new_object.MaxStrength = object->Class_Of().MaxStrength; 3069 new_object.Strength = object->Strength; 3070 new_object.CellX = (CurrentDrawCount > 0) ? root_object.CellX : Cell_X(cell); 3071 new_object.CellY = (CurrentDrawCount > 0) ? root_object.CellY : Cell_Y(cell); 3072 new_object.CenterCoordX = Coord_X(object->Center_Coord()); 3073 new_object.CenterCoordY = Coord_Y(object->Center_Coord()); 3074 new_object.DimensionX = dimx; 3075 new_object.DimensionY = dimy; 3076 new_object.SimLeptonX = (CurrentDrawCount > 0) ? root_object.SimLeptonX : sim_lepton_x; 3077 new_object.SimLeptonY = (CurrentDrawCount > 0) ? root_object.SimLeptonY : sim_lepton_y; 3078 new_object.BaseObjectID = ((CurrentDrawCount > 0) && (root_object.Type != BUILDING)) ? root_object.ID : 0; 3079 new_object.BaseObjectType = ((CurrentDrawCount > 0) && (root_object.Type != BUILDING)) ? root_object.Type : UNKNOWN; 3080 new_object.NumLines = 0; 3081 new_object.RecentlyCreated = object->IsRecentlyCreated; 3082 new_object.NumPips = 0; 3083 new_object.MaxPips = 0; 3084 new_object.CanDemolish = object->Can_Demolish(); 3085 new_object.CanDemolishUnit = object->Can_Demolish_Unit(); 3086 new_object.CanRepair = object->Can_Repair(); 3087 memset(new_object.CanMove, false, sizeof(new_object.CanMove)); 3088 memset(new_object.CanFire, false, sizeof(new_object.CanFire)); 3089 memset(new_object.ActionWithSelected, DAT_NONE, sizeof(new_object.ActionWithSelected)); 3090 3091 HouseClass* old_player_ptr = PlayerPtr; 3092 for (int i = 0; i < Houses.Count(); ++i) { 3093 HouseClass* hptr = Houses.Ptr(i); 3094 if ((hptr != nullptr) && hptr->IsActive && hptr->IsHuman) { 3095 HousesType house = hptr->Class->House; 3096 DynamicVectorClass<ObjectClass*>& selected_objects = CurrentObject.Raw(house); 3097 if (selected_objects.Count() > 0) { 3098 Logic_Switch_Player_Context(hptr); 3099 Convert_Action_Type(Best_Object_Action(selected_objects, object), (selected_objects.Count() == 1) ? selected_objects[0] : NULL, object->As_Target(), new_object.ActionWithSelected[house]); 3100 } 3101 } 3102 } 3103 Logic_Switch_Player_Context(old_player_ptr); 3104 3105 RTTIType what_is_object = object->What_Am_I(); 3106 3107 new_object.IsRepairing = false; 3108 new_object.IsDumping = false; 3109 new_object.IsALoaner = false; 3110 new_object.IsFactory = false; 3111 new_object.IsPrimaryFactory = false; 3112 new_object.IsAntiGround = false; 3113 new_object.IsAntiAircraft = false; 3114 new_object.IsSubSurface = false; 3115 new_object.IsNominal = false; 3116 new_object.IsDog = false; 3117 new_object.IsIronCurtain = false; 3118 new_object.IsInFormation = false; 3119 new_object.IsFake = false; 3120 new_object.ProductionAssetName[0] = '\0'; 3121 new_object.OverrideDisplayName = "\0"; 3122 3123 bool is_building = what_is_object == RTTI_BUILDING; 3124 if (is_building) { 3125 BuildingClass* building = static_cast<BuildingClass*>(object); 3126 new_object.IsRepairing = building->IsRepairing; 3127 new_object.IsFactory = building->Class->IsFactory; 3128 new_object.IsPrimaryFactory = building->IsLeader; 3129 } 3130 3131 if (object->Is_Techno()) { 3132 TechnoClass* techno_object = static_cast<TechnoClass*>(object); 3133 const TechnoTypeClass *ttype = techno_object->Techno_Type_Class(); 3134 3135 new_object.MaxSpeed = (unsigned char)ttype->MaxSpeed; 3136 new_object.IsALoaner = techno_object->IsALoaner; 3137 new_object.IsNominal = ttype->IsNominal; 3138 new_object.MaxPips = ttype->Max_Pips(); 3139 new_object.IsAntiGround = ttype->Primary != WEAPON_NONE; 3140 new_object.IsAntiAircraft = (ttype->Primary != WEAPON_NONE) && (Weapons[ttype->Primary].Fires != BULLET_NONE) && BulletTypeClass::As_Reference(Weapons[ttype->Primary].Fires).IsAntiAircraft; 3141 3142 HouseClass* old_player_ptr = PlayerPtr; 3143 for (int i = 0; i < Houses.Count(); ++i) { 3144 HouseClass* hptr = Houses.Ptr(i); 3145 if ((hptr != nullptr) && hptr->IsActive && hptr->IsHuman) { 3146 Logic_Switch_Player_Context(hptr); 3147 HousesType house = hptr->Class->House; 3148 new_object.CanMove[house] = techno_object->Can_Player_Move(); 3149 new_object.CanFire[house] = techno_object->Can_Player_Fire(); 3150 } 3151 } 3152 Logic_Switch_Player_Context(old_player_ptr); 3153 } 3154 3155 new_object.ControlGroup = (unsigned char)(-1); 3156 new_object.CanPlaceBombs = false; 3157 bool is_infantry = what_is_object == RTTI_INFANTRY; 3158 if (is_infantry) { 3159 InfantryClass* infantry = static_cast<InfantryClass*>(object); 3160 new_object.ControlGroup = infantry->Group; 3161 new_object.CanPlaceBombs = infantry->Class->Type == INFANTRY_RAMBO; 3162 } 3163 3164 new_object.CanHarvest = false; 3165 bool is_unit = what_is_object == RTTI_UNIT; 3166 if (is_unit) { 3167 UnitClass* unit = static_cast<UnitClass*>(object); 3168 if (unit->Class->Type == UNIT_HARVESTER) { 3169 new_object.CanHarvest = true; 3170 } 3171 3172 new_object.ControlGroup = unit->Group; 3173 } 3174 3175 new_object.IsFixedWingedAircraft = false; 3176 bool is_aircraft = what_is_object == RTTI_AIRCRAFT; 3177 if (is_aircraft) { 3178 AircraftClass* aircraft = static_cast<AircraftClass*>(object); 3179 new_object.Altitude = Pixel_To_Lepton(aircraft->Altitude); 3180 new_object.IsFixedWingedAircraft = aircraft->Class->IsFixedWing; 3181 new_object.ControlGroup = aircraft->Group; 3182 } 3183 3184 switch (what_is_object) 3185 { 3186 case RTTI_INFANTRY: 3187 case RTTI_INFANTRYTYPE: 3188 case RTTI_UNIT: 3189 case RTTI_UNITTYPE: 3190 case RTTI_AIRCRAFT: 3191 case RTTI_AIRCRAFTTYPE: 3192 case RTTI_BUILDING: 3193 case RTTI_BUILDINGTYPE: 3194 { 3195 TechnoClass* techno_object = static_cast<TechnoClass*>(object); 3196 new_object.FlashingFlags = techno_object->Get_Flashing_Flags(); 3197 new_object.Cloak = techno_object->Cloak; 3198 } 3199 break; 3200 3201 case RTTI_ANIM: 3202 { 3203 AnimClass* anim_object = static_cast<AnimClass*>(object); 3204 new_object.VisibleFlags = anim_object->Get_Visible_Flags(); 3205 } 3206 break; 3207 } 3208 } 3209 else { 3210 new_object.MaxStrength = 0; 3211 new_object.MaxSpeed = 0; 3212 new_object.Strength = 0; 3213 new_object.CellX = base_object->CellX; 3214 new_object.CellY = base_object->CellY; 3215 new_object.CenterCoordX = base_object->CenterCoordX; 3216 new_object.CenterCoordY = base_object->CenterCoordY; 3217 new_object.DimensionX = base_object->DimensionX; 3218 new_object.DimensionY = base_object->DimensionY; 3219 new_object.IsSelectable = false; 3220 new_object.IsSelectedMask = 0U; 3221 new_object.SimLeptonX = base_object->SimLeptonX; 3222 new_object.SimLeptonY = base_object->SimLeptonY; 3223 3224 new_object.PositionX = x; 3225 new_object.PositionY = y; 3226 new_object.Width = width; 3227 new_object.Height = height; 3228 new_object.Altitude = base_object->Altitude; 3229 new_object.DrawFlags = flags; 3230 new_object.ShapeIndex = (unsigned short)shape_number; 3231 new_object.IsTheaterSpecific = IsTheaterShape; 3232 new_object.Scale = scale; 3233 new_object.Rotation = 0; 3234 new_object.SubObject = CurrentDrawCount; 3235 new_object.BaseObjectID = base_object->ID; 3236 new_object.BaseObjectType = base_object->Type; 3237 new_object.FlashingFlags = base_object->FlashingFlags; 3238 new_object.Cloak = base_object->Cloak; 3239 new_object.OccupyListLength = 0; 3240 new_object.NumPips = 0; 3241 new_object.MaxPips = 0; 3242 new_object.IsRepairing = false; 3243 new_object.IsDumping = false; 3244 new_object.IsALoaner = base_object->IsALoaner; 3245 new_object.NumLines = 0; 3246 new_object.CanDemolish = base_object->CanDemolish; 3247 new_object.CanDemolishUnit = base_object->CanDemolishUnit; 3248 new_object.CanRepair = base_object->CanRepair; 3249 new_object.RecentlyCreated = base_object->RecentlyCreated; 3250 new_object.IsFactory = base_object->IsFactory; 3251 new_object.IsPrimaryFactory = base_object->IsPrimaryFactory; 3252 new_object.IsAntiGround = base_object->IsAntiGround; 3253 new_object.IsAntiAircraft = base_object->IsAntiAircraft; 3254 new_object.IsSubSurface = base_object->IsSubSurface; 3255 new_object.IsNominal = base_object->IsNominal; 3256 new_object.IsDog = base_object->IsDog; 3257 new_object.IsIronCurtain = base_object->IsIronCurtain; 3258 new_object.IsInFormation = false; 3259 new_object.CanHarvest = base_object->CanHarvest; 3260 new_object.CanPlaceBombs = base_object->CanPlaceBombs; 3261 new_object.ControlGroup = base_object->ControlGroup; 3262 new_object.VisibleFlags = base_object->VisibleFlags; 3263 new_object.SpiedByFlags = base_object->SpiedByFlags; 3264 new_object.IsFixedWingedAircraft = base_object->IsFixedWingedAircraft; 3265 new_object.IsFake = base_object->IsFake; 3266 new_object.ProductionAssetName[0] = '\0'; 3267 new_object.OverrideDisplayName = "\0"; 3268 memset(new_object.CanMove, false, sizeof(new_object.CanMove)); 3269 memset(new_object.CanFire, false, sizeof(new_object.CanFire)); 3270 memset(new_object.ActionWithSelected, DAT_NONE, sizeof(new_object.ActionWithSelected)); 3271 } 3272 3273 CurrentDrawCount++; 3274 } 3275 3276 3277 3278 void DLLExportClass::DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip) 3279 { 3280 CNCObjectStruct* base_object = NULL; 3281 for (int i = 0; i < CurrentDrawCount; ++i) { 3282 CNCObjectStruct& draw_object = ObjectList->Objects[TotalObjectCount + i]; 3283 if (draw_object.CNCInternalObjectPointer == object) { 3284 base_object = &draw_object; 3285 break; 3286 } 3287 } 3288 3289 if ((base_object != NULL) && (base_object->NumPips < MAX_OBJECT_PIPS)) { 3290 base_object->Pips[base_object->NumPips] = pip; 3291 base_object->NumPips++; 3292 base_object->MaxPips = max(base_object->MaxPips, base_object->NumPips); 3293 } 3294 } 3295 3296 3297 3298 void DLLExportClass::DLL_Draw_Line_Intercept(int x, int y, int x1, int y1, unsigned char color, int frame) 3299 { 3300 CNCObjectStruct& root_object = ObjectList->Objects[TotalObjectCount]; 3301 if (root_object.NumLines < MAX_OBJECT_LINES) { 3302 root_object.Lines[root_object.NumLines].X = x; 3303 root_object.Lines[root_object.NumLines].Y = y; 3304 root_object.Lines[root_object.NumLines].X1 = x1; 3305 root_object.Lines[root_object.NumLines].Y1 = y1; 3306 root_object.Lines[root_object.NumLines].Frame = frame; 3307 root_object.Lines[root_object.NumLines].Color = color; 3308 3309 SortOrder++; 3310 root_object.NumLines++; 3311 } 3312 } 3313 3314 3315 3316 /************************************************************************************************** 3317 * DLLExportClass::Get_Layer_State -- Get game objects from the layers 3318 * 3319 * In: 3320 * 3321 * Out: 3322 * 3323 * 3324 * 3325 * History: 1/29/2019 11:37AM - ST 3326 **************************************************************************************************/ 3327 bool DLLExportClass::Get_Layer_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) 3328 { 3329 player_id; 3330 3331 static int _export_count = 0; 3332 3333 bool got_state = false; 3334 3335 ObjectList = (CNCObjectListStruct*) buffer_in; 3336 3337 TotalObjectCount = 0; 3338 3339 /* 3340 ** Get a reference draw coordinate for cells 3341 */ 3342 int map_cell_x = Map.MapCellX; 3343 int map_cell_y = Map.MapCellY; 3344 if (map_cell_x > 0) { 3345 map_cell_x--; 3346 } 3347 if (map_cell_y > 0) { 3348 map_cell_y--; 3349 } 3350 3351 SortOrder = 0; 3352 3353 /* 3354 ** Get the ground layer first and then followed by all the layers in increasing altitude. 3355 */ 3356 for (int layer = 0; layer < DLL_LAYER_COUNT; layer++) { 3357 3358 ExportLayer = layer; 3359 3360 for (int index = 0; index < Map.Layer[layer].Count(); index++) { 3361 3362 ObjectClass *object = Map.Layer[layer][index]; 3363 if (object->IsActive) { 3364 3365 unsigned int memory_needed = sizeof(CNCObjectListStruct); 3366 memory_needed += (TotalObjectCount + 10) * sizeof(CNCObjectStruct); 3367 if (memory_needed >= buffer_size) { 3368 return false; 3369 } 3370 3371 if (object->Is_Techno()) { 3372 /* 3373 ** Skip units tethered to buildings, since the building will draw them itself 3374 */ 3375 TechnoClass* techno_object = static_cast<TechnoClass*>(object); 3376 TechnoClass* contact_object = techno_object->In_Radio_Contact() ? techno_object->Contact_With_Whom() : nullptr; 3377 if ((contact_object != nullptr) && (contact_object->What_Am_I() == RTTI_BUILDING) && contact_object->IsTethered && *((BuildingClass*)contact_object) == STRUCT_WEAP) { 3378 continue; 3379 } 3380 } 3381 3382 if (Debug_Map || Debug_Unshroud || (object->IsDown && !object->IsInLimbo)) { 3383 int x, y; 3384 Map.Coord_To_Pixel(object->Render_Coord(), x, y); 3385 3386 /* 3387 ** Call to Draw_It can result in multiple callbacks to the draw intercept 3388 */ 3389 CurrentDrawCount = 0; 3390 object->Draw_It(x, y, WINDOW_VIRTUAL); 3391 3392 /* 3393 ** Shadows need to be rendered before the base object so they appear underneath, 3394 ** even though they get drawn as sub-objects (after the base object) 3395 */ 3396 for (int i = 1; i < CurrentDrawCount; ++i) { 3397 CNCObjectStruct& sub_object = ObjectList->Objects[TotalObjectCount + i]; 3398 if (!sub_object.SubObject) { 3399 continue; 3400 } 3401 static const int shadow_flags = SHAPE_PREDATOR | SHAPE_FADING; 3402 if (((sub_object.DrawFlags & shadow_flags) == shadow_flags) || (strncmp(sub_object.AssetName, "WAKE", CNC_OBJECT_ASSET_NAME_LENGTH) == 0)) { 3403 if ((strncmp(sub_object.AssetName, "RROTOR", CNC_OBJECT_ASSET_NAME_LENGTH) != 0) && 3404 (strncmp(sub_object.AssetName, "LROTOR", CNC_OBJECT_ASSET_NAME_LENGTH) != 0)) { 3405 for (int j = i - 1; j >= 0; --j) { 3406 CNCObjectStruct& base_object = ObjectList->Objects[TotalObjectCount + j]; 3407 if (!base_object.SubObject && (base_object.CNCInternalObjectPointer == sub_object.CNCInternalObjectPointer)) { 3408 int sort_order = base_object.SortOrder; 3409 base_object.SortOrder = sub_object.SortOrder; 3410 sub_object.SortOrder = sort_order; 3411 break; 3412 } 3413 } 3414 } 3415 } 3416 } 3417 3418 TotalObjectCount += CurrentDrawCount; 3419 } 3420 } 3421 } 3422 } 3423 3424 ObjectList->Count = TotalObjectCount; 3425 3426 if (ObjectList->Count) { 3427 _export_count++; 3428 return true; 3429 } 3430 3431 return false; 3432 } 3433 3434 3435 3436 3437 void DLLExportClass::Convert_Type(const ObjectClass *object, CNCObjectStruct &object_out) 3438 { 3439 object_out.Type = UNKNOWN; 3440 object_out.ID = -1; 3441 3442 if (object == NULL) { 3443 return; 3444 } 3445 3446 RTTIType type = object->What_Am_I(); 3447 3448 switch (type) { 3449 default: 3450 break; 3451 3452 case RTTI_INFANTRY: 3453 object_out.Type = INFANTRY; 3454 object_out.ID = Infantry.ID((InfantryClass*)object); 3455 break; 3456 3457 case RTTI_UNIT: 3458 object_out.Type = UNIT; 3459 object_out.ID = Units.ID((UnitClass*)object); 3460 break; 3461 3462 case RTTI_AIRCRAFT: 3463 object_out.Type = AIRCRAFT; 3464 object_out.ID = Aircraft.ID((AircraftClass*)object); 3465 break; 3466 3467 case RTTI_BUILDING: 3468 object_out.Type = BUILDING; 3469 object_out.ID = Buildings.ID((BuildingClass*)object); 3470 break; 3471 3472 case RTTI_BULLET: 3473 object_out.Type = BULLET; 3474 object_out.ID = Bullets.ID((BulletClass*)object); 3475 break; 3476 3477 case RTTI_ANIM: 3478 object_out.Type = ANIM; 3479 object_out.ID = Anims.ID((AnimClass*)object); 3480 break; 3481 3482 case RTTI_SMUDGE: 3483 object_out.Type = SMUDGE; 3484 object_out.ID = Smudges.ID((SmudgeClass*)object); 3485 break; 3486 3487 case RTTI_TERRAIN: 3488 object_out.Type = TERRAIN; 3489 object_out.ID = Terrains.ID((TerrainClass*)object); 3490 break; 3491 } 3492 } 3493 3494 3495 3496 3497 3498 3499 /************************************************************************************************** 3500 * CNC_Handle_Input -- Process input to the game 3501 * 3502 * In: 3503 * 3504 * 3505 * 3506 * 3507 * Out: Game state returned in buffer 3508 * 3509 * 3510 * 3511 * History: 1/7/2019 5:20PM - ST 3512 **************************************************************************************************/ 3513 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Input(InputRequestEnum input_event, unsigned char special_key_flags, uint64 player_id, int x1, int y1, int x2, int y2) 3514 { 3515 3516 if (!DLLExportClass::Set_Player_Context(player_id)) { 3517 return; 3518 } 3519 3520 switch (input_event) { 3521 3522 /* 3523 ** Special keys have changed 3524 */ 3525 case INPUT_REQUEST_SPECIAL_KEYS: 3526 { 3527 DLLExportClass::Set_Special_Key_Flags(special_key_flags); 3528 break; 3529 } 3530 3531 /* 3532 ** The mouse is moving 3533 */ 3534 case INPUT_REQUEST_MOUSE_MOVE: 3535 { 3536 if (!DLLExportClass::Legacy_Render_Enabled()) { 3537 break; 3538 } 3539 3540 DLLForceMouseX = x1; 3541 DLLForceMouseY = y1; 3542 _Kbd->MouseQX = x1; 3543 _Kbd->MouseQY = y1; 3544 3545 COORDINATE coord = Map.Pixel_To_Coord(x1, y1); 3546 CELL cell = Coord_Cell(coord); 3547 if (coord) { 3548 //x -= Map.TacPixelX; 3549 //y -= Map.TacPixelY; 3550 3551 /* 3552 ** Cause any displayed cursor to move along with the mouse cursor. 3553 */ 3554 if (cell != Map.ZoneCell) { 3555 Map.Set_Cursor_Pos(cell); 3556 } 3557 } 3558 break; 3559 } 3560 3561 /* 3562 ** Player left-clicked 3563 */ 3564 case INPUT_REQUEST_MOUSE_LEFT_CLICK: 3565 { 3566 DLLExportClass::Adjust_Internal_View(); 3567 3568 DLLForceMouseX = x1; 3569 DLLForceMouseY = y1; 3570 _Kbd->MouseQX = x1; 3571 _Kbd->MouseQY = y1; 3572 3573 KeyNumType key = (KeyNumType)(KN_LMOUSE | KN_RLSE_BIT); 3574 3575 if (Map.Pixel_To_Coord(x1, y1)) { 3576 //DisplayClass::TacButton.Clicked_On(key, GadgetClass::LEFTRELEASE, x1, y1); 3577 DisplayClass::TacButton.Clicked_On(key, GadgetClass::LEFTRELEASE, 100, 100); 3578 } 3579 break; 3580 } 3581 3582 /* 3583 ** Player right-clicked (on up) 3584 */ 3585 case INPUT_REQUEST_MOUSE_RIGHT_CLICK: 3586 { 3587 DLLExportClass::Adjust_Internal_View(); 3588 3589 DLLForceMouseX = x1; 3590 DLLForceMouseY = y1; 3591 _Kbd->MouseQX = x1; 3592 _Kbd->MouseQY = y1; 3593 3594 KeyNumType key = (KeyNumType)(KN_RMOUSE | KN_RLSE_BIT); 3595 3596 if (Map.Pixel_To_Coord(x1, y1)) { 3597 //DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTRELEASE, x1, y1); 3598 DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTRELEASE, 100, 100); 3599 } 3600 break; 3601 } 3602 3603 /* 3604 ** Player right button down 3605 */ 3606 case INPUT_REQUEST_MOUSE_RIGHT_DOWN: 3607 { 3608 DLLExportClass::Adjust_Internal_View(); 3609 3610 DLLForceMouseX = x1; 3611 DLLForceMouseY = y1; 3612 _Kbd->MouseQX = x1; 3613 _Kbd->MouseQY = y1; 3614 3615 KeyNumType key = (KeyNumType)(KN_RMOUSE); 3616 3617 if (Map.Pixel_To_Coord(x1, y1)) { 3618 //DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTPRESS, x1, y1); 3619 DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTPRESS, 100, 100); 3620 } 3621 break; 3622 } 3623 3624 3625 /* 3626 ** Player drag selected 3627 */ 3628 case INPUT_REQUEST_MOUSE_AREA: 3629 { 3630 DLLExportClass::Adjust_Internal_View(); 3631 Map.Select_These(XYPixel_Coord(x1, y1), XYPixel_Coord(x2, y2), false); 3632 break; 3633 } 3634 3635 case INPUT_REQUEST_MOUSE_AREA_ADDITIVE: 3636 { 3637 DLLExportClass::Adjust_Internal_View(); 3638 Map.Select_These(XYPixel_Coord(x1, y1), XYPixel_Coord(x2, y2), true); 3639 break; 3640 } 3641 3642 case INPUT_REQUEST_SELL_AT_POSITION: 3643 { 3644 DLLExportClass::Adjust_Internal_View(); 3645 DLLForceMouseX = x1; 3646 DLLForceMouseY = y1; 3647 _Kbd->MouseQX = x1; 3648 _Kbd->MouseQY = y1; 3649 3650 COORDINATE coord = Map.Pixel_To_Coord(x1, y1); 3651 CELL cell = Coord_Cell(coord); 3652 3653 if (Map.Pixel_To_Coord(x1, y1)) 3654 { 3655 PlayerPtr->Sell_Wall(cell); 3656 } 3657 3658 break; 3659 } 3660 3661 case INPUT_REQUEST_SELECT_AT_POSITION: 3662 { 3663 DLLExportClass::Adjust_Internal_View(); 3664 DLLForceMouseX = x1; 3665 DLLForceMouseY = y1; 3666 _Kbd->MouseQX = x1; 3667 _Kbd->MouseQY = y1; 3668 3669 COORDINATE coord = Map.Pixel_To_Coord(x1, y1); 3670 CELL cell = Coord_Cell(coord); 3671 3672 if (Map.Pixel_To_Coord(x1, y1)) 3673 { 3674 KeyNumType key = (KeyNumType)(KN_LMOUSE | KN_RLSE_BIT); 3675 3676 DisplayClass::TacButton.Selection_At_Mouse(GadgetClass::LEFTRELEASE, key); 3677 } 3678 3679 break; 3680 } 3681 3682 case INPUT_REQUEST_COMMAND_AT_POSITION: 3683 { 3684 DLLExportClass::Adjust_Internal_View(); 3685 DLLForceMouseX = x1; 3686 DLLForceMouseY = y1; 3687 _Kbd->MouseQX = x1; 3688 _Kbd->MouseQY = y1; 3689 3690 COORDINATE coord = Map.Pixel_To_Coord(x1, y1); 3691 CELL cell = Coord_Cell(coord); 3692 3693 if (Map.Pixel_To_Coord(x1, y1)) 3694 { 3695 KeyNumType key = (KeyNumType)(KN_LMOUSE | KN_RLSE_BIT); 3696 DisplayClass::TacButton.Command_Object(GadgetClass::LEFTRELEASE, key); 3697 } 3698 3699 break; 3700 } 3701 3702 // MBL 09.08.2020 - Mod Support 3703 case INPUT_REQUEST_MOD_GAME_COMMAND_1_AT_POSITION: 3704 case INPUT_REQUEST_MOD_GAME_COMMAND_2_AT_POSITION: 3705 case INPUT_REQUEST_MOD_GAME_COMMAND_3_AT_POSITION: 3706 case INPUT_REQUEST_MOD_GAME_COMMAND_4_AT_POSITION: 3707 { 3708 DLLExportClass::Adjust_Internal_View(); 3709 DLLForceMouseX = x1; 3710 DLLForceMouseY = y1; 3711 _Kbd->MouseQX = x1; 3712 _Kbd->MouseQY = y1; 3713 3714 COORDINATE coord = Map.Pixel_To_Coord(x1, y1); 3715 CELL cell = Coord_Cell(coord); 3716 3717 if (Map.Pixel_To_Coord(x1, y1)) 3718 { 3719 // TBD: For our ever-awesome Community Modders! 3720 // 3721 // PlayerPtr->Handle_Mod_Game_Command(cell, input_event - INPUT_REQUEST_MOD_GAME_COMMAND_1_AT_POSITION); 3722 } 3723 3724 break; 3725 } 3726 3727 default: 3728 break; 3729 } 3730 } 3731 3732 3733 3734 3735 /************************************************************************************************** 3736 * CNC_Handle_Structure_Request -- Process requests to repair and sell structures. 3737 * 3738 * In: 3739 * 3740 * 3741 * Out: 3742 * 3743 * 3744 * 3745 * History: 4/29/2019 - LLL 3746 **************************************************************************************************/ 3747 3748 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Structure_Request(StructureRequestEnum request_type, uint64 player_id, int object_id) 3749 { 3750 if (!DLLExportClass::Set_Player_Context(player_id)) { 3751 return; 3752 } 3753 3754 switch (request_type) 3755 { 3756 case INPUT_STRUCTURE_REPAIR_START: 3757 DLLExportClass::Repair_Mode(player_id); 3758 break; 3759 case INPUT_STRUCTURE_REPAIR: 3760 DLLExportClass::Repair(player_id, object_id); 3761 break; 3762 case INPUT_STRUCTURE_SELL_START: 3763 DLLExportClass::Sell_Mode(player_id); 3764 break; 3765 case INPUT_STRUCTURE_SELL: 3766 DLLExportClass::Sell(player_id, object_id); 3767 break; 3768 case INPUT_STRUCTURE_CANCEL: 3769 DLLExportClass::Repair_Sell_Cancel(player_id); 3770 break; 3771 default: 3772 break; 3773 } 3774 } 3775 3776 3777 3778 /************************************************************************************************** 3779 * CNC_Handle_Unit_Request -- Process requests on selected units. 3780 * 3781 * In: 3782 * 3783 * 3784 * Out: 3785 * 3786 * 3787 * 3788 * History: 10/15/2019 - SKY 3789 **************************************************************************************************/ 3790 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Unit_Request(UnitRequestEnum request_type, uint64 player_id) 3791 { 3792 if (!DLLExportClass::Set_Player_Context(player_id)) { 3793 return; 3794 } 3795 3796 switch (request_type) 3797 { 3798 case INPUT_UNIT_SCATTER: 3799 DLLExportClass::Scatter_Selected(player_id); 3800 break; 3801 case INPUT_UNIT_SELECT_NEXT: 3802 DLLExportClass::Select_Next_Unit(player_id); 3803 break; 3804 case INPUT_UNIT_SELECT_PREVIOUS: 3805 DLLExportClass::Select_Previous_Unit(player_id); 3806 break; 3807 case INPUT_UNIT_GUARD_MODE: 3808 DLLExportClass::Selected_Guard_Mode(player_id); 3809 break; 3810 case INPUT_UNIT_STOP: 3811 DLLExportClass::Selected_Stop(player_id); 3812 break; 3813 case INPUT_UNIT_FORMATION_TOGGLE: 3814 DLLExportClass::Team_Units_Formation_Toggle_On(player_id); 3815 break; 3816 case INPUT_UNIT_QUEUED_MOVEMENT_ON: 3817 // Red Alert Only 3818 DLLExportClass::Units_Queued_Movement_Toggle(player_id, true); 3819 break; 3820 case INPUT_UNIT_QUEUED_MOVEMENT_OFF: 3821 // Red Alert Only 3822 DLLExportClass::Units_Queued_Movement_Toggle(player_id, false); 3823 break; 3824 default: 3825 break; 3826 } 3827 } 3828 3829 3830 3831 /************************************************************************************************** 3832 * CNC_Handle_Sidebar_Request -- Process an input request to the sidebar 3833 * 3834 * In: 3835 * 3836 * 3837 * Out: 3838 * 3839 * 3840 * 3841 * History: 1/7/2019 5:20PM - ST 3842 **************************************************************************************************/ 3843 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Sidebar_Request(SidebarRequestEnum request_type, uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y) 3844 { 3845 if (!DLLExportClass::Set_Player_Context(player_id)) { 3846 return; 3847 } 3848 3849 switch (request_type) { 3850 3851 // Changing right-click support for first put building on hold, and then subsequenct right-clicks to decrement that queue count for 1x or 5x; Then, 1x or 5x Left click will resume from hold 3852 // Handle and fall through to start construction (from hold state) below 3853 case SIDEBAR_REQUEST_START_CONSTRUCTION_MULTI: 3854 3855 case SIDEBAR_REQUEST_START_CONSTRUCTION: 3856 DLLExportClass::Start_Construction(player_id, buildable_type, buildable_id); 3857 break; 3858 3859 case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: 3860 DLLExportClass::Hold_Construction(player_id, buildable_type, buildable_id); 3861 break; 3862 3863 case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: 3864 DLLExportClass::Cancel_Construction(player_id, buildable_type, buildable_id); 3865 break; 3866 3867 case SIDEBAR_REQUEST_START_PLACEMENT: 3868 DLLExportClass::Start_Placement(player_id, buildable_type, buildable_id); 3869 break; 3870 3871 case SIDEBAR_REQUEST_PLACE: 3872 DLLExportClass::Place(player_id, buildable_type, buildable_id, cell_x, cell_y); 3873 break; 3874 3875 case SIDEBAR_CANCEL_PLACE: 3876 DLLExportClass::Cancel_Placement(player_id, buildable_type, buildable_id); 3877 break; 3878 3879 default: 3880 break; 3881 } 3882 } 3883 3884 /************************************************************************************************** 3885 * CNC_Handle_SuperWeapon_Request 3886 * 3887 * In: 3888 * 3889 * 3890 * Out: 3891 * 3892 * 3893 * 3894 * History: 3895 **************************************************************************************************/ 3896 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_SuperWeapon_Request(SuperWeaponRequestEnum request_type, uint64 player_id, int buildable_type, int buildable_id, int x1, int y1) 3897 { 3898 if (!DLLExportClass::Set_Player_Context(player_id)) { 3899 return; 3900 } 3901 3902 switch (request_type) 3903 { 3904 case SUPERWEAPON_REQUEST_PLACE_SUPER_WEAPON: 3905 DLLExportClass::Place_Super_Weapon(player_id, buildable_type, buildable_id, x1, y1); 3906 break; 3907 } 3908 } 3909 3910 /************************************************************************************************** 3911 * CNC_Handle_ControlGroup_Request 3912 * 3913 * In: 3914 * 3915 * 3916 * Out: 3917 * 3918 * 3919 * 3920 * History: 3921 **************************************************************************************************/ 3922 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_ControlGroup_Request(ControlGroupRequestEnum request_type, uint64 player_id, unsigned char control_group_index) 3923 { 3924 if (!DLLExportClass::Set_Player_Context(player_id)) { 3925 return; 3926 } 3927 3928 switch (request_type) 3929 { 3930 case CONTROL_GROUP_REQUEST_CREATE: 3931 DLLExportClass::Create_Control_Group(control_group_index); 3932 break; 3933 case CONTROL_GROUP_REQUEST_TOGGLE: 3934 DLLExportClass::Toggle_Control_Group_Selection(control_group_index); 3935 break; 3936 case CONTROL_GROUP_REQUEST_ADDITIVE_SELECTION: 3937 DLLExportClass::Add_To_Control_Group(control_group_index); 3938 break; 3939 3940 } 3941 } 3942 3943 3944 3945 3946 /************************************************************************************************** 3947 * DLLExportClass::Get_Layer_State -- Get a snapshot of the sidebar state 3948 * 3949 * In: 3950 * 3951 * Out: 3952 * 3953 * 3954 * 3955 * History: 1/29/2019 11:37AM - ST 3956 **************************************************************************************************/ 3957 bool DLLExportClass::Get_Sidebar_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) 3958 { 3959 /* 3960 ** Get the player for this... 3961 */ 3962 if (!DLLExportClass::Set_Player_Context(player_id)) { 3963 return false; 3964 } 3965 3966 CNCSidebarStruct *sidebar = (CNCSidebarStruct*) buffer_in; 3967 3968 unsigned int memory_needed = sizeof(*sidebar); // Base amount needed. Will need more depending on how many entries there are 3969 3970 int entry_index = 0; 3971 3972 sidebar->Credits = 0; 3973 sidebar->CreditsCounter = 0; 3974 sidebar->Tiberium = 0; 3975 sidebar->MaxTiberium = 0; 3976 sidebar->PowerProduced = 0; 3977 sidebar->PowerDrained = 0; 3978 sidebar->RepairBtnEnabled = false; 3979 sidebar->SellBtnEnabled = false; 3980 sidebar->RadarMapActive = false; 3981 sidebar->MissionTimer = -1; 3982 3983 sidebar->UnitsKilled = 0; 3984 sidebar->BuildingsKilled = 0; 3985 sidebar->UnitsLost = 0; 3986 sidebar->BuildingsLost = 0; 3987 sidebar->TotalHarvestedCredits = 0; 3988 3989 if (PlayerPtr) { 3990 sidebar->Credits = PlayerPtr->Credits; 3991 sidebar->CreditsCounter = PlayerPtr->VisibleCredits.Current; // Timed display 3992 // sidebar->CreditsCounter = PlayerPtr->VisibleCredits.Credits; // Actual 3993 sidebar->Tiberium = PlayerPtr->Tiberium; 3994 sidebar->MaxTiberium = PlayerPtr->Capacity; 3995 sidebar->PowerProduced = PlayerPtr->Power; 3996 sidebar->PowerDrained = PlayerPtr->Drain; 3997 3998 sidebar->RepairBtnEnabled = PlayerPtr->BScan > 0; 3999 sidebar->SellBtnEnabled = PlayerPtr->BScan > 0; 4000 sidebar->RadarMapActive = PlayerPtr->Radar == RADAR_ON; 4001 4002 4003 // A. Get the DestroyedBuildings and DestroyedInfantry stats if they are available at this point 4004 if (PlayerPtr->DestroyedBuildings) { 4005 for ( int index = 0; index < PlayerPtr->DestroyedBuildings->Get_Unit_Count(); index ++ ) 4006 { 4007 unsigned int count = (unsigned int) PlayerPtr->DestroyedBuildings->Get_Unit_Total( index ); 4008 sidebar->BuildingsKilled += count; 4009 } 4010 } 4011 if (PlayerPtr->DestroyedInfantry) { 4012 for ( int index = 0; index < PlayerPtr->DestroyedInfantry->Get_Unit_Count(); index ++ ) 4013 { 4014 unsigned int count = (unsigned int) PlayerPtr->DestroyedInfantry->Get_Unit_Total( index ); 4015 sidebar->UnitsKilled += count; // Includes Infantry, Vehicles, Aircraft 4016 } 4017 } 4018 if (PlayerPtr->DestroyedUnits) { 4019 for ( int index = 0; index < PlayerPtr->DestroyedUnits->Get_Unit_Count(); index ++ ) 4020 { 4021 unsigned int count = (unsigned int) PlayerPtr->DestroyedUnits->Get_Unit_Total( index ); 4022 sidebar->UnitsKilled += count; // Includes Infantry, Vehicles, Aircraft 4023 } 4024 } 4025 if (PlayerPtr->DestroyedAircraft) { 4026 for ( int index = 0; index < PlayerPtr->DestroyedAircraft->Get_Unit_Count(); index ++ ) 4027 { 4028 unsigned int count = (unsigned int) PlayerPtr->DestroyedAircraft->Get_Unit_Total( index ); 4029 sidebar->UnitsKilled += count; // Includes Infantry, Vehicles, Aircraft 4030 } 4031 } 4032 4033 // B. If the DestroyedBuildings and DestroyedInfantry stats seemed to be unvailable, this is another way to do it 4034 // Note that we need to do both of these depending on which type of match we are running, as well as for Replays/Observer and live stats reporting 4035 // We can't just do it this way for everything, as it does not work for all cases 4036 if (sidebar->BuildingsKilled == 0) 4037 { 4038 for (unsigned int house_index = 0; house_index < HOUSE_COUNT; house_index ++) 4039 { 4040 sidebar->BuildingsKilled += PlayerPtr->BuildingsKilled[ house_index ]; 4041 } 4042 } 4043 if (sidebar->UnitsKilled == 0) 4044 { 4045 for (unsigned int house_index = 0; house_index < HOUSE_COUNT; house_index ++) 4046 { 4047 sidebar->UnitsKilled += PlayerPtr->UnitsKilled[ house_index ]; // Includes Infantry, Vehicles, Aircraft 4048 } 4049 } 4050 4051 4052 sidebar->UnitsLost = PlayerPtr->UnitsLost; 4053 sidebar->BuildingsLost = PlayerPtr->BuildingsLost; 4054 sidebar->TotalHarvestedCredits = PlayerPtr->HarvestedCredits; 4055 } 4056 4057 if (GameToPlay == GAME_NORMAL) { 4058 4059 /* 4060 ** Get each sidebar column 4061 */ 4062 for (int c = 0 ; c < 2 ; c++) { 4063 4064 sidebar->EntryCount[c] = Map.Column[c].BuildableCount; 4065 4066 /* 4067 ** Each production slot in the column 4068 */ 4069 for (int b=0 ; b < Map.Column[c].BuildableCount ; b++) { 4070 4071 CNCSidebarEntryStruct &sidebar_entry = sidebar->Entries[entry_index++]; 4072 if ((entry_index + 1) * sizeof(CNCSidebarEntryStruct) + memory_needed > buffer_size) { 4073 return false; 4074 } 4075 4076 memset(&sidebar_entry, 0, sizeof(sidebar_entry)); 4077 4078 sidebar_entry.AssetName[0] = 0; 4079 sidebar_entry.Type = UNKNOWN; 4080 sidebar_entry.BuildableID = Map.Column[c].Buildables[b].BuildableID; 4081 sidebar_entry.BuildableType = Map.Column[c].Buildables[b].BuildableType; 4082 sidebar_entry.BuildableViaCapture = Map.Column[c].Buildables[b].BuildableViaCapture; 4083 sidebar_entry.Fake = false; 4084 4085 TechnoTypeClass const * tech = Fetch_Techno_Type(Map.Column[c].Buildables[b].BuildableType, Map.Column[c].Buildables[b].BuildableID); 4086 4087 sidebar_entry.SuperWeaponType = SW_NONE; 4088 4089 if (tech) { 4090 sidebar_entry.Cost = tech->Cost * PlayerPtr->CostBias; 4091 sidebar_entry.PowerProvided = 0; 4092 sidebar_entry.BuildTime = tech->Time_To_Build(PlayerPtr->Class->House); 4093 strncpy(sidebar_entry.AssetName, tech->IniName, CNC_OBJECT_ASSET_NAME_LENGTH); 4094 sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; 4095 } else { 4096 sidebar_entry.Cost = 0; 4097 sidebar_entry.AssetName[0] = 0; 4098 } 4099 4100 SuperClass* super_weapon = nullptr; 4101 4102 bool isbusy = false; 4103 4104 switch (Map.Column[c].Buildables[b].BuildableType) { 4105 case RTTI_INFANTRYTYPE: 4106 sidebar_entry.Type = INFANTRY_TYPE; 4107 isbusy = (PlayerPtr->InfantryFactory != -1); 4108 isbusy |= Infantry.Avail() <= 0; 4109 break; 4110 4111 case RTTI_UNITTYPE: 4112 isbusy = (PlayerPtr->UnitFactory != -1); 4113 sidebar_entry.Type = UNIT_TYPE; 4114 isbusy |= Units.Avail() <= 0; 4115 break; 4116 4117 case RTTI_AIRCRAFTTYPE: 4118 isbusy = (PlayerPtr->AircraftFactory != -1); 4119 sidebar_entry.Type = AIRCRAFT_TYPE; 4120 isbusy |= Aircraft.Avail() <= 0; 4121 break; 4122 4123 case RTTI_BUILDINGTYPE: 4124 { 4125 isbusy = (PlayerPtr->BuildingFactory != -1); 4126 isbusy |= Buildings.Avail() <= 0; 4127 sidebar_entry.Type = BUILDING_TYPE; 4128 4129 const BuildingTypeClass* build_type = static_cast<const BuildingTypeClass*>(tech); 4130 sidebar_entry.PowerProvided = build_type->Power - build_type->Drain; 4131 } 4132 break; 4133 4134 default: 4135 sidebar_entry.Type = UNKNOWN; 4136 break; 4137 4138 case RTTI_SPECIAL: 4139 switch (Map.Column[c].Buildables[b].BuildableID) 4140 { 4141 case SPC_ION_CANNON: 4142 sidebar_entry.SuperWeaponType = SW_ION_CANNON; 4143 sidebar_entry.Type = SPECIAL; 4144 strncpy(sidebar_entry.AssetName, "SW_Ion", CNC_OBJECT_ASSET_NAME_LENGTH); 4145 sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; 4146 super_weapon = &PlayerPtr->IonCannon; 4147 break; 4148 4149 case SPC_NUCLEAR_BOMB: 4150 sidebar_entry.SuperWeaponType = SW_NUKE; 4151 sidebar_entry.Type = SPECIAL; 4152 strncpy(sidebar_entry.AssetName, "SW_Nuke", CNC_OBJECT_ASSET_NAME_LENGTH); 4153 sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; 4154 super_weapon = &PlayerPtr->NukeStrike; 4155 break; 4156 4157 case SPC_AIR_STRIKE: 4158 sidebar_entry.SuperWeaponType = SW_AIR_STRIKE; 4159 sidebar_entry.Type = SPECIAL; 4160 strncpy(sidebar_entry.AssetName, "SW_AirStrike", CNC_OBJECT_ASSET_NAME_LENGTH); 4161 sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; 4162 super_weapon = &PlayerPtr->AirStrike; 4163 break; 4164 4165 default: 4166 sidebar_entry.SuperWeaponType = SW_UNKNOWN; 4167 sidebar_entry.Type = SPECIAL; 4168 break; 4169 } 4170 break; 4171 } 4172 4173 int fnumber = Map.Column[c].Buildables[b].Factory; 4174 FactoryClass * factory = NULL; 4175 if (tech && fnumber != -1) { 4176 factory = Factories.Raw_Ptr(fnumber); 4177 } 4178 4179 if (super_weapon != nullptr) 4180 { 4181 sidebar_entry.Progress = (float)super_weapon->Anim_Stage() / (float)SuperClass::ANIMATION_STAGES; 4182 sidebar_entry.Completed = super_weapon->Is_Ready(); 4183 sidebar_entry.Constructing = super_weapon->Anim_Stage() != SuperClass::ANIMATION_STAGES; 4184 sidebar_entry.ConstructionOnHold = false; 4185 sidebar_entry.PlacementListLength = 0; 4186 sidebar_entry.PowerProvided = 0; 4187 sidebar_entry.BuildTime = super_weapon->Get_Recharge_Time(); 4188 } 4189 else 4190 { 4191 4192 sidebar_entry.Completed = false; 4193 sidebar_entry.Constructing = false; 4194 sidebar_entry.ConstructionOnHold = false; 4195 sidebar_entry.Progress = 0.0f; 4196 sidebar_entry.Busy = isbusy; 4197 sidebar_entry.PlacementListLength = 0; 4198 4199 if (factory) { 4200 if (factory->Is_Building()) { 4201 sidebar_entry.Constructing = true; 4202 sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; 4203 sidebar_entry.Completed = factory->Has_Completed(); 4204 } 4205 else { 4206 sidebar_entry.Completed = factory->Has_Completed(); 4207 if (!sidebar_entry.Completed) 4208 { 4209 sidebar_entry.ConstructionOnHold = true; 4210 sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; 4211 } 4212 4213 if (sidebar_entry.Completed && sidebar_entry.Type == BUILDING_TYPE) { 4214 if (tech) { 4215 BuildingTypeClass *building_type = (BuildingTypeClass*)tech; 4216 short const *occupy_list = building_type->Occupy_List(true); 4217 if (occupy_list) { 4218 while (*occupy_list != REFRESH_EOL && sidebar_entry.PlacementListLength < MAX_OCCUPY_CELLS) { 4219 sidebar_entry.PlacementList[sidebar_entry.PlacementListLength] = *occupy_list; 4220 sidebar_entry.PlacementListLength++; 4221 occupy_list++; 4222 } 4223 } 4224 } 4225 } 4226 } 4227 } 4228 } 4229 } 4230 } 4231 4232 } else { 4233 4234 4235 if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { 4236 4237 SidebarGlyphxClass *context_sidebar = DLLExportClass::Get_Current_Context_Sidebar(); 4238 4239 /* 4240 ** Get each sidebar column 4241 */ 4242 for (int c = 0 ; c < 2 ; c++) { 4243 4244 sidebar->EntryCount[c] = context_sidebar->Column[c].BuildableCount; 4245 4246 /* 4247 ** Each production slot in the column 4248 */ 4249 for (int b=0 ; b < context_sidebar->Column[c].BuildableCount ; b++) { 4250 4251 CNCSidebarEntryStruct &sidebar_entry = sidebar->Entries[entry_index++]; 4252 if ((entry_index + 1) * sizeof(CNCSidebarEntryStruct) + memory_needed > buffer_size) { 4253 return false; 4254 } 4255 4256 memset(&sidebar_entry, 0, sizeof(sidebar_entry)); 4257 4258 sidebar_entry.AssetName[0] = 0; 4259 sidebar_entry.Type = UNKNOWN; 4260 sidebar_entry.BuildableID = context_sidebar->Column[c].Buildables[b].BuildableID; 4261 sidebar_entry.BuildableType = context_sidebar->Column[c].Buildables[b].BuildableType; 4262 sidebar_entry.BuildableViaCapture = context_sidebar->Column[c].Buildables[b].BuildableViaCapture; 4263 sidebar_entry.Fake = false; 4264 4265 TechnoTypeClass const * tech = Fetch_Techno_Type(context_sidebar->Column[c].Buildables[b].BuildableType, context_sidebar->Column[c].Buildables[b].BuildableID); 4266 4267 sidebar_entry.SuperWeaponType = SW_NONE; 4268 4269 if (tech) { 4270 sidebar_entry.Cost = tech->Cost * PlayerPtr->CostBias; 4271 sidebar_entry.PowerProvided = 0; 4272 sidebar_entry.BuildTime = tech->Time_To_Build(PlayerPtr->Class->House); 4273 strncpy(sidebar_entry.AssetName, tech->IniName, CNC_OBJECT_ASSET_NAME_LENGTH); 4274 sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; 4275 } else { 4276 sidebar_entry.Cost = 0; 4277 sidebar_entry.AssetName[0] = 0; 4278 } 4279 4280 SuperClass* super_weapon = nullptr; 4281 4282 bool isbusy = false; 4283 4284 switch (context_sidebar->Column[c].Buildables[b].BuildableType) { 4285 case RTTI_INFANTRYTYPE: 4286 sidebar_entry.Type = INFANTRY_TYPE; 4287 isbusy = (PlayerPtr->InfantryFactory != -1); 4288 isbusy |= Infantry.Avail() <= 0; 4289 break; 4290 4291 case RTTI_UNITTYPE: 4292 isbusy = (PlayerPtr->UnitFactory != -1); 4293 isbusy |= Units.Avail() <= 0; 4294 sidebar_entry.Type = UNIT_TYPE; 4295 break; 4296 4297 case RTTI_AIRCRAFTTYPE: 4298 isbusy = (PlayerPtr->AircraftFactory != -1); 4299 isbusy |= Aircraft.Avail() <= 0; 4300 sidebar_entry.Type = AIRCRAFT_TYPE; 4301 break; 4302 4303 case RTTI_BUILDINGTYPE: 4304 { 4305 isbusy = (PlayerPtr->BuildingFactory != -1); 4306 isbusy |= Buildings.Avail() <= 0; 4307 sidebar_entry.Type = BUILDING_TYPE; 4308 4309 const BuildingTypeClass* build_type = static_cast<const BuildingTypeClass*>(tech); 4310 sidebar_entry.PowerProvided = build_type->Power - build_type->Drain; 4311 } 4312 break; 4313 4314 default: 4315 sidebar_entry.Type = UNKNOWN; 4316 break; 4317 4318 case RTTI_SPECIAL: 4319 switch (context_sidebar->Column[c].Buildables[b].BuildableID) { 4320 case SPC_ION_CANNON: 4321 sidebar_entry.SuperWeaponType = SW_ION_CANNON; 4322 sidebar_entry.Type = SPECIAL; 4323 strncpy(sidebar_entry.AssetName, "SW_Ion", CNC_OBJECT_ASSET_NAME_LENGTH); 4324 sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; 4325 super_weapon = &PlayerPtr->IonCannon; 4326 break; 4327 4328 case SPC_NUCLEAR_BOMB: 4329 sidebar_entry.SuperWeaponType = SW_NUKE; 4330 sidebar_entry.Type = SPECIAL; 4331 strncpy(sidebar_entry.AssetName, "SW_Nuke", CNC_OBJECT_ASSET_NAME_LENGTH); 4332 sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; 4333 super_weapon = &PlayerPtr->NukeStrike; 4334 break; 4335 4336 case SPC_AIR_STRIKE: 4337 sidebar_entry.SuperWeaponType = SW_AIR_STRIKE; 4338 sidebar_entry.Type = SPECIAL; 4339 strncpy(sidebar_entry.AssetName, "SW_AirStrike", CNC_OBJECT_ASSET_NAME_LENGTH); 4340 sidebar_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; 4341 super_weapon = &PlayerPtr->AirStrike; 4342 break; 4343 4344 default: 4345 sidebar_entry.SuperWeaponType = SW_UNKNOWN; 4346 sidebar_entry.Type = SPECIAL; 4347 break; 4348 } 4349 break; 4350 } 4351 4352 if (super_weapon != nullptr) 4353 { 4354 sidebar_entry.Progress = (float)super_weapon->Anim_Stage() / (float)SuperClass::ANIMATION_STAGES; 4355 sidebar_entry.Completed = super_weapon->Is_Ready(); 4356 sidebar_entry.Constructing = super_weapon->Anim_Stage() != SuperClass::ANIMATION_STAGES; 4357 sidebar_entry.ConstructionOnHold = false; 4358 sidebar_entry.PlacementListLength = 0; 4359 sidebar_entry.PowerProvided = 0; 4360 sidebar_entry.BuildTime = super_weapon->Get_Recharge_Time(); 4361 } 4362 else 4363 { 4364 4365 int fnumber = context_sidebar->Column[c].Buildables[b].Factory; 4366 FactoryClass * factory = NULL; 4367 if (tech && fnumber != -1) { 4368 factory = Factories.Raw_Ptr(fnumber); 4369 } 4370 4371 sidebar_entry.Completed = false; 4372 sidebar_entry.Constructing = false; 4373 sidebar_entry.ConstructionOnHold = false; 4374 sidebar_entry.Progress = 0.0f; 4375 sidebar_entry.Busy = isbusy; 4376 sidebar_entry.PlacementListLength = 0; 4377 4378 if (factory) { 4379 if (factory->Is_Building()) { 4380 sidebar_entry.Constructing = true; 4381 sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; 4382 sidebar_entry.Completed = factory->Has_Completed(); 4383 } 4384 else { 4385 sidebar_entry.Completed = factory->Has_Completed(); 4386 if (!sidebar_entry.Completed) 4387 { 4388 sidebar_entry.ConstructionOnHold = true; 4389 sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; 4390 } 4391 4392 if (sidebar_entry.Completed && sidebar_entry.Type == BUILDING_TYPE) { 4393 if (tech) { 4394 BuildingTypeClass *building_type = (BuildingTypeClass*)tech; 4395 short const *occupy_list = building_type->Occupy_List(true); 4396 if (occupy_list) { 4397 while (*occupy_list != REFRESH_EOL && sidebar_entry.PlacementListLength < MAX_OCCUPY_CELLS) { 4398 sidebar_entry.PlacementList[sidebar_entry.PlacementListLength] = *occupy_list; 4399 sidebar_entry.PlacementListLength++; 4400 occupy_list++; 4401 } 4402 } 4403 } 4404 } 4405 } 4406 } 4407 } 4408 } 4409 } 4410 } 4411 } 4412 4413 4414 4415 return true; 4416 } 4417 4418 4419 static const int _map_width_shift_bits = 6; 4420 4421 void DLLExportClass::Calculate_Placement_Distances(BuildingTypeClass* placement_type, unsigned char* placement_distance) 4422 { 4423 int map_cell_x = Map.MapCellX; 4424 int map_cell_y = Map.MapCellY; 4425 int map_cell_width = Map.MapCellWidth; 4426 int map_cell_height = Map.MapCellHeight; 4427 4428 if (map_cell_x > 0) { 4429 map_cell_x--; 4430 map_cell_width++; 4431 } 4432 4433 if (map_cell_width < MAP_MAX_CELL_WIDTH) { 4434 map_cell_width++; 4435 } 4436 4437 if (map_cell_y > 0) { 4438 map_cell_y--; 4439 map_cell_height++; 4440 } 4441 4442 if (map_cell_height < MAP_MAX_CELL_HEIGHT) { 4443 map_cell_height++; 4444 } 4445 4446 memset(placement_distance, 255U, MAP_CELL_TOTAL); 4447 for (int y = 0; y < map_cell_height; y++) { 4448 for (int x = 0; x < map_cell_width; x++) { 4449 CELL cell = (CELL)map_cell_x + x + ((map_cell_y + y) << _map_width_shift_bits); 4450 BuildingClass* base = (BuildingClass*)Map[cell].Cell_Find_Object(RTTI_BUILDING); 4451 if ((base && base->House->Class->House == PlayerPtr->Class->House) || (Map[cell].Owner == PlayerPtr->Class->House)) { 4452 placement_distance[cell] = 0U; 4453 for (FacingType facing = FACING_N; facing < FACING_COUNT; facing++) { 4454 CELL adjcell = Adjacent_Cell(cell, facing); 4455 if (Map.In_Radar(adjcell)) { 4456 placement_distance[adjcell] = min(placement_distance[adjcell], 1U); 4457 } 4458 } 4459 } 4460 } 4461 } 4462 } 4463 4464 void Recalculate_Placement_Distances() 4465 { 4466 DLLExportClass::Recalculate_Placement_Distances(); 4467 } 4468 4469 void DLLExportClass::Recalculate_Placement_Distances() 4470 { 4471 if (PlacementType[CurrentLocalPlayerIndex] != NULL) { 4472 Calculate_Placement_Distances(PlacementType[CurrentLocalPlayerIndex], PlacementDistance[CurrentLocalPlayerIndex]); 4473 } 4474 } 4475 4476 4477 /************************************************************************************************** 4478 * DLLExportClass::Get_Placement_State -- Get a snapshot of legal validity of placing a structure on all map cells 4479 * 4480 * In: 4481 * 4482 * Out: 4483 * 4484 * 4485 * 4486 * History: 2/4/2019 3:11PM - ST 4487 **************************************************************************************************/ 4488 bool DLLExportClass::Get_Placement_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) 4489 { 4490 /* 4491 ** Get the player for this... 4492 */ 4493 if (!DLLExportClass::Set_Player_Context(player_id)) { 4494 return false; 4495 } 4496 4497 if (PlacementType[CurrentLocalPlayerIndex] == NULL) { 4498 return false; 4499 } 4500 4501 CNCPlacementInfoStruct *placement_info = (CNCPlacementInfoStruct*) buffer_in; 4502 4503 unsigned int memory_needed = sizeof(*placement_info); // Base amount needed. Will need more depending on how many entries there are 4504 4505 int map_cell_x = Map.MapCellX; 4506 int map_cell_y = Map.MapCellY; 4507 int map_cell_width = Map.MapCellWidth; 4508 int map_cell_height = Map.MapCellHeight; 4509 4510 if (map_cell_x > 0) { 4511 map_cell_x--; 4512 map_cell_width++; 4513 } 4514 4515 if (map_cell_width < MAP_MAX_CELL_WIDTH) { 4516 map_cell_width++; 4517 } 4518 4519 if (map_cell_y > 0) { 4520 map_cell_y--; 4521 map_cell_height++; 4522 } 4523 4524 if (map_cell_height < MAP_MAX_CELL_HEIGHT) { 4525 map_cell_height++; 4526 } 4527 4528 memory_needed += map_cell_width * map_cell_height * sizeof(CNCPlacementCellInfoStruct); 4529 4530 if (memory_needed + 128 >= buffer_size) { 4531 return false; 4532 } 4533 4534 placement_info->Count = map_cell_width * map_cell_height; 4535 4536 int index = 0; 4537 for (int y=0 ; y < map_cell_height ; y++) { 4538 for (int x=0 ; x < map_cell_width ; x++) { 4539 4540 CELL cell = (CELL) map_cell_x + x + ((map_cell_y + y) << _map_width_shift_bits); 4541 4542 bool pass = Passes_Proximity_Check(cell, PlacementType[CurrentLocalPlayerIndex], PlacementDistance[CurrentLocalPlayerIndex]); 4543 4544 CellClass * cellptr = &Map[cell]; 4545 bool clear = cellptr->Is_Generally_Clear(); 4546 4547 CNCPlacementCellInfoStruct &placement_cell_info = placement_info->CellInfo[index++]; 4548 placement_cell_info.PassesProximityCheck = pass; 4549 placement_cell_info.GenerallyClear = clear; 4550 } 4551 } 4552 4553 Map.ZoneOffset = 0; 4554 4555 return true; 4556 } 4557 4558 4559 bool DLLExportClass::Passes_Proximity_Check(CELL cell_in, BuildingTypeClass *placement_type, unsigned char* placement_distance) 4560 { 4561 4562 /* 4563 ** Scan through all cells that the building foundation would cover. If any adjacent 4564 ** cells to these are of friendly persuasion, then consider the proximity check to 4565 ** have been a success. 4566 */ 4567 short const *occupy_list = placement_type->Occupy_List(true); 4568 4569 while (*occupy_list != REFRESH_EOL) { 4570 4571 CELL center_cell = cell_in + *occupy_list++; 4572 4573 if (!Map.In_Radar(center_cell)) { 4574 return false; 4575 } 4576 4577 if (placement_distance[center_cell] <= 1U) { 4578 return true; 4579 } 4580 } 4581 4582 return false; 4583 } 4584 4585 4586 /************************************************************************************************** 4587 * DLLExportClass::Start_Construction -- Start sidebar construction 4588 * 4589 * In: 4590 * 4591 * Out: 4592 * 4593 * 4594 * 4595 * History: 1/29/2019 11:37AM - ST 4596 **************************************************************************************************/ 4597 bool DLLExportClass::Start_Construction(uint64 player_id, int buildable_type, int buildable_id) 4598 { 4599 /* 4600 ** Get the player for this... 4601 */ 4602 if (!DLLExportClass::Set_Player_Context(player_id)) { 4603 return false; 4604 } 4605 4606 if (GameToPlay == GAME_NORMAL) { 4607 return Construction_Action(SIDEBAR_REQUEST_START_CONSTRUCTION, player_id, buildable_type, buildable_id); 4608 } 4609 return MP_Construction_Action(SIDEBAR_REQUEST_START_CONSTRUCTION, player_id, buildable_type, buildable_id); 4610 } 4611 4612 /************************************************************************************************** 4613 * DLLExportClass::Hold_Construction -- Pause sidebar construction 4614 * 4615 * In: 4616 * 4617 * Out: 4618 * 4619 * 4620 * 4621 * History: 6/12/2019 JAS 4622 **************************************************************************************************/ 4623 bool DLLExportClass::Hold_Construction(uint64 player_id, int buildable_type, int buildable_id) 4624 { 4625 if (!DLLExportClass::Set_Player_Context(player_id)) 4626 { 4627 return false; 4628 } 4629 4630 if (GameToPlay == GAME_NORMAL) { 4631 return Construction_Action(SIDEBAR_REQUEST_HOLD_CONSTRUCTION, player_id, buildable_type, buildable_id); 4632 } 4633 return MP_Construction_Action(SIDEBAR_REQUEST_HOLD_CONSTRUCTION, player_id, buildable_type, buildable_id); 4634 } 4635 4636 /************************************************************************************************** 4637 * DLLExportClass::Cancel_Construction -- Stop sidebar construction 4638 * 4639 * In: 4640 * 4641 * Out: 4642 * 4643 * 4644 * 4645 * History: 6/12/2019 JAS 4646 **************************************************************************************************/ 4647 bool DLLExportClass::Cancel_Construction(uint64 player_id, int buildable_type, int buildable_id) 4648 { 4649 if (!DLLExportClass::Set_Player_Context(player_id)) 4650 { 4651 return false; 4652 } 4653 4654 return Cancel_Placement(player_id, buildable_type, buildable_id) && 4655 ((GameToPlay == GAME_NORMAL) ? 4656 Construction_Action(SIDEBAR_REQUEST_CANCEL_CONSTRUCTION, player_id, buildable_type, buildable_id) : 4657 MP_Construction_Action(SIDEBAR_REQUEST_CANCEL_CONSTRUCTION, player_id, buildable_type, buildable_id)); 4658 } 4659 4660 /************************************************************************************************** 4661 * DLLExportClass::Construction_Action -- Reproduce actions on the sidebar 4662 * 4663 * In: 4664 * 4665 * Out: 4666 * 4667 * 4668 * 4669 * History: 1/29/2019 11:37AM - ST 4670 **************************************************************************************************/ 4671 bool DLLExportClass::Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id) 4672 { 4673 4674 /* 4675 ** 4676 ** Based on SidebarClass::StripClass::SelectClass::Action 4677 ** 4678 ** Most of this code is validating that the game is in the correct state to be able to act on a sidebar icon 4679 ** 4680 */ 4681 4682 for (int c = 0 ; c < 2 ; c++) { 4683 4684 /* 4685 ** Each production slot in the column 4686 */ 4687 for (int b=0 ; b < Map.Column[c].BuildableCount ; b++) { 4688 if (Map.Column[c].Buildables[b].BuildableID == buildable_id) { 4689 if (Map.Column[c].Buildables[b].BuildableType == buildable_type) { 4690 4691 4692 int genfactory = -1; 4693 switch (buildable_type) { 4694 case RTTI_INFANTRYTYPE: 4695 genfactory = PlayerPtr->InfantryFactory; 4696 break; 4697 4698 case RTTI_UNITTYPE: 4699 genfactory = PlayerPtr->UnitFactory; 4700 break; 4701 4702 case RTTI_AIRCRAFTTYPE: 4703 genfactory = PlayerPtr->AircraftFactory; 4704 break; 4705 4706 case RTTI_BUILDINGTYPE: 4707 genfactory = PlayerPtr->BuildingFactory; 4708 break; 4709 4710 default: 4711 genfactory = -1; 4712 break; 4713 } 4714 4715 int fnumber = Map.Column[c].Buildables[b].Factory; 4716 int spc = 0; 4717 ObjectTypeClass const * choice = NULL; 4718 4719 if (buildable_type != RTTI_SPECIAL) { 4720 choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); 4721 } else { 4722 spc = buildable_id; 4723 } 4724 4725 FactoryClass * factory = NULL; 4726 if (fnumber != -1) { 4727 factory = Factories.Raw_Ptr(fnumber); 4728 } 4729 4730 if (spc == 0 && choice) { 4731 if (fnumber == -1 && genfactory != -1) { 4732 return(false); 4733 } 4734 4735 if (factory) { 4736 4737 /* 4738 ** If this object is currently being built, then give a scold sound and text and then 4739 ** bail. 4740 */ 4741 switch (construction_action) 4742 { 4743 case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: 4744 On_Speech(PlayerPtr, VOX_CANCELED); // Speak(VOX_CANCELED); 4745 OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); 4746 break; 4747 case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: 4748 if (factory->Is_Building()) 4749 { 4750 On_Speech(PlayerPtr, VOX_SUSPENDED); // Speak(VOX_SUSPENDED); 4751 OutList.Add(EventClass(EventClass::SUSPEND, (RTTIType)buildable_type, buildable_id)); 4752 } 4753 break; 4754 4755 default: 4756 if (factory->Is_Building()) { 4757 On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" 4758 return false; 4759 } 4760 else { 4761 4762 /* 4763 ** If production has completed, then attempt to have the object exit 4764 ** the factory or go into placement mode. 4765 */ 4766 if (factory->Has_Completed()) { 4767 4768 TechnoClass * pending = factory->Get_Object(); 4769 if (!pending && factory->Get_Special_Item()) { 4770 // TO_DO 4771 //Map.IsTargettingMode = true; 4772 } 4773 else { 4774 4775 BuildingClass * builder = pending->Who_Can_Build_Me(false, false); 4776 if (!builder) { 4777 OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); 4778 On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" 4779 } 4780 else { 4781 4782 /* 4783 ** If the completed object is a building, then change the 4784 ** game state into building placement mode. This fact is 4785 ** not transmitted to any linked computers until the moment 4786 ** the building is actually placed down. 4787 */ 4788 if (pending->What_Am_I() == RTTI_BUILDING) { 4789 if (construction_action == SIDEBAR_REQUEST_START_PLACEMENT) { 4790 PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); 4791 } 4792 } 4793 else { 4794 4795 /* 4796 ** For objects that can leave the factory under their own 4797 ** power, queue this event and process through normal house 4798 ** production channels. 4799 */ 4800 //OutList.Add(EventClass(EventClass::PLACE, otype, -1)); 4801 } 4802 } 4803 } 4804 4805 } 4806 else { 4807 4808 /* 4809 ** The factory must have been in a suspended state. Resume construction 4810 ** normally. 4811 */ 4812 if (construction_action == SIDEBAR_REQUEST_START_CONSTRUCTION) { 4813 On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); 4814 OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); 4815 return true; 4816 } 4817 } 4818 } 4819 break; 4820 } 4821 4822 } else { 4823 4824 switch (construction_action) 4825 { 4826 case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: 4827 case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: 4828 break; 4829 4830 default: 4831 /* 4832 ** If this side strip is already busy with production, then ignore the 4833 ** input and announce this fact. 4834 */ 4835 On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); 4836 OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); 4837 4838 /* 4839 ** Execute immediately so we get the sidebar feedback 4840 */ 4841 Queue_AI(); 4842 return true; 4843 } 4844 } 4845 } 4846 } 4847 } 4848 } 4849 } 4850 return false; 4851 } 4852 4853 4854 4855 4856 /************************************************************************************************** 4857 * DLLExportClass::MP_Construction_Action -- Reproduce actions on the sidebar 4858 * 4859 * In: 4860 * 4861 * Out: 4862 * 4863 * 4864 * 4865 * History: 3/26/2019 1:02PM - ST 4866 **************************************************************************************************/ 4867 bool DLLExportClass::MP_Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id) 4868 { 4869 4870 /* 4871 ** 4872 ** Based on SidebarClass::StripClass::SelectClass::Action 4873 ** 4874 ** Most of this code is validating that the game is in the correct state to be able to act on a sidebar icon 4875 ** 4876 */ 4877 4878 SidebarGlyphxClass *context_sidebar = DLLExportClass::Get_Current_Context_Sidebar(); 4879 4880 for (int c = 0 ; c < 2 ; c++) { 4881 4882 /* 4883 ** Each production slot in the column 4884 */ 4885 for (int b=0 ; b < context_sidebar->Column[c].BuildableCount ; b++) { 4886 if (context_sidebar->Column[c].Buildables[b].BuildableID == buildable_id) { 4887 if (context_sidebar->Column[c].Buildables[b].BuildableType == buildable_type) { 4888 4889 int genfactory = -1; 4890 switch (buildable_type) { 4891 case RTTI_INFANTRYTYPE: 4892 genfactory = PlayerPtr->InfantryFactory; 4893 break; 4894 4895 case RTTI_UNITTYPE: 4896 genfactory = PlayerPtr->UnitFactory; 4897 break; 4898 4899 case RTTI_AIRCRAFTTYPE: 4900 genfactory = PlayerPtr->AircraftFactory; 4901 break; 4902 4903 case RTTI_BUILDINGTYPE: 4904 genfactory = PlayerPtr->BuildingFactory; 4905 break; 4906 4907 default: 4908 genfactory = -1; 4909 break; 4910 } 4911 4912 int fnumber = context_sidebar->Column[c].Buildables[b].Factory; 4913 int spc = 0; 4914 ObjectTypeClass const * choice = NULL; 4915 4916 if (buildable_type != RTTI_SPECIAL) { 4917 choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); 4918 } else { 4919 spc = buildable_id; 4920 } 4921 4922 FactoryClass * factory = NULL; 4923 if (fnumber != -1) { 4924 factory = Factories.Raw_Ptr(fnumber); 4925 } 4926 4927 if (spc == 0 && choice) { 4928 /* 4929 ** If there is already a factory attached to this strip but the player didn't click 4930 ** on the icon that has the attached factory, then say that the factory is busy and 4931 ** ignore the click. 4932 */ 4933 if (fnumber == -1 && genfactory != -1) { 4934 On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" 4935 return(false); 4936 } 4937 4938 if (factory) { 4939 switch (construction_action) 4940 { 4941 case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: 4942 On_Speech(PlayerPtr, VOX_CANCELED); // Speak(VOX_CANCELED); 4943 OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); 4944 break; 4945 case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: 4946 if (factory->Is_Building()) 4947 { 4948 On_Speech(PlayerPtr, VOX_SUSPENDED); // Speak(VOX_SUSPENDED); 4949 OutList.Add(EventClass(EventClass::SUSPEND, (RTTIType)buildable_type, buildable_id)); 4950 } 4951 break; 4952 4953 default: 4954 /* 4955 ** If this object is currently being built, then give a scold sound and text and then 4956 ** bail. 4957 */ 4958 if (factory->Is_Building()) { 4959 On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" 4960 return false; 4961 } 4962 else { 4963 4964 /* 4965 ** If production has completed, then attempt to have the object exit 4966 ** the factory or go into placement mode. 4967 */ 4968 if (factory->Has_Completed()) { 4969 4970 TechnoClass * pending = factory->Get_Object(); 4971 if (!pending && factory->Get_Special_Item()) { 4972 // TO_DO 4973 //Map.IsTargettingMode = true; 4974 } 4975 else { 4976 4977 BuildingClass * builder = pending->Who_Can_Build_Me(false, false); 4978 if (!builder) { 4979 On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" 4980 OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); 4981 } 4982 else { 4983 4984 /* 4985 ** If the completed object is a building, then change the 4986 ** game state into building placement mode. This fact is 4987 ** not transmitted to any linked computers until the moment 4988 ** the building is actually placed down. 4989 */ 4990 if (pending->What_Am_I() == RTTI_BUILDING) { 4991 if (construction_action == SIDEBAR_REQUEST_START_PLACEMENT) { 4992 if (DLLExportClass::Legacy_Render_Enabled()) { 4993 PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); 4994 } else { 4995 Unselect_All(); 4996 } 4997 } 4998 } 4999 else { 5000 5001 /* 5002 ** For objects that can leave the factory under their own 5003 ** power, queue this event and process through normal house 5004 ** production channels. 5005 */ 5006 //OutList.Add(EventClass(EventClass::PLACE, otype, -1)); 5007 } 5008 } 5009 } 5010 5011 } 5012 else { 5013 5014 /* 5015 ** The factory must have been in a suspended state. Resume construction 5016 ** normally. 5017 */ 5018 if (construction_action == SIDEBAR_REQUEST_START_CONSTRUCTION) { 5019 On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); 5020 OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); 5021 return true; 5022 } 5023 } 5024 } 5025 break; 5026 } 5027 5028 } else { 5029 5030 switch (construction_action) 5031 { 5032 case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: 5033 case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: 5034 break; 5035 5036 default: 5037 /* 5038 ** 5039 */ 5040 On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); 5041 OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); 5042 5043 /* 5044 ** Execute immediately so we get the sidebar feedback 5045 */ 5046 DLLExportClass::Glyphx_Queue_AI(); 5047 return true; 5048 } 5049 } 5050 } 5051 } 5052 } 5053 } 5054 } 5055 return false; 5056 } 5057 5058 5059 5060 5061 5062 /************************************************************************************************** 5063 * DLLExportClass::Start_Placement -- Start placing a completed structure 5064 * 5065 * In: 5066 * 5067 * Out: 5068 * 5069 * 5070 * 5071 * History: 1/29/2019 11:37AM - ST 5072 **************************************************************************************************/ 5073 bool DLLExportClass::Start_Placement(uint64 player_id, int buildable_type, int buildable_id) 5074 { 5075 /* 5076 ** Get the player for this... 5077 */ 5078 if (!DLLExportClass::Set_Player_Context(player_id)) { 5079 return false; 5080 } 5081 5082 BuildingClass *building = Get_Pending_Placement_Object(player_id, buildable_type, buildable_id); 5083 5084 if (building) { 5085 5086 TechnoTypeClass const * tech = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); 5087 5088 if (tech) { 5089 BuildingTypeClass *building_type = (BuildingTypeClass*) tech; 5090 //short const *occupy_list = building_type->Get_Occupy_List(true); 5091 5092 PlacementType[CurrentLocalPlayerIndex] = building_type; 5093 Recalculate_Placement_Distances(); 5094 5095 if (GameToPlay == GAME_NORMAL) { 5096 return Construction_Action(SIDEBAR_REQUEST_START_PLACEMENT, player_id, buildable_type, buildable_id); 5097 } 5098 return MP_Construction_Action(SIDEBAR_REQUEST_START_PLACEMENT, player_id, buildable_type, buildable_id); 5099 } 5100 } 5101 return true; 5102 } 5103 5104 5105 /************************************************************************************************** 5106 * DLLExportClass::Cancel_Placement -- Cancel placing a completed structure 5107 * 5108 * In: 5109 * 5110 * Out: 5111 * 5112 * 5113 * 5114 * History: 2/7/2019 10:52AM - ST 5115 **************************************************************************************************/ 5116 bool DLLExportClass::Cancel_Placement(uint64 player_id, int buildable_type, int buildable_id) 5117 { 5118 /* 5119 ** Get the player for this... 5120 */ 5121 if (!DLLExportClass::Set_Player_Context(player_id)) { 5122 return false; 5123 } 5124 5125 PlacementType[CurrentLocalPlayerIndex] = NULL; 5126 5127 Map.PendingObjectPtr = 0; 5128 Map.PendingObject = 0; 5129 Map.PendingHouse = HOUSE_NONE; 5130 Map.Set_Cursor_Shape(0); 5131 5132 return true; 5133 } 5134 5135 5136 5137 /************************************************************************************************** 5138 * DLLExportClass::Place -- Place a completed structure down 5139 * 5140 * In: 5141 * 5142 * Out: 5143 * 5144 * 5145 * 5146 * History: 2/6/2019 11:51AM - ST 5147 **************************************************************************************************/ 5148 bool DLLExportClass::Place(uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y) 5149 { 5150 /* 5151 ** Get the player for this... 5152 */ 5153 if (!DLLExportClass::Set_Player_Context(player_id)) { 5154 return false; 5155 } 5156 5157 /* 5158 ** Need to check for proximity again here? 5159 */ 5160 #if (0) 5161 Map.Passes_Proximity_Check 5162 5163 Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List()); 5164 5165 5166 OutList.Add(EventClass(EventClass::PLACE, PendingObjectPtr->What_Am_I(), cell + ZoneOffset)); 5167 #endif 5168 5169 5170 BuildingClass *building = Get_Pending_Placement_Object(player_id, buildable_type, buildable_id); 5171 5172 if (building) { 5173 5174 TechnoTypeClass const * tech = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); 5175 5176 if (tech) { 5177 BuildingTypeClass *building_type = (BuildingTypeClass*) tech; 5178 //short const *occupy_list = building_type->Get_Occupy_List(true); 5179 5180 PlacementType[CurrentLocalPlayerIndex] = building_type; 5181 5182 /* 5183 ** The cell coordinates passed in will be relative to the playable area that the client knows about 5184 */ 5185 5186 int map_cell_x = Map.MapCellX; 5187 int map_cell_y = Map.MapCellY; 5188 int map_cell_width = Map.MapCellWidth; 5189 int map_cell_height = Map.MapCellHeight; 5190 5191 if (map_cell_x > 0) { 5192 map_cell_x--; 5193 map_cell_width++; 5194 } 5195 5196 if (map_cell_y > 0) { 5197 map_cell_y--; 5198 map_cell_height++; 5199 } 5200 5201 CELL cell = (CELL) (map_cell_x + cell_x) + ( (map_cell_y + cell_y) << _map_width_shift_bits ); 5202 5203 /* 5204 ** Call the place directly instead of queueing it, so we can evaluate the return code. 5205 */ 5206 if (PlayerPtr->Place_Object(building->What_Am_I(), cell + Map.ZoneOffset)) { 5207 PlacementType[CurrentLocalPlayerIndex] = NULL; 5208 } 5209 } 5210 } 5211 return true; 5212 5213 } 5214 5215 5216 5217 5218 BuildingClass *DLLExportClass::Get_Pending_Placement_Object(uint64 player_id, int buildable_type, int buildable_id) 5219 { 5220 /* 5221 ** 5222 ** Based on SidebarClass::StripClass::SelectClass::Action 5223 ** 5224 ** 5225 */ 5226 if (GameToPlay == GAME_NORMAL) { 5227 5228 for (int c = 0 ; c < 2 ; c++) { 5229 5230 /* 5231 ** Each production slot in the column 5232 */ 5233 for (int b=0 ; b < Map.Column[c].BuildableCount ; b++) { 5234 if (Map.Column[c].Buildables[b].BuildableID == buildable_id) { 5235 if (Map.Column[c].Buildables[b].BuildableType == buildable_type) { 5236 5237 5238 int genfactory = -1; 5239 switch (buildable_type) { 5240 case RTTI_INFANTRYTYPE: 5241 genfactory = PlayerPtr->InfantryFactory; 5242 break; 5243 5244 case RTTI_UNITTYPE: 5245 genfactory = PlayerPtr->UnitFactory; 5246 break; 5247 5248 case RTTI_AIRCRAFTTYPE: 5249 genfactory = PlayerPtr->AircraftFactory; 5250 break; 5251 5252 case RTTI_BUILDINGTYPE: 5253 genfactory = PlayerPtr->BuildingFactory; 5254 break; 5255 5256 default: 5257 genfactory = -1; 5258 break; 5259 } 5260 5261 int fnumber = Map.Column[c].Buildables[b].Factory; 5262 int spc = 0; 5263 ObjectTypeClass const * choice = NULL; 5264 5265 if (buildable_type != RTTI_SPECIAL) { 5266 choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); 5267 } else { 5268 spc = buildable_id; 5269 } 5270 5271 FactoryClass * factory = NULL; 5272 if (fnumber != -1) { 5273 factory = Factories.Raw_Ptr(fnumber); 5274 } 5275 5276 if (spc == 0 && choice) { 5277 if (fnumber == -1 && genfactory != -1) { 5278 return(NULL); 5279 } 5280 5281 if (factory) { 5282 5283 /* 5284 ** If production has completed, then attempt to have the object exit 5285 ** the factory or go into placement mode. 5286 */ 5287 if (factory->Has_Completed()) { 5288 5289 TechnoClass * pending = factory->Get_Object(); 5290 if (!pending && factory->Get_Special_Item()) { 5291 //Map.IsTargettingMode = true; 5292 } else { 5293 BuildingClass * builder = pending->Who_Can_Build_Me(false, false); 5294 if (!builder) { 5295 OutList.Add(EventClass(EventClass::ABANDON, buildable_type, buildable_id)); 5296 On_Speech(PlayerPtr, VOX_NO_FACTORY); // Speak(VOX_NO_FACTORY); 5297 } else { 5298 5299 /* 5300 ** If the completed object is a building, then change the 5301 ** game state into building placement mode. This fact is 5302 ** not transmitted to any linked computers until the moment 5303 ** the building is actually placed down. 5304 */ 5305 if (pending->What_Am_I() == RTTI_BUILDING) { 5306 return (BuildingClass*)pending; 5307 //PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); 5308 } 5309 } 5310 } 5311 } 5312 } 5313 } 5314 } 5315 } 5316 } 5317 } 5318 5319 } else { 5320 5321 if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { 5322 5323 5324 SidebarGlyphxClass *context_sidebar = DLLExportClass::Get_Current_Context_Sidebar(); 5325 5326 for (int c = 0 ; c < 2 ; c++) { 5327 5328 /* 5329 ** Each production slot in the column 5330 */ 5331 for (int b=0 ; b < context_sidebar->Column[c].BuildableCount ; b++) { 5332 if (context_sidebar->Column[c].Buildables[b].BuildableID == buildable_id) { 5333 if (context_sidebar->Column[c].Buildables[b].BuildableType == buildable_type) { 5334 5335 5336 int genfactory = -1; 5337 switch (buildable_type) { 5338 case RTTI_INFANTRYTYPE: 5339 genfactory = PlayerPtr->InfantryFactory; 5340 break; 5341 5342 case RTTI_UNITTYPE: 5343 genfactory = PlayerPtr->UnitFactory; 5344 break; 5345 5346 case RTTI_AIRCRAFTTYPE: 5347 genfactory = PlayerPtr->AircraftFactory; 5348 break; 5349 5350 case RTTI_BUILDINGTYPE: 5351 genfactory = PlayerPtr->BuildingFactory; 5352 break; 5353 5354 default: 5355 genfactory = -1; 5356 break; 5357 } 5358 5359 int fnumber = context_sidebar->Column[c].Buildables[b].Factory; 5360 int spc = 0; 5361 ObjectTypeClass const * choice = NULL; 5362 5363 if (buildable_type != RTTI_SPECIAL) { 5364 choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); 5365 } else { 5366 spc = buildable_id; 5367 } 5368 5369 FactoryClass * factory = NULL; 5370 if (fnumber != -1) { 5371 factory = Factories.Raw_Ptr(fnumber); 5372 } 5373 5374 if (spc == 0 && choice) { 5375 if (fnumber == -1 && genfactory != -1) { 5376 return(NULL); 5377 } 5378 5379 if (factory) { 5380 5381 /* 5382 ** If production has completed, then attempt to have the object exit 5383 ** the factory or go into placement mode. 5384 */ 5385 if (factory->Has_Completed()) { 5386 5387 TechnoClass * pending = factory->Get_Object(); 5388 if (!pending && factory->Get_Special_Item()) { 5389 //Map.IsTargettingMode = true; 5390 } else { 5391 BuildingClass * builder = pending->Who_Can_Build_Me(false, false); 5392 if (!builder) { 5393 OutList.Add(EventClass(EventClass::ABANDON, buildable_type, buildable_id)); 5394 On_Speech(PlayerPtr, VOX_NO_FACTORY); // Speak(VOX_NO_FACTORY); 5395 } else { 5396 5397 /* 5398 ** If the completed object is a building, then change the 5399 ** game state into building placement mode. This fact is 5400 ** not transmitted to any linked computers until the moment 5401 ** the building is actually placed down. 5402 */ 5403 if (pending->What_Am_I() == RTTI_BUILDING) { 5404 return (BuildingClass*)pending; 5405 //PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); 5406 } 5407 } 5408 } 5409 } 5410 } 5411 } 5412 } 5413 } 5414 } 5415 } 5416 } 5417 } 5418 return NULL; 5419 } 5420 5421 /************************************************************************************************** 5422 * DLLExportClass::Place_Super_Weapon 5423 * 5424 * History: 5425 **************************************************************************************************/ 5426 bool DLLExportClass::Place_Super_Weapon(uint64 player_id, int buildable_type, int buildable_id, int x, int y) 5427 { 5428 if (buildable_type != RTTI_SPECIAL) 5429 { 5430 return false; 5431 } 5432 5433 COORDINATE coord = Map.Pixel_To_Coord(x, y); 5434 CELL cell = Coord_Cell(coord); 5435 5436 SpecialWeaponType weapon_type = (SpecialWeaponType)buildable_id; 5437 5438 OutList.Add(EventClass(EventClass::SPECIAL_PLACE, weapon_type, cell)); 5439 5440 return true; 5441 } 5442 5443 /************************************************************************************************** 5444 * DLLExportClass::Create_Control_Group 5445 * 5446 * History: 5447 **************************************************************************************************/ 5448 bool DLLExportClass::Create_Control_Group(unsigned char control_group_index) 5449 { 5450 Handle_Team(control_group_index, 2); 5451 5452 return true; 5453 } 5454 5455 /************************************************************************************************** 5456 * DLLExportClass::Add_To_Control_Group 5457 * 5458 * History: 5459 **************************************************************************************************/ 5460 bool DLLExportClass::Add_To_Control_Group(unsigned char control_group_index) 5461 { 5462 Handle_Team(control_group_index, 1); 5463 5464 return true; 5465 } 5466 5467 /************************************************************************************************** 5468 * DLLExportClass::Toggle_Control_Group_Selection 5469 * 5470 * History: 5471 **************************************************************************************************/ 5472 bool DLLExportClass::Toggle_Control_Group_Selection(unsigned char control_group_index) 5473 { 5474 Handle_Team(control_group_index, 0); 5475 5476 return true; 5477 } 5478 5479 5480 5481 /************************************************************************************************** 5482 * DLLExportClass::Get_Shroud_State -- Get a snapshot of the shroud for the given player 5483 * 5484 * In: 5485 * 5486 * Out: 5487 * 5488 * 5489 * 5490 * History: 4/12/2019 3:44PM - ST 5491 **************************************************************************************************/ 5492 bool DLLExportClass::Get_Shroud_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) 5493 { 5494 if (!DLLExportClass::Set_Player_Context(player_id)) { 5495 return false; 5496 } 5497 5498 CNCShroudStruct *shroud = (CNCShroudStruct*) buffer_in; 5499 5500 unsigned int memory_needed = sizeof(*shroud) + 256; // Base amount needed. Will need more depending on how many entries there are 5501 5502 int entry_index = 0; 5503 5504 /* 5505 ** 5506 ** Based loosely on DisplayClass::Redraw_Icons 5507 ** 5508 ** 5509 */ 5510 int map_cell_x = Map.MapCellX; 5511 int map_cell_y = Map.MapCellY; 5512 int map_cell_width = Map.MapCellWidth; 5513 int map_cell_height = Map.MapCellHeight; 5514 5515 if (map_cell_x > 0) { 5516 map_cell_x--; 5517 map_cell_width++; 5518 } 5519 5520 if (map_cell_width < MAP_MAX_CELL_WIDTH) { 5521 map_cell_width++; 5522 } 5523 5524 if (map_cell_y > 0) { 5525 map_cell_y--; 5526 map_cell_height++; 5527 } 5528 5529 if (map_cell_height < MAP_MAX_CELL_HEIGHT) { 5530 map_cell_height++; 5531 } 5532 5533 for (int y = 0 ; y < map_cell_height ; y++) { 5534 for (int x = 0 ; x < map_cell_width ; x++) { 5535 CELL cell = XY_Cell(map_cell_x+x, map_cell_y+y); 5536 COORDINATE coord = Cell_Coord(cell) & 0xFF00FF00L; 5537 5538 memory_needed += sizeof(CNCShroudEntryStruct); 5539 if (memory_needed >= buffer_size) { 5540 return false; 5541 } 5542 5543 int xpixel; 5544 int ypixel; 5545 5546 Map.Coord_To_Pixel(coord, xpixel, ypixel); 5547 5548 CellClass * cellptr = &Map[Coord_Cell(coord)]; 5549 5550 CNCShroudEntryStruct &shroud_entry = shroud->Entries[entry_index]; 5551 5552 shroud_entry.IsVisible = cellptr->Is_Visible(PlayerPtr); 5553 shroud_entry.IsMapped = cellptr->Is_Mapped(PlayerPtr); 5554 shroud_entry.IsJamming = false; 5555 shroud_entry.ShadowIndex = -1; 5556 5557 if (!shroud_entry.IsMapped) { 5558 if (shroud_entry.IsVisible) { 5559 shroud_entry.ShadowIndex = (char) Map.Cell_Shadow(cell, PlayerPtr); 5560 } 5561 } 5562 5563 entry_index++; 5564 } 5565 } 5566 5567 shroud->Count = entry_index; 5568 5569 return true; 5570 } 5571 5572 5573 5574 5575 /************************************************************************************************** 5576 * DLLExportClass::Get_Occupier_State -- Get the occupier state for this player 5577 * 5578 * In: 5579 * 5580 * Out: 5581 * 5582 * 5583 * 5584 * History: 10/25/2019 - SKY 5585 **************************************************************************************************/ 5586 bool DLLExportClass::Get_Occupier_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) 5587 { 5588 UNREFERENCED_PARAMETER(player_id); 5589 5590 CNCOccupierHeaderStruct* occupiers = (CNCOccupierHeaderStruct*)buffer_in; 5591 CNCOccupierEntryHeaderStruct* entry = reinterpret_cast<CNCOccupierEntryHeaderStruct*>(occupiers + 1U); 5592 occupiers->Count = 0; 5593 5594 unsigned int memory_needed = sizeof(CNCOccupierHeaderStruct); 5595 5596 int map_cell_x = Map.MapCellX; 5597 int map_cell_y = Map.MapCellY; 5598 int map_cell_width = Map.MapCellWidth; 5599 int map_cell_height = Map.MapCellHeight; 5600 5601 if (map_cell_x > 0) { 5602 map_cell_x--; 5603 map_cell_width++; 5604 } 5605 5606 if (map_cell_width < MAP_MAX_CELL_WIDTH) { 5607 map_cell_width++; 5608 } 5609 5610 if (map_cell_y > 0) { 5611 map_cell_y--; 5612 map_cell_height++; 5613 } 5614 5615 if (map_cell_height < MAP_MAX_CELL_HEIGHT) { 5616 map_cell_height++; 5617 } 5618 5619 for (int y = 0; y < map_cell_height; y++) { 5620 for (int x = 0; x < map_cell_width; x++, occupiers->Count++) { 5621 CELL cell = XY_Cell(map_cell_x + x, map_cell_y + y); 5622 CellClass * cellptr = &Map[cell]; 5623 5624 int occupier_count = 0; 5625 ObjectClass* const cell_occupier = cellptr->Cell_Occupier(); 5626 for (ObjectClass* optr = cell_occupier; optr != NULL; optr = optr->Next) { 5627 if (optr->IsActive) { 5628 occupier_count++; 5629 } 5630 } 5631 5632 memory_needed += sizeof(CNCOccupierEntryHeaderStruct) + (sizeof(CNCOccupierObjectStruct) * occupier_count); 5633 if (memory_needed >= buffer_size) { 5634 return false; 5635 } 5636 5637 CNCOccupierObjectStruct* occupier = reinterpret_cast<CNCOccupierObjectStruct*>(entry + 1U); 5638 entry->Count = occupier_count; 5639 5640 for (ObjectClass* optr = cell_occupier; optr != NULL; optr = optr->Next) { 5641 if (optr->IsActive) { 5642 CNCObjectStruct object; 5643 Convert_Type(optr, object); 5644 occupier->Type = object.Type; 5645 occupier->ID = object.ID; 5646 occupier++; 5647 } 5648 } 5649 5650 entry = reinterpret_cast<CNCOccupierEntryHeaderStruct*>(occupier + 1U); 5651 } 5652 } 5653 5654 return true; 5655 } 5656 5657 5658 5659 5660 /************************************************************************************************** 5661 * DLLExportClass::Get_Player_Info_State -- Get the multiplayer info for this player 5662 * 5663 * In: 5664 * 5665 * Out: 5666 * 5667 * 5668 * 5669 * History: 4/22/2019 10:33AM - ST 5670 **************************************************************************************************/ 5671 bool DLLExportClass::Get_Player_Info_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) 5672 { 5673 if (!DLLExportClass::Set_Player_Context(player_id)) { 5674 return false; 5675 } 5676 5677 CNCPlayerInfoStruct *player_info = (CNCPlayerInfoStruct*) buffer_in; 5678 5679 unsigned int memory_needed = sizeof(*player_info) + 32; // A little extra for no reason 5680 5681 if (memory_needed >= buffer_size) { 5682 return false; 5683 } 5684 5685 player_info->GlyphxPlayerID = 0; 5686 5687 if (PlayerPtr == NULL) { 5688 return false;; 5689 } 5690 5691 strncpy(&player_info->Name[0], MPlayerNames[CurrentLocalPlayerIndex], MPLAYER_NAME_MAX); 5692 player_info->Name[MPLAYER_NAME_MAX - 1] = 0; // Make sure it's terminated 5693 player_info->House = PlayerPtr->Class->House; 5694 player_info->AllyFlags = PlayerPtr->Get_Ally_Flags(); 5695 player_info->ColorIndex = MPlayerID_To_ColorIndex(MPlayerID[CurrentLocalPlayerIndex]); 5696 player_info->GlyphxPlayerID = player_id; 5697 player_info->HomeCellX = Cell_X(MultiplayerStartPositions[CurrentLocalPlayerIndex]); 5698 player_info->HomeCellY = Cell_Y(MultiplayerStartPositions[CurrentLocalPlayerIndex]); 5699 player_info->IsDefeated = PlayerPtr->IsDefeated; 5700 player_info->SpiedPowerFlags = 0U; 5701 player_info->SpiedMoneyFlags = 0U; 5702 player_info->IsRadarJammed = false; 5703 5704 // Populate selection info 5705 if (CurrentObject.Count() > 0) { 5706 CNCObjectStruct object; 5707 Convert_Type(CurrentObject[0], object); 5708 player_info->SelectedID = object.ID; 5709 player_info->SelectedType = object.Type; 5710 5711 const int left = Map.MapCellX; 5712 const int right = Map.MapCellX + Map.MapCellWidth - 1; 5713 const int top = Map.MapCellY; 5714 const int bottom = Map.MapCellY + Map.MapCellHeight - 1; 5715 5716 // Use first object with a weapon, or first object if none 5717 ObjectClass* action_object = nullptr; 5718 for (int i = 0; i < CurrentObject.Count(); ++i) { 5719 ObjectClass* object = CurrentObject[i]; 5720 if (object->Is_Techno()) { 5721 TechnoClass* techno = (TechnoClass*)object; 5722 if (techno->Techno_Type_Class()->Primary != WEAPON_NONE || techno->Techno_Type_Class()->Secondary != WEAPON_NONE) { 5723 action_object = object; 5724 break; 5725 } 5726 } 5727 } 5728 if (action_object == nullptr) { 5729 action_object = CurrentObject[0]; 5730 } 5731 5732 int index = 0; 5733 for (int y = top; y <= bottom; ++y) { 5734 for (int x = left; x <= right; ++x, ++index) { 5735 Convert_Action_Type(action_object->What_Action(XY_Cell(x, y)), (CurrentObject.Count() == 1) ? action_object : NULL, As_Target(XY_Cell(x, y)), player_info->ActionWithSelected[index]); 5736 } 5737 } 5738 5739 player_info->ActionWithSelectedCount = Map.MapCellWidth * Map.MapCellHeight; 5740 } 5741 else { 5742 player_info->SelectedID = -1; 5743 player_info->SelectedType = UNKNOWN; 5744 player_info->ActionWithSelectedCount = 0U; 5745 } 5746 5747 // Screen shake 5748 player_info->ScreenShake = PlayerPtr->ScreenShakeTime; 5749 5750 return true; 5751 }; 5752 5753 5754 5755 5756 /************************************************************************************************** 5757 * DLLExportClass::Get_Dynamic_Map_State -- Get a snapshot of the smudges and overlays on the terrain 5758 * 5759 * In: 5760 * 5761 * Out: 5762 * 5763 * 5764 * 5765 * History: 2/8/2019 10:45AM - ST 5766 **************************************************************************************************/ 5767 bool DLLExportClass::Get_Dynamic_Map_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) 5768 { 5769 /* 5770 ** Get the player for this... 5771 */ 5772 player_id; 5773 5774 static int _call_count = 0; 5775 5776 CNCDynamicMapStruct *dynamic_map = (CNCDynamicMapStruct*) buffer_in; 5777 5778 unsigned int memory_needed = sizeof(*dynamic_map) + 256; // Base amount needed. Will need more depending on how many entries there are 5779 5780 int entry_index = 0; 5781 5782 /* 5783 ** 5784 ** Based loosely on DisplayClass::Redraw_Icons 5785 ** 5786 ** 5787 */ 5788 int map_cell_x = Map.MapCellX; 5789 int map_cell_y = Map.MapCellY; 5790 int map_cell_width = Map.MapCellWidth; 5791 int map_cell_height = Map.MapCellHeight; 5792 5793 if (map_cell_x > 0) { 5794 map_cell_x--; 5795 map_cell_width++; 5796 } 5797 5798 if (map_cell_width < MAP_MAX_CELL_WIDTH) { 5799 map_cell_width++; 5800 } 5801 5802 if (map_cell_y > 0) { 5803 map_cell_y--; 5804 map_cell_height++; 5805 } 5806 5807 if (map_cell_height < MAP_MAX_CELL_HEIGHT) { 5808 map_cell_height++; 5809 } 5810 5811 int cell_index = 0; 5812 5813 bool debug_output = false; 5814 //if (_call_count == 20) { 5815 //debug_output = true; 5816 //} 5817 5818 // Need to ignore view constraints for dynamic map updates, so the radar map 5819 // has the latest tiberium state for cells outside the tactical view 5820 DLLExportClass::Adjust_Internal_View(true); 5821 5822 for (int y = 0 ; y < map_cell_height ; y++) { 5823 for (int x = 0 ; x < map_cell_width ; x++) { 5824 CELL cell = XY_Cell(map_cell_x+x, map_cell_y+y); 5825 COORDINATE coord = Cell_Coord(cell) & 0xFF00FF00L; 5826 5827 memory_needed += sizeof(CNCDynamicMapEntryStruct) * 2; 5828 if (memory_needed >= buffer_size) { 5829 return false; 5830 } 5831 5832 /* 5833 ** Only cells flagged to be redraw are examined. 5834 */ 5835 //if (In_View(cell) && Is_Cell_Flagged(cell)) { 5836 int xpixel; 5837 int ypixel; 5838 5839 if (Map.Coord_To_Pixel(coord, xpixel, ypixel)) { 5840 CellClass * cellptr = &Map[Coord_Cell(coord)]; 5841 5842 /* 5843 ** If there is a portion of the underlying icon that could be visible, 5844 ** then draw it. Also draw the cell if the shroud is off. 5845 */ 5846 if (GameToPlay == GAME_GLYPHX_MULTIPLAYER || cellptr->IsVisible || Debug_Unshroud) { 5847 Cell_Class_Draw_It(dynamic_map, entry_index, cellptr, xpixel, ypixel, debug_output); 5848 } 5849 5850 /* 5851 ** If any cell is not fully mapped, then flag it so that the shadow drawing 5852 ** process will occur. Only draw the shadow if Debug_Unshroud is false. 5853 */ 5854 //if (!cellptr->IsMapped && !Debug_Unshroud) { 5855 // IsShadowPresent = true; 5856 //} 5857 } 5858 //} 5859 } 5860 } 5861 5862 if (entry_index) { 5863 _call_count++; 5864 } 5865 5866 dynamic_map->Count = entry_index; 5867 dynamic_map->VortexActive = false; 5868 5869 return true; 5870 } 5871 5872 5873 5874 5875 5876 5877 /************************************************************************************************** 5878 * DLLExportClass::Cell_Class_Draw_It -- Go through the motions of drawing a cell to get the smudge and overlay info 5879 * 5880 * In: 5881 * 5882 * Out: 5883 * 5884 * 5885 * 5886 * History: 2/8/2019 11:09AM - ST 5887 **************************************************************************************************/ 5888 void DLLExportClass::Cell_Class_Draw_It(CNCDynamicMapStruct *dynamic_map, int &entry_index, CellClass *cell_ptr, int xpixel, int ypixel, bool debug_output) 5889 { 5890 /* 5891 ** 5892 ** Based on CellClass::Draw_It and SmudgeTypeClass::Draw_It 5893 ** 5894 ** 5895 */ 5896 5897 CELL cell = cell_ptr->Cell_Number(); 5898 5899 /* 5900 ** Redraw any smudge. 5901 */ 5902 if (cell_ptr->Smudge != SMUDGE_NONE) { 5903 //SmudgeTypeClass::As_Reference(Smudge).Draw_It(x, y, SmudgeData); 5904 5905 const SmudgeTypeClass &smudge_type = SmudgeTypeClass::As_Reference(cell_ptr->Smudge); 5906 5907 if (smudge_type.Get_Image_Data() != NULL) { 5908 5909 if (debug_output) { 5910 IsTheaterShape = true; 5911 Debug_Write_Shape_Type(&smudge_type, 0); 5912 IsTheaterShape = false; 5913 } 5914 5915 CNCDynamicMapEntryStruct &smudge_entry = dynamic_map->Entries[entry_index++]; 5916 5917 strncpy(smudge_entry.AssetName, smudge_type.IniName, CNC_OBJECT_ASSET_NAME_LENGTH); 5918 smudge_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; 5919 smudge_entry.Type = (short) cell_ptr->Smudge; 5920 smudge_entry.Owner = (char)cell_ptr->Owner; 5921 smudge_entry.DrawFlags = SHAPE_WIN_REL; // Looks like smudges are drawn top left 5922 smudge_entry.PositionX = xpixel; 5923 smudge_entry.PositionY = ypixel; 5924 smudge_entry.Width = Get_Build_Frame_Width(smudge_type.Get_Image_Data()); 5925 smudge_entry.Height = Get_Build_Frame_Height(smudge_type.Get_Image_Data()); 5926 smudge_entry.CellX = Cell_X(cell); 5927 smudge_entry.CellY = Cell_Y(cell); 5928 smudge_entry.ShapeIndex = cell_ptr->SmudgeData; 5929 smudge_entry.IsSmudge = true; 5930 smudge_entry.IsOverlay = false; 5931 smudge_entry.IsResource = false; 5932 smudge_entry.IsSellable = false; 5933 smudge_entry.IsTheaterShape = true; // Smudges are always theater-specific 5934 smudge_entry.IsFlag = false; 5935 } 5936 } 5937 5938 /* 5939 ** Draw the overlay object. 5940 */ 5941 if (cell_ptr->Overlay != OVERLAY_NONE) { 5942 //OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(Overlay); 5943 //IsTheaterShape = (bool)otype.IsTheater; 5944 //CC_Draw_Shape(otype.Get_Image_Data(), OverlayData, (x+(CELL_PIXEL_W>>1)), (y+(CELL_PIXEL_H>>1)), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, Map.UnitShadow); 5945 //IsTheaterShape = false; 5946 5947 const OverlayTypeClass &overlay_type = OverlayTypeClass::As_Reference(cell_ptr->Overlay); 5948 5949 if (overlay_type.Get_Image_Data() != NULL) { 5950 5951 CNCDynamicMapEntryStruct &overlay_entry = dynamic_map->Entries[entry_index++]; 5952 5953 5954 if (debug_output) { 5955 IsTheaterShape = (bool)overlay_type.IsTheater; 5956 Debug_Write_Shape_Type(&overlay_type, 0); 5957 IsTheaterShape = false; 5958 } 5959 5960 strncpy(overlay_entry.AssetName, overlay_type.IniName, CNC_OBJECT_ASSET_NAME_LENGTH); 5961 overlay_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; 5962 overlay_entry.Type = (short)cell_ptr->Overlay; 5963 overlay_entry.Owner = (char) cell_ptr->Owner; 5964 overlay_entry.DrawFlags = SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST; // Looks like overlays are drawn centered and translucent 5965 overlay_entry.PositionX = xpixel + (CELL_PIXEL_W>>1); 5966 overlay_entry.PositionY = ypixel + (CELL_PIXEL_H>>1); 5967 overlay_entry.Width = Get_Build_Frame_Width(overlay_type.Get_Image_Data()); 5968 overlay_entry.Height = Get_Build_Frame_Height(overlay_type.Get_Image_Data()); 5969 overlay_entry.CellX = Cell_X(cell); 5970 overlay_entry.CellY = Cell_Y(cell); 5971 overlay_entry.ShapeIndex = cell_ptr->OverlayData; 5972 overlay_entry.IsSmudge = false; 5973 overlay_entry.IsOverlay = true; 5974 overlay_entry.IsResource = overlay_entry.Type >= OVERLAY_TIBERIUM1 && overlay_entry.Type <= OVERLAY_TIBERIUM12; 5975 overlay_entry.IsSellable = overlay_entry.Type >= OVERLAY_SANDBAG_WALL && overlay_entry.Type <= OVERLAY_WOOD_WALL; 5976 overlay_entry.IsTheaterShape = (bool)overlay_type.IsTheater; 5977 overlay_entry.IsFlag = false; 5978 } 5979 } 5980 5981 5982 if (cell_ptr->IsFlagged) { 5983 5984 const void* image_data = MixFileClass::Retrieve("FLAGFLY.SHP"); 5985 if (image_data != NULL) { 5986 5987 CNCDynamicMapEntryStruct &flag_entry = dynamic_map->Entries[entry_index++]; 5988 5989 strncpy(flag_entry.AssetName, "FLAGFLY", CNC_OBJECT_ASSET_NAME_LENGTH); 5990 flag_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; 5991 flag_entry.Type = -1; 5992 flag_entry.Owner = cell_ptr->Owner; 5993 flag_entry.DrawFlags = SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING; 5994 flag_entry.PositionX = xpixel + (ICON_PIXEL_W / 2); 5995 flag_entry.PositionY = ypixel + (ICON_PIXEL_H / 2); 5996 flag_entry.Width = Get_Build_Frame_Width(image_data); 5997 flag_entry.Height = Get_Build_Frame_Height(image_data); 5998 flag_entry.CellX = Cell_X(cell); 5999 flag_entry.CellY = Cell_Y(cell); 6000 flag_entry.ShapeIndex = Frame % 14; 6001 flag_entry.IsSmudge = false; 6002 flag_entry.IsOverlay = false; 6003 flag_entry.IsResource = false; 6004 flag_entry.IsSellable = false; 6005 flag_entry.IsTheaterShape = false; 6006 flag_entry.IsFlag = true; 6007 } 6008 6009 } 6010 6011 } 6012 6013 6014 6015 6016 /************************************************************************************************** 6017 * DLLExportClass::Glyphx_Queue_AI -- Special queue processing for Glyphx multiplayer mode 6018 * 6019 * In: 6020 * 6021 * Out: 6022 * 6023 * 6024 * 6025 * History: 3/12/2019 10:52AM - ST 6026 **************************************************************************************************/ 6027 void DLLExportClass::Glyphx_Queue_AI(void) 6028 { 6029 6030 //------------------------------------------------------------------------ 6031 // Move events from the OutList (events generated by this player) into the 6032 // DoList (the list of events to execute). 6033 //------------------------------------------------------------------------ 6034 while (OutList.Count) { 6035 OutList.First().IsExecuted = false; 6036 if (!DoList.Add(OutList.First())) { 6037 ; 6038 } 6039 OutList.Next(); 6040 } 6041 6042 /* 6043 ** Based on Execute_DoList in queue.cpp 6044 ** 6045 ** The events have the ID of the player encoded in them, so no special per-player processing should be needed. 6046 ** When the event is created, the 'local player' is assumed to be the originator of the event, so PlayerPtr will need 6047 ** to be swapped out to represent the real originating player prior to any events being created as a result of GlyphX input 6048 ** 6049 ** ST - 3/12/2019 10:51AM 6050 */ 6051 6052 for (int i = 0; i < MPlayerCount; i++) { 6053 6054 HousesType house; 6055 HouseClass *housep; 6056 6057 house = MPlayerHouses [i]; 6058 housep= HouseClass::As_Pointer (house); 6059 6060 //..................................................................... 6061 // If for some reason this house doesn't exist, skip it. 6062 // Also, if this house has exited the game, skip it. (The user can 6063 // generate events after he exits, because the exit event is scheduled 6064 // at least FrameSendRate*3 frames ahead. If one system gets these 6065 // packets & another system doesn't, they'll go out of sync because 6066 // they aren't checking the CommandCount for that house, since that 6067 // house isn't connected any more.) 6068 //..................................................................... 6069 if (!housep){ 6070 continue; 6071 } 6072 6073 if (!housep->IsHuman){ 6074 continue; 6075 } 6076 6077 //..................................................................... 6078 // Loop through all events 6079 //..................................................................... 6080 for (int j = 0; j < DoList.Count; j++) { 6081 6082 if (!DoList[j].IsExecuted && (unsigned)Frame >= DoList[j].Frame) { 6083 DoList[j].Execute(); 6084 6085 //............................................................... 6086 // Mark this event as executed. 6087 //............................................................... 6088 DoList[j].IsExecuted = 1; 6089 } 6090 } 6091 } 6092 6093 6094 6095 6096 //------------------------------------------------------------------------ 6097 // Clean out the DoList 6098 //------------------------------------------------------------------------ 6099 while (DoList.Count) { 6100 6101 //..................................................................... 6102 // Discard events that have been executed, OR it's too late to execute. 6103 // (This happens if another player exits the game; he'll leave FRAMEINFO 6104 // events lying around in my queue. They won't have been "executed", 6105 // because his IPX connection was destroyed.) 6106 //..................................................................... 6107 if ( (DoList.First().IsExecuted) || ((unsigned)Frame > DoList.First().Frame) ) { 6108 DoList.Next(); 6109 } 6110 else { 6111 break; 6112 } 6113 } 6114 6115 } 6116 6117 6118 6119 6120 6121 /************************************************************************************************** 6122 * DLLExportClass::Reset_Sidebars -- Init the multiplayer sidebars 6123 * 6124 * In: 6125 * 6126 * Out: 6127 * 6128 * 6129 * 6130 * History: 3/14/2019 3:10PM - ST 6131 **************************************************************************************************/ 6132 void DLLExportClass::Reset_Sidebars(void) 6133 { 6134 for (int i=0 ; i<MPlayerCount ; i++) { 6135 HouseClass *player_ptr = HouseClass::As_Pointer(MPlayerHouses[i]); //HouseClass::As_Pointer(HOUSE_MULTI2); 6136 MultiplayerSidebars[i].Init_Clear(player_ptr); 6137 } 6138 } 6139 6140 6141 6142 6143 /************************************************************************************************** 6144 * DLLExportClass::Set_Player_Context -- Switch the C&C local player context 6145 * 6146 * In: 6147 * 6148 * Out: 6149 * 6150 * 6151 * 6152 * History: 3/14/2019 3:20PM - ST 6153 **************************************************************************************************/ 6154 bool DLLExportClass::Set_Player_Context(uint64 glyphx_player_id, bool force) 6155 { 6156 /* 6157 ** Context never needs to change in single player 6158 */ 6159 if (GameToPlay == GAME_NORMAL) { 6160 if (PlayerPtr) { 6161 CurrentObject.Set_Active_Context(PlayerPtr->Class->House); 6162 } 6163 return true; 6164 } 6165 6166 /* 6167 ** C&C relies a lot on PlayerPtr, which is a pointer to the 'local' player's house. Historically, in a peer-to-peer 6168 ** multiplayer game, each player's PlayerPtr pointed to their own local player. 6169 ** 6170 ** Since much of the IO logic depends on PlayerPtr being the player performing the action, we need to set PlayerPtr 6171 ** correctly depending on which player generated input or needs output 6172 */ 6173 6174 for (int i=0 ; i<MPlayerCount ; i++) { 6175 if (GlyphxPlayerIDs[i] == glyphx_player_id) { 6176 6177 if (!force && i == CurrentLocalPlayerIndex) { 6178 return true; 6179 } 6180 6181 MPlayerLocalID = MPlayerID[i]; 6182 PlayerPtr = HouseClass::As_Pointer(MPlayerHouses[i]); //HouseClass::As_Pointer(HOUSE_MULTI2); 6183 CurrentObject.Set_Active_Context(PlayerPtr->Class->House); 6184 CurrentLocalPlayerIndex = i; 6185 6186 return true; 6187 } 6188 } 6189 6190 return false; 6191 } 6192 6193 6194 6195 /************************************************************************************************** 6196 * DLLExportClass::Reset_Player_Context -- Clear out old player context data 6197 * 6198 * In: 6199 * 6200 * Out: 6201 * 6202 * 6203 * 6204 * History: 4/16/2019 10:36AM - ST 6205 **************************************************************************************************/ 6206 void DLLExportClass::Reset_Player_Context(void) 6207 { 6208 for (int i=0 ; i<MAX_PLAYERS ; i++) { 6209 PlacementType[i] = NULL; 6210 } 6211 CurrentLocalPlayerIndex = 0; 6212 CurrentObject.Clear_All(); 6213 } 6214 6215 6216 6217 /************************************************************************************************** 6218 * Logic_Switch_Player_Context -- Called when the internal game locic needs to switch player context 6219 * 6220 * In: 6221 * 6222 * Out: 6223 * 6224 * 6225 * 6226 * History: 4/17/2019 9:45AM - ST 6227 **************************************************************************************************/ 6228 void Logic_Switch_Player_Context(ObjectClass *object) 6229 { 6230 DLLExportClass::Logic_Switch_Player_Context(object); 6231 } 6232 6233 6234 /************************************************************************************************** 6235 * DLLExportClass::Logic_Switch_Player_Context -- Called when the internal game locic needs to switch player context 6236 * 6237 * In: 6238 * 6239 * Out: 6240 * 6241 * 6242 * 6243 * History: 4/17/2019 9:45AM - ST 6244 **************************************************************************************************/ 6245 void DLLExportClass::Logic_Switch_Player_Context(ObjectClass *object) 6246 { 6247 if (object == NULL) { 6248 return; 6249 } 6250 6251 /* 6252 ** If it's not a techno, it can't be owned. 6253 */ 6254 if (!object->Is_Techno()) { 6255 return; 6256 } 6257 6258 TechnoClass *tech = static_cast<TechnoClass*>(object); 6259 6260 //HousesType house = tech->House->Class->House; 6261 DLLExportClass::Logic_Switch_Player_Context(tech->House); 6262 } 6263 6264 6265 6266 /************************************************************************************************** 6267 * Logic_Switch_Player_Context -- Called when the internal game locic needs to switch player context 6268 * 6269 * In: 6270 * 6271 * Out: 6272 * 6273 * 6274 * 6275 * History: 4/17/2019 9:45AM - ST 6276 **************************************************************************************************/ 6277 void Logic_Switch_Player_Context(HouseClass *object) 6278 { 6279 DLLExportClass::Logic_Switch_Player_Context(object); 6280 } 6281 6282 6283 /************************************************************************************************** 6284 * DLLExportClass::Logic_Switch_Player_Context -- Called when the internal game locic needs to switch player context 6285 * 6286 * In: 6287 * 6288 * Out: 6289 * 6290 * 6291 * 6292 * History: 4/17/2019 9:45AM - ST 6293 **************************************************************************************************/ 6294 void DLLExportClass::Logic_Switch_Player_Context(HouseClass *house) 6295 { 6296 if (GameToPlay == GAME_NORMAL) { 6297 CurrentObject.Set_Active_Context(PlayerPtr->Class->House); 6298 return; 6299 } 6300 6301 if (house == NULL) { 6302 return; 6303 } 6304 6305 /* 6306 ** C&C relies a lot on PlayerPtr, which is a pointer to the 'local' player's house. Historically, in a peer-to-peer 6307 ** multiplayer game, each player's PlayerPtr pointed to their own local player. 6308 ** 6309 ** Since much of the IO logic depends on PlayerPtr being the player performing the action, we need to set PlayerPtr 6310 ** correctly depending on which player generated input or needs output 6311 */ 6312 6313 HousesType house_type = house->Class->House; 6314 6315 for (int i=0 ; i<MPlayerCount ; i++) { 6316 6317 if (house_type == MPlayerHouses[i]) { 6318 6319 if (i == CurrentLocalPlayerIndex) { 6320 return; 6321 } 6322 6323 MPlayerLocalID = MPlayerID[i]; 6324 PlayerPtr = HouseClass::As_Pointer(MPlayerHouses[i]); //HouseClass::As_Pointer(HOUSE_MULTI2); 6325 CurrentObject.Set_Active_Context(PlayerPtr->Class->House); 6326 CurrentLocalPlayerIndex = i; 6327 6328 return; 6329 } 6330 } 6331 } 6332 6333 6334 /************************************************************************************************** 6335 * DLLExportClass::Calculate_Start_Positions -- Calculate the initial view positions for the players 6336 * 6337 * In: 6338 * 6339 * Out: 6340 * 6341 * 6342 * 6343 * History: 4/16/2019 6/12/2019 3:00PM - ST 6344 **************************************************************************************************/ 6345 void DLLExportClass::Calculate_Start_Positions(void) 6346 { 6347 if (GameToPlay == GAME_NORMAL) { 6348 MultiplayerStartPositions[0] = Views[0]; 6349 return; 6350 } 6351 6352 HouseClass *player_ptr = PlayerPtr; 6353 6354 ScenarioInit++; 6355 COORDINATE old_tac = Map.TacticalCoord; 6356 for (int i=0 ; i<MPlayerCount ; i++) { 6357 PlayerPtr = HouseClass::As_Pointer(MPlayerHouses[i]); 6358 if (PlayerPtr) { 6359 long x, y; 6360 Map.Compute_Start_Pos(x, y); 6361 MultiplayerStartPositions[i] = XY_Cell(x, y); 6362 } 6363 } 6364 Map.TacticalCoord = old_tac; 6365 ScenarioInit--; 6366 6367 PlayerPtr = player_ptr; 6368 } 6369 6370 6371 /************************************************************************************************** 6372 * DLLExportClass::Get_GlyphX_Player_ID -- Get the external GlyphX player ID from the C&C house/player pointer 6373 * Returns 0 in single player or if player ID isn't found 6374 * 6375 * In: 6376 * 6377 * Out: 6378 * 6379 * 6380 * 6381 * History: 4/22/2019 6:23PM - ST 6382 **************************************************************************************************/ 6383 __int64 DLLExportClass::Get_GlyphX_Player_ID(const HouseClass *house) 6384 { 6385 /* 6386 ** C&C relies a lot on PlayerPtr, which is a pointer to the 'local' player's house. Historically, in a peer-to-peer 6387 ** multiplayer game, each player's PlayerPtr pointed to their own local player. 6388 ** 6389 ** Since much of the IO logic depends on PlayerPtr being the player performing the action, we need to set PlayerPtr 6390 ** correctly depending on which player generated input or needs output 6391 */ 6392 6393 if (GameToPlay == GAME_NORMAL) { 6394 return 0; 6395 } 6396 6397 if (house == NULL) { 6398 return 0; 6399 } 6400 6401 HousesType house_type = house->Class->House; 6402 6403 for (int i=0 ; i<MPlayerCount ; i++) { 6404 6405 if (house_type == MPlayerHouses[i]) { 6406 6407 return GlyphxPlayerIDs[i]; 6408 } 6409 } 6410 6411 /* 6412 ** Failure case. 6413 */ 6414 return 0; 6415 } 6416 6417 6418 6419 6420 /************************************************************************************************** 6421 * DLLExportClass::Adjust_Internal_View -- Set the internal tactical view to encompass the input coordinates 6422 * 6423 * In: 6424 * 6425 * Out: 6426 * 6427 * 6428 * 6429 * History: 4/16/2019 3:00PM - ST 6430 **************************************************************************************************/ 6431 void DLLExportClass::Adjust_Internal_View(bool force_ignore_view_constraints) 6432 { 6433 /* 6434 ** When legacy rendering is disabled (especially in multiplayer) we can get input coordinates that 6435 ** fall outside the engine's tactical view. In this case, we need to adjust the tactical view before the 6436 ** input will behave as expected. 6437 */ 6438 6439 if (!force_ignore_view_constraints && Legacy_Render_Enabled()) { 6440 /* 6441 ** Render view should already be tracking the player's local view 6442 */ 6443 DisplayClass::IgnoreViewConstraints = false; 6444 return; 6445 } 6446 6447 DisplayClass::IgnoreViewConstraints = true; 6448 } 6449 6450 6451 6452 6453 6454 /************************************************************************************************** 6455 * DLLExportClass::Get_Current_Context_Sidebar -- Get the sidebar data for the current player context 6456 * 6457 * In: 6458 * 6459 * Out: 6460 * 6461 * 6462 * 6463 * History: 3/14/2019 3:20PM - ST 6464 **************************************************************************************************/ 6465 SidebarGlyphxClass *DLLExportClass::Get_Current_Context_Sidebar(HouseClass *player_ptr) 6466 { 6467 if (player_ptr) { 6468 6469 for (int i=0 ; i<MPlayerCount ; i++) { 6470 if (player_ptr == HouseClass::As_Pointer(MPlayerHouses[i])) { 6471 return &MultiplayerSidebars[i]; 6472 } 6473 } 6474 } 6475 return &MultiplayerSidebars[CurrentLocalPlayerIndex]; 6476 } 6477 6478 6479 SidebarGlyphxClass *Get_Current_Context_Sidebar(HouseClass *player_ptr) 6480 { 6481 return DLLExportClass::Get_Current_Context_Sidebar(player_ptr); 6482 } 6483 6484 6485 /************************************************************************************************** 6486 * DLLExportClass::Repair_Mode -- Starts the player's repair mode. All it does here is unselect all units. 6487 * 6488 * In: 6489 * 6490 * Out: 6491 * 6492 * 6493 * 6494 * History: 5/1/2019 - LLL 6495 **************************************************************************************************/ 6496 void DLLExportClass::Repair_Mode(uint64 player_id) 6497 { 6498 /* 6499 ** Get the player for this... 6500 */ 6501 if (!DLLExportClass::Set_Player_Context(player_id)) { 6502 return; 6503 } 6504 6505 Unselect_All(); 6506 } 6507 6508 /************************************************************************************************** 6509 * DLLExportClass::Repair -- Repairs a specific building 6510 * 6511 * In: 6512 * 6513 * Out: 6514 * 6515 * 6516 * 6517 * History: 5/1/2019 - LLL 6518 **************************************************************************************************/ 6519 void DLLExportClass::Repair(uint64 player_id, int object_id) 6520 { 6521 /* 6522 ** Get the player for this... 6523 */ 6524 if (!DLLExportClass::Set_Player_Context(player_id)) { 6525 return; 6526 } 6527 6528 TARGET target = Build_Target(KIND_BUILDING, object_id); 6529 if (target != TARGET_NONE) 6530 { 6531 BuildingClass* building = As_Building(target); 6532 if (building) { 6533 if (!building->IsActive) { 6534 GlyphX_Debug_Print("DLLExportClass::Repair -- trying to repair a non-active building"); 6535 } else { 6536 6537 if (building && building->Can_Repair() && building->House && building->House->Class->House == PlayerPtr->Class->House) 6538 { 6539 building->Repair(-1); 6540 } 6541 } 6542 } 6543 } 6544 } 6545 6546 /************************************************************************************************** 6547 * DLLExportClass::Sell_Mode -- Starts the player's sell mode. All it does here is unselect all units. 6548 * 6549 * In: 6550 * 6551 * Out: 6552 * 6553 * 6554 * 6555 * History: 5/1/2019 - LLL 6556 **************************************************************************************************/ 6557 void DLLExportClass::Sell_Mode(uint64 player_id) 6558 { 6559 /* 6560 ** Get the player for this... 6561 */ 6562 if (!DLLExportClass::Set_Player_Context(player_id)) { 6563 return; 6564 } 6565 6566 Unselect_All(); 6567 } 6568 6569 /************************************************************************************************** 6570 * DLLExportClass::Sell -- Sell's a player's speceific building. 6571 * 6572 * In: 6573 * 6574 * Out: 6575 * 6576 * 6577 * 6578 * History: 5/1/2019 - LLL 6579 **************************************************************************************************/ 6580 void DLLExportClass::Sell(uint64 player_id, int object_id) 6581 { 6582 /* 6583 ** Get the player for this... 6584 */ 6585 if (!DLLExportClass::Set_Player_Context(player_id)) { 6586 return; 6587 } 6588 6589 TARGET target = Build_Target(KIND_BUILDING, object_id); 6590 if (target != TARGET_NONE) 6591 { 6592 BuildingClass* building = As_Building(target); 6593 if (building) { 6594 if (!building->IsActive) { 6595 GlyphX_Debug_Print("DLLExportClass::Sell -- trying to sell a non-active building"); 6596 } else { 6597 if (building->House && building->House->Class->House == PlayerPtr->Class->House) 6598 { 6599 building->Sell_Back(1); 6600 } 6601 } 6602 } 6603 } 6604 } 6605 6606 6607 6608 /************************************************************************************************** 6609 * DLLExportClass::Repair_Sell_Cancel -- Ends the player's repair or sell mode. Doesn't do anything right now. 6610 * 6611 * In: 6612 * 6613 * Out: 6614 * 6615 * 6616 * 6617 * History: 5/1/2019 - LLL 6618 **************************************************************************************************/ 6619 void DLLExportClass::Repair_Sell_Cancel(uint64 player_id) 6620 { 6621 //OutputDebugString("Repair_Sell_Cancel\n"); 6622 } 6623 6624 /************************************************************************************************** 6625 * DLLExportClass::Scatter_Selected -- Scatter the selected units 6626 * 6627 * In: 6628 * 6629 * Out: 6630 * 6631 * 6632 * 6633 * History: 10/15/2019 - SKY 6634 **************************************************************************************************/ 6635 void DLLExportClass::Scatter_Selected(uint64 player_id) 6636 { 6637 /* 6638 ** Get the player for this... 6639 */ 6640 if (!DLLExportClass::Set_Player_Context(player_id)) { 6641 return; 6642 } 6643 6644 if (CurrentObject.Count()) { 6645 for (int index = 0; index < CurrentObject.Count(); index++) { 6646 ObjectClass const * tech = CurrentObject[index]; 6647 6648 if (tech && tech->Can_Player_Move()) { 6649 OutList.Add(EventClass(EventClass::SCATTER, tech->As_Target())); 6650 } 6651 } 6652 } 6653 } 6654 6655 /************************************************************************************************** 6656 * DLLExportClass::Select_Next_Unit 6657 * 6658 * History: 03.02.2020 MBL 6659 **************************************************************************************************/ 6660 void DLLExportClass::Select_Next_Unit(uint64 player_id) 6661 { 6662 /* 6663 ** Get the player for this... 6664 */ 6665 if (!DLLExportClass::Set_Player_Context(player_id)) { 6666 return; 6667 } 6668 6669 ObjectClass* obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); 6670 if (obj) { 6671 Unselect_All(); 6672 obj->Select(); 6673 6674 COORDINATE center = Map.Center_Map(); 6675 Map.Flag_To_Redraw(true); 6676 if (center) { 6677 On_Center_Camera(PlayerPtr, Coord_X(center), Coord_Y(center)); 6678 } 6679 } 6680 } 6681 6682 /************************************************************************************************** 6683 * DLLExportClass::Select_Previous_Unit 6684 * 6685 * History: 03.02.2020 MBL 6686 **************************************************************************************************/ 6687 void DLLExportClass::Select_Previous_Unit(uint64 player_id) 6688 { 6689 /* 6690 ** Get the player for this... 6691 */ 6692 if (!DLLExportClass::Set_Player_Context(player_id)) { 6693 return; 6694 } 6695 6696 ObjectClass* obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); 6697 if (obj) { 6698 Unselect_All(); 6699 obj->Select(); 6700 6701 COORDINATE center = Map.Center_Map(); 6702 Map.Flag_To_Redraw(true); 6703 if (center) { 6704 On_Center_Camera(PlayerPtr, Coord_X(center), Coord_Y(center)); 6705 } 6706 } 6707 } 6708 6709 /************************************************************************************************** 6710 * DLLExportClass::Selected_Guard_Mode 6711 * 6712 * History: 03.03.2020 MBL 6713 **************************************************************************************************/ 6714 void DLLExportClass::Selected_Guard_Mode(uint64 player_id) 6715 { 6716 /* 6717 ** Get the player for this... 6718 */ 6719 if (!DLLExportClass::Set_Player_Context(player_id)) { 6720 return; 6721 } 6722 6723 if (CurrentObject.Count()) { 6724 for (int index = 0; index < CurrentObject.Count(); index++) { 6725 ObjectClass const * tech = CurrentObject[index]; 6726 6727 if (tech && tech->Can_Player_Fire()) { 6728 if (tech->Can_Player_Move()) { 6729 OutList.Add(EventClass(tech->As_Target(), MISSION_GUARD_AREA)); 6730 } else { 6731 OutList.Add(EventClass(tech->As_Target(), MISSION_GUARD)); 6732 } 6733 } 6734 } 6735 } 6736 } 6737 6738 /************************************************************************************************** 6739 * DLLExportClass::Selected_Stop 6740 * 6741 * History: 03.03.2020 MBL 6742 **************************************************************************************************/ 6743 void DLLExportClass::Selected_Stop(uint64 player_id) 6744 { 6745 /* 6746 ** Get the player for this... 6747 */ 6748 if (!DLLExportClass::Set_Player_Context(player_id)) { 6749 return; 6750 } 6751 6752 // Copied from TiberianDawn/Conquer.cpp - Keyboard_Process() with VK_S 6753 if (CurrentObject.Count()) { 6754 for (int index = 0; index < CurrentObject.Count(); index++) { 6755 ObjectClass const * tech = CurrentObject[index]; 6756 6757 if (tech && (tech->Can_Player_Move() || (tech->Can_Player_Fire() && 6758 tech->What_Am_I() != RTTI_BUILDING))) { 6759 OutList.Add(EventClass(EventClass::IDLE, tech->As_Target())); 6760 } 6761 } 6762 } 6763 } 6764 6765 6766 /************************************************************************************************** 6767 * DLLExportClass::Units_Queued_Movement_Toggle 6768 * 6769 * History: 03.03.2020 MBL 6770 **************************************************************************************************/ 6771 void DLLExportClass::Units_Queued_Movement_Toggle(uint64 /*player_id*/, bool /*toggle*/) 6772 { 6773 // Currently Red Alert only but stubbed in support in case we add to Tiberian Dawn later 6774 } 6775 6776 /************************************************************************************************** 6777 * DLLExportClass::Team_Units_Formation_Toggle_On 6778 * 6779 * History: 03.03.2020 MBL 6780 **************************************************************************************************/ 6781 void DLLExportClass::Team_Units_Formation_Toggle_On(uint64 player_id) 6782 { 6783 /* 6784 ** Get the player for this... 6785 */ 6786 if (!DLLExportClass::Set_Player_Context(player_id)) { 6787 return; 6788 } 6789 6790 // Red Alert only at this time, unless we do some updates to support in Tiberian Dawn 6791 #if 0 6792 Toggle_Formation(); // Conquer.cpp 6793 #endif 6794 } 6795 6796 6797 /************************************************************************************************** 6798 * CNC_Handle_Debug_Request -- Process a debug input request 6799 * 6800 * In: 6801 * 6802 * 6803 * Out: 6804 * 6805 * 6806 * History: 1/7/2019 5:20PM - ST 6807 **************************************************************************************************/ 6808 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Debug_Request(DebugRequestEnum debug_request_type, uint64 player_id, const char *object_name, int x, int y, bool unshroud, bool enemy) 6809 { 6810 if (!DLLExportClass::Set_Player_Context(player_id)) { 6811 return; 6812 } 6813 6814 switch (debug_request_type) { 6815 6816 case DEBUG_REQUEST_SPAWN_OBJECT: 6817 { 6818 DLLExportClass::Debug_Spawn_Unit(object_name, x, y, enemy); 6819 } 6820 break; 6821 6822 case DEBUG_REQUEST_FORCE_CRASH: 6823 Debug_Force_Crash = true; 6824 break; 6825 6826 case DEBUG_REQUEST_KILL_OBJECT: 6827 if (strcmp(object_name, "HEAL") == 0) { 6828 DLLExportClass::Debug_Heal_Unit(x, y); 6829 } 6830 else { 6831 DLLExportClass::Debug_Kill_Unit(x, y); 6832 } 6833 break; 6834 6835 case DEBUG_REQUEST_END_GAME: 6836 { 6837 bool win = true; 6838 6839 const char lose[] = "LOSE"; 6840 if (strcmp(lose, object_name) == 0) { 6841 win = false; 6842 } 6843 6844 PlayerWins = win; 6845 PlayerLoses = !win; 6846 } 6847 break; 6848 6849 case DEBUG_REQUEST_UNSHROUD: 6850 Debug_Unshroud = unshroud; 6851 Map.Flag_To_Redraw(true); 6852 break; 6853 6854 case DEBUG_REQUEST_SUPERWEAPON_RECHARGE: 6855 PlayerPtr->IonCannon.Forced_Charge(true); 6856 PlayerPtr->NukeStrike.Forced_Charge(true); 6857 PlayerPtr->AirStrike.Forced_Charge(true); 6858 break; 6859 6860 case DEBUG_REQUEST_END_PRODUCTION: 6861 { 6862 for (int index = 0; index < Factories.Count(); index++) { 6863 FactoryClass* factory = Factories.Ptr(index); 6864 if (factory->Get_House()->IsHuman) { 6865 Factories.Ptr(index)->Force_Complete(); 6866 } 6867 } 6868 } 6869 break; 6870 6871 case DEBUG_REQUEST_ADD_RESOURCES: 6872 { 6873 if (object_name) { 6874 int amount = atoi(object_name); 6875 PlayerPtr->Credits += amount; 6876 if (PlayerPtr->Credits < 0) { 6877 PlayerPtr->Credits = 0; 6878 } 6879 } 6880 } 6881 break; 6882 6883 case DEBUG_REQUEST_UNLOCK_BUILDABLES: 6884 PlayerPtr->DebugUnlockBuildables = !PlayerPtr->DebugUnlockBuildables; 6885 PlayerPtr->IsRecalcNeeded = true; 6886 break; 6887 6888 default: 6889 break; 6890 } 6891 6892 6893 } 6894 6895 6896 extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Beacon_Request(BeaconRequestEnum beacon_request_type, uint64 player_id, int pixel_x, int pixel_y) 6897 { 6898 if (!DLLExportClass::Set_Player_Context(player_id)) { 6899 return; 6900 } 6901 6902 // Beacons are only available if legacy rendering is disabled 6903 if (DLLExportClass::Legacy_Render_Enabled()) { 6904 return; 6905 } 6906 6907 // Only allow one beacon per player 6908 for (int index = 0; index < Anims.Count(); ++index) { 6909 AnimClass* anim = Anims.Ptr(index); 6910 if (anim != NULL && 6911 (*anim == ANIM_BEACON || *anim == ANIM_BEACON_VIRTUAL) && 6912 anim->OwnerHouse == PlayerPtr->Class->House) { 6913 delete anim; 6914 } 6915 } 6916 6917 OutList.Add(EventClass(ANIM_BEACON, PlayerPtr->Class->House, Map.Pixel_To_Coord(pixel_x, pixel_y), PlayerPtr->Get_Allies())); 6918 6919 // Send sound effect to allies 6920 for (int index = 0; index < Houses.Count(); ++index) { 6921 HouseClass* hptr = Houses.Ptr(index); 6922 if (hptr != NULL && hptr->IsActive && hptr->IsHuman && PlayerPtr->Is_Ally(hptr)) { 6923 DLLExportClass::On_Sound_Effect(hptr, VOC_BEACON, "", 0, 0); 6924 } 6925 } 6926 } 6927 6928 6929 /************************************************************************************************** 6930 * DLLExportClass::Debug_Spawn_All -- Debug spawn all buildable units and structures 6931 * 6932 * In: Object to unlimbo , x & y cell positions 6933 * 6934 * 6935 * Out: True if unlimbo succeeded 6936 * 6937 * 6938 * 6939 * History: 1/22/2020 2:57PM - ST 6940 **************************************************************************************************/ 6941 bool DLLExportClass::Try_Debug_Spawn_Unlimbo(TechnoClass *techno, int &cell_x, int &cell_y) 6942 { 6943 if (techno) { 6944 6945 int map_cell_x = Map.MapCellX; 6946 int map_cell_y = Map.MapCellY; 6947 int map_cell_right = map_cell_x + Map.MapCellWidth; 6948 int map_cell_bottom = map_cell_y + Map.MapCellHeight; 6949 6950 map_cell_right = min(map_cell_right, cell_x + 26); // Generally try to prevent the objects from spawing off the right of the screen 6951 6952 int try_x = cell_x; 6953 int try_y = cell_y; 6954 6955 while (try_y < map_cell_bottom) { 6956 6957 CELL cell = XY_Cell(try_x, try_y); 6958 6959 if (techno->Unlimbo(Cell_Coord(cell))) { 6960 6961 try_x++; 6962 if (try_x > map_cell_right - 2) { 6963 try_x = cell_x; //map_cell_x + 2; 6964 try_y++; 6965 } 6966 6967 cell_x = try_x; 6968 cell_y = try_y; 6969 return true; 6970 } 6971 6972 try_x++; 6973 if (try_x > map_cell_right - 2) { 6974 try_x = cell_x; //map_cell_x + 2; 6975 try_y++; 6976 } 6977 } 6978 6979 cell_x = try_x; 6980 cell_y = try_y; 6981 } 6982 6983 return false; 6984 } 6985 6986 6987 /************************************************************************************************** 6988 * DLLExportClass::Debug_Spawn_All -- Debug spawn all buildable units and structures 6989 * 6990 * In: 6991 * 6992 * Out: 6993 * 6994 * 6995 * 6996 * History: 1/22/2020 2:57PM - ST 6997 **************************************************************************************************/ 6998 void DLLExportClass::Debug_Spawn_All(int x, int y) 6999 { 7000 int map_cell_x = Map.MapCellX; 7001 int map_cell_y = Map.MapCellY; 7002 7003 int map_cell_bottom = map_cell_y + Map.MapCellHeight; 7004 7005 int origin_x = map_cell_x + 2; 7006 int origin_y = map_cell_y + 2; 7007 7008 if (x != 0 || y != 0) { 7009 CELL screen_cell = Coord_Cell(Map.Pixel_To_Coord(x, y)); 7010 origin_x = Cell_X(screen_cell); 7011 origin_y = Cell_Y(screen_cell); 7012 } 7013 7014 int try_x = origin_x; 7015 int try_y = origin_y; 7016 7017 HousesType house = PlayerPtr->Class->House; 7018 7019 for (StructType sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) { 7020 BuildingTypeClass const & building_type = BuildingTypeClass::As_Reference(sindex); 7021 7022 if (building_type.IsBuildable) { 7023 7024 BuildingClass * building = new BuildingClass(building_type, house); 7025 if (building) { 7026 7027 try_x = origin_x; 7028 try_y = origin_y; 7029 7030 while (try_y < map_cell_bottom) { 7031 if (Try_Debug_Spawn_Unlimbo(building, try_x, try_y)) { 7032 break; 7033 } 7034 } 7035 } 7036 } 7037 } 7038 7039 7040 for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { 7041 UnitTypeClass const & unit_type = UnitTypeClass::As_Reference(index); 7042 7043 /* 7044 ** Fetch the sidebar cameo image for this building. 7045 */ 7046 if (unit_type.IsBuildable) { 7047 7048 UnitClass * unit = (UnitClass*) unit_type.Create_One_Of(PlayerPtr); 7049 if (unit) { 7050 7051 try_x = origin_x; 7052 try_y = origin_y; 7053 7054 while (try_y < map_cell_bottom) { 7055 if (Try_Debug_Spawn_Unlimbo(unit, try_x, try_y)) { 7056 break; 7057 } 7058 } 7059 } 7060 } 7061 } 7062 7063 7064 for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { 7065 InfantryTypeClass const &infantry_type = InfantryTypeClass::As_Reference(index); 7066 7067 /* 7068 ** Fetch the sidebar cameo image for this building. 7069 */ 7070 if (infantry_type.IsBuildable) { 7071 7072 InfantryClass * inf = (InfantryClass*) infantry_type.Create_One_Of(PlayerPtr); 7073 if (inf) { 7074 7075 try_x = origin_x; 7076 try_y = origin_y; 7077 7078 while (try_y < map_cell_bottom) { 7079 if (Try_Debug_Spawn_Unlimbo(inf, try_x, try_y)) { 7080 break; 7081 } 7082 } 7083 } 7084 } 7085 } 7086 7087 for (AircraftType index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { 7088 AircraftTypeClass const &aircraft_type = AircraftTypeClass::As_Reference(index); 7089 7090 /* 7091 ** Fetch the sidebar cameo image for this building. 7092 */ 7093 if (aircraft_type.IsBuildable) { 7094 7095 AircraftClass * air = (AircraftClass*) aircraft_type.Create_One_Of(PlayerPtr); 7096 if (air) { 7097 7098 try_x = origin_x; 7099 try_y = origin_y; 7100 7101 while (try_y < map_cell_bottom) { 7102 if (Try_Debug_Spawn_Unlimbo(air, try_x, try_y)) { 7103 break; 7104 } 7105 } 7106 } 7107 } 7108 } 7109 7110 } 7111 7112 7113 7114 7115 7116 /************************************************************************************************** 7117 * DLLExportClass::Debug_Spawn_Unit -- Debug spawn a unit at the specified location 7118 * 7119 * In: 7120 * 7121 * Out: 7122 * 7123 * 7124 * 7125 * History: 3/14/2019 3:20PM - ST 7126 **************************************************************************************************/ 7127 void DLLExportClass::Debug_Spawn_Unit(const char *object_name, int x, int y, bool enemy) 7128 { 7129 if (object_name == NULL) { 7130 return; 7131 } 7132 7133 if (strlen(object_name) == 0) { 7134 return; 7135 } 7136 7137 COORDINATE coord = Map.Pixel_To_Coord(x, y); 7138 CELL cell = Coord_Cell(coord); 7139 7140 HousesType house = PlayerPtr->Class->House; 7141 7142 /* 7143 ** Place all? 7144 */ 7145 if (stricmp(object_name, "ALLOBJECTS") == 0) { 7146 Debug_Spawn_All(x, y); 7147 return; 7148 } 7149 7150 /* 7151 ** If this is for the enemy, find the enemy with the most stuff 7152 */ 7153 7154 if (enemy) { 7155 unsigned max_count = 0; 7156 for (int i = 0; i < Houses.Count(); ++i) { 7157 const HouseClass* player = Houses.Ptr(i); 7158 const unsigned count = player->CurUnits + player->CurBuildings + player->CurInfantry + player->CurAircraft; 7159 if (!PlayerPtr->Is_Ally(player) && (count >= max_count)) { 7160 house = player->Class->House; 7161 max_count = count; 7162 } 7163 } 7164 } 7165 7166 /* 7167 ** What is this thing? 7168 */ 7169 7170 StructType structure_type = BuildingTypeClass::From_Name(object_name); 7171 if (structure_type != STRUCT_NONE) { 7172 7173 BuildingClass * building = new BuildingClass(structure_type, house); 7174 if (building) { 7175 if (!building->Unlimbo(Cell_Coord(cell))) { 7176 delete building; 7177 } 7178 } 7179 7180 #if (0) 7181 Map.PendingObject = &BuildingTypeClass::As_Reference(structure_type); 7182 Map.PendingHouse = PlayerPtr->ActLike; 7183 Map.PendingObjectPtr = Map.PendingObject->Create_One_Of(PlayerPtr); 7184 if (Map.PendingObjectPtr) { 7185 Map.Set_Cursor_Pos(); 7186 Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List()); 7187 7188 //OutList.Add(EventClass(EventClass::PLACE, RTTI_BUILDING, (CELL)(cell + Map.ZoneOffset))); 7189 } 7190 #endif 7191 return; 7192 } 7193 7194 7195 UnitType unit_type = UnitTypeClass::From_Name(object_name); 7196 if (unit_type != UNIT_NONE) { 7197 7198 UnitClass * unit = new UnitClass(unit_type, house); 7199 if (unit) { 7200 unit->Unlimbo(Map.Pixel_To_Coord(x, y), DIR_N); 7201 } 7202 7203 return; 7204 } 7205 7206 7207 InfantryType infantry_type = InfantryTypeClass::From_Name(object_name); 7208 if (infantry_type != INFANTRY_NONE) { 7209 7210 InfantryClass * inf = new InfantryClass(infantry_type, house); 7211 if (inf) { 7212 inf->Unlimbo(Map.Pixel_To_Coord(x, y), DIR_N); 7213 } 7214 return; 7215 } 7216 7217 AircraftType aircraft_type = AircraftTypeClass::From_Name(object_name); 7218 if (aircraft_type != AIRCRAFT_NONE) { 7219 7220 AircraftClass * air = new AircraftClass(aircraft_type, house); 7221 if (air) { 7222 air->Altitude = 0; 7223 air->Unlimbo(Map.Pixel_To_Coord(x, y), DIR_N); 7224 } 7225 return; 7226 } 7227 7228 OverlayType overlay_type = OverlayTypeClass::From_Name(object_name); 7229 if (overlay_type != OVERLAY_NONE) 7230 { 7231 new OverlayClass(overlay_type, cell); 7232 return; 7233 } 7234 } 7235 7236 7237 /************************************************************************************************** 7238 * DLLExportClass::Debug_Kill_Unit -- Kill a unit at the specified location 7239 * 7240 * In: 7241 * 7242 * Out: 7243 * 7244 * 7245 * 7246 * History: 8/19/2019 3:09PM - ST 7247 **************************************************************************************************/ 7248 void DLLExportClass::Debug_Kill_Unit(int x, int y) 7249 { 7250 COORDINATE coord = Map.Pixel_To_Coord(x, y); 7251 CELL cell = Coord_Cell(coord); 7252 7253 CellClass * cellptr = &Map[cell]; 7254 7255 if (cellptr) { 7256 ObjectClass *obj = cellptr->Cell_Object(); 7257 static const int debug_damage = 1000; 7258 if (obj) { 7259 int damage = debug_damage; 7260 obj->Take_Damage(damage, 0, WARHEAD_HE, 0); 7261 } else { 7262 if (cellptr->Overlay != OVERLAY_NONE) { 7263 OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); 7264 if (optr->IsTiberium) { 7265 cellptr->Reduce_Tiberium(1); 7266 } 7267 if (optr->IsWall) { 7268 Map[cell].Reduce_Wall(debug_damage); 7269 } 7270 } 7271 } 7272 } 7273 } 7274 7275 void DLLExportClass::Debug_Heal_Unit(int x, int y) 7276 { 7277 COORDINATE coord = Map.Pixel_To_Coord(x, y); 7278 CELL cell = Coord_Cell(coord); 7279 7280 CellClass * cellptr = &Map[cell]; 7281 7282 if (cellptr) { 7283 ObjectClass *obj = cellptr->Cell_Object(); 7284 if (obj) { 7285 obj->Strength = obj->Class_Of().MaxStrength; 7286 } 7287 else { 7288 if (cellptr->Overlay != OVERLAY_NONE) { 7289 OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); 7290 if (optr->IsTiberium) { 7291 const int cellcount = (int)FACING_COUNT + 1; 7292 CellClass* cells[cellcount]; 7293 cells[0] = cellptr; 7294 for (FacingType index = FACING_N; index < FACING_COUNT; index++) { 7295 cells[(int)index + 1] = cellptr->Adjacent_Cell(index); 7296 } 7297 7298 for (int index = 0; index < cellcount; index++) { 7299 CellClass *newcell = cells[index]; 7300 7301 if (newcell && newcell->Cell_Object() == NULL) { 7302 if (newcell->Land_Type() == LAND_CLEAR && newcell->Overlay == OVERLAY_NONE) { 7303 switch (newcell->TType) { 7304 case TEMPLATE_BRIDGE1: 7305 case TEMPLATE_BRIDGE2: 7306 case TEMPLATE_BRIDGE3: 7307 case TEMPLATE_BRIDGE4: 7308 break; 7309 7310 default: 7311 new OverlayClass(Random_Pick(OVERLAY_TIBERIUM1, OVERLAY_TIBERIUM12), newcell->Cell_Number()); 7312 newcell->OverlayData = 1; 7313 break; 7314 7315 } 7316 } 7317 else if (newcell->Land_Type() == LAND_TIBERIUM) { 7318 newcell->OverlayData = MIN(newcell->OverlayData + 1, 11); 7319 newcell->Recalc_Attributes(); 7320 newcell->Redraw_Objects(); 7321 } 7322 } 7323 } 7324 } 7325 } 7326 } 7327 } 7328 } 7329 7330 7331 7332 7333 /************************************************************************************************** 7334 * DLLExportClass::Legacy_Render_Enabled -- Is the legacy rendering enabled? 7335 * 7336 * In: 7337 * 7338 * Out: 7339 * 7340 * 7341 * 7342 * History: 4/15/2019 5:46PM - ST 7343 **************************************************************************************************/ 7344 bool DLLExportClass::Legacy_Render_Enabled(void) 7345 { 7346 if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { 7347 unsigned int num_humans = 0U; 7348 for (int i = 0; i < MPlayerCount; ++i) { 7349 HouseClass *player_ptr = HouseClass::As_Pointer(MPlayerHouses[i]); 7350 if (player_ptr && player_ptr->IsHuman) { 7351 if (++num_humans > 1) break; 7352 } 7353 } 7354 return num_humans < 2; 7355 } 7356 7357 //return false; 7358 return true; 7359 } 7360 7361 7362 /************************************************************************************************** 7363 * DLLExportClass::Computer_Message -- Replacement for original Computer_Message function 7364 * 7365 * In: 7366 * 7367 * Out: 7368 * 7369 * 7370 * 7371 * History: 1/27/2020 1:42PM - ST 7372 **************************************************************************************************/ 7373 void DLLExportClass::Computer_Message(bool last_player_taunt) 7374 { 7375 HousesType house; 7376 HouseClass *ptr; 7377 7378 HouseClass *ai_players[MAX_PLAYERS]; 7379 int ai_player_count = 0; 7380 7381 /*------------------------------------------------------------------------ 7382 Find the computer house that the message will be from 7383 ------------------------------------------------------------------------*/ 7384 for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MPlayerMax); house++) { 7385 ptr = HouseClass::As_Pointer(house); 7386 7387 if (!ptr || ptr->IsHuman || ptr->IsDefeated) { 7388 continue; 7389 } 7390 7391 ai_players[ai_player_count++] = ptr; 7392 } 7393 7394 if (ai_player_count) { 7395 int ai_player_index = 0; 7396 if (ai_player_count > 1) { 7397 ai_player_index = IRandom(0, ai_player_count - 1); 7398 } 7399 7400 int taunt_index; 7401 if (last_player_taunt) { 7402 taunt_index = 13; 7403 } else { 7404 taunt_index = IRandom(0,12); 7405 } 7406 7407 On_Message(ai_players[ai_player_index], "", 15.0f, MESSAGE_TYPE_COMPUTER_TAUNT, taunt_index); 7408 } 7409 } 7410 7411 7412 /************************************************************************************************** 7413 * DLLExportClass::Set_Special_Key_Flags -- 7414 * 7415 * In: 7416 * 7417 * Out: 7418 * 7419 * 7420 * 7421 * History: 6/27/2019 - JAS 7422 **************************************************************************************************/ 7423 void DLLExportClass::Set_Special_Key_Flags(unsigned char special_key_flags) 7424 { 7425 SpecialKeyFlags[CurrentLocalPlayerIndex] = special_key_flags; 7426 } 7427 7428 /************************************************************************************************** 7429 * DLLExportClass::Clear_Special_Key_Flags -- 7430 * 7431 * In: 7432 * 7433 * Out: 7434 * 7435 * 7436 * 7437 * History: 6/27/2019 - JAS 7438 **************************************************************************************************/ 7439 void DLLExportClass::Clear_Special_Key_Flags() 7440 { 7441 SpecialKeyFlags[CurrentLocalPlayerIndex] = 0; 7442 } 7443 7444 /************************************************************************************************** 7445 * DLLExportClass::Get_Input_Key_State -- 7446 * 7447 * In: 7448 * 7449 * Out: 7450 * 7451 * 7452 * 7453 * History: 6/27/2019 - JAS 7454 **************************************************************************************************/ 7455 bool DLLExportClass::Get_Input_Key_State(KeyNumType key) 7456 { 7457 switch (key) 7458 { 7459 case KN_LCTRL: 7460 return (SpecialKeyFlags[CurrentLocalPlayerIndex] & INPUT_SPECIAL_KEY_CTRL) != 0; 7461 break; 7462 case KN_LSHIFT: 7463 return (SpecialKeyFlags[CurrentLocalPlayerIndex] & INPUT_SPECIAL_KEY_SHIFT) != 0; 7464 break; 7465 case KN_LALT: 7466 return (SpecialKeyFlags[CurrentLocalPlayerIndex] & INPUT_SPECIAL_KEY_ALT) != 0; 7467 break; 7468 default: 7469 break; 7470 }; 7471 7472 return false; 7473 7474 } 7475 7476 7477 /************************************************************************************************** 7478 * Get_Input_Key_State 7479 * 7480 * History: 6/27/2019 - JAS 7481 **************************************************************************************************/ 7482 bool DLL_Export_Get_Input_Key_State(KeyNumType key) 7483 { 7484 return DLLExportClass::Get_Input_Key_State(key); 7485 } 7486 7487 7488 7489 bool DLLSave(FileClass &file) 7490 { 7491 return DLLExportClass::Save(file); 7492 } 7493 7494 bool DLLLoad(FileClass &file) 7495 { 7496 return DLLExportClass::Load(file); 7497 } 7498 7499 7500 /************************************************************************************************** 7501 * DLLExportClass::Save -- 7502 * 7503 * In: 7504 * 7505 * Out: 7506 * 7507 * 7508 * 7509 * History: 9/10/2019 10:24AM - ST 7510 **************************************************************************************************/ 7511 bool DLLExportClass::Save(FileClass & file) 7512 { 7513 /* 7514 ** Version first 7515 */ 7516 unsigned int version = CNC_DLL_API_VERSION; 7517 if (file.Write(&version, sizeof(version)) != sizeof(version)) { 7518 return false; 7519 } 7520 7521 if (file.Write(MultiplayerStartPositions, sizeof(MultiplayerStartPositions)) != sizeof(MultiplayerStartPositions)) { 7522 return false; 7523 } 7524 7525 if (file.Write(GlyphxPlayerIDs, sizeof(GlyphxPlayerIDs)) != sizeof(GlyphxPlayerIDs)) { 7526 return false; 7527 } 7528 7529 if (file.Write(&GlyphXClientSidebarWidthInLeptons, sizeof(GlyphXClientSidebarWidthInLeptons)) != sizeof(GlyphXClientSidebarWidthInLeptons)) { 7530 return false; 7531 } 7532 7533 if (file.Write(MPlayerIsHuman, sizeof(MPlayerIsHuman)) != sizeof(MPlayerIsHuman)) { 7534 return false; 7535 } 7536 7537 if (file.Write(MultiplayerStartPositions, sizeof(MultiplayerStartPositions)) != sizeof(MultiplayerStartPositions)) { 7538 return false; 7539 } 7540 7541 if (file.Write(PlacementType, sizeof(PlacementType)) != sizeof(PlacementType)) { 7542 return false; 7543 } 7544 7545 if (file.Write(&MPlayerCount, sizeof(MPlayerCount)) != sizeof(MPlayerCount)) { 7546 return false; 7547 } 7548 7549 if (file.Write(&MPlayerBases, sizeof(MPlayerBases)) != sizeof(MPlayerBases)) { 7550 return false; 7551 } 7552 7553 if (file.Write(&MPlayerCredits, sizeof(MPlayerCredits)) != sizeof(MPlayerCredits)) { 7554 return false; 7555 } 7556 7557 if (file.Write(&MPlayerTiberium, sizeof(MPlayerTiberium)) != sizeof(MPlayerTiberium)) { 7558 return false; 7559 } 7560 7561 if (file.Write(&MPlayerGoodies, sizeof(MPlayerGoodies)) != sizeof(MPlayerGoodies)) { 7562 return false; 7563 } 7564 7565 if (file.Write(&MPlayerGhosts, sizeof(MPlayerGhosts)) != sizeof(MPlayerGhosts)) { 7566 return false; 7567 } 7568 7569 if (file.Write(&MPlayerSolo, sizeof(MPlayerSolo)) != sizeof(MPlayerSolo)) { 7570 return false; 7571 } 7572 7573 if (file.Write(&MPlayerUnitCount, sizeof(MPlayerUnitCount)) != sizeof(MPlayerUnitCount)) { 7574 return false; 7575 } 7576 7577 if (file.Write(&MPlayerLocalID, sizeof(MPlayerLocalID)) != sizeof(MPlayerLocalID)) { 7578 return false; 7579 } 7580 7581 if (file.Write(MPlayerHouses, sizeof(MPlayerHouses)) != sizeof(MPlayerHouses)) { 7582 return false; 7583 } 7584 7585 if (file.Write(MPlayerNames, sizeof(MPlayerNames)) != sizeof(MPlayerNames)) { 7586 return false; 7587 } 7588 7589 if (file.Write(MPlayerID, sizeof(MPlayerID)) != sizeof(MPlayerID)) { 7590 return false; 7591 } 7592 7593 if (file.Write(MPlayerIsHuman, sizeof(MPlayerIsHuman)) != sizeof(MPlayerIsHuman)) { 7594 return false; 7595 } 7596 7597 for (int i=0 ; i<MAX_PLAYERS ; i++) { 7598 Sidebar_Glyphx_Save(file, &MultiplayerSidebars[i]); 7599 } 7600 7601 if (file.Write(&Special, sizeof(Special)) != sizeof(Special)) { 7602 return false; 7603 } 7604 7605 /* 7606 ** Special case for Rule.AllowSuperWeapons - store negated value so it defaults to enabled 7607 */ 7608 bool not_allow_super_weapons = !Rule.AllowSuperWeapons; 7609 if (file.Write(¬_allow_super_weapons, sizeof(not_allow_super_weapons)) != sizeof(not_allow_super_weapons)) { 7610 return false; 7611 } 7612 7613 /* 7614 ** Room for save game expansion 7615 */ 7616 unsigned char padding[4095]; 7617 memset(padding, 0, sizeof(padding)); 7618 7619 if (file.Write(padding, sizeof(padding)) != sizeof(padding)) { 7620 return false; 7621 } 7622 7623 7624 return true; 7625 } 7626 7627 7628 /************************************************************************************************** 7629 * DLLExportClass::Load -- 7630 * 7631 * In: 7632 * 7633 * Out: 7634 * 7635 * 7636 * 7637 * History: 9/10/2019 10:24AM - ST 7638 **************************************************************************************************/ 7639 bool DLLExportClass::Load(FileClass & file) 7640 { 7641 unsigned int version = 0; 7642 7643 if (file.Read(&version, sizeof(version)) != sizeof(version)) { 7644 return false; 7645 } 7646 7647 if (file.Read(MultiplayerStartPositions, sizeof(MultiplayerStartPositions)) != sizeof(MultiplayerStartPositions)) { 7648 return false; 7649 } 7650 7651 if (file.Read(GlyphxPlayerIDs, sizeof(GlyphxPlayerIDs)) != sizeof(GlyphxPlayerIDs)) { 7652 return false; 7653 } 7654 7655 if (file.Read(&GlyphXClientSidebarWidthInLeptons, sizeof(GlyphXClientSidebarWidthInLeptons)) != sizeof(GlyphXClientSidebarWidthInLeptons)) { 7656 return false; 7657 } 7658 7659 if (file.Read(MPlayerIsHuman, sizeof(MPlayerIsHuman)) != sizeof(MPlayerIsHuman)) { 7660 return false; 7661 } 7662 7663 if (file.Read(MultiplayerStartPositions, sizeof(MultiplayerStartPositions)) != sizeof(MultiplayerStartPositions)) { 7664 return false; 7665 } 7666 7667 if (file.Read(PlacementType, sizeof(PlacementType)) != sizeof(PlacementType)) { 7668 return false; 7669 } 7670 7671 if (file.Read(&MPlayerCount, sizeof(MPlayerCount)) != sizeof(MPlayerCount)) { 7672 return false; 7673 } 7674 7675 if (file.Read(&MPlayerBases, sizeof(MPlayerBases)) != sizeof(MPlayerBases)) { 7676 return false; 7677 } 7678 7679 if (file.Read(&MPlayerCredits, sizeof(MPlayerCredits)) != sizeof(MPlayerCredits)) { 7680 return false; 7681 } 7682 7683 if (file.Read(&MPlayerTiberium, sizeof(MPlayerTiberium)) != sizeof(MPlayerTiberium)) { 7684 return false; 7685 } 7686 7687 if (file.Read(&MPlayerGoodies, sizeof(MPlayerGoodies)) != sizeof(MPlayerGoodies)) { 7688 return false; 7689 } 7690 7691 if (file.Read(&MPlayerGhosts, sizeof(MPlayerGhosts)) != sizeof(MPlayerGhosts)) { 7692 return false; 7693 } 7694 7695 if (file.Read(&MPlayerSolo, sizeof(MPlayerSolo)) != sizeof(MPlayerSolo)) { 7696 return false; 7697 } 7698 7699 if (file.Read(&MPlayerUnitCount, sizeof(MPlayerUnitCount)) != sizeof(MPlayerUnitCount)) { 7700 return false; 7701 } 7702 7703 if (file.Read(&MPlayerLocalID, sizeof(MPlayerLocalID)) != sizeof(MPlayerLocalID)) { 7704 return false; 7705 } 7706 7707 if (file.Read(MPlayerHouses, sizeof(MPlayerHouses)) != sizeof(MPlayerHouses)) { 7708 return false; 7709 } 7710 7711 if (file.Read(MPlayerNames, sizeof(MPlayerNames)) != sizeof(MPlayerNames)) { 7712 return false; 7713 } 7714 7715 if (file.Read(MPlayerID, sizeof(MPlayerID)) != sizeof(MPlayerID)) { 7716 return false; 7717 } 7718 7719 if (file.Read(MPlayerIsHuman, sizeof(MPlayerIsHuman)) != sizeof(MPlayerIsHuman)) { 7720 return false; 7721 } 7722 7723 for (int i=0 ; i<MAX_PLAYERS ; i++) { 7724 Sidebar_Glyphx_Load(file, &MultiplayerSidebars[i]); 7725 } 7726 7727 if (file.Read(&Special, sizeof(Special)) != sizeof(Special)) { 7728 return false; 7729 } 7730 7731 /* 7732 ** Special case for Rule.AllowSuperWeapons - store negated value so it defaults to enabled 7733 */ 7734 bool not_allow_super_weapons = false; 7735 if (file.Read(¬_allow_super_weapons, sizeof(not_allow_super_weapons)) != sizeof(not_allow_super_weapons)) { 7736 return false; 7737 } 7738 Rule.AllowSuperWeapons = !not_allow_super_weapons; 7739 7740 unsigned char padding[4095]; 7741 7742 if (file.Read(padding, sizeof(padding)) != sizeof(padding)) { 7743 return false; 7744 } 7745 7746 7747 return true; 7748 } 7749 7750 7751 7752 /************************************************************************************************** 7753 * DLLExportClass::Code_Pointers -- 7754 * 7755 * In: 7756 * 7757 * Out: 7758 * 7759 * 7760 * 7761 * History: 9/10/2019 10:24AM - ST 7762 **************************************************************************************************/ 7763 void DLLExportClass::Code_Pointers(void) 7764 { 7765 for (int i=0 ; i<MAX_PLAYERS ; i++) { 7766 Sidebar_Glyphx_Code_Pointers(&MultiplayerSidebars[i]); 7767 7768 if (PlacementType[i]) { 7769 PlacementType[i] = (BuildingTypeClass *) PlacementType[i]->Type; 7770 } 7771 } 7772 } 7773 7774 7775 7776 /************************************************************************************************** 7777 * DLLExportClass::Decode_Pointers -- 7778 * 7779 * In: 7780 * 7781 * Out: 7782 * 7783 * 7784 * 7785 * History: 9/10/2019 10:24AM - ST 7786 **************************************************************************************************/ 7787 void DLLExportClass::Decode_Pointers(void) 7788 { 7789 for (int i=0 ; i<MAX_PLAYERS ; i++) { 7790 Sidebar_Glyphx_Decode_Pointers(&MultiplayerSidebars[i]); 7791 7792 if (PlacementType[i]) { 7793 StructType type = (StructType) reinterpret_cast<unsigned int>(PlacementType[i]); 7794 PlacementType[i] = NULL; 7795 if (type >= STRUCT_FIRST && type < STRUCT_COUNT) { 7796 7797 TechnoTypeClass const * tech = Fetch_Techno_Type(RTTI_BUILDINGTYPE, type); 7798 if (tech) { 7799 BuildingTypeClass* build_type = (BuildingTypeClass*)(tech); 7800 if (build_type) { 7801 PlacementType[i] = build_type; 7802 } 7803 } 7804 } 7805 } 7806 } 7807 } 7808 7809 7810 void DLL_Code_Pointers(void) 7811 { 7812 DLLExportClass::Code_Pointers(); 7813 } 7814 7815 void DLL_Decode_Pointers(void) 7816 { 7817 DLLExportClass::Decode_Pointers(); 7818 }