CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

DLLInterface.cpp (257887B)


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